Refactored remaining headers in components/esm except defs.hpp.

Removed obsolete *.cpp files.
actorid
Nikolay Kasyanov 14 years ago
parent a2c42ab5a2
commit 1e5768170f

@ -127,10 +127,7 @@ source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp) file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp)
set(ESM set(ESM
${COMP_DIR}/esm/load_impl.cpp
${COMP_DIR}/esm/skill.cpp
${COMP_DIR}/esm/attr.cpp ${COMP_DIR}/esm/attr.cpp
${COMP_DIR}/esm/class.cpp
${COMP_DIR}/esm/esm_reader.cpp ${COMP_DIR}/esm/esm_reader.cpp
${COMP_DIR}/esm/loadland.cpp ${COMP_DIR}/esm/loadland.cpp
${COMP_DIR}/esm/loadacti.cpp ${COMP_DIR}/esm/loadacti.cpp
@ -143,6 +140,34 @@ set(ESM
${COMP_DIR}/esm/loadcell.cpp ${COMP_DIR}/esm/loadcell.cpp
${COMP_DIR}/esm/loadclas.cpp ${COMP_DIR}/esm/loadclas.cpp
${COMP_DIR}/esm/loadclot.cpp ${COMP_DIR}/esm/loadclot.cpp
${COMP_DIR}/esm/loadcont.cpp
${COMP_DIR}/esm/loadcrea.cpp
${COMP_DIR}/esm/loaddial.cpp
${COMP_DIR}/esm/loaddoor.cpp
${COMP_DIR}/esm/loadench.cpp
${COMP_DIR}/esm/loadfact.cpp
${COMP_DIR}/esm/loadglob.cpp
${COMP_DIR}/esm/loadgmst.cpp
${COMP_DIR}/esm/loadinfo.cpp
${COMP_DIR}/esm/loadingr.cpp
${COMP_DIR}/esm/loadlevlist.cpp
${COMP_DIR}/esm/loadligh.cpp
${COMP_DIR}/esm/loadlocks.cpp
${COMP_DIR}/esm/loadltex.cpp
${COMP_DIR}/esm/loadmgef.cpp
${COMP_DIR}/esm/loadmisc.cpp
${COMP_DIR}/esm/loadnpc.cpp
${COMP_DIR}/esm/loadpgrd.cpp
${COMP_DIR}/esm/loadrace.cpp
${COMP_DIR}/esm/loadregn.cpp
${COMP_DIR}/esm/loadscpt.cpp
${COMP_DIR}/esm/loadskil.cpp
${COMP_DIR}/esm/loadsndg.cpp
${COMP_DIR}/esm/loadsoun.cpp
${COMP_DIR}/esm/loadspel.cpp
${COMP_DIR}/esm/loadsscr.cpp
${COMP_DIR}/esm/loadstat.cpp
${COMP_DIR}/esm/loadweap.cpp
) )
source_group(components\\esm FILES ${ESM_HEADER} ${ESM}) source_group(components\\esm FILES ${ESM_HEADER} ${ESM})

@ -1,15 +0,0 @@
#include "loadclas.hpp"
using namespace ESM;
const Class::Specialization Class::specializationIds[3] = {
Class::Combat,
Class::Magic,
Class::Stealth
};
const char *Class::gmstSpecializationIds[3] = {
"sSpecializationCombat",
"sSpecializationMagic",
"sSpecializationStealth"
};

@ -1,175 +0,0 @@
#include "records.hpp"
/** Implementation for some of the load() functions. Most are found in
the header files themselves, but this is a bit irritating to
compile if you're changing the functions often, as virtually the
entire engine depends on these headers.
*/
/*
#include <iostream>
using namespace std;
*/
namespace ESM
{
void NPC::load(ESMReader &esm, const std::string& id)
{
mId = id;
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if(esm.getSubSize() == 52) esm.getExact(&npdt52, 52);
else if(esm.getSubSize() == 12) esm.getExact(&npdt12, 12);
else esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if(esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else hasAI = false;
esm.skipRecord();
}
void DialInfo::load(ESMReader &esm)
{
id = esm.getHNString("INAM");
prev = esm.getHNString("PNAM");
next = esm.getHNString("NNAM");
// Not present if deleted
if(esm.isNextSub("DATA"))
esm.getHT(data, 12);
// What follows is somewhat spaghetti-ish, but it's worth if for
// an extra speedup. INFO is by far the most common record type.
// subName is a reference to the original, so it changes whenever
// a new sub name is read. esm.isEmptyOrGetName() will get the
// next name for us, or return true if there are no more records.
esm.getSubName();
const NAME &subName = esm.retSubName();
if(subName.val == REC_ONAM)
{
actor = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_RNAM)
{
race = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_CNAM)
{
clas = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
factionLess = false;
if(subName.val == REC_FNAM)
{
npcFaction = esm.getHString();
if(npcFaction == "FFFF") factionLess = true;
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_ANAM)
{
cell = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_DNAM)
{
pcFaction = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_SNAM)
{
sound = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_NAME)
{
response = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
while(subName.val == REC_SCVR)
{
SelectStruct ss;
ss.selectRule = esm.getHString();
esm.isEmptyOrGetName();
if(subName.val == REC_INTV)
{
ss.type = VT_Int;
esm.getHT(ss.i);
}
else if(subName.val == REC_FLTV)
{
ss.type = VT_Float;
esm.getHT(ss.f);
}
else
esm.fail("INFO.SCVR must precede INTV or FLTV, not "
+ subName.toString());
selects.push_back(ss);
if(esm.isEmptyOrGetName()) return;
}
if(subName.val == REC_BNAM)
{
resultScript = esm.getHString();
if(esm.isEmptyOrGetName()) return;
}
questStatus = QS_None;
if (subName.val == REC_QSTN) questStatus = QS_Name;
else if(subName.val == REC_QSTF) questStatus = QS_Finished;
else if(subName.val == REC_QSTR) questStatus = QS_Restart;
else if(subName.val == REC_DELE) questStatus = QS_Deleted;
else
esm.fail("Don't know what to do with " + subName.toString() + " in INFO " + id);
if(questStatus != QS_None)
// Skip rest of record
esm.skipRecord();
}
void Sound::load(ESMReader &esm)
{
sound = esm.getHNString("FNAM");
esm.getHNT(data, "DATA", 3);
/*
cout << "vol=" << (int)data.volume
<< " min=" << (int)data.minRange
<< " max=" << (int)data.maxRange
<< endl;
*/
}
}

@ -3,6 +3,18 @@
namespace ESM namespace ESM
{ {
const Class::Specialization Class::specializationIds[3] = {
Class::Combat,
Class::Magic,
Class::Stealth
};
const char *Class::gmstSpecializationIds[3] = {
"sSpecializationCombat",
"sSpecializationMagic",
"sSpecializationStealth"
};
void Class::load(ESMReader &esm) void Class::load(ESMReader &esm)
{ {
name = esm.getHNString("FNAM"); name = esm.getHNString("FNAM");

@ -0,0 +1,33 @@
#include "loadcont.hpp"
namespace ESM
{
void InventoryList::load(ESMReader &esm)
{
ContItem ci;
while (esm.isNextSub("NPCO"))
{
esm.getHT(ci, 36);
list.push_back(ci);
}
}
void Container::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(weight, "CNDT", 4);
esm.getHNT(flags, "FLAG", 4);
if (flags & 0xf4)
esm.fail("Unknown flags");
if (!(flags & 0x8))
esm.fail("Flag 8 not set");
script = esm.getHNOString("SCRI");
inventory.load(esm);
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Container definition * Container definition
@ -11,54 +12,33 @@ namespace ESM {
struct ContItem struct ContItem
{ {
int count; int count;
NAME32 item; NAME32 item;
}; };
struct InventoryList struct InventoryList
{ {
std::vector<ContItem> list; std::vector<ContItem> list;
void load(ESMReader &esm) void load(ESMReader &esm);
{
ContItem ci;
while(esm.isNextSub("NPCO"))
{
esm.getHT(ci, 36);
list.push_back(ci);
}
}
}; };
struct Container struct Container
{ {
enum Flags enum Flags
{ {
Organic = 1, // Objects cannot be placed in this container Organic = 1, // Objects cannot be placed in this container
Respawn = 2, // Respawns after 4 months Respawn = 2, // Respawns after 4 months
Unknown = 8 Unknown = 8
}; };
std::string name, model, script; std::string name, model, script;
float weight; // Not sure, might be max total weight allowed?
int flags;
InventoryList inventory;
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(weight, "CNDT", 4);
esm.getHNT(flags, "FLAG", 4);
if(flags & 0xf4) esm.fail("Unknown flags");
if(!(flags & 0x8)) esm.fail("Flag 8 not set");
script = esm.getHNOString("SCRI"); float weight; // Not sure, might be max total weight allowed?
int flags;
InventoryList inventory;
inventory.load(esm); void load(ESMReader &esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,40 @@
#include "loadcrea.hpp"
namespace ESM {
void Creature::load(ESMReader &esm, const std::string& id)
{
mId = id;
model = esm.getHNString("MODL");
original = esm.getHNOString("CNAM");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "NPDT", 96);
esm.getHNT(flags, "FLAG");
scale = 1.0;
esm.getHNOT(scale, "XSCL");
inventory.load(esm);
// More subrecords:
// AIDT - data (12 bytes, unknown)
// AI_W - wander (14 bytes, i don't understand it)
// short distance
// byte duration
// byte timeOfDay
// byte idle[10]
//
// Rest is optional:
// AI_T - travel?
// AI_F - follow?
// AI_E - escort?
// AI_A - activate?
esm.skipRecord();
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "loadcont.hpp" #include "loadcont.hpp"
namespace ESM { namespace ESM
{
/* /*
* Creature definition * Creature definition
@ -13,91 +14,53 @@ namespace ESM {
struct Creature struct Creature
{ {
// Default is 0x48? // Default is 0x48?
enum Flags enum Flags
{ {
Biped = 0x001, Biped = 0x001, Respawn = 0x002, Weapon = 0x004, // Has weapon and shield
Respawn = 0x002, None = 0x008, // ??
Weapon = 0x004, // Has weapon and shield Swims = 0x010,
None = 0x008, // ?? Flies = 0x020, // Don't know what happens if several
Swims = 0x010, Walks = 0x040, // of these are set
Flies = 0x020, // Don't know what happens if several Essential = 0x080,
Walks = 0x040, // of these are set Skeleton = 0x400, // Does not have normal blood
Essential = 0x080, Metal = 0x800
Skeleton = 0x400, // Does not have normal blood // Has 'golden' blood
Metal = 0x800 // Has 'golden' blood
}; };
enum Type enum Type
{ {
Creatures = 0, Creatures = 0, Deadra = 1, Undead = 2, Humanoid = 3
Deadra = 1,
Undead = 2,
Humanoid = 3
}; };
struct NPDTstruct struct NPDTstruct
{ {
int type; int type;
// For creatures we obviously have to use ints, not shorts and // For creatures we obviously have to use ints, not shorts and
// bytes like we use for NPCs.... this file format just makes so // bytes like we use for NPCs.... this file format just makes so
// much sense! (Still, _much_ easier to decode than the NIFs.) // much sense! (Still, _much_ easier to decode than the NIFs.)
int level; int level;
int strength, intelligence, willpower, agility, speed, endurance, int strength, intelligence, willpower, agility, speed, endurance,
personality, luck, health, mana, fatigue; // Stats personality, luck, health, mana, fatigue; // Stats
int soul; // The creatures soul value (used with soul gems.) int soul; // The creatures soul value (used with soul gems.)
int combat, magic, stealth; // Don't know yet. int combat, magic, stealth; // Don't know yet.
int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int attack[6]; // AttackMin1, AttackMax1, ditto2, ditto3
int gold; int gold;
}; // 96 bytes }; // 96 bytes
NPDTstruct data; NPDTstruct data;
int flags; int flags;
float scale; float scale;
std::string model, name, script, std::string model, name, script, original; // Base creature that this is a modification of
original; // Base creature that this is a modification of
// Defined in loadcont.hpp // Defined in loadcont.hpp
InventoryList inventory; InventoryList inventory;
std::string mId; std::string mId;
void load(ESMReader &esm, const std::string& id) void load(ESMReader &esm, const std::string& id);
{
mId = id;
model = esm.getHNString("MODL");
original = esm.getHNOString("CNAM");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
esm.getHNT(data, "NPDT", 96);
esm.getHNT(flags, "FLAG");
scale = 1.0;
esm.getHNOT(scale, "XSCL");
inventory.load(esm);
// More subrecords:
// AIDT - data (12 bytes, unknown)
// AI_W - wander (14 bytes, i don't understand it)
// short distance
// byte duration
// byte timeOfDay
// byte idle[10]
//
// Rest is optional:
// AI_T - travel?
// AI_F - follow?
// AI_E - escort?
// AI_A - activate?
esm.skipRecord();
}
}; };
} }
#endif #endif

@ -0,0 +1,25 @@
#include "loaddial.hpp"
namespace ESM
{
void Dialogue::load(ESMReader &esm)
{
esm.getSubNameIs("DATA");
esm.getSubHeader();
int si = esm.getSubSize();
if (si == 1)
esm.getT(type);
else if (si == 4)
{
// These are just markers, their values are not used.
int i;
esm.getT(i);
esm.getHNT(i, "DELE");
type = Deleted;
}
else
esm.fail("Unknown sub record size");
}
}

@ -6,7 +6,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "loadinfo.hpp" #include "loadinfo.hpp"
namespace ESM { namespace ESM
{
/* /*
* Dialogue topic and journal entries. The actual data is contained in * Dialogue topic and journal entries. The actual data is contained in
@ -15,36 +16,20 @@ namespace ESM {
struct Dialogue struct Dialogue
{ {
enum Type enum Type
{ {
Topic = 0, Topic = 0,
Voice = 1, Voice = 1,
Greeting = 2, Greeting = 2,
Persuasion = 3, Persuasion = 3,
Journal = 4, Journal = 4,
Deleted = -1 Deleted = -1
}; };
char type; char type;
std::vector<DialInfo> mInfo; std::vector<DialInfo> mInfo;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getSubNameIs("DATA");
esm.getSubHeader();
int si = esm.getSubSize();
if(si == 1)
esm.getT(type);
else if(si == 4)
{
// These are just markers, their values are not used.
int i;
esm.getT(i);
esm.getHNT(i,"DELE");
type = Deleted;
}
else esm.fail("Unknown sub record size");
}
}; };
} }
#endif #endif

@ -0,0 +1,15 @@
#include "loaddoor.hpp"
namespace ESM
{
void Door::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
openSound = esm.getHNOString("SNAM");
closeSound = esm.getHNOString("ANAM");
}
}

@ -3,20 +3,14 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct Door struct Door
{ {
std::string name, model, script, openSound, closeSound; std::string name, model, script, openSound, closeSound;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
script = esm.getHNOString("SCRI");
openSound = esm.getHNOString("SNAM");
closeSound = esm.getHNOString("ANAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,12 @@
#include "loadench.hpp"
namespace ESM
{
void Enchantment::load(ESMReader &esm)
{
esm.getHNT(data, "ENDT", 16);
effects.load(esm);
}
}

@ -2,8 +2,10 @@
#define _ESM_ENCH_H #define _ESM_ENCH_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Enchantments * Enchantments
@ -11,31 +13,27 @@ namespace ESM {
struct Enchantment struct Enchantment
{ {
enum Type enum Type
{ {
CastOnce = 0, CastOnce = 0,
WhenStrikes = 1, WhenStrikes = 1,
WhenUsed = 2, WhenUsed = 2,
ConstantEffect = 3 ConstantEffect = 3
}; };
struct ENDTstruct struct ENDTstruct
{ {
int type; int type;
int cost; int cost;
int charge; int charge;
int autocalc; // Guessing this is 1 if we are supposed to auto int autocalc; // Guessing this is 1 if we are supposed to auto
// calculate // calculate
}; };
ENDTstruct data; ENDTstruct data;
EffectList effects; EffectList effects;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(data, "ENDT", 16);
effects.load(esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,31 @@
#include "loadfact.hpp"
namespace ESM
{
void Faction::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
// Read rank names. These are optional.
int i = 0;
while (esm.isNextSub("RNAM") && i < 10)
ranks[i++] = esm.getHString();
// Main data struct
esm.getHNT(data, "FADT", 240);
if (data.isHidden > 1)
esm.fail("Unknown flag!");
// Read faction response values
while (esm.hasMoreSubs())
{
Reaction r;
r.faction = esm.getHNString("ANAM");
esm.getHNT(r.reaction, "INTV");
reactions.push_back(r);
}
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Faction definitions * Faction definitions
@ -12,67 +13,46 @@ namespace ESM {
// Requirements for each rank // Requirements for each rank
struct RankData struct RankData
{ {
int attribute1, attribute2; // Attribute level int attribute1, attribute2; // Attribute level
int skill1, skill2; // Skill level (faction skills given in int skill1, skill2; // Skill level (faction skills given in
// skillID below.) You need one skill at // skillID below.) You need one skill at
// level 'skill1' and two skills at level // level 'skill1' and two skills at level
// 'skill2' to advance to this rank. // 'skill2' to advance to this rank.
int factReaction; // Reaction from faction members int factReaction; // Reaction from faction members
}; };
struct Faction struct Faction
{ {
std::string id, name; std::string id, name;
struct FADTstruct
{
// Which attributes we like
int attribute1, attribute2;
RankData rankData[10];
int skillID[6]; // IDs of skills this faction require
int unknown; // Always -1?
int isHidden; // 1 - hidden from player
}; // 240 bytes
FADTstruct data;
struct Reaction struct FADTstruct
{ {
std::string faction; // Which attributes we like
int reaction; int attribute1, attribute2;
};
std::vector<Reaction> reactions; RankData rankData[10];
// Name of faction ranks (may be empty for NPC factions) int skillID[6]; // IDs of skills this faction require
std::string ranks[10]; int unknown; // Always -1?
int isHidden; // 1 - hidden from player
}; // 240 bytes
void load(ESMReader &esm) FADTstruct data;
{
name = esm.getHNString("FNAM");
// Read rank names. These are optional. struct Reaction
int i = 0; {
while(esm.isNextSub("RNAM") && i<10) ranks[i++] = esm.getHString(); std::string faction;
int reaction;
};
// Main data struct std::vector<Reaction> reactions;
esm.getHNT(data, "FADT", 240);
if(data.isHidden > 1) esm.fail("Unknown flag!"); // Name of faction ranks (may be empty for NPC factions)
std::string ranks[10];
// Read faction response values void load(ESMReader &esm);
while(esm.hasMoreSubs())
{
Reaction r;
r.faction = esm.getHNString("ANAM");
esm.getHNT(r.reaction, "INTV");
reactions.push_back(r);
}
}
}; };
} }
#endif #endif

@ -0,0 +1,24 @@
#include "loadglob.hpp"
namespace ESM
{
void Global::load(ESMReader &esm)
{
VarType t;
std::string tmp = esm.getHNString("FNAM");
if (tmp == "s")
t = VT_Short;
else if (tmp == "l")
t = VT_Int;
else if (tmp == "f")
t = VT_Float;
else
esm.fail("Illegal global variable type " + tmp);
type = t;
// Note: Both floats and longs are represented as floats.
esm.getHNT(value, "FLTV");
}
}

@ -2,8 +2,10 @@
#define _ESM_GLOB_H #define _ESM_GLOB_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Global script variables * Global script variables
@ -11,22 +13,10 @@ namespace ESM {
struct Global struct Global
{ {
unsigned value; unsigned value;
VarType type; VarType type;
void load(ESMReader &esm)
{
VarType t;
std::string tmp = esm.getHNString("FNAM");
if(tmp == "s") t = VT_Short;
else if(tmp == "l") t = VT_Int;
else if(tmp == "f") t = VT_Float;
else esm.fail("Illegal global variable type " + tmp);
type = t;
// Note: Both floats and longs are represented as floats. void load(ESMReader &esm);
esm.getHNT(value, "FLTV");
}
}; };
} }
#endif #endif

@ -0,0 +1,172 @@
#include "loadgmst.hpp"
namespace ESM
{
// Some handy macros
#define cI(s,x) { if(id == (s)) return (i == (x)); }
#define cF(s,x) { if(id == (s)) return (f == (x)); }
#define cS(s,x) { if(id == (s)) return (str == (x)); }
bool GameSetting::isDirtyTribunal()
{
/*
Here, id contains the game setting name, and we check the
setting for certain values. If it matches, this is a "dirty"
entry. The correct entry (as defined in Tribunal and Bloodmoon
esms) are given in the comments. Many of the values are correct,
and are marked as 'same'. We still ignore them though, as they
are still in the wrong file and might override custom values
from other mods.
*/
// Strings
cS("sProfitValue", "Profit Value"); // 'Profit:'
cS("sEditNote", "Edit Note"); // same
cS("sDeleteNote", "Delete Note?"); // same
cS("sMaxSale", "Max Sale"); // 'Seller Max'
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon'
cS("sTeleportDisabled",
"Teleportation magic does not work here.");// same
cS("sLevitateDisabled",
"Levitation magic does not work here."); // same
cS("sCompanionShare", "Companion Share"); // 'Share'
cS("sCompanionWarningButtonOne",
"Let the mercenary quit."); // same
cS("sCompanionWarningButtonTwo",
"Return to Companion Share display."); // same
cS("sCompanionWarningMessage",
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value.");
// 'Your mercenary is poorer now than when he contracted with
// you. Your mercenary will quit if you do not give him gold
// or goods to bring his Profit to a positive value.'
// [The difference here is "Profit Value" -> "Profit"]
// Strings that matches the id
cS("sEffectSummonFabricant", id);// 'Summon Fabricant'
return false;
}
// Bloodmoon variant
bool GameSetting::isDirtyBloodmoon()
{
// Strings
cS("sWerewolfPopup", "Werewolf"); // same
cS("sWerewolfRestMessage",
"You cannot rest in werewolf form."); // same
cS("sWerewolfRefusal",
"You cannot do this as a werewolf."); // same
cS("sWerewolfAlarmMessage",
"You have been detected changing from a werewolf state.");
// 'You have been detected as a known werewolf.'
// Strings that matches the id
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon'
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon'
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
cS("sMagicCreature04ID", id); // same
cS("sMagicCreature05ID", id); // same
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
cS("sEffectSummonCreature02", id); // 'Calf Bear'
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
cS("sEffectSummonCreature04", id); // same
cS("sEffectSummonCreature05", id); // same
// Integers
cI("iWereWolfBounty", 10000); // 1000
cI("iWereWolfFightMod", 100); // same
cI("iWereWolfFleeMod", 100); // same
cI("iWereWolfLevelToAttack", 20); // same
// Floats
cF("fFleeDistance", 3000); // same
cF("fCombatDistanceWerewolfMod", 0.3); // same
cF("fWereWolfFatigue", 400); // same
cF("fWereWolfEnchant", 1); // 0
cF("fWereWolfArmorer", 1); // 0
cF("fWereWolfBlock", 1); // 0
cF("fWereWolfSneak", 1); // 95
cF("fWereWolfDestruction", 1); // 0
cF("fWereWolfEndurance", 150); // same
cF("fWereWolfConjuration", 1); // 0
cF("fWereWolfRestoration", 1); // 0
cF("fWereWolfAthletics", 150); // 50
cF("fWereWolfLuck", 1); // 25
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
cF("fWereWolfMediumArmor", 1); // 0
cF("fWereWolfShortBlade", 1); // 0
cF("fWereWolfAcrobatics", 150); // 80
cF("fWereWolfSpeechcraft", 1); // 0
cF("fWereWolfAlteration", 1); // 0
cF("fWereWolfIllusion", 1); // 0
cF("fWereWolfLongBlade", 1); // 0
cF("fWereWolfMarksman", 1); // 0
cF("fWereWolfHandtoHand", 100); // same
cF("fWereWolfIntellegence", 1); // 0
cF("fWereWolfAlchemy", 1); // 0
cF("fWereWolfUnarmored", 100); // same
cF("fWereWolfAxe", 1); // 0
cF("fWereWolfRunMult", 1.5); // 1.3
cF("fWereWolfMagicka", 100); // same
cF("fWereWolfAgility", 150); // same
cF("fWereWolfBluntWeapon", 1); // 0
cF("fWereWolfSecurity", 1); // 0
cF("fWereWolfPersonality", 1); // 0
cF("fWereWolfMerchantile", 1); // 0
cF("fWereWolfHeavyArmor", 1); // 0
cF("fWereWolfSpear", 1); // 0
cF("fWereWolfStrength", 150); // same
cF("fWereWolfHealth", 2); // same
cF("fWereWolfMysticism", 1); // 0
cF("fWereWolfLightArmor", 1); // 0
cF("fWereWolfWillPower", 1); // 0
cF("fWereWolfSpeed", 150); // 90
return false;
}
void GameSetting::load(ESMReader &esm)
{
assert(id != "");
dirty = false;
// We are apparently allowed to be empty
if (!esm.hasMoreSubs())
{
type = VT_None;
return;
}
// Load some data
esm.getSubName();
NAME n = esm.retSubName();
if (n == "STRV")
{
str = esm.getHString();
type = VT_String;
}
else if (n == "INTV")
{
esm.getHT(i);
type = VT_Int;
}
else if (n == "FLTV")
{
esm.getHT(f);
type = VT_Float;
}
else
esm.fail("Unwanted subrecord type");
int spf = esm.getSpecial();
// Check if this is one of the dirty values mentioned above. If it
// is, we set the dirty flag. This will ONLY work if you've set
// the 'id' string correctly before calling load().
if ((spf != SF_Tribunal && isDirtyTribunal()) || (spf != SF_Bloodmoon
&& isDirtyBloodmoon()))
dirty = true;
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp" #include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Game setting, with automatic cleaning of "dirty" entries. * Game setting, with automatic cleaning of "dirty" entries.
@ -13,238 +14,75 @@ namespace ESM {
struct GameSetting struct GameSetting
{ {
std::string id; std::string id;
// One of these is used depending on the variable type // One of these is used depending on the variable type
std::string str; std::string str;
int i; int i;
float f; float f;
VarType type; VarType type;
// Set to true if this is a 'dirty' entry which should be ignored // Set to true if this is a 'dirty' entry which should be ignored
bool dirty; bool dirty;
/*
These functions check if this game setting is one of the "dirty"
GMST records found in many mods. These are due to a serious bug in
the official TES3 editor. It only occurs in the newer editor
versions that came with Tribunal and Bloodmoon, and only if a
modder tries to make a mod without loading the corresponding
expansion master file. For example, if you have Tribunal installed
and try to make a mod without loading Tribunal.esm, the editor
will insert these GMST records as a replacement for the entries it
cannot find in the ESMs.
The values of these "dirty" records differ in general from their
values as defined in Tribunal.esm and Bloodmoon.esm, and are
always set to the same "default" values. Most of these values are
nonsensical, ie. changing the "Seller Max" string to "Max Sale",
or change the stats of werewolves to useless values like 1. Some
of them break certain spell effects.
It is most likely that these values are just leftover values from
an early stage of development that are inserted as default values
by the editor code. They are supposed to be overridden when the
correct esm file is loaded. When it isn't loaded however, you get
stuck with the initial value, and this gets written to every mod
by the editor, for some reason.
Bethesda themselves have fallen for this bug. If you install both
Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
some of the dirty settings from Tribunal. In other words, this bug
affects the game EVEN IF YOU DO NOT USE ANY MODS!
The guys at Bethesda are well aware of this bug (and many others),
as the mod community and fan base complained about them for a long
time. But unfortunately it was never fixed.
There are several tools available to help modders remove these
records from their files, but not all modders use them, and they
really shouldn't have to. In this file we choose instead to reject
all the corrupt values at load time.
These functions checks if the current game setting is one of the
"dirty" ones as described above. TODO: I have not checked this
against other sources yet, do that later. Currently recognizes 22
values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
(setting values other than the default "dirty" ones) are not
affected and will work correctly.
*/
// Some handy macros
#define cI(s,x) { if(id == (s)) return (i == (x)); }
#define cF(s,x) { if(id == (s)) return (f == (x)); }
#define cS(s,x) { if(id == (s)) return (str == (x)); }
/*
Checks for dirty tribunal values. These will be ignored if found
in any file except when they are found in "Tribunal.esm".
*/
bool isDirtyTribunal()
{
/* /*
Here, id contains the game setting name, and we check the These functions check if this game setting is one of the "dirty"
setting for certain values. If it matches, this is a "dirty" GMST records found in many mods. These are due to a serious bug in
entry. The correct entry (as defined in Tribunal and Bloodmoon the official TES3 editor. It only occurs in the newer editor
esms) are given in the comments. Many of the values are correct, versions that came with Tribunal and Bloodmoon, and only if a
and are marked as 'same'. We still ignore them though, as they modder tries to make a mod without loading the corresponding
are still in the wrong file and might override custom values expansion master file. For example, if you have Tribunal installed
from other mods. and try to make a mod without loading Tribunal.esm, the editor
*/ will insert these GMST records as a replacement for the entries it
cannot find in the ESMs.
// Strings
cS("sProfitValue", "Profit Value"); // 'Profit:' The values of these "dirty" records differ in general from their
cS("sEditNote", "Edit Note"); // same values as defined in Tribunal.esm and Bloodmoon.esm, and are
cS("sDeleteNote", "Delete Note?"); // same always set to the same "default" values. Most of these values are
cS("sMaxSale", "Max Sale"); // 'Seller Max' nonsensical, ie. changing the "Seller Max" string to "Max Sale",
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon' or change the stats of werewolves to useless values like 1. Some
cS("sTeleportDisabled", of them break certain spell effects.
"Teleportation magic does not work here.");// same
cS("sLevitateDisabled", It is most likely that these values are just leftover values from
"Levitation magic does not work here."); // same an early stage of development that are inserted as default values
cS("sCompanionShare", "Companion Share"); // 'Share' by the editor code. They are supposed to be overridden when the
cS("sCompanionWarningButtonOne", correct esm file is loaded. When it isn't loaded however, you get
"Let the mercenary quit."); // same stuck with the initial value, and this gets written to every mod
cS("sCompanionWarningButtonTwo", by the editor, for some reason.
"Return to Companion Share display."); // same
cS("sCompanionWarningMessage", Bethesda themselves have fallen for this bug. If you install both
"Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value."); Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
// 'Your mercenary is poorer now than when he contracted with dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
// you. Your mercenary will quit if you do not give him gold some of the dirty settings from Tribunal. In other words, this bug
// or goods to bring his Profit to a positive value.' affects the game EVEN IF YOU DO NOT USE ANY MODS!
// [The difference here is "Profit Value" -> "Profit"]
The guys at Bethesda are well aware of this bug (and many others),
// Strings that matches the id as the mod community and fan base complained about them for a long
cS("sEffectSummonFabricant", id);// 'Summon Fabricant' time. But unfortunately it was never fixed.
return false;
} There are several tools available to help modders remove these
records from their files, but not all modders use them, and they
// Bloodmoon variant really shouldn't have to. In this file we choose instead to reject
bool isDirtyBloodmoon() all the corrupt values at load time.
{
// Strings These functions checks if the current game setting is one of the
cS("sWerewolfPopup", "Werewolf"); // same "dirty" ones as described above. TODO: I have not checked this
cS("sWerewolfRestMessage", against other sources yet, do that later. Currently recognizes 22
"You cannot rest in werewolf form."); // same values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
cS("sWerewolfRefusal", (setting values other than the default "dirty" ones) are not
"You cannot do this as a werewolf."); // same affected and will work correctly.
cS("sWerewolfAlarmMessage", */
"You have been detected changing from a werewolf state.");
// 'You have been detected as a known werewolf.' /*
Checks for dirty tribunal values. These will be ignored if found
// Strings that matches the id in any file except when they are found in "Tribunal.esm".
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon' */
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon' bool isDirtyTribunal();
cS("sMagicCreature03ID", id); // 'BM_wolf_bone_summon'
cS("sMagicCreature04ID", id); // same // Bloodmoon variant
cS("sMagicCreature05ID", id); // same bool isDirtyBloodmoon();
cS("sEffectSummonCreature01", id); // 'Calf Wolf'
cS("sEffectSummonCreature02", id); // 'Calf Bear' void load(ESMReader &esm);
cS("sEffectSummonCreature03", id); // 'Summon Bonewolf'
cS("sEffectSummonCreature04", id); // same
cS("sEffectSummonCreature05", id); // same
// Integers
cI("iWereWolfBounty", 10000); // 1000
cI("iWereWolfFightMod", 100); // same
cI("iWereWolfFleeMod", 100); // same
cI("iWereWolfLevelToAttack", 20); // same
// Floats
cF("fFleeDistance", 3000); // same
cF("fCombatDistanceWerewolfMod", 0.3); // same
cF("fWereWolfFatigue", 400); // same
cF("fWereWolfEnchant", 1); // 0
cF("fWereWolfArmorer", 1); // 0
cF("fWereWolfBlock", 1); // 0
cF("fWereWolfSneak", 1); // 95
cF("fWereWolfDestruction", 1); // 0
cF("fWereWolfEndurance", 150); // same
cF("fWereWolfConjuration", 1); // 0
cF("fWereWolfRestoration", 1); // 0
cF("fWereWolfAthletics", 150); // 50
cF("fWereWolfLuck", 1); // 25
cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2
cF("fWereWolfMediumArmor", 1); // 0
cF("fWereWolfShortBlade", 1); // 0
cF("fWereWolfAcrobatics", 150); // 80
cF("fWereWolfSpeechcraft", 1); // 0
cF("fWereWolfAlteration", 1); // 0
cF("fWereWolfIllusion", 1); // 0
cF("fWereWolfLongBlade", 1); // 0
cF("fWereWolfMarksman", 1); // 0
cF("fWereWolfHandtoHand", 100); // same
cF("fWereWolfIntellegence", 1); // 0
cF("fWereWolfAlchemy", 1); // 0
cF("fWereWolfUnarmored", 100); // same
cF("fWereWolfAxe", 1); // 0
cF("fWereWolfRunMult", 1.5); // 1.3
cF("fWereWolfMagicka", 100); // same
cF("fWereWolfAgility", 150); // same
cF("fWereWolfBluntWeapon", 1); // 0
cF("fWereWolfSecurity", 1); // 0
cF("fWereWolfPersonality", 1); // 0
cF("fWereWolfMerchantile", 1); // 0
cF("fWereWolfHeavyArmor", 1); // 0
cF("fWereWolfSpear", 1); // 0
cF("fWereWolfStrength", 150); // same
cF("fWereWolfHealth", 2); // same
cF("fWereWolfMysticism", 1); // 0
cF("fWereWolfLightArmor", 1); // 0
cF("fWereWolfWillPower", 1); // 0
cF("fWereWolfSpeed", 150); // 90
return false;
}
#undef cI
#undef cF
#undef cS
void load(ESMReader &esm)
{
assert(id != "");
dirty = false;
// We are apparently allowed to be empty
if(!esm.hasMoreSubs())
{
type = VT_None;
return;
}
// Load some data
esm.getSubName();
NAME n = esm.retSubName();
if(n == "STRV")
{
str = esm.getHString();
type = VT_String;
}
else if(n == "INTV")
{
esm.getHT(i);
type = VT_Int;
}
else if(n == "FLTV")
{
esm.getHT(f);
type = VT_Float;
}
else
esm.fail("Unwanted subrecord type");
int spf = esm.getSpecial();
// Check if this is one of the dirty values mentioned above. If it
// is, we set the dirty flag. This will ONLY work if you've set
// the 'id' string correctly before calling load().
if( ( spf != SF_Tribunal && isDirtyTribunal() ) ||
( spf != SF_Bloodmoon && isDirtyBloodmoon() ) )
dirty = true;
}
}; };
} }
#endif #endif

@ -0,0 +1,133 @@
#include "loadinfo.hpp"
namespace ESM
{
void DialInfo::load(ESMReader &esm)
{
id = esm.getHNString("INAM");
prev = esm.getHNString("PNAM");
next = esm.getHNString("NNAM");
// Not present if deleted
if (esm.isNextSub("DATA"))
esm.getHT(data, 12);
// What follows is somewhat spaghetti-ish, but it's worth if for
// an extra speedup. INFO is by far the most common record type.
// subName is a reference to the original, so it changes whenever
// a new sub name is read. esm.isEmptyOrGetName() will get the
// next name for us, or return true if there are no more records.
esm.getSubName();
const NAME &subName = esm.retSubName();
if (subName.val == REC_ONAM)
{
actor = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_RNAM)
{
race = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_CNAM)
{
clas = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
factionLess = false;
if (subName.val == REC_FNAM)
{
npcFaction = esm.getHString();
if (npcFaction == "FFFF")
factionLess = true;
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_ANAM)
{
cell = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_DNAM)
{
pcFaction = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_SNAM)
{
sound = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_NAME)
{
response = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
while (subName.val == REC_SCVR)
{
SelectStruct ss;
ss.selectRule = esm.getHString();
esm.isEmptyOrGetName();
if (subName.val == REC_INTV)
{
ss.type = VT_Int;
esm.getHT(ss.i);
}
else if (subName.val == REC_FLTV)
{
ss.type = VT_Float;
esm.getHT(ss.f);
}
else
esm.fail(
"INFO.SCVR must precede INTV or FLTV, not "
+ subName.toString());
selects.push_back(ss);
if (esm.isEmptyOrGetName())
return;
}
if (subName.val == REC_BNAM)
{
resultScript = esm.getHString();
if (esm.isEmptyOrGetName())
return;
}
questStatus = QS_None;
if (subName.val == REC_QSTN)
questStatus = QS_Name;
else if (subName.val == REC_QSTF)
questStatus = QS_Finished;
else if (subName.val == REC_QSTR)
questStatus = QS_Restart;
else if (subName.val == REC_DELE)
questStatus = QS_Deleted;
else
esm.fail(
"Don't know what to do with " + subName.toString()
+ " in INFO " + id);
if (questStatus != QS_None)
// Skip rest of record
esm.skipRecord();
}
}

@ -4,7 +4,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp" #include "defs.hpp"
namespace ESM { namespace ESM
{
// NOT DONE // NOT DONE
@ -15,88 +16,88 @@ namespace ESM {
struct DialInfo struct DialInfo
{ {
enum Gender enum Gender
{
Male = 0,
Female = 1,
NA = -1
};
struct DATAstruct
{
int unknown1;
int disposition;
char rank; // Rank of NPC
char gender; // See Gender enum
char PCrank; // Player rank
char unknown2;
}; // 12 bytes
DATAstruct data;
// The rules for whether or not we will select this dialog item.
struct SelectStruct
{ {
Male = 0, std::string selectRule; // This has a complicated format
Female = 1, float f; // Only one of 'f' or 'i' is used
NA = -1 int i;
VarType type;
}; };
struct DATAstruct // Journal quest indices (introduced with the quest system in Tribunal)
{ enum QuestStatus
int unknown1;
int disposition;
char rank; // Rank of NPC
char gender; // See Gender enum
char PCrank; // Player rank
char unknown2;
}; // 12 bytes
DATAstruct data;
// The rules for whether or not we will select this dialog item.
struct SelectStruct
{
std::string selectRule; // This has a complicated format
float f; // Only one of 'f' or 'i' is used
int i;
VarType type;
};
// Journal quest indices (introduced with the quest system in Tribunal)
enum QuestStatus
{ {
QS_None, QS_None,
QS_Name, QS_Name,
QS_Finished, QS_Finished,
QS_Restart, QS_Restart,
QS_Deleted QS_Deleted
}; };
// Rules for when to include this item in the final list of options // Rules for when to include this item in the final list of options
// visible to the player. // visible to the player.
std::vector<SelectStruct> selects; std::vector<SelectStruct> selects;
// Id of this, previous and next INFO items // Id of this, previous and next INFO items
std::string id, prev, next, std::string id, prev, next,
// Various references used in determining when to select this item. // Various references used in determining when to select this item.
actor, race, clas, npcFaction, pcFaction, cell, actor, race, clas, npcFaction, pcFaction, cell,
// Sound and text associated with this item // Sound and text associated with this item
sound, response, sound, response,
// Result script (uncomiled) to run whenever this dialog item is // Result script (uncomiled) to run whenever this dialog item is
// selected // selected
resultScript; resultScript;
// ONLY include this item the NPC is not part of any faction. // ONLY include this item the NPC is not part of any faction.
bool factionLess; bool factionLess;
// Status of this quest item // Status of this quest item
QuestStatus questStatus; QuestStatus questStatus;
// Hexadecimal versions of the various subrecord names. // Hexadecimal versions of the various subrecord names.
enum SubNames enum SubNames
{ {
REC_ONAM = 0x4d414e4f, REC_ONAM = 0x4d414e4f,
REC_RNAM = 0x4d414e52, REC_RNAM = 0x4d414e52,
REC_CNAM = 0x4d414e43, REC_CNAM = 0x4d414e43,
REC_FNAM = 0x4d414e46, REC_FNAM = 0x4d414e46,
REC_ANAM = 0x4d414e41, REC_ANAM = 0x4d414e41,
REC_DNAM = 0x4d414e44, REC_DNAM = 0x4d414e44,
REC_SNAM = 0x4d414e53, REC_SNAM = 0x4d414e53,
REC_NAME = 0x454d414e, REC_NAME = 0x454d414e,
REC_SCVR = 0x52564353, REC_SCVR = 0x52564353,
REC_INTV = 0x56544e49, REC_INTV = 0x56544e49,
REC_FLTV = 0x56544c46, REC_FLTV = 0x56544c46,
REC_BNAM = 0x4d414e42, REC_BNAM = 0x4d414e42,
REC_QSTN = 0x4e545351, REC_QSTN = 0x4e545351,
REC_QSTF = 0x46545351, REC_QSTF = 0x46545351,
REC_QSTR = 0x52545351, REC_QSTR = 0x52545351,
REC_DELE = 0x454c4544 REC_DELE = 0x454c4544
}; };
void load(ESMReader &esm); void load(ESMReader &esm);
}; };
/* /*

@ -0,0 +1,15 @@
#include "loadingr.hpp"
namespace ESM
{
void Ingredient::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "IRDT", 56);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Alchemy ingredient * Alchemy ingredient
@ -11,26 +12,19 @@ namespace ESM {
struct Ingredient struct Ingredient
{ {
struct IRDTstruct struct IRDTstruct
{ {
float weight; float weight;
int value; int value;
int effectID[4]; // Effect, 0 or -1 means none int effectID[4]; // Effect, 0 or -1 means none
int skills[4]; // SkillEnum related to effect int skills[4]; // SkillEnum related to effect
int attributes[4]; // Attribute related to effect int attributes[4]; // Attribute related to effect
}; };
IRDTstruct data; IRDTstruct data;
std::string name, model, icon, script; std::string name, model, icon, script;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getHNT(data, "IRDT", 56);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}; };
} }
#endif #endif

@ -0,0 +1,34 @@
#include "loadlevlist.hpp"
namespace ESM
{
void LeveledListBase::load(ESMReader &esm)
{
esm.getHNT(flags, "DATA");
esm.getHNT(chanceNone, "NNAM");
if (esm.isNextSub("INDX"))
{
int len;
esm.getHT(len);
list.resize(len);
}
else
return;
// TODO: Merge with an existing lists here. This can be done
// simply by adding the lists together, making sure that they are
// sorted by level. A better way might be to exclude repeated
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for (size_t i = 0; i < list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);
esm.getHNT(li.level, "INTV");
}
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Leveled lists. Since these have identical layout, I only bothered * Leveled lists. Since these have identical layout, I only bothered
@ -15,65 +16,50 @@ namespace ESM {
struct LeveledListBase struct LeveledListBase
{ {
enum Flags enum Flags
{ {
AllLevels = 0x01, // Calculate from all levels <= player AllLevels = 0x01, // Calculate from all levels <= player
// level, not just the closest below // level, not just the closest below
// player. // player.
Each = 0x02 // Select a new item each time this Each = 0x02 // Select a new item each time this
// list is instantiated, instead of // list is instantiated, instead of
// giving several identical items // giving several identical items
}; // (used when a container has more }; // (used when a container has more
// than one instance of one leveled // than one instance of one leveled
// list.) // list.)
int flags; int flags;
unsigned char chanceNone; // Chance that none are selected (0-255?) unsigned char chanceNone; // Chance that none are selected (0-255?)
// Record name used to read references. Must be set before load() is
// called.
const char *recName;
struct LevelItem
{
std::string id;
short level;
};
std::vector<LevelItem> list; // Record name used to read references. Must be set before load() is
// called.
const char *recName;
void load(ESMReader &esm) struct LevelItem
{ {
esm.getHNT(flags, "DATA"); std::string id;
esm.getHNT(chanceNone, "NNAM"); short level;
};
if(esm.isNextSub("INDX")) std::vector<LevelItem> list;
{
int len;
esm.getHT(len);
list.resize(len);
}
else return;
// TODO: Merge with an existing lists here. This can be done void load(ESMReader &esm);
// simply by adding the lists together, making sure that they are
// sorted by level. A better way might be to exclude repeated
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for(size_t i=0; i<list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);
esm.getHNT(li.level, "INTV");
}
}
}; };
struct CreatureLevList : LeveledListBase struct CreatureLevList: LeveledListBase
{ CreatureLevList() { recName = "CNAM"; } }; {
CreatureLevList()
{
recName = "CNAM";
}
};
struct ItemLevList : LeveledListBase struct ItemLevList: LeveledListBase
{ ItemLevList() { recName = "INAM"; } }; {
ItemLevList()
{
recName = "INAM";
}
};
} }
#endif #endif

@ -0,0 +1,17 @@
#include "loadligh.hpp"
namespace ESM
{
void Light::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
icon = esm.getHNOString("ITEX");
assert(sizeof(data) == 24);
esm.getHNT(data, "LHDT", 24);
script = esm.getHNOString("SCRI");
sound = esm.getHNOString("SNAM");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Lights. Includes static light sources and also carryable candles * Lights. Includes static light sources and also carryable candles
@ -12,43 +13,34 @@ namespace ESM {
struct Light struct Light
{ {
enum Flags enum Flags
{ {
Dynamic = 0x001, Dynamic = 0x001,
Carry = 0x002, // Can be carried Carry = 0x002, // Can be carried
Negative = 0x004, // Negative light? Negative = 0x004, // Negative light?
Flicker = 0x008, Flicker = 0x008,
Fire = 0x010, Fire = 0x010,
OffDefault = 0x020, // Off by default OffDefault = 0x020, // Off by default
FlickerSlow = 0x040, FlickerSlow = 0x040,
Pulse = 0x080, Pulse = 0x080,
PulseSlow = 0x100 PulseSlow = 0x100
}; };
struct LHDTstruct struct LHDTstruct
{ {
float weight; float weight;
int value; int value;
int time; // Duration int time; // Duration
int radius; int radius;
int color; // 4-byte rgba value int color; // 4-byte rgba value
int flags; int flags;
}; // Size = 24 bytes }; // Size = 24 bytes
LHDTstruct data; LHDTstruct data;
std::string sound, script, model, icon, name; std::string sound, script, model, icon, name;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
icon = esm.getHNOString("ITEX");
assert(sizeof(data) == 24);
esm.getHNT(data, "LHDT", 24);
script = esm.getHNOString("SCRI");
sound = esm.getHNOString("SNAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,30 @@
#include "loadlocks.hpp"
namespace ESM
{
void Tool::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getSubName();
NAME n = esm.retSubName();
// The data name varies, RIDT for repair items, LKDT for lock
// picks, PBDT for probes
esm.getHT(data, 16);
if (n == "RIDT")
{
// Swap t.data.quality and t.data.uses for repair items (sigh)
float tmp = *((float*) &data.uses);
data.uses = *((int*) &data.quality);
data.quality = tmp;
}
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* This file covers lockpicks (LOCK), probes (PROB) and armor repair * This file covers lockpicks (LOCK), probes (PROB) and armor repair
@ -12,51 +13,29 @@ namespace ESM {
struct Tool struct Tool
{ {
struct Data struct Data
{ {
float weight; float weight;
int value; int value;
float quality; // And when I say nearly identical structure, I
int uses; // mean perfectly identical except that these two
// variables are swaped for repair items. Don't ask
// me why.
}; // Size = 16
Data data;
std::string name, model, icon, script;
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNString("FNAM");
esm.getSubName();
NAME n = esm.retSubName();
// The data name varies, RIDT for repair items, LKDT for lock
// picks, PBDT for probes
esm.getHT(data, 16); float quality; // And when I say nearly identical structure, I
int uses; // mean perfectly identical except that these two
// variables are swaped for repair items. Don't ask
// me why.
}; // Size = 16
if(n == "RIDT") Data data;
{ std::string name, model, icon, script;
// Swap t.data.quality and t.data.uses for repair items (sigh)
float tmp = *((float*)&data.uses);
data.uses = *((int*)&data.quality);
data.quality = tmp;
}
script = esm.getHNOString("SCRI"); void load(ESMReader &esm);
icon = esm.getHNOString("ITEX");
}
}; };
struct Probe : Tool struct Probe: Tool
{ {
}; };
struct Repair : Tool struct Repair: Tool
{ {
}; };

@ -0,0 +1,12 @@
#include "loadltex.hpp"
namespace ESM
{
void LandTexture::load(ESMReader &esm)
{
esm.getHNT(index, "INTV");
texture = esm.getHNString("DATA");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Texture used for texturing landscape. * Texture used for texturing landscape.
@ -23,14 +24,10 @@ namespace ESM {
struct LandTexture struct LandTexture
{ {
std::string id, texture; std::string id, texture;
int index; int index;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(index, "INTV");
texture = esm.getHNString("DATA");
}
}; };
} }
#endif #endif

@ -0,0 +1,27 @@
#include "loadmgef.hpp"
namespace ESM
{
void MagicEffect::load(ESMReader &esm)
{
esm.getHNT(index, "INDX");
esm.getHNT(data, "MEDT", 36);
icon = esm.getHNOString("ITEX");
particle = esm.getHNOString("PTEX");
boltSound = esm.getHNOString("BSND");
castSound = esm.getHNOString("CSND");
hitSound = esm.getHNOString("HSND");
areaSound = esm.getHNOString("ASND");
casting = esm.getHNOString("CVFX");
bolt = esm.getHNOString("BVFX");
hit = esm.getHNOString("HVFX");
area = esm.getHNOString("AVFX");
description = esm.getHNOString("DESC");
}
}

@ -3,68 +3,49 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct MagicEffect struct MagicEffect
{ {
enum Flags enum Flags
{ {
SpellMaking = 0x0200, SpellMaking = 0x0200,
Enchanting = 0x0400, Enchanting = 0x0400,
Negative = 0x0800 // A harmful effect. Will determine whether Negative = 0x0800 // A harmful effect. Will determine whether
// eg. NPCs regard this spell as an attack. // eg. NPCs regard this spell as an attack.
}; };
struct MEDTstruct struct MEDTstruct
{ {
int school; // SpellSchool, see defs.hpp int school; // SpellSchool, see defs.hpp
float baseCost; float baseCost;
int flags; int flags;
// Properties of the fired magic 'ball' I think // Properties of the fired magic 'ball' I think
int red, blue, green; int red, blue, green;
float speed, size, sizeCap; float speed, size, sizeCap;
}; // 36 bytes }; // 36 bytes
MEDTstruct data; MEDTstruct data;
std::string icon, particle, // Textures std::string icon, particle, // Textures
casting, hit, area, // Statics casting, hit, area, // Statics
bolt, // Weapon bolt, // Weapon
castSound, boltSound, castSound, boltSound, hitSound, areaSound, // Sounds
hitSound, areaSound, // Sounds description;
description;
// Index of this magical effect. Corresponds to one of the
// Index of this magical effect. Corresponds to one of the // hard-coded effects in the original engine:
// hard-coded effects in the original engine: // 0-136 in Morrowind
// 0-136 in Morrowind // 137 in Tribunal
// 137 in Tribunal // 138-140 in Bloodmoon (also changes 64?)
// 138-140 in Bloodmoon (also changes 64?) // 141-142 are summon effects introduced in bloodmoon, but not used
// 141-142 are summon effects introduced in bloodmoon, but not used // there. They can be redefined in mods by setting the name in GMST
// there. They can be redefined in mods by setting the name in GMST // sEffectSummonCreature04/05 creature id in
// sEffectSummonCreature04/05 creature id in // sMagicCreature04ID/05ID.
// sMagicCreature04ID/05ID. int index;
int index;
void load(ESMReader &esm);
void load(ESMReader &esm)
{
esm.getHNT(index, "INDX");
esm.getHNT(data, "MEDT", 36);
icon = esm.getHNOString("ITEX");
particle = esm.getHNOString("PTEX");
boltSound = esm.getHNOString("BSND");
castSound = esm.getHNOString("CSND");
hitSound = esm.getHNOString("HSND");
areaSound = esm.getHNOString("ASND");
casting = esm.getHNOString("CVFX");
bolt = esm.getHNOString("BVFX");
hit = esm.getHNOString("HVFX");
area = esm.getHNOString("AVFX");
description = esm.getHNOString("DESC");
}
}; };
} }
#endif #endif

@ -0,0 +1,15 @@
#include "loadmisc.hpp"
namespace ESM
{
void Misc::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "MCDT", 12);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Misc inventory items, basically things that have no use but can be * Misc inventory items, basically things that have no use but can be
@ -12,26 +13,19 @@ namespace ESM {
struct Misc struct Misc
{ {
struct MCDTstruct struct MCDTstruct
{ {
float weight; float weight;
int value; int value;
int isKey; // There are many keys in Morrowind.esm that has this int isKey; // There are many keys in Morrowind.esm that has this
// set to 0. TODO: Check what this field corresponds to // set to 0. TODO: Check what this field corresponds to
// in the editor. // in the editor.
}; };
MCDTstruct data; MCDTstruct data;
std::string name, model, icon, script; std::string name, model, icon, script;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "MCDT", 12);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
}
}; };
} }
#endif #endif

@ -0,0 +1,48 @@
#include "loadnpc.hpp"
namespace ESM
{
void NPC::load(ESMReader &esm, const std::string& id)
{
mId = id;
npdt52.gold = -10;
model = esm.getHNOString("MODL");
name = esm.getHNOString("FNAM");
race = esm.getHNString("RNAM");
cls = esm.getHNString("CNAM");
faction = esm.getHNString("ANAM");
head = esm.getHNString("BNAM");
hair = esm.getHNString("KNAM");
script = esm.getHNOString("SCRI");
esm.getSubNameIs("NPDT");
esm.getSubHeader();
if (esm.getSubSize() == 52)
esm.getExact(&npdt52, 52);
else if (esm.getSubSize() == 12)
esm.getExact(&npdt12, 12);
else
esm.fail("NPC_NPDT must be 12 or 52 bytes long");
esm.getHNT(flags, "FLAG");
inventory.load(esm);
spells.load(esm);
if (esm.isNextSub("AIDT"))
{
esm.getHExact(&AI, sizeof(AI));
hasAI = true;
}
else
hasAI = false;
esm.skipRecord();
}
}

@ -0,0 +1,35 @@
#include "loadpgrd.hpp"
namespace ESM
{
void PathGrid::load(ESMReader &esm)
{
esm.getHNT(data, "DATA", 12);
cell = esm.getHNString("NAME");
// Remember this file position
context = esm.getContext();
// Check that the sizes match up. Size = 16 * s2 (path points?)
if (esm.isNextSub("PGRP"))
{
esm.skipHSub();
int size = esm.getSubSize();
if (size != 16 * data.s2)
esm.fail("Path grid table size mismatch");
}
// Size varies. Path grid chances? Connections? Multiples of 4
// suggest either int or two shorts, or perhaps a float. Study
// it later.
if (esm.isNextSub("PGRC"))
{
esm.skipHSub();
int size = esm.getSubSize();
if (size % 4 != 0)
esm.fail("PGRC size not a multiple of 4");
}
}
}

@ -3,54 +3,28 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Path grid. * Path grid.
*/ */
struct PathGrid struct PathGrid
{ {
struct DATAstruct struct DATAstruct
{ {
int x, y; // Grid location, matches cell for exterior cells int x, y; // Grid location, matches cell for exterior cells
short s1; // ?? Usually but not always a power of 2. Doesn't seem short s1; // ?? Usually but not always a power of 2. Doesn't seem
// to have any relation to the size of PGRC. // to have any relation to the size of PGRC.
short s2; // Number of path points? Size of PGRP block is always 16 * s2; short s2; // Number of path points? Size of PGRP block is always 16 * s2;
}; // 12 bytes }; // 12 bytes
std::string cell; // Cell name
DATAstruct data;
ESM_Context context; // Context so we can return here later and
// finish the job
void load(ESMReader &esm)
{
esm.getHNT(data, "DATA", 12);
cell = esm.getHNString("NAME");
// Remember this file position std::string cell; // Cell name
context = esm.getContext(); DATAstruct data;
ESM_Context context; // Context so we can return here later and
// Check that the sizes match up. Size = 16 * s2 (path points?) // finish the job
if(esm.isNextSub("PGRP"))
{
esm.skipHSub();
int size = esm.getSubSize();
if(size != 16*data.s2)
esm.fail("Path grid table size mismatch");
}
// Size varies. Path grid chances? Connections? Multiples of 4 void load(ESMReader &esm);
// suggest either int or two shorts, or perhaps a float. Study
// it later.
if(esm.isNextSub("PGRC"))
{
esm.skipHSub();
int size = esm.getSubSize();
if(size % 4 != 0)
esm.fail("PGRC size not a multiple of 4");
}
}
}; };
} }
#endif #endif

@ -0,0 +1,14 @@
#include "loadrace.hpp"
namespace ESM
{
void Race::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
esm.getHNT(data, "RADT", 140);
powers.load(esm);
description = esm.getHNOString("DESC");
}
}

@ -2,8 +2,10 @@
#define _ESM_RACE_H #define _ESM_RACE_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
/* /*
* Race definition * Race definition
@ -11,58 +13,51 @@ namespace ESM {
struct Race struct Race
{ {
struct SkillBonus struct SkillBonus
{ {
int skill; // SkillEnum int skill; // SkillEnum
int bonus; int bonus;
}; };
struct MaleFemale struct MaleFemale
{ {
int male, female; int male, female;
}; };
struct MaleFemaleF struct MaleFemaleF
{ {
float male, female; float male, female;
}; };
enum Flags enum Flags
{ {
Playable = 0x01, Playable = 0x01,
Beast = 0x02 Beast = 0x02
}; };
struct RADTstruct struct RADTstruct
{ {
// List of skills that get a bonus // List of skills that get a bonus
SkillBonus bonus[7]; SkillBonus bonus[7];
// Attribute values for male/female // Attribute values for male/female
MaleFemale strength, intelligence, willpower, agility, MaleFemale strength, intelligence, willpower, agility, speed,
speed, endurance, personality, luck; endurance, personality, luck;
// The actual eye level height (in game units) is (probably) given // The actual eye level height (in game units) is (probably) given
// as 'height' times 128. This has not been tested yet. // as 'height' times 128. This has not been tested yet.
MaleFemaleF height, weight; MaleFemaleF height, weight;
int flags; // 0x1 - playable, 0x2 - beast race int flags; // 0x1 - playable, 0x2 - beast race
// Size = 140 bytes }; // Size = 140 bytes
};
RADTstruct data; RADTstruct data;
std::string name, description; std::string name, description;
SpellList powers; SpellList powers;
void load(ESMReader &esm) void load(ESMReader &esm);
{
name = esm.getHNString("FNAM");
esm.getHNT(data, "RADT", 140);
powers.load(esm);
description = esm.getHNOString("DESC");
}
}; };
} }
#endif #endif

@ -0,0 +1,29 @@
#include "loadregn.hpp"
namespace ESM
{
void Region::load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
if (esm.getVer() == VER_12)
esm.getHNExact(&data, sizeof(data) - 2, "WEAT");
else if (esm.getVer() == VER_13)
esm.getHNExact(&data, sizeof(data), "WEAT");
else
esm.fail("Don't know what to do in this version");
sleepList = esm.getHNOString("BNAM");
esm.getHNT(mapColor, "CNAM");
while (esm.hasMoreSubs())
{
SoundRef sr;
esm.getHNT(sr, "SNAM", 33);
soundList.push_back(sr);
}
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Region data * Region data
@ -13,54 +14,33 @@ struct Region
{ {
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
struct WEATstruct struct WEATstruct
{ {
// I guess these are probabilities // I guess these are probabilities
char clear, cloudy, foggy, overcast, rain, thunder, ash, char clear, cloudy, foggy, overcast, rain, thunder, ash, blight,
blight, // Unknown weather, probably snow and something. Only
// Unknown weather, probably snow and something. Only // present in file version 1.3.
// present in file version 1.3. a, b;
a,b; }; // 10 bytes
}; // 10 bytes
// Reference to a sound that is played randomly in this region
// Reference to a sound that is played randomly in this region struct SoundRef
struct SoundRef {
{ NAME32 sound;
NAME32 sound; char chance;
char chance; }; // 33 bytes
}; // 33 bytes
#pragma pack(pop) #pragma pack(pop)
WEATstruct data; WEATstruct data;
int mapColor; // RGBA int mapColor; // RGBA
// sleepList refers to a eveled list of creatures you can meet if
// you sleep outside in this region.
std::string name, sleepList;
std::vector<SoundRef> soundList;
void load(ESMReader &esm)
{
name = esm.getHNString("FNAM");
if(esm.getVer() == VER_12)
esm.getHNExact(&data, sizeof(data)-2, "WEAT");
else if(esm.getVer() == VER_13)
esm.getHNExact(&data, sizeof(data), "WEAT");
else esm.fail("Don't know what to do in this version");
sleepList = esm.getHNOString("BNAM"); // sleepList refers to a eveled list of creatures you can meet if
// you sleep outside in this region.
std::string name, sleepList;
esm.getHNT(mapColor, "CNAM"); std::vector<SoundRef> soundList;
while(esm.hasMoreSubs()) void load(ESMReader &esm);
{
SoundRef sr;
esm.getHNT(sr, "SNAM", 33);
soundList.push_back(sr);
}
}
}; };
} }
#endif #endif

@ -0,0 +1,42 @@
#include "loadscpt.hpp"
namespace ESM
{
void Script::load(ESMReader &esm)
{
esm.getHNT(data, "SCHD", 52);
// List of local variables
if (esm.isNextSub("SCVR"))
{
int s = data.stringTableSize;
char* tmp = new char[s];
esm.getHExact(tmp, s);
// Set up the list of variable names
varNames.resize(data.numShorts + data.numLongs + data.numFloats);
// The tmp buffer is a null-byte separated string list, we
// just have to pick out one string at a time.
char* str = tmp;
for (size_t i = 0; i < varNames.size(); i++)
{
varNames[i] = std::string(str);
str += varNames[i].size() + 1;
if (str - tmp > s)
esm.fail("String table overflow");
}
delete[] tmp;
}
// Script data
scriptData.resize(data.scriptDataSize);
esm.getHNExact(&scriptData[0], scriptData.size(), "SCDT");
// Script text
scriptText = esm.getHNOString("SCTX");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Script definitions * Script definitions
@ -12,80 +13,43 @@ namespace ESM {
class Script class Script
{ {
public: public:
struct SCHDstruct struct SCHDstruct
{ {
/* Script name. /* Script name.
NOTE: You should handle the name "Main" (case insensitive) with NOTE: You should handle the name "Main" (case insensitive) with
care. With tribunal, modders got the ability to add 'start care. With tribunal, modders got the ability to add 'start
scripts' to their mods, which is a script that is run at scripts' to their mods, which is a script that is run at
startup and which runs throughout the game (I think.) startup and which runs throughout the game (I think.)
However, before Tribunal, there was only one startup script, However, before Tribunal, there was only one startup script,
called "Main". If mods wanted to make their own start scripts, called "Main". If mods wanted to make their own start scripts,
they had to overwrite Main. This is obviously problem if they had to overwrite Main. This is obviously problem if
multiple mods to this at the same time. multiple mods to this at the same time.
Although most mods have switched to using Trib-style startup Although most mods have switched to using Trib-style startup
scripts, some legacy mods might still overwrite Main, and this scripts, some legacy mods might still overwrite Main, and this
can cause problems if several mods do it. I think the best can cause problems if several mods do it. I think the best
course of action is to NEVER overwrite main, but instead add course of action is to NEVER overwrite main, but instead add
each with a separate unique name and add them to the start each with a separate unique name and add them to the start
script list. But there might be other problems with this script list. But there might be other problems with this
approach though. approach though.
*/ */
NAME32 name; NAME32 name;
// These describe the sizes we need to allocate for the script // These describe the sizes we need to allocate for the script
// data. // data.
int numShorts, numLongs, numFloats, int numShorts, numLongs, numFloats, scriptDataSize, stringTableSize;
scriptDataSize, stringTableSize; }; // 52 bytes
}; // 52 bytes
SCHDstruct data;
SCHDstruct data;
std::vector<std::string> varNames; // Variable names
std::vector<std::string> varNames; // Variable names std::vector<char> scriptData; // Compiled bytecode
std::vector<char> scriptData; // Compiled bytecode std::string scriptText; // Uncompiled script
std::string scriptText; // Uncompiled script
void load(ESMReader &esm);
void load(ESMReader &esm)
{
esm.getHNT(data, "SCHD", 52);
// List of local variables
if(esm.isNextSub("SCVR"))
{
int s = data.stringTableSize;
char* tmp = new char[s];
esm.getHExact(tmp, s);
// Set up the list of variable names
varNames.resize(data.numShorts +
data.numLongs +
data.numFloats);
// The tmp buffer is a null-byte separated string list, we
// just have to pick out one string at a time.
char* str = tmp;
for(size_t i=0; i< varNames.size(); i++)
{
varNames[i] = std::string(str);
str += varNames[i].size()+1;
if(str - tmp > s)
esm.fail("String table overflow");
}
delete[] tmp;
}
// Script data
scriptData.resize(data.scriptDataSize);
esm.getHNExact(&scriptData[0], scriptData.size(), "SCDT");
// Script text
scriptText = esm.getHNOString("SCTX");
}
}; };
} }
#endif #endif

@ -1,33 +1,70 @@
#include "loadskil.hpp" #include "loadskil.hpp"
namespace ESMS namespace ESM
{ {
const std::string Skill::sSkillNames[Length] = { const std::string Skill::sSkillNameIds[Length] = {
"Block", "sSkillBlock",
"Armorer", "sSkillArmorer",
"Medium Armor", "sSkillMediumarmor",
"Heavy Armor", "sSkillHeavyarmor",
"Blunt Weapon", "sSkillBluntweapon",
"Long Blade", "sSkillLongblade",
"Axe", "sSkillAxe",
"Spear", "sSkillSpear",
"Athletics", "sSkillAthletics",
"Enchant", "sSkillEnchant",
"Destruction", "sSkillDestruction",
"Alteration", "sSkillAlteration",
"Illusion", "sSkillIllusion",
"Conjuration", "sSkillConjuration",
"Mysticism", "sSkillMysticism",
"Restoration", "sSkillRestoration",
"Alchemy", "sSkillAlchemy",
"Unarmored", "sSkillUnarmored",
"Security", "sSkillSecurity",
"Sneak", "sSkillSneak",
"Acrobatics", "sSkillAcrobatics",
"Light Armor", "sSkillLightarmor",
"Short Blade", "sSkillShortblade",
"Marksman", "sSkillMarksman",
"Speechcraft", "sSkillMercantile",
"Hand To Hand", "sSkillSpeechcraft",
"sSkillHandtohand",
}; };
const boost::array<Skill::SkillEnum, Skill::Length> Skill::skillIds = {{
Block,
Armorer,
MediumArmor,
HeavyArmor,
BluntWeapon,
LongBlade,
Axe,
Spear,
Athletics,
Enchant,
Destruction,
Alteration,
Illusion,
Conjuration,
Mysticism,
Restoration,
Alchemy,
Unarmored,
Security,
Sneak,
Acrobatics,
LightArmor,
ShortBlade,
Marksman,
Mercantile,
Speechcraft,
HandToHand
}};
void Skill::load(ESMReader &esm)
{
esm.getHNT(index, "INDX");
esm.getHNT(data, "SKDT", 24);
description = esm.getHNOString("DESC");
}
} }

@ -66,12 +66,7 @@ struct Skill
static const std::string sSkillNameIds[Length]; static const std::string sSkillNameIds[Length];
static const boost::array<SkillEnum, Length> skillIds; static const boost::array<SkillEnum, Length> skillIds;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(index, "INDX");
esm.getHNT(data, "SKDT", 24);
description = esm.getHNOString("DESC");
}
}; };
} }
#endif #endif

@ -0,0 +1,14 @@
#include "loadsndg.hpp"
namespace ESM
{
void SoundGenerator::load(ESMReader &esm)
{
esm.getHNT(type, "DATA", 4);
creature = esm.getHNOString("CNAM");
sound = esm.getHNOString("SNAM");
}
}

@ -28,13 +28,7 @@ struct SoundGenerator
std::string creature, sound; std::string creature, sound;
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getHNT(type, "DATA", 4);
creature = esm.getHNOString("CNAM");
sound = esm.getHNOString("SNAM");
}
}; };
} }
#endif #endif

@ -0,0 +1,18 @@
#include "loadsoun.hpp"
namespace ESM
{
void Sound::load(ESMReader &esm)
{
sound = esm.getHNString("FNAM");
esm.getHNT(data, "DATA", 3);
/*
cout << "vol=" << (int)data.volume
<< " min=" << (int)data.minRange
<< " max=" << (int)data.maxRange
<< endl;
*/
}
}

@ -3,20 +3,20 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
struct SOUNstruct struct SOUNstruct
{ {
unsigned char volume, minRange, maxRange; unsigned char volume, minRange, maxRange;
}; };
struct Sound struct Sound
{ {
SOUNstruct data; SOUNstruct data;
std::string sound; std::string sound;
// Body moved to load_impl.cpp void load(ESMReader &esm);
void load(ESMReader &esm);
}; };
} }
#endif #endif

@ -0,0 +1,13 @@
#include "loadspel.hpp"
namespace ESM
{
void Spell::load(ESMReader &esm)
{
name = esm.getHNOString("FNAM");
esm.getHNT(data, "SPDT", 12);
effects.load(esm);
}
}

@ -2,45 +2,42 @@
#define _ESM_SPEL_H #define _ESM_SPEL_H
#include "esm_reader.hpp" #include "esm_reader.hpp"
#include "defs.hpp"
namespace ESM { namespace ESM
{
struct Spell struct Spell
{ {
enum SpellType enum SpellType
{ {
ST_Spell = 0, // Normal spell, must be cast and costs mana ST_Spell = 0, // Normal spell, must be cast and costs mana
ST_Ability = 1, // Inert ability, always in effect ST_Ability = 1, // Inert ability, always in effect
ST_Blight = 2, // Blight disease ST_Blight = 2, // Blight disease
ST_Disease = 3, // Common disease ST_Disease = 3, // Common disease
ST_Curse = 4, // Curse (?) ST_Curse = 4, // Curse (?)
ST_Power = 5 // Power, can use once a day ST_Power = 5 // Power, can use once a day
}; };
enum Flags enum Flags
{ {
F_Autocalc = 1, F_Autocalc = 1,
F_PCStart = 2, F_PCStart = 2,
F_Always = 4 // Casting always succeeds F_Always = 4 // Casting always succeeds
}; };
struct SPDTstruct struct SPDTstruct
{ {
int type; // SpellType int type; // SpellType
int cost; // Mana cost int cost; // Mana cost
int flags; // Flags int flags; // Flags
}; };
SPDTstruct data; SPDTstruct data;
std::string name; std::string name;
EffectList effects; EffectList effects;
void load(ESMReader &esm) void load(ESMReader &esm);
{
name = esm.getHNOString("FNAM");
esm.getHNT(data, "SPDT", 12);
effects.load(esm);
}
}; };
} }
#endif #endif

@ -0,0 +1,13 @@
#include "loadsscr.hpp"
namespace ESM
{
void StartScript::load(ESMReader &esm)
{
esm.getSubNameIs("DATA");
esm.skipHSub();
script = esm.getHNString("NAME");
}
}

@ -3,27 +3,23 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
Startup script. I think this is simply a 'main' script that is run Startup script. I think this is simply a 'main' script that is run
from the begining. The SSCR records contain a DATA identifier which from the begining. The SSCR records contain a DATA identifier which
is totally useless (TODO: don't remember what it contains exactly, is totally useless (TODO: don't remember what it contains exactly,
document it below later.), and a NAME which is simply a script document it below later.), and a NAME which is simply a script
reference. reference.
*/ */
struct StartScript struct StartScript
{ {
std::string script; std::string script;
// Load a record and add it to the list // Load a record and add it to the list
void load(ESMReader &esm) void load(ESMReader &esm);
{
esm.getSubNameIs("DATA");
esm.skipHSub();
script = esm.getHNString("NAME");
}
}; };
} }

@ -0,0 +1,11 @@
#include "loadstat.hpp"
namespace ESM
{
void Static::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
}
}

@ -21,10 +21,7 @@ struct Static
{ {
std::string model; std::string model;
void load(ESMReader &esm) void load(ESMReader &esm);
{
model = esm.getHNString("MODL");
}
}; };
} }
#endif #endif

@ -0,0 +1,16 @@
#include "loadweap.hpp"
namespace ESM
{
void Weapon::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "WPDT", 32);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
enchant = esm.getHNOString("ENAM");
}
}

@ -3,7 +3,8 @@
#include "esm_reader.hpp" #include "esm_reader.hpp"
namespace ESM { namespace ESM
{
/* /*
* Weapon definition * Weapon definition
@ -11,58 +12,50 @@ namespace ESM {
struct Weapon struct Weapon
{ {
enum Type enum Type
{ {
ShortBladeOneHand = 0, ShortBladeOneHand = 0,
LongBladeOneHand = 1, LongBladeOneHand = 1,
LongBladeTwoHand = 2, LongBladeTwoHand = 2,
BluntOneHand = 3, BluntOneHand = 3,
BluntTwoClose = 4, BluntTwoClose = 4,
BluntTwoWide = 5, BluntTwoWide = 5,
SpearTwoWide = 6, SpearTwoWide = 6,
AxeOneHand = 7, AxeOneHand = 7,
AxeTwoHand = 8, AxeTwoHand = 8,
MarksmanBow = 9, MarksmanBow = 9,
MarksmanCrossbow = 10, MarksmanCrossbow = 10,
MarksmanThrown = 11, MarksmanThrown = 11,
Arrow = 12, Arrow = 12,
Bolt = 13 Bolt = 13
}; };
enum Flags enum Flags
{ {
Magical = 0x01, Magical = 0x01,
Silver = 0x02 Silver = 0x02
}; };
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)
struct WPDTstruct struct WPDTstruct
{ {
float weight; float weight;
int value; int value;
short type; short type;
short health; short health;
float speed, reach; float speed, reach;
short enchant; // Enchantment points short enchant; // Enchantment points
unsigned char chop[2], slash[2], thrust[2]; // Min and max unsigned char chop[2], slash[2], thrust[2]; // Min and max
int flags; int flags;
}; // 32 bytes }; // 32 bytes
#pragma pack(pop) #pragma pack(pop)
WPDTstruct data; WPDTstruct data;
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, "WPDT", 32);
script = esm.getHNOString("SCRI");
icon = esm.getHNOString("ITEX");
enchant = esm.getHNOString("ENAM");
}
}; };
} }
#endif #endif

@ -1,63 +0,0 @@
#include "loadskil.hpp"
namespace ESM
{
const std::string Skill::sSkillNameIds[Length] = {
"sSkillBlock",
"sSkillArmorer",
"sSkillMediumarmor",
"sSkillHeavyarmor",
"sSkillBluntweapon",
"sSkillLongblade",
"sSkillAxe",
"sSkillSpear",
"sSkillAthletics",
"sSkillEnchant",
"sSkillDestruction",
"sSkillAlteration",
"sSkillIllusion",
"sSkillConjuration",
"sSkillMysticism",
"sSkillRestoration",
"sSkillAlchemy",
"sSkillUnarmored",
"sSkillSecurity",
"sSkillSneak",
"sSkillAcrobatics",
"sSkillLightarmor",
"sSkillShortblade",
"sSkillMarksman",
"sSkillMercantile",
"sSkillSpeechcraft",
"sSkillHandtohand",
};
const boost::array<Skill::SkillEnum, Skill::Length> Skill::skillIds = {{
Block,
Armorer,
MediumArmor,
HeavyArmor,
BluntWeapon,
LongBlade,
Axe,
Spear,
Athletics,
Enchant,
Destruction,
Alteration,
Illusion,
Conjuration,
Mysticism,
Restoration,
Alchemy,
Unarmored,
Security,
Sneak,
Acrobatics,
LightArmor,
ShortBlade,
Marksman,
Mercantile,
Speechcraft,
HandToHand
}};
}
Loading…
Cancel
Save