forked from mirror/openmw-tes3mp
Made esmtool test, finished BODY record (loadbody.hpp)
This commit is contained in:
parent
bbf0bbe982
commit
1d70b2c257
9 changed files with 290 additions and 147 deletions
4
esm/.gitignore
vendored
4
esm/.gitignore
vendored
|
@ -1,4 +1,2 @@
|
||||||
old
|
old
|
||||||
*.esm
|
*.d
|
||||||
*.esp
|
|
||||||
reader
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
all: reader
|
|
||||||
|
|
||||||
reader: reader.cpp ../tools/stringops.cpp
|
|
|
@ -1,14 +1,15 @@
|
||||||
#include <iostream>
|
#ifndef _ESM_READER_H
|
||||||
|
#define _ESM_READER_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
using namespace std;
|
#include <vector>
|
||||||
|
|
||||||
#include "../mangle/stream/stream.h"
|
#include "../mangle/stream/stream.h"
|
||||||
#include "../mangle/stream/servers/file_stream.h"
|
#include "../mangle/stream/servers/file_stream.h"
|
||||||
#include "../mangle/tools/str_exception.h"
|
#include "../mangle/tools/str_exception.h"
|
||||||
|
|
||||||
#include "../tools/stringops.h"
|
#include "../tools/stringops.h"
|
||||||
|
|
||||||
/* A structure used for holding fixed-length strings. In the case of
|
/* A structure used for holding fixed-length strings. In the case of
|
||||||
|
@ -30,16 +31,16 @@ union NAME_T
|
||||||
}
|
}
|
||||||
bool operator!=(const char *str) { return !((*this)==str); }
|
bool operator!=(const char *str) { return !((*this)==str); }
|
||||||
|
|
||||||
bool operator==(const string &str)
|
bool operator==(const std::string &str)
|
||||||
{
|
{
|
||||||
return (*this) == str.c_str();
|
return (*this) == str.c_str();
|
||||||
}
|
}
|
||||||
bool operator!=(const string &str) { return !((*this)==str); }
|
bool operator!=(const std::string &str) { return !((*this)==str); }
|
||||||
|
|
||||||
bool operator==(int v) { return v == val; }
|
bool operator==(int v) { return v == val; }
|
||||||
bool operator!=(int v) { return v != val; }
|
bool operator!=(int v) { return v != val; }
|
||||||
|
|
||||||
string toString() { return string(name, strnlen(name, LEN)); }
|
std::string toString() { return std::string(name, strnlen(name, LEN)); }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NAME_T<4> NAME;
|
typedef NAME_T<4> NAME;
|
||||||
|
@ -49,15 +50,13 @@ typedef NAME_T<256> NAME256;
|
||||||
|
|
||||||
class ESMReader
|
class ESMReader
|
||||||
{
|
{
|
||||||
Mangle::Stream::StreamPtr esm;
|
public:
|
||||||
size_t leftFile;
|
|
||||||
uint32_t leftRec, leftSub;
|
|
||||||
string filename;
|
|
||||||
|
|
||||||
NAME recName, subName;
|
/*************************************************************************
|
||||||
|
*
|
||||||
// True if subName has been read but not used.
|
* Public type definitions
|
||||||
bool subCached;
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
@ -74,6 +73,14 @@ class ESMReader
|
||||||
int records; // Number of records? Not used.
|
int records; // Number of records? Not used.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Defines another files (esm or esp) that this file depends upon.
|
||||||
|
struct MasterData
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
uint64_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Data that is only present in save game files
|
||||||
struct SaveData
|
struct SaveData
|
||||||
{
|
{
|
||||||
float pos[6]; // Player position and rotation
|
float pos[6]; // Player position and rotation
|
||||||
|
@ -84,11 +91,8 @@ class ESMReader
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
HEDRstruct header;
|
typedef std::vector<MasterData> MasterList;
|
||||||
SaveData saveData;
|
|
||||||
int spf; // Special file signifier (see SpecialFile below)
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum Version
|
enum Version
|
||||||
{
|
{
|
||||||
VER_12 = 0x3f99999a,
|
VER_12 = 0x3f99999a,
|
||||||
|
@ -113,16 +117,48 @@ public:
|
||||||
SF_Bloodmoon
|
SF_Bloodmoon
|
||||||
};
|
};
|
||||||
|
|
||||||
void open(Mangle::Stream::StreamPtr _esm, const string &name)
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* Information retrieval
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
int getVer() { return header.version; }
|
||||||
|
float getFVer() { return *((float*)&header.version); }
|
||||||
|
int getSpecial() { return spf; }
|
||||||
|
const std::string getAuthor() { return header.author.toString(); }
|
||||||
|
const std::string getDesc() { return header.desc.toString(); }
|
||||||
|
const SaveData &getSaveData() { return saveData; }
|
||||||
|
const MasterList &getMasters() { return masters; }
|
||||||
|
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
*
|
||||||
|
* Opening and closing
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// Close the file, resets all information. After calling close()
|
||||||
|
/// the structure may be reused to load a new file.
|
||||||
|
void close()
|
||||||
{
|
{
|
||||||
esm = _esm;
|
esm.reset();
|
||||||
filename = name;
|
filename.clear();
|
||||||
leftFile = esm->size();
|
leftFile = 0;
|
||||||
leftRec = 0;
|
leftRec = 0;
|
||||||
leftSub = 0;
|
leftSub = 0;
|
||||||
subCached = false;
|
subCached = false;
|
||||||
recName.val = 0;
|
recName.val = 0;
|
||||||
subName.val = 0;
|
subName.val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load ES file from a new stream. Calls close() automatically.
|
||||||
|
void open(Mangle::Stream::StreamPtr _esm, const std::string &name)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
esm = _esm;
|
||||||
|
filename = name;
|
||||||
|
leftFile = esm->size();
|
||||||
|
|
||||||
// Flag certain files for special treatment, based on the file
|
// Flag certain files for special treatment, based on the file
|
||||||
// name.
|
// name.
|
||||||
|
@ -135,9 +171,7 @@ public:
|
||||||
if(getRecName() != "TES3")
|
if(getRecName() != "TES3")
|
||||||
fail("Not a valid Morrowind file");
|
fail("Not a valid Morrowind file");
|
||||||
|
|
||||||
// The flags are always zero
|
getRecHeader();
|
||||||
uint32_t flags;
|
|
||||||
getRecHeader(flags);
|
|
||||||
|
|
||||||
// Get the header
|
// Get the header
|
||||||
getHNT(header, "HEDR");
|
getHNT(header, "HEDR");
|
||||||
|
@ -146,13 +180,12 @@ public:
|
||||||
header.version != VER_13)
|
header.version != VER_13)
|
||||||
fail("Unsupported file format version");
|
fail("Unsupported file format version");
|
||||||
|
|
||||||
cout << "Description: " << header.desc.toString() << endl;
|
|
||||||
cout << "Author: " << header.author.toString() << endl;
|
|
||||||
|
|
||||||
while(isNextSub("MAST"))
|
while(isNextSub("MAST"))
|
||||||
{
|
{
|
||||||
cout << "Master: " << getHString() << endl;
|
MasterData m;
|
||||||
cout << " size: " << getHNLong("DATA") << endl;
|
m.name = getHString();
|
||||||
|
m.size = getHNLong("DATA");
|
||||||
|
masters.push_back(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(header.type == FT_ESS)
|
if(header.type == FT_ESS)
|
||||||
|
@ -180,7 +213,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void open(const string &file)
|
void open(const std::string &file)
|
||||||
{
|
{
|
||||||
using namespace Mangle::Stream;
|
using namespace Mangle::Stream;
|
||||||
open(StreamPtr(new FileStream(file)), file);
|
open(StreamPtr(new FileStream(file)), file);
|
||||||
|
@ -217,7 +250,13 @@ public:
|
||||||
getT(x);
|
getT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
string getHString()
|
std::string getHNString(const char* name)
|
||||||
|
{
|
||||||
|
getSubNameIs(name);
|
||||||
|
return getHString();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getHString()
|
||||||
{
|
{
|
||||||
getSubHeader();
|
getSubHeader();
|
||||||
|
|
||||||
|
@ -249,7 +288,7 @@ public:
|
||||||
{
|
{
|
||||||
getSubName();
|
getSubName();
|
||||||
if(subName != name)
|
if(subName != name)
|
||||||
fail("Expected subrecord " + string(name) + " but got " + subName.toString());
|
fail("Expected subrecord " + std::string(name) + " but got " + subName.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks if the next sub record name matches the parameter. If it
|
/** Checks if the next sub record name matches the parameter. If it
|
||||||
|
@ -293,7 +332,7 @@ public:
|
||||||
void skipHSub()
|
void skipHSub()
|
||||||
{
|
{
|
||||||
getSubHeader();
|
getSubHeader();
|
||||||
esm->seek(esm->tell()+leftSub);
|
skip(leftSub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip sub record and check its size
|
// Skip sub record and check its size
|
||||||
|
@ -340,10 +379,27 @@ public:
|
||||||
return recName;
|
return recName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip the rest of this record. Assumes the name and header have
|
||||||
|
// already been read
|
||||||
|
void skipRecord()
|
||||||
|
{
|
||||||
|
skip(leftRec);
|
||||||
|
leftRec = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip an entire record, including the header (but not the name)
|
||||||
|
void skipHRecord()
|
||||||
|
{
|
||||||
|
if(!leftFile) return;
|
||||||
|
getRecHeader();
|
||||||
|
skipRecord();
|
||||||
|
}
|
||||||
|
|
||||||
/* Read record header. This updatesleftFile BEYOND the data that
|
/* Read record header. This updatesleftFile BEYOND the data that
|
||||||
follows the header, ie beyond the entire record. You should use
|
follows the header, ie beyond the entire record. You should use
|
||||||
leftRec to orient yourself inside the record itself.
|
leftRec to orient yourself inside the record itself.
|
||||||
*/
|
*/
|
||||||
|
void getRecHeader() { uint32_t u; getRecHeader(u); }
|
||||||
void getRecHeader(uint32_t &flags)
|
void getRecHeader(uint32_t &flags)
|
||||||
{
|
{
|
||||||
// General error checking
|
// General error checking
|
||||||
|
@ -390,11 +446,13 @@ public:
|
||||||
// Not very optimized, but we'll fix that later
|
// Not very optimized, but we'll fix that later
|
||||||
char *ptr = new char[size];
|
char *ptr = new char[size];
|
||||||
esm->read(ptr,size);
|
esm->read(ptr,size);
|
||||||
string res(ptr,size);
|
std::string res(ptr,size);
|
||||||
delete[] ptr;
|
delete[] ptr;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void skip(int bytes) { esm->seek(esm->tell()+bytes); }
|
||||||
|
|
||||||
/// Used for error handling
|
/// Used for error handling
|
||||||
void fail(const std::string &msg)
|
void fail(const std::string &msg)
|
||||||
{
|
{
|
||||||
|
@ -404,18 +462,22 @@ public:
|
||||||
err += "\n Subrecord: " + subName.toString();
|
err += "\n Subrecord: " + subName.toString();
|
||||||
throw str_exception(err);
|
throw str_exception(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mangle::Stream::StreamPtr esm;
|
||||||
|
size_t leftFile;
|
||||||
|
uint32_t leftRec, leftSub;
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
NAME recName, subName;
|
||||||
|
|
||||||
|
// True if subName has been read but not used.
|
||||||
|
bool subCached;
|
||||||
|
|
||||||
|
HEDRstruct header;
|
||||||
|
SaveData saveData;
|
||||||
|
MasterList masters;
|
||||||
|
int spf; // Special file signifier (see SpecialFile below)
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char**argv)
|
#endif
|
||||||
{
|
|
||||||
if(argc != 2)
|
|
||||||
{
|
|
||||||
cout << "Specify an ES file\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESMReader esm;
|
|
||||||
esm.open(argv[1]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
59
esm/loadbody.hpp
Normal file
59
esm/loadbody.hpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#ifndef _ESM_BODY_H
|
||||||
|
#define _ESM_BODY_H
|
||||||
|
|
||||||
|
#include "esm_reader.hpp"
|
||||||
|
|
||||||
|
struct BodyPart
|
||||||
|
{
|
||||||
|
enum MeshPart
|
||||||
|
{
|
||||||
|
MP_Head = 0,
|
||||||
|
MP_Hair = 1,
|
||||||
|
MP_Neck = 2,
|
||||||
|
MP_Chest = 3,
|
||||||
|
MP_Groin = 4,
|
||||||
|
MP_Hand = 5,
|
||||||
|
MP_Wrist = 6,
|
||||||
|
MP_Forearm = 7,
|
||||||
|
MP_Upperarm = 8,
|
||||||
|
MP_Foot = 9,
|
||||||
|
MP_Ankle = 10,
|
||||||
|
MP_Knee = 11,
|
||||||
|
MP_Upperleg = 12,
|
||||||
|
MP_Clavicle = 13,
|
||||||
|
MP_Tail = 14
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Flags
|
||||||
|
{
|
||||||
|
BPF_Female = 1,
|
||||||
|
BPF_Playable = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MeshType
|
||||||
|
{
|
||||||
|
MT_Skin = 0,
|
||||||
|
MT_Clothing = 1,
|
||||||
|
MT_Armor = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BYDTstruct
|
||||||
|
{
|
||||||
|
char part;
|
||||||
|
char vampire;
|
||||||
|
char flags;
|
||||||
|
char type;
|
||||||
|
};
|
||||||
|
|
||||||
|
BYDTstruct data;
|
||||||
|
std::string model, name;
|
||||||
|
|
||||||
|
void load(ESMReader &esm)
|
||||||
|
{
|
||||||
|
model = esm.getHNString("MODL");
|
||||||
|
name = esm.getHNString("FNAM");
|
||||||
|
esm.getHNT(data, "BYDT");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
54
esm/records.hpp
Normal file
54
esm/records.hpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef _ESM_RECORDS_H
|
||||||
|
#define _ESM_RECORDS_H
|
||||||
|
|
||||||
|
//#include "loadarmo.hpp"
|
||||||
|
#include "loadbody.hpp"
|
||||||
|
|
||||||
|
// Integer versions of all the record names, used for faster lookup
|
||||||
|
enum RecNameInts
|
||||||
|
{
|
||||||
|
REC_ACTI = 0x49544341,
|
||||||
|
REC_ALCH = 0x48434c41,
|
||||||
|
REC_APPA = 0x41505041,
|
||||||
|
REC_ARMO = 0x4f4d5241,
|
||||||
|
REC_BODY = 0x59444f42,
|
||||||
|
REC_BOOK = 0x4b4f4f42,
|
||||||
|
REC_BSGN = 0x4e475342,
|
||||||
|
REC_CELL = 0x4c4c4543,
|
||||||
|
REC_CLAS = 0x53414c43,
|
||||||
|
REC_CLOT = 0x544f4c43,
|
||||||
|
REC_CONT = 0x544e4f43,
|
||||||
|
REC_CREA = 0x41455243,
|
||||||
|
REC_CREC = 0x43455243,
|
||||||
|
REC_DIAL = 0x4c414944,
|
||||||
|
REC_DOOR = 0x524f4f44,
|
||||||
|
REC_ENCH = 0x48434e45,
|
||||||
|
REC_FACT = 0x54434146,
|
||||||
|
REC_GLOB = 0x424f4c47,
|
||||||
|
REC_GMST = 0x54534d47,
|
||||||
|
REC_INFO = 0x4f464e49,
|
||||||
|
REC_INGR = 0x52474e49,
|
||||||
|
REC_LEVC = 0x4356454c,
|
||||||
|
REC_LEVI = 0x4956454c,
|
||||||
|
REC_LIGH = 0x4847494c,
|
||||||
|
REC_LOCK = 0x4b434f4c,
|
||||||
|
REC_LTEX = 0x5845544c,
|
||||||
|
REC_MGEF = 0x4645474d,
|
||||||
|
REC_MISC = 0x4353494d,
|
||||||
|
REC_NPC_ = 0x5f43504e,
|
||||||
|
REC_NPCC = 0x4343504e,
|
||||||
|
REC_PROB = 0x424f5250,
|
||||||
|
REC_RACE = 0x45434152,
|
||||||
|
REC_REGN = 0x4e474552,
|
||||||
|
REC_REPA = 0x41504552,
|
||||||
|
REC_SCPT = 0x54504353,
|
||||||
|
REC_SKIL = 0x4c494b53,
|
||||||
|
REC_SNDG = 0x47444e53,
|
||||||
|
REC_SOUN = 0x4e554f53,
|
||||||
|
REC_SPEL = 0x4c455053,
|
||||||
|
REC_SSCR = 0x52435353,
|
||||||
|
REC_STAT = 0x54415453,
|
||||||
|
REC_WEAP = 0x50414557
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
5
esm/tests/.gitignore
vendored
Normal file
5
esm/tests/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*.esp
|
||||||
|
*.esm
|
||||||
|
*.ess
|
||||||
|
*_test
|
||||||
|
esmtool
|
7
esm/tests/Makefile
Normal file
7
esm/tests/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
all: esmtool
|
||||||
|
|
||||||
|
esmtool: esmtool.cpp ../esm_reader.hpp ../records.hpp
|
||||||
|
g++ esmtool.cpp ../../tools/stringops.cpp -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm *_test esmtool
|
54
esm/tests/esmtool.cpp
Normal file
54
esm/tests/esmtool.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "../esm_reader.hpp"
|
||||||
|
#include "../records.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char**argv)
|
||||||
|
{
|
||||||
|
if(argc != 2)
|
||||||
|
{
|
||||||
|
cout << "Specify an ES file\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESMReader esm;
|
||||||
|
esm.open(argv[1]);
|
||||||
|
|
||||||
|
cout << "\nFile: " << argv[1] << endl;
|
||||||
|
cout << "Author: " << esm.getAuthor() << endl;
|
||||||
|
cout << "Description: " << esm.getDesc() << endl;
|
||||||
|
cout << "File format version: " << esm.getFVer() << endl;
|
||||||
|
cout << "Special flag: " << esm.getSpecial() << endl;
|
||||||
|
cout << "Masters:\n";
|
||||||
|
ESMReader::MasterList m = esm.getMasters();
|
||||||
|
for(int i=0;i<m.size();i++)
|
||||||
|
cout << " " << m[i].name << ", " << m[i].size << " bytes\n";
|
||||||
|
|
||||||
|
// Loop through all records
|
||||||
|
while(esm.hasMoreRecs())
|
||||||
|
{
|
||||||
|
NAME n = esm.getRecName();
|
||||||
|
cout << "\nRecord: " << n.toString();
|
||||||
|
esm.getRecHeader();
|
||||||
|
cout << " '" << esm.getHNString("NAME") << "'\n";
|
||||||
|
|
||||||
|
switch(n.val)
|
||||||
|
{
|
||||||
|
case REC_BODY:
|
||||||
|
{
|
||||||
|
BodyPart bp;
|
||||||
|
bp.load(esm);
|
||||||
|
cout << " Name: " << bp.name << endl;
|
||||||
|
cout << " Mesh: " << bp.model << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
cout << " Skipping\n";
|
||||||
|
esm.skipRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,93 +0,0 @@
|
||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (loadbody.d) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module esm.loadbody;
|
|
||||||
import esm.imports;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Body part mesh. NPCs have several mesh files, one for each element
|
|
||||||
* in the enum below. One BODY record is given for each part.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct BodyPart
|
|
||||||
{
|
|
||||||
enum MeshType : byte
|
|
||||||
{
|
|
||||||
Head = 0,
|
|
||||||
Hair = 1,
|
|
||||||
Neck = 2,
|
|
||||||
Chest = 3,
|
|
||||||
Groin = 4,
|
|
||||||
Hand = 5,
|
|
||||||
Wrist = 6,
|
|
||||||
Forearm = 7,
|
|
||||||
Upperarm = 8,
|
|
||||||
Foot = 9,
|
|
||||||
Ankle = 10,
|
|
||||||
Knee = 11,
|
|
||||||
Upperleg = 12,
|
|
||||||
Clavicle = 13,
|
|
||||||
Tail = 14
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Type : byte
|
|
||||||
{
|
|
||||||
Skin = 0,
|
|
||||||
Clothing = 1,
|
|
||||||
Armor = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Flags : byte
|
|
||||||
{
|
|
||||||
Female = 1,
|
|
||||||
Playable = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
align(1) struct BYDTstruct
|
|
||||||
{
|
|
||||||
MeshType part;
|
|
||||||
byte vampire;
|
|
||||||
Flags flags;
|
|
||||||
Type type;
|
|
||||||
static assert(BYDTstruct.sizeof==4);
|
|
||||||
}
|
|
||||||
|
|
||||||
BYDTstruct data;
|
|
||||||
|
|
||||||
mixin LoadT;
|
|
||||||
|
|
||||||
MeshIndex model;
|
|
||||||
|
|
||||||
void load()
|
|
||||||
{with(esFile){
|
|
||||||
model = getMesh();
|
|
||||||
name = getHNString("FNAM");
|
|
||||||
readHNExact(&data, data.sizeof, "BYDT");
|
|
||||||
|
|
||||||
// don't need to run makeProto here yet, no BodyPart monster
|
|
||||||
// class.
|
|
||||||
}}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ListID!(BodyPart) bodyParts;
|
|
Loading…
Reference in a new issue