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

Removed obsolete *.cpp files.
This commit is contained in:
Nikolay Kasyanov 2011-04-08 17:58:21 +04:00 committed by Pieter van der Kloet
parent 7168795ae6
commit fcfe267fc9
60 changed files with 1571 additions and 1325 deletions

View file

@ -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})

View file

@ -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"
};

View file

@ -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;
*/
}
}

View file

@ -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");

View file

@ -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);
}
}

View file

@ -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? float weight; // Not sure, might be max total weight allowed?
int flags; int flags;
InventoryList inventory; InventoryList inventory;
void load(ESMReader &esm) 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");
inventory.load(esm);
}
}; };
} }
#endif #endif

View file

@ -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();
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

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

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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 struct FADTstruct
{ {
// Which attributes we like // Which attributes we like
int attribute1, attribute2; int attribute1, attribute2;
RankData rankData[10]; RankData rankData[10];
int skillID[6]; // IDs of skills this faction require int skillID[6]; // IDs of skills this faction require
int unknown; // Always -1? int unknown; // Always -1?
int isHidden; // 1 - hidden from player int isHidden; // 1 - hidden from player
}; // 240 bytes }; // 240 bytes
FADTstruct data; FADTstruct data;
struct Reaction struct Reaction
{ {
std::string faction; std::string faction;
int reaction; int reaction;
}; };
std::vector<Reaction> reactions; std::vector<Reaction> reactions;
// Name of faction ranks (may be empty for NPC factions) // Name of faction ranks (may be empty for NPC factions)
std::string ranks[10]; std::string ranks[10];
void load(ESMReader &esm) void 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);
}
}
}; };
} }
#endif #endif

View file

@ -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");
}
}

View file

@ -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) 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.
esm.getHNT(value, "FLTV");
}
}; };
} }
#endif #endif

172
components/esm/loadgmst.cpp Normal file
View file

@ -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;
}
}

View file

@ -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 The values of these "dirty" records differ in general from their
cS("sProfitValue", "Profit Value"); // 'Profit:' values as defined in Tribunal.esm and Bloodmoon.esm, and are
cS("sEditNote", "Edit Note"); // same always set to the same "default" values. Most of these values are
cS("sDeleteNote", "Delete Note?"); // same nonsensical, ie. changing the "Seller Max" string to "Max Sale",
cS("sMaxSale", "Max Sale"); // 'Seller Max' or change the stats of werewolves to useless values like 1. Some
cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon' of them break certain spell effects.
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 It is most likely that these values are just leftover values from
cS("sEffectSummonFabricant", id);// 'Summon Fabricant' an early stage of development that are inserted as default values
return false; 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.
// Bloodmoon variant Bethesda themselves have fallen for this bug. If you install both
bool isDirtyBloodmoon() Tribunal and Bloodmoon, the updated Tribunal.esm will contain the
{ dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain
// Strings some of the dirty settings from Tribunal. In other words, this bug
cS("sWerewolfPopup", "Werewolf"); // same affects the game EVEN IF YOU DO NOT USE ANY MODS!
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 The guys at Bethesda are well aware of this bug (and many others),
cS("sMagicCreature01ID", id); // 'BM_wolf_grey_summon' as the mod community and fan base complained about them for a long
cS("sMagicCreature02ID", id); // 'BM_bear_black_summon' time. But unfortunately it was never fixed.
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 There are several tools available to help modders remove these
cI("iWereWolfBounty", 10000); // 1000 records from their files, but not all modders use them, and they
cI("iWereWolfFightMod", 100); // same really shouldn't have to. In this file we choose instead to reject
cI("iWereWolfFleeMod", 100); // same all the corrupt values at load time.
cI("iWereWolfLevelToAttack", 20); // same
// Floats These functions checks if the current game setting is one of the
cF("fFleeDistance", 3000); // same "dirty" ones as described above. TODO: I have not checked this
cF("fCombatDistanceWerewolfMod", 0.3); // same against other sources yet, do that later. Currently recognizes 22
cF("fWereWolfFatigue", 400); // same values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods
cF("fWereWolfEnchant", 1); // 0 (setting values other than the default "dirty" ones) are not
cF("fWereWolfArmorer", 1); // 0 affected and will work correctly.
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 Checks for dirty tribunal values. These will be ignored if found
#undef cS in any file except when they are found in "Tribunal.esm".
*/
bool isDirtyTribunal();
void load(ESMReader &esm) // Bloodmoon variant
{ bool isDirtyBloodmoon();
assert(id != "");
dirty = false; void load(ESMReader &esm);
// 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

133
components/esm/loadinfo.cpp Normal file
View file

@ -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();
}
}

View file

