Made esmtool test, finished BODY record (loadbody.hpp)

pull/7/head
Nicolay Korslund 15 years ago
parent bbf0bbe982
commit 1d70b2c257

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;
NAME recName, subName;
public:
// 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);
}
};
int main(int argc, char**argv)
{
if(argc != 2)
{
cout << "Specify an ES file\n";
return 1;
}
private:
Mangle::Stream::StreamPtr esm;
size_t leftFile;
uint32_t leftRec, leftSub;
std::string filename;
ESMReader esm;
esm.open(argv[1]);
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)
};
return 0;
}
#endif

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

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

@ -0,0 +1,5 @@
*.esp
*.esm
*.ess
*_test
esmtool

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

@ -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…
Cancel
Save