mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 15:09:39 +00:00
components/esm header refactoring in progress. Refactored:
esm_reader.hpp loadacti.hpp loadalch.hpp loadappa.hpp loadarmo.hpp loadbody.hpp loadbook.hpp loadbsgn.hpp loadcell.hpp loadclas.hpp loadclot.hpp loadland.hpp Updated code style in defs.hpp
This commit is contained in:
parent
4e7a056e9e
commit
a2c42ab5a2
26 changed files with 942 additions and 762 deletions
|
@ -131,6 +131,19 @@ set(ESM
|
||||||
${COMP_DIR}/esm/skill.cpp
|
${COMP_DIR}/esm/skill.cpp
|
||||||
${COMP_DIR}/esm/attr.cpp
|
${COMP_DIR}/esm/attr.cpp
|
||||||
${COMP_DIR}/esm/class.cpp
|
${COMP_DIR}/esm/class.cpp
|
||||||
|
${COMP_DIR}/esm/esm_reader.cpp
|
||||||
|
${COMP_DIR}/esm/loadland.cpp
|
||||||
|
${COMP_DIR}/esm/loadacti.cpp
|
||||||
|
${COMP_DIR}/esm/loadalch.cpp
|
||||||
|
${COMP_DIR}/esm/loadappa.cpp
|
||||||
|
${COMP_DIR}/esm/loadarmo.cpp
|
||||||
|
${COMP_DIR}/esm/loadbody.cpp
|
||||||
|
${COMP_DIR}/esm/loadbook.cpp
|
||||||
|
${COMP_DIR}/esm/loadbsgn.cpp
|
||||||
|
${COMP_DIR}/esm/loadcell.cpp
|
||||||
|
${COMP_DIR}/esm/loadclas.cpp
|
||||||
|
${COMP_DIR}/esm/loadclot.cpp
|
||||||
|
|
||||||
)
|
)
|
||||||
source_group(components\\esm FILES ${ESM_HEADER} ${ESM})
|
source_group(components\\esm FILES ${ESM_HEADER} ${ESM})
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
|
// Pixel color value. Standard four-byte rr,gg,bb,aa format.
|
||||||
typedef int32_t Color;
|
typedef int32_t Color;
|
||||||
|
|
||||||
enum VarType
|
enum VarType
|
||||||
{
|
{
|
||||||
VT_Unknown,
|
VT_Unknown,
|
||||||
VT_None,
|
VT_None,
|
||||||
VT_Short,
|
VT_Short,
|
||||||
|
@ -18,79 +19,75 @@ enum VarType
|
||||||
VT_Float,
|
VT_Float,
|
||||||
VT_String,
|
VT_String,
|
||||||
VT_Ignored
|
VT_Ignored
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Specialization
|
enum Specialization
|
||||||
{
|
{
|
||||||
SPC_Combat = 0,
|
SPC_Combat = 0, SPC_Magic = 1, SPC_Stealth = 2
|
||||||
SPC_Magic = 1,
|
};
|
||||||
SPC_Stealth = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RangeType
|
enum RangeType
|
||||||
{
|
{
|
||||||
RT_Self = 0,
|
RT_Self = 0, RT_Touch = 1, RT_Target = 2
|
||||||
RT_Touch = 1,
|
};
|
||||||
RT_Target = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/** A list of references to spells and spell effects. This is shared
|
/** A list of references to spells and spell effects. This is shared
|
||||||
between the records BSGN, NPC and RACE.
|
between the records BSGN, NPC and RACE.
|
||||||
*/
|
*/
|
||||||
struct SpellList
|
struct SpellList
|
||||||
{
|
{
|
||||||
std::vector<std::string> list;
|
std::vector<std::string> list;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
while(esm.isNextSub("NPCS"))
|
while (esm.isNextSub("NPCS"))
|
||||||
list.push_back(esm.getHString());
|
list.push_back(esm.getHString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Defines a spell effect. Shared between SPEL (Spells), ALCH
|
/** Defines a spell effect. Shared between SPEL (Spells), ALCH
|
||||||
(Potions) and ENCH (Item enchantments) records
|
(Potions) and ENCH (Item enchantments) records
|
||||||
*/
|
*/
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
// Position and rotation
|
// Position and rotation
|
||||||
struct Position
|
struct Position
|
||||||
{
|
{
|
||||||
float pos[3];
|
float pos[3];
|
||||||
float rot[3];
|
float rot[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ENAMstruct
|
struct ENAMstruct
|
||||||
{
|
{
|
||||||
// Magical effect, hard-coded ID
|
// Magical effect, hard-coded ID
|
||||||
short effectID;
|
short effectID;
|
||||||
|
|
||||||
// Which skills/attributes are affected (for restore/drain spells
|
// Which skills/attributes are affected (for restore/drain spells
|
||||||
// etc.)
|
// etc.)
|
||||||
signed char skill, attribute; // -1 if N/A
|
signed char skill, attribute; // -1 if N/A
|
||||||
|
|
||||||
// Other spell parameters
|
// Other spell parameters
|
||||||
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
int range; // 0 - self, 1 - touch, 2 - target (RangeType enum)
|
||||||
int area, duration, magnMin, magnMax;
|
int area, duration, magnMin, magnMax;
|
||||||
|
|
||||||
// Struct size should be 24 bytes
|
// Struct size should be 24 bytes
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct EffectList
|
struct EffectList
|
||||||
{
|
{
|
||||||
std::vector<ENAMstruct> list;
|
std::vector<ENAMstruct> list;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
ENAMstruct s;
|
ENAMstruct s;
|
||||||
while(esm.isNextSub("ENAM"))
|
while (esm.isNextSub("ENAM"))
|
||||||
{
|
{
|
||||||
esm.getHT(s, 24);
|
esm.getHT(s, 24);
|
||||||
list.push_back(s);
|
list.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
345
components/esm/esm_reader.cpp
Normal file
345
components/esm/esm_reader.cpp
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
ESM_Context ESMReader::getContext()
|
||||||
|
{
|
||||||
|
// Update the file position before returning
|
||||||
|
mCtx.filePos = mEsm->tell();
|
||||||
|
return mCtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::restoreContext(const ESM_Context &rc)
|
||||||
|
{
|
||||||
|
// Reopen the file if necessary
|
||||||
|
if (mCtx.filename != rc.filename)
|
||||||
|
openRaw(rc.filename);
|
||||||
|
|
||||||
|
// Copy the data
|
||||||
|
mCtx = rc;
|
||||||
|
|
||||||
|
// Make sure we seek to the right place
|
||||||
|
mEsm->seek(mCtx.filePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::close()
|
||||||
|
{
|
||||||
|
mEsm.reset();
|
||||||
|
mCtx.filename.clear();
|
||||||
|
mCtx.leftFile = 0;
|
||||||
|
mCtx.leftRec = 0;
|
||||||
|
mCtx.leftSub = 0;
|
||||||
|
mCtx.subCached = false;
|
||||||
|
mCtx.recName.val = 0;
|
||||||
|
mCtx.subName.val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
mEsm = _esm;
|
||||||
|
mCtx.filename = name;
|
||||||
|
mCtx.leftFile = mEsm->size();
|
||||||
|
|
||||||
|
// Flag certain files for special treatment, based on the file
|
||||||
|
// name.
|
||||||
|
const char *cstr = mCtx.filename.c_str();
|
||||||
|
if (iends(cstr, "Morrowind.esm"))
|
||||||
|
mSpf = SF_Morrowind;
|
||||||
|
else if (iends(cstr, "Tribunal.esm"))
|
||||||
|
mSpf = SF_Tribunal;
|
||||||
|
else if (iends(cstr, "Bloodmoon.esm"))
|
||||||
|
mSpf = SF_Bloodmoon;
|
||||||
|
else
|
||||||
|
mSpf = SF_Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||||
|
{
|
||||||
|
openRaw(_esm, name);
|
||||||
|
|
||||||
|
if (getRecName() != "TES3")
|
||||||
|
fail("Not a valid Morrowind file");
|
||||||
|
|
||||||
|
getRecHeader();
|
||||||
|
|
||||||
|
// Get the header
|
||||||
|
getHNT(mCtx.header, "HEDR", 300);
|
||||||
|
|
||||||
|
if (mCtx.header.version != VER_12 && mCtx.header.version != VER_13)
|
||||||
|
fail("Unsupported file format version");
|
||||||
|
|
||||||
|
while (isNextSub("MAST"))
|
||||||
|
{
|
||||||
|
MasterData m;
|
||||||
|
m.name = getHString();
|
||||||
|
m.size = getHNLong("DATA");
|
||||||
|
mMasters.push_back(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCtx.header.type == FT_ESS)
|
||||||
|
{
|
||||||
|
// Savegame-related data
|
||||||
|
|
||||||
|
// Player position etc
|
||||||
|
getHNT(mSaveData, "GMDT", 124);
|
||||||
|
|
||||||
|
/* Image properties, five ints. Is always:
|
||||||
|
Red-mask: 0xff0000
|
||||||
|
Blue-mask: 0x00ff00
|
||||||
|
Green-mask: 0x0000ff
|
||||||
|
Alpha-mask: 0x000000
|
||||||
|
Bpp: 32
|
||||||
|
*/
|
||||||
|
getSubNameIs("SCRD");
|
||||||
|
skipHSubSize(20);
|
||||||
|
|
||||||
|
/* Savegame screenshot:
|
||||||
|
128x128 pixels * 4 bytes per pixel
|
||||||
|
*/
|
||||||
|
getSubNameIs("SCRS");
|
||||||
|
skipHSubSize(65536);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::open(const std::string &file)
|
||||||
|
{
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
open(StreamPtr(new FileStream(file)), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::openRaw(const std::string &file)
|
||||||
|
{
|
||||||
|
using namespace Mangle::Stream;
|
||||||
|
openRaw(StreamPtr(new FileStream(file)), file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ESMReader::getHNLong(const char *name)
|
||||||
|
{
|
||||||
|
int64_t val;
|
||||||
|
getHNT(val, name);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHNOString(const char* name)
|
||||||
|
{
|
||||||
|
if (isNextSub(name))
|
||||||
|
return getHString();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHNString(const char* name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
return getHString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getHString()
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
|
||||||
|
// Hack to make MultiMark.esp load. Zero-length strings do not
|
||||||
|
// occur in any of the official mods, but MultiMark makes use of
|
||||||
|
// them. For some reason, they break the rules, and contain a byte
|
||||||
|
// (value 0) even if the header says there is no data. If
|
||||||
|
// Morrowind accepts it, so should we.
|
||||||
|
if (mCtx.leftSub == 0)
|
||||||
|
{
|
||||||
|
// Skip the following zero byte
|
||||||
|
mCtx.leftRec--;
|
||||||
|
char c;
|
||||||
|
mEsm->read(&c, 1);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return getString(mCtx.leftSub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getHExact(void*p, int size)
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
if (size != static_cast<int> (mCtx.leftSub))
|
||||||
|
fail("getHExact() size mismatch");
|
||||||
|
getExact(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the given number of bytes from a named subrecord
|
||||||
|
void ESMReader::getHNExact(void*p, int size, const char* name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
getHExact(p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next subrecord name and check if it matches the parameter
|
||||||
|
void ESMReader::getSubNameIs(const char* name)
|
||||||
|
{
|
||||||
|
getSubName();
|
||||||
|
if (mCtx.subName != name)
|
||||||
|
fail(
|
||||||
|
"Expected subrecord " + std::string(name) + " but got "
|
||||||
|
+ mCtx.subName.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESMReader::isNextSub(const char* name)
|
||||||
|
{
|
||||||
|
if (!mCtx.leftRec)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
getSubName();
|
||||||
|
|
||||||
|
// If the name didn't match, then mark the it as 'cached' so it's
|
||||||
|
// available for the next call to getSubName.
|
||||||
|
mCtx.subCached = (mCtx.subName != name);
|
||||||
|
|
||||||
|
// If subCached is false, then subName == name.
|
||||||
|
return !mCtx.subCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||||
|
// slightly.
|
||||||
|
void ESMReader::getSubName()
|
||||||
|
{
|
||||||
|
// If the name has already been read, do nothing
|
||||||
|
if (mCtx.subCached)
|
||||||
|
{
|
||||||
|
mCtx.subCached = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reading the subrecord data anyway.
|
||||||
|
mEsm->read(mCtx.subName.name, 4);
|
||||||
|
mCtx.leftRec -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESMReader::isEmptyOrGetName()
|
||||||
|
{
|
||||||
|
if (mCtx.leftRec)
|
||||||
|
{
|
||||||
|
mEsm->read(mCtx.subName.name, 4);
|
||||||
|
mCtx.leftRec -= 4;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHSub()
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
skip(mCtx.leftSub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHSubSize(int size)
|
||||||
|
{
|
||||||
|
skipHSub();
|
||||||
|
if (static_cast<int> (mCtx.leftSub) != size)
|
||||||
|
fail("skipHSubSize() mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getSubHeader()
|
||||||
|
{
|
||||||
|
if (mCtx.leftRec < 4)
|
||||||
|
fail("End of record while reading sub-record header");
|
||||||
|
|
||||||
|
// Get subrecord size
|
||||||
|
getT(mCtx.leftSub);
|
||||||
|
|
||||||
|
// Adjust number of record bytes left
|
||||||
|
mCtx.leftRec -= mCtx.leftSub + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getSubHeaderIs(int size)
|
||||||
|
{
|
||||||
|
getSubHeader();
|
||||||
|
if (size != static_cast<int> (mCtx.leftSub))
|
||||||
|
fail("getSubHeaderIs(): Sub header mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
NAME ESMReader::getRecName()
|
||||||
|
{
|
||||||
|
if (!hasMoreRecs())
|
||||||
|
fail("No more records, getRecName() failed");
|
||||||
|
getName(mCtx.recName);
|
||||||
|
mCtx.leftFile -= 4;
|
||||||
|
|
||||||
|
// Make sure we don't carry over any old cached subrecord
|
||||||
|
// names. This can happen in some cases when we skip parts of a
|
||||||
|
// record.
|
||||||
|
mCtx.subCached = false;
|
||||||
|
|
||||||
|
return mCtx.recName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipRecord()
|
||||||
|
{
|
||||||
|
skip(mCtx.leftRec);
|
||||||
|
mCtx.leftRec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::skipHRecord()
|
||||||
|
{
|
||||||
|
if (!mCtx.leftFile)
|
||||||
|
return;
|
||||||
|
getRecHeader();
|
||||||
|
skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::getRecHeader(uint32_t &flags)
|
||||||
|
{
|
||||||
|
// General error checking
|
||||||
|
if (mCtx.leftFile < 12)
|
||||||
|
fail("End of file while reading record header");
|
||||||
|
if (mCtx.leftRec)
|
||||||
|
fail("Previous record contains unread bytes");
|
||||||
|
|
||||||
|
getUint(mCtx.leftRec);
|
||||||
|
getUint(flags);// This header entry is always zero
|
||||||
|
getUint(flags);
|
||||||
|
mCtx.leftFile -= 12;
|
||||||
|
|
||||||
|
// Check that sizes add up
|
||||||
|
if (mCtx.leftFile < mCtx.leftRec)
|
||||||
|
fail("Record size is larger than rest of file");
|
||||||
|
|
||||||
|
// Adjust number of bytes mCtx.left in file
|
||||||
|
mCtx.leftFile -= mCtx.leftRec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* Lowest level data reading and misc methods
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
void ESMReader::getExact(void*x, int size)
|
||||||
|
{
|
||||||
|
int t = mEsm->read(x, size);
|
||||||
|
if (t != size)
|
||||||
|
fail("Read error");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ESMReader::getString(int size)
|
||||||
|
{
|
||||||
|
char *ptr = ToUTF8::getBuffer(size);
|
||||||
|
mEsm->read(ptr, size);
|
||||||
|
|
||||||
|
// Convert to UTF8 and return
|
||||||
|
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESMReader::fail(const std::string &msg)
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
stringstream ss;
|
||||||
|
|
||||||
|
ss << "ESM Error: " << msg;
|
||||||
|
ss << "\n File: " << mCtx.filename;
|
||||||
|
ss << "\n Record: " << mCtx.recName.toString();
|
||||||
|
ss << "\n Subrecord: " << mCtx.subName.toString();
|
||||||
|
if (mEsm != NULL)
|
||||||
|
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
||||||
|
throw std::runtime_error(ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -171,122 +171,27 @@ public:
|
||||||
/** Save the current file position and information in a ESM_Context
|
/** Save the current file position and information in a ESM_Context
|
||||||
struct
|
struct
|
||||||
*/
|
*/
|
||||||
ESM_Context getContext()
|
ESM_Context getContext();
|
||||||
{
|
|
||||||
// Update the file position before returning
|
|
||||||
mCtx.filePos = mEsm->tell();
|
|
||||||
return mCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Restore a previously saved context */
|
/** Restore a previously saved context */
|
||||||
void restoreContext(const ESM_Context &rc)
|
void restoreContext(const ESM_Context &rc);
|
||||||
{
|
|
||||||
// Reopen the file if necessary
|
|
||||||
if(mCtx.filename != rc.filename)
|
|
||||||
openRaw(rc.filename);
|
|
||||||
|
|
||||||
// Copy the data
|
|
||||||
mCtx = rc;
|
|
||||||
|
|
||||||
// Make sure we seek to the right place
|
|
||||||
mEsm->seek(mCtx.filePos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Close the file, resets all information. After calling close()
|
/** Close the file, resets all information. After calling close()
|
||||||
the structure may be reused to load a new file.
|
the structure may be reused to load a new file.
|
||||||
*/
|
*/
|
||||||
void close()
|
void close();
|
||||||
{
|
|
||||||
mEsm.reset();
|
|
||||||
mCtx.filename.clear();
|
|
||||||
mCtx.leftFile = 0;
|
|
||||||
mCtx.leftRec = 0;
|
|
||||||
mCtx.leftSub = 0;
|
|
||||||
mCtx.subCached = false;
|
|
||||||
mCtx.recName.val = 0;
|
|
||||||
mCtx.subName.val = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raw opening. Opens the file and sets everything up but doesn't
|
/// Raw opening. Opens the file and sets everything up but doesn't
|
||||||
/// parse the header.
|
/// parse the header.
|
||||||
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
void openRaw(Mangle::Stream::StreamPtr _esm, const std::string &name);
|
||||||
{
|
|
||||||
close();
|
|
||||||
mEsm = _esm;
|
|
||||||
mCtx.filename = name;
|
|
||||||
mCtx.leftFile = mEsm->size();
|
|
||||||
|
|
||||||
// Flag certain files for special treatment, based on the file
|
|
||||||
// name.
|
|
||||||
const char *cstr = mCtx.filename.c_str();
|
|
||||||
if(iends(cstr, "Morrowind.esm")) mSpf = SF_Morrowind;
|
|
||||||
else if(iends(cstr, "Tribunal.esm")) mSpf = SF_Tribunal;
|
|
||||||
else if(iends(cstr, "Bloodmoon.esm")) mSpf = SF_Bloodmoon;
|
|
||||||
else mSpf = SF_Other;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load ES file from a new stream, parses the header. Closes the
|
/// Load ES file from a new stream, parses the header. Closes the
|
||||||
/// currently open file first, if any.
|
/// currently open file first, if any.
|
||||||
void open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
void open(Mangle::Stream::StreamPtr _esm, const std::string &name);
|
||||||
{
|
|
||||||
openRaw(_esm, name);
|
|
||||||
|
|
||||||
if(getRecName() != "TES3")
|
void open(const std::string &file);
|
||||||
fail("Not a valid Morrowind file");
|
|
||||||
|
|
||||||
getRecHeader();
|
void openRaw(const std::string &file);
|
||||||
|
|
||||||
// Get the header
|
|
||||||
getHNT(mCtx.header, "HEDR", 300);
|
|
||||||
|
|
||||||
if(mCtx.header.version != VER_12 &&
|
|
||||||
mCtx.header.version != VER_13)
|
|
||||||
fail("Unsupported file format version");
|
|
||||||
|
|
||||||
while(isNextSub("MAST"))
|
|
||||||
{
|
|
||||||
MasterData m;
|
|
||||||
m.name = getHString();
|
|
||||||
m.size = getHNLong("DATA");
|
|
||||||
mMasters.push_back(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mCtx.header.type == FT_ESS)
|
|
||||||
{
|
|
||||||
// Savegame-related data
|
|
||||||
|
|
||||||
// Player position etc
|
|
||||||
getHNT(mSaveData, "GMDT", 124);
|
|
||||||
|
|
||||||
/* Image properties, five ints. Is always:
|
|
||||||
Red-mask: 0xff0000
|
|
||||||
Blue-mask: 0x00ff00
|
|
||||||
Green-mask: 0x0000ff
|
|
||||||
Alpha-mask: 0x000000
|
|
||||||
Bpp: 32
|
|
||||||
*/
|
|
||||||
getSubNameIs("SCRD");
|
|
||||||
skipHSubSize(20);
|
|
||||||
|
|
||||||
/* Savegame screenshot:
|
|
||||||
128x128 pixels * 4 bytes per pixel
|
|
||||||
*/
|
|
||||||
getSubNameIs("SCRS");
|
|
||||||
skipHSubSize(65536);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void open(const std::string &file)
|
|
||||||
{
|
|
||||||
using namespace Mangle::Stream;
|
|
||||||
open(StreamPtr(new FileStream(file)), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void openRaw(const std::string &file)
|
|
||||||
{
|
|
||||||
using namespace Mangle::Stream;
|
|
||||||
openRaw(StreamPtr(new FileStream(file)), file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -306,8 +211,8 @@ public:
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHNOT(X &x, const char* name)
|
void getHNOT(X &x, const char* name)
|
||||||
{
|
{
|
||||||
if(isNextSub(name))
|
if(isNextSub(name))
|
||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version with extra size checking, to make sure the compiler
|
// Version with extra size checking, to make sure the compiler
|
||||||
|
@ -315,26 +220,21 @@ public:
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHNT(X &x, const char* name, int size)
|
void getHNT(X &x, const char* name, int size)
|
||||||
{
|
{
|
||||||
assert(sizeof(X) == size);
|
assert(sizeof(X) == size);
|
||||||
getSubNameIs(name);
|
getSubNameIs(name);
|
||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t getHNLong(const char *name)
|
int64_t getHNLong(const char *name);
|
||||||
{
|
|
||||||
int64_t val;
|
|
||||||
getHNT(val, name);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get data of a given type/size, including subrecord header
|
// Get data of a given type/size, including subrecord header
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHT(X &x)
|
void getHT(X &x)
|
||||||
{
|
{
|
||||||
getSubHeader();
|
getSubHeader();
|
||||||
if(mCtx.leftSub != sizeof(X))
|
if (mCtx.leftSub != sizeof(X))
|
||||||
fail("getHT(): subrecord size mismatch");
|
fail("getHT(): subrecord size mismatch");
|
||||||
getT(x);
|
getT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Version with extra size checking, to make sure the compiler
|
// Version with extra size checking, to make sure the compiler
|
||||||
|
@ -342,62 +242,24 @@ public:
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getHT(X &x, int size)
|
void getHT(X &x, int size)
|
||||||
{
|
{
|
||||||
assert(sizeof(X) == size);
|
assert(sizeof(X) == size);
|
||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a string by the given name if it is the next record.
|
// Read a string by the given name if it is the next record.
|
||||||
std::string getHNOString(const char* name)
|
std::string getHNOString(const char* name);
|
||||||
{
|
|
||||||
if(isNextSub(name))
|
|
||||||
return getHString();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a string with the given sub-record name
|
// Read a string with the given sub-record name
|
||||||
std::string getHNString(const char* name)
|
std::string getHNString(const char* name);
|
||||||
{
|
|
||||||
getSubNameIs(name);
|
|
||||||
return getHString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a string, including the sub-record header (but not the name)
|
// Read a string, including the sub-record header (but not the name)
|
||||||
std::string getHString()
|
std::string getHString();
|
||||||
{
|
|
||||||
getSubHeader();
|
|
||||||
|
|
||||||
// Hack to make MultiMark.esp load. Zero-length strings do not
|
|
||||||
// occur in any of the official mods, but MultiMark makes use of
|
|
||||||
// them. For some reason, they break the rules, and contain a byte
|
|
||||||
// (value 0) even if the header says there is no data. If
|
|
||||||
// Morrowind accepts it, so should we.
|
|
||||||
if(mCtx.leftSub == 0)
|
|
||||||
{
|
|
||||||
// Skip the following zero byte
|
|
||||||
mCtx.leftRec--;
|
|
||||||
char c;
|
|
||||||
mEsm->read(&c,1);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return getString(mCtx.leftSub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the given number of bytes from a subrecord
|
// Read the given number of bytes from a subrecord
|
||||||
void getHExact(void*p, int size)
|
void getHExact(void*p, int size);
|
||||||
{
|
|
||||||
getSubHeader();
|
|
||||||
if(size !=static_cast<int> (mCtx.leftSub))
|
|
||||||
fail("getHExact() size mismatch");
|
|
||||||
getExact(p,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the given number of bytes from a named subrecord
|
// Read the given number of bytes from a named subrecord
|
||||||
void getHNExact(void*p, int size, const char* name)
|
void getHNExact(void*p, int size, const char* name);
|
||||||
{
|
|
||||||
getSubNameIs(name);
|
|
||||||
getHExact(p,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -406,100 +268,37 @@ public:
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
// Get the next subrecord name and check if it matches the parameter
|
// Get the next subrecord name and check if it matches the parameter
|
||||||
void getSubNameIs(const char* name)
|
void getSubNameIs(const char* name);
|
||||||
{
|
|
||||||
getSubName();
|
|
||||||
if(mCtx.subName != name)
|
|
||||||
fail("Expected subrecord " + std::string(name) + " but got " + mCtx.subName.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Checks if the next sub record name matches the parameter. If it
|
/** Checks if the next sub record name matches the parameter. If it
|
||||||
does, it is read into 'subName' just as if getSubName() was
|
does, it is read into 'subName' just as if getSubName() was
|
||||||
called. If not, the read name will still be available for future
|
called. If not, the read name will still be available for future
|
||||||
calls to getSubName(), isNextSub() and getSubNameIs().
|
calls to getSubName(), isNextSub() and getSubNameIs().
|
||||||
*/
|
*/
|
||||||
bool isNextSub(const char* name)
|
bool isNextSub(const char* name);
|
||||||
{
|
|
||||||
if(!mCtx.leftRec) return false;
|
|
||||||
|
|
||||||
getSubName();
|
|
||||||
|
|
||||||
// If the name didn't match, then mark the it as 'cached' so it's
|
|
||||||
// available for the next call to getSubName.
|
|
||||||
mCtx.subCached = (mCtx.subName != name);
|
|
||||||
|
|
||||||
// If subCached is false, then subName == name.
|
|
||||||
return !mCtx.subCached;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read subrecord name. This gets called a LOT, so I've optimized it
|
// Read subrecord name. This gets called a LOT, so I've optimized it
|
||||||
// slightly.
|
// slightly.
|
||||||
void getSubName()
|
void getSubName();
|
||||||
{
|
|
||||||
// If the name has already been read, do nothing
|
|
||||||
if(mCtx.subCached)
|
|
||||||
{
|
|
||||||
mCtx.subCached = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't bother with error checking, we will catch an EOF upon
|
|
||||||
// reading the subrecord data anyway.
|
|
||||||
mEsm->read(mCtx.subName.name, 4);
|
|
||||||
mCtx.leftRec -= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is specially optimized for LoadINFO.
|
// This is specially optimized for LoadINFO.
|
||||||
bool isEmptyOrGetName()
|
bool isEmptyOrGetName();
|
||||||
{
|
|
||||||
if(mCtx.leftRec)
|
|
||||||
{
|
|
||||||
mEsm->read(mCtx.subName.name, 4);
|
|
||||||
mCtx.leftRec -= 4;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip current sub record, including header (but not including
|
// Skip current sub record, including header (but not including
|
||||||
// name.)
|
// name.)
|
||||||
void skipHSub()
|
void skipHSub();
|
||||||
{
|
|
||||||
getSubHeader();
|
|
||||||
skip(mCtx.leftSub);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip sub record and check its size
|
// Skip sub record and check its size
|
||||||
void skipHSubSize(int size)
|
void skipHSubSize(int size);
|
||||||
{
|
|
||||||
skipHSub();
|
|
||||||
if(static_cast<int> (mCtx.leftSub) != size)
|
|
||||||
fail("skipHSubSize() mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sub-record header. This updates leftRec beyond the current
|
/* Sub-record header. This updates leftRec beyond the current
|
||||||
sub-record as well. leftSub contains size of current sub-record.
|
sub-record as well. leftSub contains size of current sub-record.
|
||||||
*/
|
*/
|
||||||
void getSubHeader()
|
void getSubHeader();
|
||||||
{
|
|
||||||
if(mCtx.leftRec < 4)
|
|
||||||
fail("End of record while reading sub-record header");
|
|
||||||
|
|
||||||
// Get subrecord size
|
|
||||||
getT(mCtx.leftSub);
|
|
||||||
|
|
||||||
// Adjust number of record bytes left
|
|
||||||
mCtx.leftRec -= mCtx.leftSub + 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get sub header and check the size
|
/** Get sub header and check the size
|
||||||
*/
|
*/
|
||||||
void getSubHeaderIs(int size)
|
void getSubHeaderIs(int size);
|
||||||
{
|
|
||||||
getSubHeader();
|
|
||||||
if(size != static_cast<int> (mCtx.leftSub))
|
|
||||||
fail("getSubHeaderIs(): Sub header mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -508,62 +307,21 @@ public:
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
// Get the next record name
|
// Get the next record name
|
||||||
NAME getRecName()
|
NAME getRecName();
|
||||||
{
|
|
||||||
if(!hasMoreRecs())
|
|
||||||
fail("No more records, getRecName() failed");
|
|
||||||
getName(mCtx.recName);
|
|
||||||
mCtx.leftFile -= 4;
|
|
||||||
|
|
||||||
// Make sure we don't carry over any old cached subrecord
|
|
||||||
// names. This can happen in some cases when we skip parts of a
|
|
||||||
// record.
|
|
||||||
mCtx.subCached = false;
|
|
||||||
|
|
||||||
return mCtx.recName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip the rest of this record. Assumes the name and header have
|
// Skip the rest of this record. Assumes the name and header have
|
||||||
// already been read
|
// already been read
|
||||||
void skipRecord()
|
void skipRecord();
|
||||||
{
|
|
||||||
skip(mCtx.leftRec);
|
|
||||||
mCtx.leftRec = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip an entire record, including the header (but not the name)
|
// Skip an entire record, including the header (but not the name)
|
||||||
void skipHRecord()
|
void skipHRecord();
|
||||||
{
|
|
||||||
if(!mCtx.leftFile) return;
|
|
||||||
getRecHeader();
|
|
||||||
skipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read record header. This updatesleftFile BEYOND the data that
|
/* Read record header. This updatesleftFile BEYOND the data that
|
||||||
follows the header, ie beyond the entire record. You should use
|
follows the header, ie beyond the entire record. You should use
|
||||||
leftRec to orient yourself inside the record itself.
|
leftRec to orient yourself inside the record itself.
|
||||||
*/
|
*/
|
||||||
void getRecHeader() { uint32_t u; getRecHeader(u); }
|
void getRecHeader() { uint32_t u; getRecHeader(u); }
|
||||||
void getRecHeader(uint32_t &flags)
|
void getRecHeader(uint32_t &flags);
|
||||||
{
|
|
||||||
// General error checking
|
|
||||||
if(mCtx.leftFile < 12)
|
|
||||||
fail("End of file while reading record header");
|
|
||||||
if(mCtx.leftRec)
|
|
||||||
fail("Previous record contains unread bytes");
|
|
||||||
|
|
||||||
getUint(mCtx.leftRec);
|
|
||||||
getUint(flags);// This header entry is always zero
|
|
||||||
getUint(flags);
|
|
||||||
mCtx.leftFile -= 12;
|
|
||||||
|
|
||||||
// Check that sizes add up
|
|
||||||
if(mCtx.leftFile < mCtx.leftRec)
|
|
||||||
fail("Record size is larger than rest of file");
|
|
||||||
|
|
||||||
// Adjust number of bytes mCtx.left in file
|
|
||||||
mCtx.leftFile -= mCtx.leftRec;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasMoreRecs() { return mCtx.leftFile > 0; }
|
bool hasMoreRecs() { return mCtx.leftFile > 0; }
|
||||||
bool hasMoreSubs() { return mCtx.leftRec > 0; }
|
bool hasMoreSubs() { return mCtx.leftRec > 0; }
|
||||||
|
@ -578,44 +336,19 @@ public:
|
||||||
template <typename X>
|
template <typename X>
|
||||||
void getT(X &x) { getExact(&x, sizeof(X)); }
|
void getT(X &x) { getExact(&x, sizeof(X)); }
|
||||||
|
|
||||||
void getExact(void*x, int size)
|
void getExact(void*x, int size);
|
||||||
{
|
|
||||||
int t = mEsm->read(x, size);
|
|
||||||
if(t != size)
|
|
||||||
fail("Read error");
|
|
||||||
}
|
|
||||||
void getName(NAME &name) { getT(name); }
|
void getName(NAME &name) { getT(name); }
|
||||||
void getUint(uint32_t &u) { getT(u); }
|
void getUint(uint32_t &u) { getT(u); }
|
||||||
|
|
||||||
// Read the next 'size' bytes and return them as a string. Converts
|
// Read the next 'size' bytes and return them as a string. Converts
|
||||||
// them from native encoding to UTF8 in the process.
|
// them from native encoding to UTF8 in the process.
|
||||||
std::string getString(int size)
|
std::string getString(int size);
|
||||||
{
|
|
||||||
char *ptr = ToUTF8::getBuffer(size);
|
|
||||||
mEsm->read(ptr,size);
|
|
||||||
|
|
||||||
// Convert to UTF8 and return
|
|
||||||
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); }
|
||||||
uint64_t getOffset() { return mEsm->tell(); }
|
uint64_t getOffset() { return mEsm->tell(); }
|
||||||
|
|
||||||
/// Used for error handling
|
/// Used for error handling
|
||||||
void fail(const std::string &msg)
|
void fail(const std::string &msg);
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
stringstream ss;
|
|
||||||
|
|
||||||
ss << "ESM Error: " << msg;
|
|
||||||
ss << "\n File: " << mCtx.filename;
|
|
||||||
ss << "\n Record: " << mCtx.recName.toString();
|
|
||||||
ss << "\n Subrecord: " << mCtx.subName.toString();
|
|
||||||
if(mEsm != NULL)
|
|
||||||
ss << "\n Offset: 0x" << hex << mEsm->tell();
|
|
||||||
throw std::runtime_error(ss.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mangle::Stream::StreamPtr mEsm;
|
Mangle::Stream::StreamPtr mEsm;
|
||||||
|
|
11
components/esm/loadacti.cpp
Normal file
11
components/esm/loadacti.cpp
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include "loadacti.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Activator::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,18 +3,14 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
struct Activator
|
struct Activator
|
||||||
{
|
{
|
||||||
std::string name, script, model;
|
std::string name, script, model;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNString("FNAM");
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
14
components/esm/loadalch.cpp
Normal file
14
components/esm/loadalch.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "loadalch.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Potion::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "ALDT", 12);
|
||||||
|
effects.load(esm);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,8 @@
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alchemy item (potions)
|
* Alchemy item (potions)
|
||||||
|
@ -12,26 +13,18 @@ namespace ESM {
|
||||||
|
|
||||||
struct Potion
|
struct Potion
|
||||||
{
|
{
|
||||||
struct ALDTstruct
|
struct ALDTstruct
|
||||||
{
|
{
|
||||||
float weight;
|
float weight;
|
||||||
int value;
|
int value;
|
||||||
int autoCalc;
|
int autoCalc;
|
||||||
};
|
};
|
||||||
ALDTstruct data;
|
ALDTstruct data;
|
||||||
|
|
||||||
std::string name, model, icon, script;
|
std::string name, model, icon, script;
|
||||||
EffectList effects;
|
EffectList effects;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
name = esm.getHNOString("FNAM");
|
|
||||||
esm.getHNT(data, "ALDT", 12);
|
|
||||||
effects.load(esm);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
13
components/esm/loadappa.cpp
Normal file
13
components/esm/loadappa.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include "loadappa.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Apparatus::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "AADT", 16);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNString("ITEX");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alchemist apparatus
|
* Alchemist apparatus
|
||||||
|
@ -11,33 +12,23 @@ namespace ESM {
|
||||||
|
|
||||||
struct Apparatus
|
struct Apparatus
|
||||||
{
|
{
|
||||||
enum AppaType
|
enum AppaType
|
||||||
{
|
{
|
||||||
MortarPestle = 0,
|
MortarPestle = 0, Albemic = 1, Calcinator = 2, Retort = 3
|
||||||
Albemic = 1,
|
|
||||||
Calcinator = 2,
|
|
||||||
Retort = 3
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AADTstruct
|
struct AADTstruct
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
float quality;
|
float quality;
|
||||||
float weight;
|
float weight;
|
||||||
int value;
|
int value;
|
||||||
};
|
};
|
||||||
|
|
||||||
AADTstruct data;
|
AADTstruct data;
|
||||||
std::string model, icon, script, name;
|
std::string model, icon, script, name;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNString("FNAM");
|
|
||||||
esm.getHNT(data, "AADT", 16);
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
icon = esm.getHNString("ITEX");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
28
components/esm/loadarmo.cpp
Normal file
28
components/esm/loadarmo.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include "loadarmo.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void PartReferenceList::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
while (esm.isNextSub("INDX"))
|
||||||
|
{
|
||||||
|
PartReference pr;
|
||||||
|
esm.getHT(pr.part); // The INDX byte
|
||||||
|
pr.male = esm.getHNOString("BNAM");
|
||||||
|
pr.female = esm.getHNOString("CNAM");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Armor::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
esm.getHNT(data, "AODT", 24);
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
parts.load(esm);
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,102 +3,85 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
enum PartReferenceType
|
enum PartReferenceType
|
||||||
{
|
{
|
||||||
PRT_Head = 0,
|
PRT_Head = 0,
|
||||||
PRT_Hair = 1,
|
PRT_Hair = 1,
|
||||||
PRT_Neck = 2,
|
PRT_Neck = 2,
|
||||||
PRT_Cuirass = 3,
|
PRT_Cuirass = 3,
|
||||||
PRT_Groin = 4,
|
PRT_Groin = 4,
|
||||||
PRT_Skirt = 5,
|
PRT_Skirt = 5,
|
||||||
PRT_RHand = 6,
|
PRT_RHand = 6,
|
||||||
PRT_LHand = 7,
|
PRT_LHand = 7,
|
||||||
PRT_RWrist = 8,
|
PRT_RWrist = 8,
|
||||||
PRT_LWrist = 9,
|
PRT_LWrist = 9,
|
||||||
PRT_Shield = 10,
|
PRT_Shield = 10,
|
||||||
PRT_RForearm = 11,
|
PRT_RForearm = 11,
|
||||||
PRT_LForearm = 12,
|
PRT_LForearm = 12,
|
||||||
PRT_RUpperarm = 13,
|
PRT_RUpperarm = 13,
|
||||||
PRT_LUpperarm = 14,
|
PRT_LUpperarm = 14,
|
||||||
PRT_RFoot = 15,
|
PRT_RFoot = 15,
|
||||||
PRT_LFoot = 16,
|
PRT_LFoot = 16,
|
||||||
PRT_RAnkle = 17,
|
PRT_RAnkle = 17,
|
||||||
PRT_LAnkle = 18,
|
PRT_LAnkle = 18,
|
||||||
PRT_RKnee = 19,
|
PRT_RKnee = 19,
|
||||||
PRT_LKnee = 20,
|
PRT_LKnee = 20,
|
||||||
PRT_RLeg = 21,
|
PRT_RLeg = 21,
|
||||||
PRT_LLeg = 22,
|
PRT_LLeg = 22,
|
||||||
PRT_RPauldron = 23,
|
PRT_RPauldron = 23,
|
||||||
PRT_LPauldron = 24,
|
PRT_LPauldron = 24,
|
||||||
PRT_Weapon = 25,
|
PRT_Weapon = 25,
|
||||||
PRT_Tail = 26
|
PRT_Tail = 26
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reference to body parts
|
// Reference to body parts
|
||||||
struct PartReference
|
struct PartReference
|
||||||
{
|
{
|
||||||
char part;
|
char part;
|
||||||
std::string male, female;
|
std::string male, female;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A list of references to body parts
|
// A list of references to body parts
|
||||||
struct PartReferenceList
|
struct PartReferenceList
|
||||||
{
|
{
|
||||||
std::vector<PartReference> parts;
|
std::vector<PartReference> parts;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
while(esm.isNextSub("INDX"))
|
|
||||||
{
|
|
||||||
PartReference pr;
|
|
||||||
esm.getHT(pr.part); // The INDX byte
|
|
||||||
pr.male = esm.getHNOString("BNAM");
|
|
||||||
pr.female = esm.getHNOString("CNAM");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Armor
|
struct Armor
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Helmet = 0,
|
Helmet = 0,
|
||||||
Cuirass = 1,
|
Cuirass = 1,
|
||||||
LPauldron = 2,
|
LPauldron = 2,
|
||||||
RPauldron = 3,
|
RPauldron = 3,
|
||||||
Greaves = 4,
|
Greaves = 4,
|
||||||
Boots = 5,
|
Boots = 5,
|
||||||
LGauntlet = 6,
|
LGauntlet = 6,
|
||||||
RGauntlet = 7,
|
RGauntlet = 7,
|
||||||
Shield = 8,
|
Shield = 8,
|
||||||
LBracer = 9,
|
LBracer = 9,
|
||||||
RBracer = 10
|
RBracer = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AODTstruct
|
struct AODTstruct
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
float weight;
|
float weight;
|
||||||
int value, health, enchant, armor;
|
int value, health, enchant, armor;
|
||||||
};
|
};
|
||||||
|
|
||||||
AODTstruct data;
|
AODTstruct data;
|
||||||
PartReferenceList parts;
|
PartReferenceList parts;
|
||||||
|
|
||||||
std::string name, model, icon, script, enchant;
|
std::string name, model, icon, script, enchant;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNString("FNAM");
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
esm.getHNT(data, "AODT", 24);
|
|
||||||
icon = esm.getHNOString("ITEX");
|
|
||||||
parts.load(esm);
|
|
||||||
enchant = esm.getHNOString("ENAM");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
13
components/esm/loadbody.cpp
Normal file
13
components/esm/loadbody.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include "loadbody.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void BodyPart::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "BYDT", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,59 +3,52 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
struct BodyPart
|
struct BodyPart
|
||||||
{
|
{
|
||||||
enum MeshPart
|
enum MeshPart
|
||||||
{
|
{
|
||||||
MP_Head = 0,
|
MP_Head = 0,
|
||||||
MP_Hair = 1,
|
MP_Hair = 1,
|
||||||
MP_Neck = 2,
|
MP_Neck = 2,
|
||||||
MP_Chest = 3,
|
MP_Chest = 3,
|
||||||
MP_Groin = 4,
|
MP_Groin = 4,
|
||||||
MP_Hand = 5,
|
MP_Hand = 5,
|
||||||
MP_Wrist = 6,
|
MP_Wrist = 6,
|
||||||
MP_Forearm = 7,
|
MP_Forearm = 7,
|
||||||
MP_Upperarm = 8,
|
MP_Upperarm = 8,
|
||||||
MP_Foot = 9,
|
MP_Foot = 9,
|
||||||
MP_Ankle = 10,
|
MP_Ankle = 10,
|
||||||
MP_Knee = 11,
|
MP_Knee = 11,
|
||||||
MP_Upperleg = 12,
|
MP_Upperleg = 12,
|
||||||
MP_Clavicle = 13,
|
MP_Clavicle = 13,
|
||||||
MP_Tail = 14
|
MP_Tail = 14
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Flags
|
enum Flags
|
||||||
{
|
{
|
||||||
BPF_Female = 1,
|
BPF_Female = 1, BPF_Playable = 2
|
||||||
BPF_Playable = 2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MeshType
|
enum MeshType
|
||||||
{
|
{
|
||||||
MT_Skin = 0,
|
MT_Skin = 0, MT_Clothing = 1, MT_Armor = 2
|
||||||
MT_Clothing = 1,
|
|
||||||
MT_Armor = 2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BYDTstruct
|
struct BYDTstruct
|
||||||
{
|
{
|
||||||
char part;
|
char part;
|
||||||
char vampire;
|
char vampire;
|
||||||
char flags;
|
char flags;
|
||||||
char type;
|
char type;
|
||||||
};
|
};
|
||||||
|
|
||||||
BYDTstruct data;
|
BYDTstruct data;
|
||||||
std::string model, name;
|
std::string model, name;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNString("FNAM");
|
|
||||||
esm.getHNT(data, "BYDT", 4);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
17
components/esm/loadbook.cpp
Normal file
17
components/esm/loadbook.cpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "loadbook.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Book::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "BKDT", 20);
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
text = esm.getHNOString("TEXT");
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Books, magic scrolls, notes and so on
|
* Books, magic scrolls, notes and so on
|
||||||
|
@ -11,25 +12,16 @@ namespace ESM {
|
||||||
|
|
||||||
struct Book
|
struct Book
|
||||||
{
|
{
|
||||||
struct BKDTstruct
|
struct BKDTstruct
|
||||||
{
|
{
|
||||||
float weight;
|
float weight;
|
||||||
int value, isScroll, skillID, enchant;
|
int value, isScroll, skillID, enchant;
|
||||||
};
|
};
|
||||||
|
|
||||||
BKDTstruct data;
|
BKDTstruct data;
|
||||||
std::string name, model, icon, script, enchant, text;
|
std::string name, model, icon, script, enchant, text;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNOString("FNAM");
|
|
||||||
esm.getHNT(data, "BKDT", 20);
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
icon = esm.getHNOString("ITEX");
|
|
||||||
text = esm.getHNOString("TEXT");
|
|
||||||
enchant = esm.getHNOString("ENAM");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
15
components/esm/loadbsgn.cpp
Normal file
15
components/esm/loadbsgn.cpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include "loadbsgn.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void BirthSign::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
texture = esm.getHNOString("TNAM");
|
||||||
|
description = esm.getHNOString("DESC");
|
||||||
|
|
||||||
|
powers.load(esm);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,23 +4,17 @@
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
struct BirthSign
|
struct BirthSign
|
||||||
{
|
{
|
||||||
std::string name, description, texture;
|
std::string name, description, texture;
|
||||||
|
|
||||||
// List of powers and abilities that come with this birth sign.
|
// List of powers and abilities that come with this birth sign.
|
||||||
SpellList powers;
|
SpellList powers;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
name = esm.getHNString("FNAM");
|
|
||||||
texture = esm.getHNOString("TNAM");
|
|
||||||
description = esm.getHNOString("DESC");
|
|
||||||
|
|
||||||
powers.load(esm);
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
113
components/esm/loadcell.cpp
Normal file
113
components/esm/loadcell.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include "loadcell.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Cell::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
// Ignore this for now, it might mean we should delete the entire
|
||||||
|
// cell?
|
||||||
|
if (esm.isNextSub("DELE"))
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
|
esm.getHNT(data, "DATA", 12);
|
||||||
|
|
||||||
|
// Water level
|
||||||
|
water = 0;
|
||||||
|
|
||||||
|
if (data.flags & Interior)
|
||||||
|
{
|
||||||
|
// Interior cells
|
||||||
|
|
||||||
|
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
||||||
|
esm.getHT(water);
|
||||||
|
|
||||||
|
// Quasi-exterior cells have a region (which determines the
|
||||||
|
// weather), pure interior cells have ambient lighting
|
||||||
|
// instead.
|
||||||
|
if (data.flags & QuasiEx)
|
||||||
|
region = esm.getHNOString("RGNN");
|
||||||
|
else
|
||||||
|
esm.getHNT(ambi, "AMBI", 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Exterior cells
|
||||||
|
region = esm.getHNOString("RGNN");
|
||||||
|
esm.getHNOT(mapColor, "NAM5");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save position of the cell references and move on
|
||||||
|
context = esm.getContext();
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cell::restore(ESMReader &esm) const
|
||||||
|
{
|
||||||
|
esm.restoreContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
|
{
|
||||||
|
if (!esm.hasMoreSubs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Number of references in the cell? Maximum once in each cell,
|
||||||
|
// but not always at the beginning, and not always right. In other
|
||||||
|
// words, completely useless.
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
esm.getHNOT(i, "NAM0");
|
||||||
|
}
|
||||||
|
|
||||||
|
esm.getHNT(ref.refnum, "FRMR");
|
||||||
|
ref.refID = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
// getHNOT will not change the existing value if the subrecord is
|
||||||
|
// missing
|
||||||
|
ref.scale = 1.0;
|
||||||
|
esm.getHNOT(ref.scale, "XSCL");
|
||||||
|
|
||||||
|
ref.owner = esm.getHNOString("ANAM");
|
||||||
|
ref.glob = esm.getHNOString("BNAM");
|
||||||
|
ref.soul = esm.getHNOString("XSOL");
|
||||||
|
|
||||||
|
ref.faction = esm.getHNOString("CNAM");
|
||||||
|
ref.factIndex = -1;
|
||||||
|
esm.getHNOT(ref.factIndex, "INDX");
|
||||||
|
|
||||||
|
ref.charge = -1.0;
|
||||||
|
esm.getHNOT(ref.charge, "XCHG");
|
||||||
|
|
||||||
|
ref.intv = 0;
|
||||||
|
ref.nam9 = 0;
|
||||||
|
esm.getHNOT(ref.intv, "INTV");
|
||||||
|
esm.getHNOT(ref.nam9, "NAM9");
|
||||||
|
|
||||||
|
// Present for doors that teleport you to another cell.
|
||||||
|
if (esm.isNextSub("DODT"))
|
||||||
|
{
|
||||||
|
ref.teleport = true;
|
||||||
|
esm.getHT(ref.doorDest);
|
||||||
|
ref.destCell = esm.getHNOString("DNAM");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ref.teleport = false;
|
||||||
|
|
||||||
|
// Integer, despite the name suggesting otherwise
|
||||||
|
ref.lockLevel = 0;
|
||||||
|
esm.getHNOT(ref.lockLevel, "FLTV");
|
||||||
|
ref.key = esm.getHNOString("KNAM");
|
||||||
|
ref.trap = esm.getHNOString("TNAM");
|
||||||
|
|
||||||
|
ref.unam = 0;
|
||||||
|
ref.fltv = 0;
|
||||||
|
esm.getHNOT(ref.unam, "UNAM");
|
||||||
|
esm.getHNOT(ref.fltv, "FLTV");
|
||||||
|
|
||||||
|
esm.getHNT(ref.pos, "DATA", 24);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -117,51 +117,14 @@ struct Cell
|
||||||
int water; // Water level
|
int water; // Water level
|
||||||
int mapColor;
|
int mapColor;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
// Ignore this for now, it might mean we should delete the entire
|
|
||||||
// cell?
|
|
||||||
if(esm.isNextSub("DELE")) esm.skipHSub();
|
|
||||||
|
|
||||||
esm.getHNT(data, "DATA", 12);
|
|
||||||
|
|
||||||
// Water level
|
|
||||||
water = 0;
|
|
||||||
|
|
||||||
if(data.flags & Interior)
|
|
||||||
{
|
|
||||||
// Interior cells
|
|
||||||
|
|
||||||
if(esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
|
|
||||||
esm.getHT(water);
|
|
||||||
|
|
||||||
// Quasi-exterior cells have a region (which determines the
|
|
||||||
// weather), pure interior cells have ambient lighting
|
|
||||||
// instead.
|
|
||||||
if(data.flags & QuasiEx)
|
|
||||||
region = esm.getHNOString("RGNN");
|
|
||||||
else
|
|
||||||
esm.getHNT(ambi, "AMBI", 16);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Exterior cells
|
|
||||||
region = esm.getHNOString("RGNN");
|
|
||||||
esm.getHNOT(mapColor, "NAM5");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save position of the cell references and move on
|
|
||||||
context = esm.getContext();
|
|
||||||
esm.skipRecord();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the given reader to the stored position. Will try to open
|
// Restore the given reader to the stored position. Will try to open
|
||||||
// the file matching the stored file name. If you want to read from
|
// the file matching the stored file name. If you want to read from
|
||||||
// somewhere other than the file system, you need to pre-open the
|
// somewhere other than the file system, you need to pre-open the
|
||||||
// ESMReader, and the filename must match the stored filename
|
// ESMReader, and the filename must match the stored filename
|
||||||
// exactly.
|
// exactly.
|
||||||
void restore(ESMReader &esm) const
|
void restore(ESMReader &esm) const;
|
||||||
{ esm.restoreContext(context); }
|
|
||||||
|
|
||||||
/* Get the next reference in this cell, if any. Returns false when
|
/* Get the next reference in this cell, if any. Returns false when
|
||||||
there are no more references in the cell.
|
there are no more references in the cell.
|
||||||
|
@ -169,66 +132,7 @@ struct Cell
|
||||||
All fields of the CellRef struct are overwritten. You can safely
|
All fields of the CellRef struct are overwritten. You can safely
|
||||||
reuse one memory location without blanking it between calls.
|
reuse one memory location without blanking it between calls.
|
||||||
*/
|
*/
|
||||||
static bool getNextRef(ESMReader &esm, CellRef &ref)
|
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||||
{
|
|
||||||
if(!esm.hasMoreSubs()) return false;
|
|
||||||
|
|
||||||
// Number of references in the cell? Maximum once in each cell,
|
|
||||||
// but not always at the beginning, and not always right. In other
|
|
||||||
// words, completely useless.
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
esm.getHNOT(i, "NAM0");
|
|
||||||
}
|
|
||||||
|
|
||||||
esm.getHNT(ref.refnum, "FRMR");
|
|
||||||
ref.refID = esm.getHNString("NAME");
|
|
||||||
|
|
||||||
// getHNOT will not change the existing value if the subrecord is
|
|
||||||
// missing
|
|
||||||
ref.scale = 1.0;
|
|
||||||
esm.getHNOT(ref.scale, "XSCL");
|
|
||||||
|
|
||||||
ref.owner = esm.getHNOString("ANAM");
|
|
||||||
ref.glob = esm.getHNOString("BNAM");
|
|
||||||
ref.soul = esm.getHNOString("XSOL");
|
|
||||||
|
|
||||||
ref.faction = esm.getHNOString("CNAM");
|
|
||||||
ref.factIndex = -1;
|
|
||||||
esm.getHNOT(ref.factIndex, "INDX");
|
|
||||||
|
|
||||||
ref.charge = -1.0;
|
|
||||||
esm.getHNOT(ref.charge, "XCHG");
|
|
||||||
|
|
||||||
ref.intv = 0;
|
|
||||||
ref.nam9 = 0;
|
|
||||||
esm.getHNOT(ref.intv, "INTV");
|
|
||||||
esm.getHNOT(ref.nam9, "NAM9");
|
|
||||||
|
|
||||||
// Present for doors that teleport you to another cell.
|
|
||||||
if(esm.isNextSub("DODT"))
|
|
||||||
{
|
|
||||||
ref.teleport = true;
|
|
||||||
esm.getHT(ref.doorDest);
|
|
||||||
ref.destCell = esm.getHNOString("DNAM");
|
|
||||||
}
|
|
||||||
else ref.teleport = false;
|
|
||||||
|
|
||||||
// Integer, despite the name suggesting otherwise
|
|
||||||
ref.lockLevel = 0;
|
|
||||||
esm.getHNOT(ref.lockLevel, "FLTV");
|
|
||||||
ref.key = esm.getHNOString("KNAM");
|
|
||||||
ref.trap = esm.getHNOString("TNAM");
|
|
||||||
|
|
||||||
ref.unam = 0;
|
|
||||||
ref.fltv = 0;
|
|
||||||
esm.getHNOT(ref.unam, "UNAM");
|
|
||||||
esm.getHNOT(ref.fltv, "FLTV");
|
|
||||||
|
|
||||||
esm.getHNT(ref.pos, "DATA", 24);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
17
components/esm/loadclas.cpp
Normal file
17
components/esm/loadclas.cpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "loadclas.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Class::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "CLDT", 60);
|
||||||
|
|
||||||
|
if (data.isPlayable > 1)
|
||||||
|
esm.fail("Unknown bool value");
|
||||||
|
|
||||||
|
description = esm.getHNOString("DESC");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,8 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Character class definitions
|
* Character class definitions
|
||||||
|
@ -13,62 +14,51 @@ namespace ESM {
|
||||||
// class
|
// class
|
||||||
struct Class
|
struct Class
|
||||||
{
|
{
|
||||||
enum AutoCalc
|
enum AutoCalc
|
||||||
{
|
{
|
||||||
Weapon = 0x00001,
|
Weapon = 0x00001,
|
||||||
Armor = 0x00002,
|
Armor = 0x00002,
|
||||||
Clothing = 0x00004,
|
Clothing = 0x00004,
|
||||||
Books = 0x00008,
|
Books = 0x00008,
|
||||||
Ingredient = 0x00010,
|
Ingredient = 0x00010,
|
||||||
Lockpick = 0x00020,
|
Lockpick = 0x00020,
|
||||||
Probe = 0x00040,
|
Probe = 0x00040,
|
||||||
Lights = 0x00080,
|
Lights = 0x00080,
|
||||||
Apparatus = 0x00100,
|
Apparatus = 0x00100,
|
||||||
Repair = 0x00200,
|
Repair = 0x00200,
|
||||||
Misc = 0x00400,
|
Misc = 0x00400,
|
||||||
Spells = 0x00800,
|
Spells = 0x00800,
|
||||||
MagicItems = 0x01000,
|
MagicItems = 0x01000,
|
||||||
Potions = 0x02000,
|
Potions = 0x02000,
|
||||||
Training = 0x04000,
|
Training = 0x04000,
|
||||||
Spellmaking = 0x08000,
|
Spellmaking = 0x08000,
|
||||||
Enchanting = 0x10000,
|
Enchanting = 0x10000,
|
||||||
RepairItem = 0x20000
|
RepairItem = 0x20000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Specialization
|
enum Specialization
|
||||||
{
|
{
|
||||||
Combat = 0,
|
Combat = 0, Magic = 1, Stealth = 2
|
||||||
Magic = 1,
|
|
||||||
Stealth = 2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const Specialization specializationIds[3];
|
static const Specialization specializationIds[3];
|
||||||
static const char *gmstSpecializationIds[3];
|
static const char *gmstSpecializationIds[3];
|
||||||
|
|
||||||
struct CLDTstruct
|
struct CLDTstruct
|
||||||
{
|
|
||||||
int attribute[2]; // Attributes that get class bonus
|
|
||||||
int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
|
|
||||||
int skills[5][2]; // Minor and major skills.
|
|
||||||
int isPlayable; // 0x0001 - Playable class
|
|
||||||
|
|
||||||
// I have no idea how to autocalculate these items...
|
|
||||||
int calc;
|
|
||||||
}; // 60 bytes
|
|
||||||
|
|
||||||
std::string name, description;
|
|
||||||
CLDTstruct data;
|
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
|
||||||
{
|
{
|
||||||
name = esm.getHNString("FNAM");
|
int attribute[2]; // Attributes that get class bonus
|
||||||
esm.getHNT(data, "CLDT", 60);
|
int specialization; // 0 = Combat, 1 = Magic, 2 = Stealth
|
||||||
|
int skills[5][2]; // Minor and major skills.
|
||||||
|
int isPlayable; // 0x0001 - Playable class
|
||||||
|
|
||||||
if(data.isPlayable > 1)
|
// I have no idea how to autocalculate these items...
|
||||||
esm.fail("Unknown bool value");
|
int calc;
|
||||||
|
}; // 60 bytes
|
||||||
|
|
||||||
description = esm.getHNOString("DESC");
|
std::string name, description;
|
||||||
}
|
CLDTstruct data;
|
||||||
|
|
||||||
|
void load(ESMReader &esm);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
20
components/esm/loadclot.cpp
Normal file
20
components/esm/loadclot.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "loadclot.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
|
void Clothing::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNOString("FNAM");
|
||||||
|
esm.getHNT(data, "CTDT", 12);
|
||||||
|
|
||||||
|
script = esm.getHNOString("SCRI");
|
||||||
|
icon = esm.getHNOString("ITEX");
|
||||||
|
|
||||||
|
parts.load(esm);
|
||||||
|
|
||||||
|
enchant = esm.getHNOString("ENAM");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,7 +4,8 @@
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
#include "loadarmo.hpp"
|
#include "loadarmo.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clothing
|
* Clothing
|
||||||
|
@ -12,46 +13,34 @@ namespace ESM {
|
||||||
|
|
||||||
struct Clothing
|
struct Clothing
|
||||||
{
|
{
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Pants = 0,
|
Pants = 0,
|
||||||
Shoes = 1,
|
Shoes = 1,
|
||||||
Shirt = 2,
|
Shirt = 2,
|
||||||
Belt = 3,
|
Belt = 3,
|
||||||
Robe = 4,
|
Robe = 4,
|
||||||
RGlove = 5,
|
RGlove = 5,
|
||||||
LGlove = 6,
|
LGlove = 6,
|
||||||
Skirt = 7,
|
Skirt = 7,
|
||||||
Ring = 8,
|
Ring = 8,
|
||||||
Amulet = 9
|
Amulet = 9
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CTDTstruct
|
struct CTDTstruct
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
float weight;
|
float weight;
|
||||||
short value;
|
short value;
|
||||||
short enchant;
|
short enchant;
|
||||||
};
|
};
|
||||||
CTDTstruct data;
|
CTDTstruct data;
|
||||||
|
|
||||||
PartReferenceList parts;
|
PartReferenceList parts;
|
||||||
|
|
||||||
std::string name, model, icon, enchant, script;
|
std::string name, model, icon, enchant, script;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
model = esm.getHNString("MODL");
|
|
||||||
name = esm.getHNOString("FNAM");
|
|
||||||
esm.getHNT(data, "CTDT", 12);
|
|
||||||
|
|
||||||
script = esm.getHNOString("SCRI");
|
|
||||||
icon = esm.getHNOString("ITEX");
|
|
||||||
|
|
||||||
parts.load(esm);
|
|
||||||
|
|
||||||
enchant = esm.getHNOString("ENAM");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
32
components/esm/loadland.cpp
Normal file
32
components/esm/loadland.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "loadland.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void Land::load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
// Get the grid location
|
||||||
|
esm.getSubNameIs("INTV");
|
||||||
|
esm.getSubHeaderIs(8);
|
||||||
|
esm.getT<int>(X);
|
||||||
|
esm.getT<int>(Y);
|
||||||
|
|
||||||
|
esm.getHNT(flags, "DATA");
|
||||||
|
|
||||||
|
// Store the file position
|
||||||
|
context = esm.getContext();
|
||||||
|
|
||||||
|
hasData = false;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
// Skip these here. Load the actual data when the cell is loaded.
|
||||||
|
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
||||||
|
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
||||||
|
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
||||||
|
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
||||||
|
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
||||||
|
|
||||||
|
// We need all three of VNML, VHGT and VTEX in order to use the
|
||||||
|
// landscape.
|
||||||
|
hasData = (cnt == 3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,51 +3,25 @@
|
||||||
|
|
||||||
#include "esm_reader.hpp"
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
namespace ESM {
|
namespace ESM
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
* Landscape data.
|
* Landscape data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Land
|
struct Land
|
||||||
{
|
{
|
||||||
int flags; // Only first four bits seem to be used, don't know what
|
int flags; // Only first four bits seem to be used, don't know what
|
||||||
// they mean.
|
// they mean.
|
||||||
int X, Y; // Map coordinates.
|
int X, Y; // Map coordinates.
|
||||||
|
|
||||||
// File context. This allows the ESM reader to be 'reset' to this
|
// File context. This allows the ESM reader to be 'reset' to this
|
||||||
// location later when we are ready to load the full data set.
|
// location later when we are ready to load the full data set.
|
||||||
ESM_Context context;
|
ESM_Context context;
|
||||||
|
|
||||||
bool hasData;
|
bool hasData;
|
||||||
|
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm);
|
||||||
{
|
|
||||||
// Get the grid location
|
|
||||||
esm.getSubNameIs("INTV");
|
|
||||||
esm.getSubHeaderIs(8);
|
|
||||||
esm.getT<int>(X);
|
|
||||||
esm.getT<int>(Y);
|
|
||||||
|
|
||||||
esm.getHNT(flags, "DATA");
|
|
||||||
|
|
||||||
// Store the file position
|
|
||||||
context = esm.getContext();
|
|
||||||
|
|
||||||
hasData = false;
|
|
||||||
int cnt = 0;
|
|
||||||
|
|
||||||
// Skip these here. Load the actual data when the cell is loaded.
|
|
||||||
if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;}
|
|
||||||
if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;}
|
|
||||||
if(esm.isNextSub("WNAM")) esm.skipHSubSize(81);
|
|
||||||
if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675);
|
|
||||||
if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;}
|
|
||||||
|
|
||||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
|
||||||
// landscape.
|
|
||||||
hasData = (cnt == 3);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue