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
|
||||
*.esm
|
||||
*.esp
|
||||
reader
|
||||
*.d
|
||||
|
|
|
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
using namespace std;
|
||||
#include <vector>
|
||||
|
||||
#include "../mangle/stream/stream.h"
|
||||
#include "../mangle/stream/servers/file_stream.h"
|
||||
#include "../mangle/tools/str_exception.h"
|
||||
|
||||
#include "../tools/stringops.h"
|
||||
|
||||
/* 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 string &str)
|
||||
bool operator==(const std::string &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; }
|
||||
|
||||
string toString() { return string(name, strnlen(name, LEN)); }
|
||||
std::string toString() { return std::string(name, strnlen(name, LEN)); }
|
||||
};
|
||||
|
||||
typedef NAME_T<4> NAME;
|
||||
|
@ -49,15 +50,13 @@ typedef NAME_T<256> NAME256;
|
|||
|
||||
class ESMReader
|
||||
{
|
||||
Mangle::Stream::StreamPtr esm;
|
||||
size_t leftFile;
|
||||
uint32_t leftRec, leftSub;
|
||||
string filename;
|
||||
public:
|
||||
|
||||
NAME recName, subName;
|
||||
|
||||
// True if subName has been read but not used.
|
||||
bool subCached;
|
||||
/*************************************************************************
|
||||
*
|
||||
* Public type definitions
|
||||
*
|
||||
*************************************************************************/
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
@ -74,6 +73,14 @@ class ESMReader
|
|||
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
|
||||
{
|
||||
float pos[6]; // Player position and rotation
|
||||
|
@ -84,11 +91,8 @@ class ESMReader
|
|||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
HEDRstruct header;
|
||||
SaveData saveData;
|
||||
int spf; // Special file signifier (see SpecialFile below)
|
||||
typedef std::vector<MasterData> MasterList;
|
||||
|
||||
public:
|
||||
enum Version
|
||||
{
|
||||
VER_12 = 0x3f99999a,
|
||||
|
@ -113,16 +117,48 @@ public:
|
|||
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;
|
||||
filename = name;
|
||||
leftFile = esm->size();
|
||||
esm.reset();
|
||||
filename.clear();
|
||||
leftFile = 0;
|
||||
leftRec = 0;
|
||||
leftSub = 0;
|
||||
subCached = false;
|
||||
recName.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
|
||||
// name.
|
||||
|
@ -135,9 +171,7 @@ public:
|
|||
if(getRecName() != "TES3")
|
||||
fail("Not a valid Morrowind file");
|
||||
|
||||
// The flags are always zero
|
||||
uint32_t flags;
|
||||
getRecHeader(flags);
|
||||
getRecHeader();
|
||||
|
||||
// Get the header
|
||||
getHNT(header, "HEDR");
|
||||
|
@ -146,13 +180,12 @@ public:
|
|||
header.version != VER_13)
|
||||
fail("Unsupported file format version");
|
||||
|
||||
cout << "Description: " << header.desc.toString() << endl;
|
||||
cout << "Author: " << header.author.toString() << endl;
|
||||
|
||||
while(isNextSub("MAST"))
|
||||
{
|
||||
cout << "Master: " << getHString() << endl;
|
||||
cout << " size: " << getHNLong("DATA") << endl;
|
||||
MasterData m;
|
||||
m.name = getHString();
|
||||
m.size = getHNLong("DATA");
|
||||
masters.push_back(m);
|
||||
}
|
||||
|
||||
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;
|
||||
open(StreamPtr(new FileStream(file)), file);
|
||||
|
@ -217,7 +250,13 @@ public:
|
|||
getT(x);
|
||||
}
|
||||
|
||||
string getHString()
|
||||
std::string getHNString(const char* name)
|
||||
{
|
||||
getSubNameIs(name);
|
||||
return getHString();
|
||||
}
|
||||
|
||||
std::string getHString()
|
||||
{
|
||||
getSubHeader();
|
||||
|
||||
|
@ -249,7 +288,7 @@ public:
|
|||
{
|
||||
getSubName();
|
||||
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
|
||||
|
@ -293,7 +332,7 @@ public:
|
|||
void skipHSub()
|
||||
{
|
||||
getSubHeader();
|
||||
esm->seek(esm->tell()+leftSub);
|
||||
skip(leftSub);
|
||||
}
|
||||
|
||||
// Skip sub record and check its size
|
||||
|
@ -340,10 +379,27 @@ public:
|
|||
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
|
||||
follows the header, ie beyond the entire record. You should use
|
||||
leftRec to orient yourself inside the record itself.
|
||||
*/
|
||||
void getRecHeader() { uint32_t u; getRecHeader(u); }
|
||||
void getRecHeader(uint32_t &flags)
|
||||
{
|
||||
// General error checking
|
||||
|
@ -390,11 +446,13 @@ public:
|
|||
// Not very optimized, but we'll fix that later
|
||||
char *ptr = new char[size];
|
||||
esm->read(ptr,size);
|
||||
string res(ptr,size);
|
||||
std::string res(ptr,size);
|
||||
delete[] ptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
void skip(int bytes) { esm->seek(esm->tell()+bytes); }
|
||||
|
||||
/// Used for error handling
|
||||
void fail(const std::string &msg)
|
||||
{
|
||||
|
@ -404,18 +462,22 @@ public:
|
|||
err += "\n Subrecord: " + subName.toString();
|
||||
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)
|
||||
{
|
||||
if(argc != 2)
|
||||
{
|
||||
cout << "Specify an ES file\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ESMReader esm;
|
||||
esm.open(argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
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