@ -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, Male = 0,
Female = 1, Female = 1,
NA = -1 NA = -1
}; };
struct DATAstruct 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
{
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, int unknown1;
QS_Name, int disposition;
QS_Finished, char rank; // Rank of NPC
QS_Restart, char gender; // See Gender enum
QS_Deleted 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;
}; };
// Rules for when to include this item in the final list of options // Journal quest indices (introduced with the quest system in Tribunal)
// visible to the player. enum QuestStatus
std::vector<SelectStruct> selects;
// Id of this, previous and next INFO items
std::string id, prev, next,
// Various references used in determining when to select this item.
actor, race, clas, npcFaction, pcFaction, cell,
// Sound and text associated with this item
sound, response,
// Result script (uncomiled) to run whenever this dialog item is
// selected
resultScript;
// ONLY include this item the NPC is not part of any faction.
bool factionLess;
// Status of this quest item
QuestStatus questStatus;
// Hexadecimal versions of the various subrecord names.
enum SubNames
{ {
REC_ONAM = 0x4d414e4f, QS_None,
REC_RNAM = 0x4d414e52, QS_Name,
REC_CNAM = 0x4d414e43, QS_Finished,
REC_FNAM = 0x4d414e46, QS_Restart,
REC_ANAM = 0x4d414e41, QS_Deleted
REC_DNAM = 0x4d414e44,
REC_SNAM = 0x4d414e53,
REC_NAME = 0x454d414e,
REC_SCVR = 0x52564353,
REC_INTV = 0x56544e49,
REC_FLTV = 0x56544c46,
REC_BNAM = 0x4d414e42,
REC_QSTN = 0x4e545351,
REC_QSTF = 0x46545351,
REC_QSTR = 0x52545351,
REC_DELE = 0x454c4544
}; };
void load(ESMReader &esm); // Rules for when to include this item in the final list of options
// visible to the player.
std::vector<SelectStruct> selects;
// Id of this, previous and next INFO items
std::string id, prev, next,
// Various references used in determining when to select this item.
actor, race, clas, npcFaction, pcFaction, cell,
// Sound and text associated with this item
sound, response,
// Result script (uncomiled) to run whenever this dialog item is
// selected
resultScript;
// ONLY include this item the NPC is not part of any faction.
bool factionLess;
// Status of this quest item
QuestStatus questStatus;
// Hexadecimal versions of the various subrecord names.
enum SubNames
{
REC_ONAM = 0x4d414e4f,
REC_RNAM = 0x4d414e52,
REC_CNAM = 0x4d414e43,
REC_FNAM = 0x4d414e46,
REC_ANAM = 0x4d414e41,
REC_DNAM = 0x4d414e44,
REC_SNAM = 0x4d414e53,
REC_NAME = 0x454d414e,
REC_SCVR = 0x52564353,
REC_INTV = 0x56544e49,
REC_FLTV = 0x56544c46,
REC_BNAM = 0x4d414e42,
REC_QSTN = 0x4e545351,
REC_QSTF = 0x46545351,
REC_QSTR = 0x52545351,
REC_DELE = 0x454c4544
};
void load(ESMReader &esm);
}; };
/* /*

View file

@ -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");
}
}

View file

@ -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

View file

@ -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");
}
}
}

View file

@ -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 // Record name used to read references. Must be set before load() is
// called. // called.
const char *recName; const char *recName;
struct LevelItem struct LevelItem
{ {
std::string id; std::string id;
short level; short level;
}; };
std::vector<LevelItem> list; std::vector<LevelItem> list;
void load(ESMReader &esm) void 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");
}
}
}; };
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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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 float quality; // And when I say nearly identical structure, I
int uses; // mean perfectly identical except that these two int uses; // mean perfectly identical except that these two
// variables are swaped for repair items. Don't ask // variables are swaped for repair items. Don't ask
// me why. // me why.
}; // Size = 16 }; // Size = 16
Data data; Data 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.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");
}
}; };
struct Probe : Tool struct Probe: Tool
{ {
}; };
struct Repair : Tool struct Repair: Tool
{ {
}; };

View file

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

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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");
}
}
}

View file

@ -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 std::string cell; // Cell name
DATAstruct data; DATAstruct data;
ESM_Context context; // Context so we can return here later and ESM_Context context; // Context so we can return here later and
// finish the job // finish the job
void load(ESMReader &esm) void 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");
}
}
}; };
} }
#endif #endif

View file

@ -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");
}
}

View file

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

View file

@ -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);
}
}
}

View file

@ -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 // sleepList refers to a eveled list of creatures you can meet if
// you sleep outside in this region. // you sleep outside in this region.
std::string name, sleepList; std::string name, sleepList;
std::vector<SoundRef> soundList; std::vector<SoundRef> soundList;
void load(ESMReader &esm) 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");
esm.getHNT(mapColor, "CNAM");
while(esm.hasMoreSubs())
{
SoundRef sr;
esm.getHNT(sr, "SNAM", 33);
soundList.push_back(sr);
}
}
}; };
} }
#endif #endif

View file

@ -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");
}
}

View file

@ -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

View file

@ -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");
}
} }

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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;
*/
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

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

View file

@ -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");
}
}; };
} }

View file

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

View file

@ -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

View file

@ -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");
}
}

View file

@ -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

View file

@ -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
}};
}