* added experimental M2 mesh loader, file by shlainn.

* updated stuffextract to extract creature model related files + new scp data
This commit is contained in:
false_genesis 2008-04-03 00:59:45 +00:00
parent 57fed6d6cc
commit 91ce4aa4ce
11 changed files with 465 additions and 11 deletions

BIN
bin/data/misc/square.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

View File

@ -53,6 +53,9 @@ LOADSCP,race data/scp/race.scp
EXLOADSCP,sound data/scp/sound.scp EXLOADSCP,sound data/scp/sound.scp
EXLOADSCP,emote data/scp/emote.scp EXLOADSCP,emote data/scp/emote.scp
EXLOADSCP,zone data/scp/zone.scp EXLOADSCP,zone data/scp/zone.scp
EXLOADSCP,creaturedisplayinfo data/scp/creaturedisplayinfo.scp
EXLOADSCP,creaturemodeldata data/scp/creaturemodeldata.scp
EXLOADSCP,npcsound data/scp/npcsound.scp
LOG * SCP loaded. LOG * SCP loaded.

View File

@ -0,0 +1,128 @@
#include <iostream>
#include "CM2MeshFileLoader.h"
namespace irr
{
namespace scene
{
CM2MeshFileLoader::CM2MeshFileLoader(IrrlichtDevice* device):Device(device)
{
Mesh = NULL;
aniMesh = NULL;
}
CM2MeshFileLoader::~CM2MeshFileLoader()
{
}
bool CM2MeshFileLoader::isALoadableFileExtension(const c8* filename)
{
return strstr(filename, ".m2")!=0;
}
//! creates/loads an animated mesh from the file.
//! \return Pointer to the created mesh. Returns 0 if loading failed.
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
//! See IUnknown::drop() for more information.
IAnimatedMesh* CM2MeshFileLoader::createMesh(io::IReadFile* file)
{
ILogger* logger =Device->getLogger();
logger->log("Trying to open file",file->getFileName(),ELL_INFORMATION);
file->read(&header,sizeof(ModelHeader));
if (header.version[0] != 4 && header.version[1] != 1 && header.version[2] != 0 && header.version[3] != 0) {
logger->log("Something wrong!",ELL_ERROR);
return 0;
}
else logger->log(L"header okay",ELL_INFORMATION);
//Name -> not very important I think, but save it nontheless;
file->seek(header.nameOfs);
file->read(&M2MeshName[0],header.nameLength);
logger->log("Mesh Name",M2MeshName.c_str(),ELL_INFORMATION);
ModelVertex tempM2MVert;
file->seek(header.ofsVertices);
for(u32 i =0;i<header.nVertices;i++)
{
file->read(&tempM2MVert,sizeof(ModelVertex));
M2MVertices.push_back(tempM2MVert);
}
std::cout << "Read "<<M2MVertices.size()<<"/"<<header.nVertices<<" Vertices\n";
ModelView tempM2MView;
file->seek(header.ofsViews);
for(u32 i =0;i<header.nViews;i++)
{
file->read(&tempM2MView,sizeof(ModelView));
M2MViews.push_back(tempM2MView);
}
std::cout << "Read "<<M2MViews.size()<<"/"<<header.nViews<<" Views\n";
logger->log("Using View 0 for all further operations",ELL_INFORMATION);
u16 tempM2Index;
file->seek(M2MViews[0].ofsIndex);
for(u32 i =0;i<M2MViews[0].nIndex;i++)
{
file->read(&tempM2Index,sizeof(u16));
M2MIndices.push_back(tempM2Index);
}
std::cout << "Read "<<M2MIndices.size()<<"/"<<M2MViews[0].nIndex<<" Indices\n";
u16 tempM2Triangle;
file->seek(M2MViews[0].ofsTris);
for(u32 i =0;i<M2MViews[0].nTris;i++)
{
file->read(&tempM2Triangle,sizeof(u16));
M2MTriangles.push_back(tempM2Triangle);
}
std::cout << "Read "<<M2MTriangles.size()<<"/"<<M2MViews[0].nTris<<" Triangle Indices\n";
//Now, M2MTriangles refers to M2MIndices and not to M2MVertices.
//And M2MVertices are not usable like this. Thus we transform
for(u32 i=0;i<M2MVertices.size();i++)
{
M2Vertices.push_back(video::S3DVertex(M2MVertices[i].pos,M2MVertices[i].normal, video::SColor(255,100,100,100),M2MVertices[i].texcoords));
}
for(u32 i=0;i<M2MTriangles.size();i++)
{
M2Indices.push_back(M2MIndices[M2MTriangles[i]]);
}
Mesh=new SMesh();
// irr 1.3
SMeshBuffer* IMB = new SMeshBuffer();
for(u32 i = 0; i < IMB->Vertices.size(); i++)
IMB->Vertices.push_back(M2Vertices[i]);
for(u32 i = 0; i < IMB->Indices.size(); i++)
IMB->Indices.push_back(M2Indices[i]);
// irr 1.4
//IMB->append(M2Vertices.const_pointer(),M2Vertices.size(),M2Indices.const_pointer(),M2Indices.size());
//Device->getSceneManager()->getMeshManipulator()->recalculateNormals(IMB,false);
IMB->recalculateBoundingBox();
Mesh->addMeshBuffer(IMB);
//IMesh* tangentMesh = Device->getSceneManager()->getMeshManipulator()->createMeshWithTangents(Mesh);
aniMesh= new SAnimatedMesh();
aniMesh->addMesh(Mesh);
aniMesh->recalculateBoundingBox();
IMB->drop();
Mesh->drop();
return aniMesh;
}
}
}

View File

@ -0,0 +1,151 @@
#include "irrlicht/irrlicht.h"
#include "irrlicht/IMeshLoader.h"
namespace irr
{
namespace scene
{
class CM2MeshFileLoader : public IMeshLoader
{
public:
//! Constructor
CM2MeshFileLoader(IrrlichtDevice* device);
//! destructor
~CM2MeshFileLoader();
//! returns true if the file maybe is able to be loaded by this class
//! based on the file extension (e.g. ".cob")
virtual bool isALoadableFileExtension(const c8* fileName);
//! creates/loads an animated mesh from the file.
//! \return Pointer to the created mesh. Returns 0 if loading failed.
//! If you no longer need the mesh, you should call IAnimatedMesh::drop().
//! See IUnknown::drop() for more information.
virtual scene::IAnimatedMesh* createMesh(irr::io::IReadFile* file);
private:
struct ModelHeader {
c8 id[4];
u8 version[4];
u32 nameLength;
u32 nameOfs;
u32 type;
u32 nGlobalSequences;
u32 ofsGlobalSequences;
u32 nAnimations;
u32 ofsAnimations;
u32 nC;
u32 ofsC;
u32 nD;
u32 ofsD;
u32 nBones;
u32 ofsBones;
u32 nF;
u32 ofsF;
u32 nVertices;
u32 ofsVertices;
u32 nViews;
u32 ofsViews;
u32 nColors;
u32 ofsColors;
u32 nTextures;
u32 ofsTextures;
u32 nTransparency; // H
u32 ofsTransparency;
u32 nI; // always unused ?
u32 ofsI;
u32 nTexAnims; // J
u32 ofsTexAnims;
u32 nTexReplace;
u32 ofsTexReplace;
u32 nTexFlags;
u32 ofsTexFlags;
u32 nY;
u32 ofsY;
u32 nTexLookup;
u32 ofsTexLookup;
u32 nTexUnitLookup; // L
u32 ofsTexUnitLookup;
u32 nTransparencyLookup; // M
u32 ofsTransparencyLookup;
u32 nTexAnimLookup;
u32 ofsTexAnimLookup;
f32 floats[14];
u32 nBoundingTriangles;
u32 ofsBoundingTriangles;
u32 nBoundingVertices;
u32 ofsBoundingVertices;
u32 nBoundingNormals;
u32 ofsBoundingNormals;
u32 nAttachments; // O
u32 ofsAttachments;
u32 nAttachLookup; // P
u32 ofsAttachLookup;
u32 nQ; // Q
u32 ofsQ;
u32 nLights; // R
u32 ofsLights;
u32 nCameras; // S
u32 ofsCameras;
u32 nT;
u32 ofsT;
u32 nRibbonEmitters; // U
u32 ofsRibbonEmitters;
u32 nParticleEmitters; // V
u32 ofsParticleEmitters;
} header;
struct ModelVertex {
core::vector3df pos;//Use Irrlicht Vector here!
u8 weights[4];
u8 bones[4];
core::vector3df normal;//Use Irrlicht Vector here!
core::vector2df texcoords;//Use Irrlicht Vector here!
u32 unk1, unk2; // always 0,0 so this is probably unused
};
struct ModelView {
u32 nIndex, ofsIndex; // Vertices in this model (index into vertices[])
u32 nTris, ofsTris; // indices
u32 nProps, ofsProps; // additional vtx properties
u32 nSub, ofsSub; // materials/renderops/submeshes
u32 nTex, ofsTex; // material properties/textures
s32 lod; // LOD bias?
};
// io::IFileSystem* FileSystem;
IrrlichtDevice* Device;
// scene::IMeshManipulator* Manipulator;
core::stringc M2MeshName;
SAnimatedMesh* aniMesh;
SMesh* Mesh;
//Taken from the Model file, thus m2M*
core::array<ModelVertex> M2MVertices;
core::array<ModelView> M2MViews;
core::array<u16> M2MIndices;
core::array<u16> M2MTriangles;
//Used for the Mesh, thus m2_noM_*
core::array<video::S3DVertex> M2Vertices;
core::array<u16> M2Indices;
};
}//namespace scene
}//namespace irr

View File

@ -4,13 +4,17 @@
#include "PseuWoW.h" #include "PseuWoW.h"
#include "Object.h" #include "Object.h"
DrawObject::DrawObject(irr::IrrlichtDevice *device, Object *obj) using namespace irr;
DrawObject::DrawObject(irr::IrrlichtDevice *device, Object *obj, PseuInstance *ins)
{ {
_initialized = false; _initialized = false;
Unlink(); Unlink();
_device = device;
_smgr = device->getSceneManager(); _smgr = device->getSceneManager();
_guienv = device->getGUIEnvironment(); _guienv = device->getGUIEnvironment();
_obj = obj; _obj = obj;
_instance = ins;
DEBUG( logdebug("create DrawObject() this=%X obj=%X name='%s' smgr=%X",this,_obj,_obj->GetName().c_str(),_smgr) ); DEBUG( logdebug("create DrawObject() this=%X obj=%X name='%s' smgr=%X",this,_obj,_obj->GetName().c_str(),_smgr) );
} }
@ -34,10 +38,26 @@ void DrawObject::_Init(void)
{ {
if(!cube && _obj->IsWorldObject()) // only world objects have coords and can be drawn if(!cube && _obj->IsWorldObject()) // only world objects have coords and can be drawn
{ {
uint32 displayid = _obj->IsUnit() ? _obj->GetUInt32Value(UNIT_FIELD_DISPLAYID) : 0; // TODO: in case its GO get it from proto data
uint32 modelid = _instance->dbmgr.GetDB("creaturedisplayinfo").GetField(displayid).GetInteger("model");
std::string modelfile = std::string("data/model/") + _instance->dbmgr.GetDB("creaturemodeldata").GetField(modelid).GetString("file");
//scene::IAnimatedMesh *mesh = _smgr->getMesh(modelfile.c_str());
// ok we use a model that works for sure until crashs with some M2 files, especially character models, are fixed
/*scene::IAnimatedMesh *mesh = _smgr->getMesh("data/model/gyrocopter.m2");
if(mesh)
{
cube = _smgr->addAnimatedMeshSceneNode(mesh);
//video::ITexture *tex = _device->getVideoDriver()->getTexture("data/misc/square.jpg");
//cube->setMaterialTexture(0, tex);
//tex->drop();
}
else
{*/
cube = _smgr->addCubeSceneNode(2); cube = _smgr->addCubeSceneNode(2);
cube->setName("CUBE"); //}
cube->setName("OBJECT");
//cube->setPosition(irr::core::vector3di(100,100,100)); //cube->setPosition(irr::core::vector3di(100,100,100));
cube->setRotation(irr::core::vector3df(0,0,0)); cube->setRotation(core::vector3df(0,0,0));
if(_obj->IsPlayer()) if(_obj->IsPlayer())
{ {

View File

@ -5,11 +5,12 @@
#include "irrlicht/irrlicht.h" #include "irrlicht/irrlicht.h"
class Object; class Object;
class PseuInstance;
class DrawObject class DrawObject
{ {
public: public:
DrawObject(irr::IrrlichtDevice *device, Object*); DrawObject(irr::IrrlichtDevice *device, Object*, PseuInstance *ins);
~DrawObject(); ~DrawObject();
void Draw(void); // call only in threadsafe environment!! (ensure the obj ptr is still valid!) void Draw(void); // call only in threadsafe environment!! (ensure the obj ptr is still valid!)
void Unlink(void); void Unlink(void);
@ -19,10 +20,12 @@ private:
void _Init(void); void _Init(void);
Object *_obj; Object *_obj;
bool _initialized : 1; bool _initialized : 1;
irr::IrrlichtDevice *_device;
irr::scene::ISceneManager *_smgr; irr::scene::ISceneManager *_smgr;
irr::gui::IGUIEnvironment* _guienv; irr::gui::IGUIEnvironment* _guienv;
irr::scene::ISceneNode* cube; irr::scene::ISceneNode* cube;
irr::scene::ITextSceneNode *text; irr::scene::ITextSceneNode *text;
PseuInstance *_instance;
}; };

View File

@ -1,5 +1,6 @@
#include "common.h" #include "common.h"
#include "irrlicht/irrlicht.h" #include "irrlicht/irrlicht.h"
#include "CM2MeshFileLoader.h"
#include "Object.h" #include "Object.h"
#include "DrawObject.h" #include "DrawObject.h"
#include "PseuWoW.h" #include "PseuWoW.h"
@ -119,6 +120,11 @@ void PseuGUI::_Init(void)
_guienv = _device->getGUIEnvironment(); _guienv = _device->getGUIEnvironment();
_timer = _device->getTimer(); _timer = _device->getTimer();
//... //...
// register external loaders for not supported filetypes
scene::CM2MeshFileLoader* m2loader = new scene::CM2MeshFileLoader(_device);
_smgr->addExternalMeshLoader(m2loader);
_initialized = true; _initialized = true;
} }
@ -229,7 +235,7 @@ void PseuGUI::NotifyObjectDeletion(uint64 guid)
// called from ObjMgr::Add(Object*) // called from ObjMgr::Add(Object*)
void PseuGUI::NotifyObjectCreation(Object *o) void PseuGUI::NotifyObjectCreation(Object *o)
{ {
DrawObject *d = new DrawObject(_device,o); DrawObject *d = new DrawObject(_device,o,_instance);
domgr.Add(o->GetGUID(),d); domgr.Add(o->GetGUID(),d);
} }

View File

@ -417,6 +417,12 @@
<Filter <Filter
Name="GUI" Name="GUI"
Filter=""> Filter="">
<File
RelativePath=".\Client\Gui\CM2MeshFileLoader.cpp">
</File>
<File
RelativePath=".\Client\Gui\CM2MeshFileLoader.h">
</File>
<File <File
RelativePath=".\Client\Gui\DrawObject.cpp"> RelativePath=".\Client\Gui\DrawObject.cpp">
</File> </File>

View File

@ -557,6 +557,14 @@
<Filter <Filter
Name="GUI" Name="GUI"
> >
<File
RelativePath=".\Client\Gui\CM2MeshFileLoader.cpp"
>
</File>
<File
RelativePath=".\Client\Gui\CM2MeshFileLoader.h"
>
</File>
<File <File
RelativePath=".\Client\Gui\DrawObject.cpp" RelativePath=".\Client\Gui\DrawObject.cpp"
> >

View File

@ -304,4 +304,75 @@ static const char *ItemDisplayInfoFormat = {
"ssi" "ssi"
}; };
enum CreatureModelDataEnum
{
CREATUREMODELDATA_ID = 0,
CREATUREMODELDATA_FILE = 2,
// other fields unknown
CREATUREMODELDATA_END = 24
};
static const char *CreatureModelDataFieldNames[] = {
"","","file","","","","","","", // 0-9
"","","","","","","","","", // 10-19
"","","","","",
""
};
static const char *CreatureModelDataFormat = {
"ixsxxxxxx" // 0-9
"xxxxxxxxx" // 10-19
"xxxxx"
};
enum CreatureDisplayInfoEnum
{
CREATUREDISPLAYINFO_ID = 0, // <<-- this is the so-called DisplayID!!
CREATUREDISPLAYINFO_MODEL = 1, // id from CreatureModelData
CREATUREDISPLAYINFO_UNK = 2,
CREATUREDISPLAYINFO_EXTRA = 3, // id from CreatureDisplayInfoExtra
CREATUREDISPLAYINFO_SCALE = 4, // default scale, float
CREATUREDISPLAYINFO_OPACITY = 5, // 0 = 100% tansparent, 255 = 100% solid
CREATUREDISPLAYINFO_NAME1 = 6, // next 3 fields are some names, not always present
CREATUREDISPLAYINFO_NAME2 = 7,
CREATUREDISPLAYINFO_NAME3 = 8,
CREATUREDISPLAYINFO_NPCSOUNDS = 11, // id from NPCSounds
CREATUREDISPLAYINFO_END = 21
};
static const char *CreatureDisplayInfoFieldNames[] = {
"","model","unk","extra","scale","opacity","name1","name2","name3","", // 0-9
"","sound","","","","","","","","", // 10-19
"",
""
};
static const char *CreatureDisplayInfoFormat = {
"iiiifisssx" // 0-9
"xixxxxxxxx" // 10-19
"x"
};
enum NPCSoundsEnum
{
NPCSOUNDS_ID = 0,
NPCSOUNDS_GREETINGS = 1,
NPCSOUNDS_FAREWELLS = 2,
NPCSOUNDS_PISSED = 3,
NPCSOUNDS_UNK = 4, // always 0
NPCSOUNDS_END = 5
};
static const char *NPCSoundsFieldNames[] = {
"","greet","bye","pissed","",
""
};
static const char *NPCSoundsFormat = {
"iiiix"
};
#endif #endif

View File

@ -119,7 +119,6 @@ void ProcessCmdArgs(int argc, char *argv[])
{ {
doTextures = false; doTextures = false;
doWmos = false; doWmos = false;
doModels = false;
} }
if(help) if(help)
{ {
@ -149,7 +148,7 @@ void PrintHelp(void)
printf("maps - map extraction\n"); printf("maps - map extraction\n");
printf("textures - extract textures (requires maps extraction, for now)\n"); printf("textures - extract textures (requires maps extraction, for now)\n");
printf("wmos - extract map WMOs (requires maps extraction)\n"); printf("wmos - extract map WMOs (requires maps extraction)\n");
printf("models - extract models (required maps extraction, for now)\n"); printf("models - extract models\n");
printf("sounds - extract sound files (wav/mp3)\n"); printf("sounds - extract sound files (wav/mp3)\n");
printf("md5 - write MD5 checksum lists of extracted files\n"); printf("md5 - write MD5 checksum lists of extracted files\n");
printf("autoclose - close program when done\n"); printf("autoclose - close program when done\n");
@ -250,8 +249,10 @@ void OutMD5(char *path, MD5FileMap& fm)
bool ConvertDBC(void) bool ConvertDBC(void)
{ {
std::map<uint8,std::string> racemap; // needed to extract other dbc files correctly std::map<uint8,std::string> racemap; // needed to extract other dbc files correctly
SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage; // will store the converted data from dbc files SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage,
DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo; CreatureModelStorage,CreatureDisplayInfoStorage,NPCSoundStorage; // will store the converted data from dbc files
DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo,
CreatureModelData,CreatureDisplayInfo,NPCSounds;
printf("Opening DBC archive...\n"); printf("Opening DBC archive...\n");
MPQHelper mpq("dbc"); MPQHelper mpq("dbc");
@ -264,6 +265,9 @@ bool ConvertDBC(void)
Map.openmem(mpq.ExtractFile("DBFilesClient\\Map.dbc")); Map.openmem(mpq.ExtractFile("DBFilesClient\\Map.dbc"));
AreaTable.openmem(mpq.ExtractFile("DBFilesClient\\AreaTable.dbc")); AreaTable.openmem(mpq.ExtractFile("DBFilesClient\\AreaTable.dbc"));
ItemDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\ItemDisplayInfo.dbc")); ItemDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\ItemDisplayInfo.dbc"));
CreatureModelData.openmem(mpq.ExtractFile("DBFilesClient\\CreatureModelData.dbc"));
CreatureDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\CreatureDisplayInfo.dbc"));
NPCSounds.openmem(mpq.ExtractFile("DBFilesClient\\NPCSounds.dbc"));
//... //...
printf("DBC files opened.\n"); printf("DBC files opened.\n");
//... //...
@ -409,6 +413,57 @@ bool ConvertDBC(void)
} }
} }
printf("creaturemodeldata..");
for(DBCFile::Iterator it = CreatureModelData.begin(); it != CreatureModelData.end(); ++it)
{
uint32 id = it->getUInt(CREATUREMODELDATA_ID);
for(uint32 field=CREATUREMODELDATA_ID; field < CREATUREMODELDATA_END; field++)
{
if(strlen(CreatureModelDataFieldNames[field]))
{
std::string value = AutoGetDataString(it,CreatureModelDataFormat,field);
if(value.size()) // only store if not null
{
modelNames.insert(value); // we need to extract model later, store it
std::string fn = _PathToFileName(value);
if(stricmp(fn.c_str()+fn.length()-4, "mdx"))
fn = fn.substr(0,fn.length()-3) + "m2";
CreatureModelStorage[id].push_back(std::string(CreatureModelDataFieldNames[field]) + "=" + fn);
}
}
}
}
printf("creaturedisplayinfo..");
for(DBCFile::Iterator it = CreatureDisplayInfo.begin(); it != CreatureDisplayInfo.end(); ++it)
{
uint32 id = it->getUInt(CREATUREDISPLAYINFO_ID);
for(uint32 field=CREATUREDISPLAYINFO_ID; field < CREATUREDISPLAYINFO_END; field++)
{
if(strlen(CreatureDisplayInfoFieldNames[field]))
{
std::string value = AutoGetDataString(it,CreatureDisplayInfoFormat,field);
if(value.size()) // only store if not null
CreatureDisplayInfoStorage[id].push_back(std::string(CreatureDisplayInfoFieldNames[field]) + "=" + value);
}
}
}
printf("npcsounds..");
for(DBCFile::Iterator it = NPCSounds.begin(); it != NPCSounds.end(); ++it)
{
uint32 id = it->getUInt(NPCSOUNDS_ID);
for(uint32 field=NPCSOUNDS_ID; field < NPCSOUNDS_END; field++)
{
if(strlen(NPCSoundsFieldNames[field]))
{
std::string value = AutoGetDataString(it,NPCSoundsFormat,field);
if(value.size()) // only store if not null
NPCSoundStorage[id].push_back(std::string(NPCSoundsFieldNames[field]) + "=" + value);
}
}
}
//... //...
@ -423,7 +478,10 @@ bool ConvertDBC(void)
printf("sound.."); OutSCP(SCPDIR "/sound.scp",SoundDataStorage, "sound"); printf("sound.."); OutSCP(SCPDIR "/sound.scp",SoundDataStorage, "sound");
printf("map.."); OutSCP(SCPDIR "/map.scp",MapDataStorage, "map"); printf("map.."); OutSCP(SCPDIR "/map.scp",MapDataStorage, "map");
printf("area.."); OutSCP(SCPDIR "/zone.scp",ZoneDataStorage, "zone"); printf("area.."); OutSCP(SCPDIR "/zone.scp",ZoneDataStorage, "zone");
printf("itemdisplayinfo."); OutSCP(SCPDIR "/itemdisplayinfo.scp",ItemDisplayInfoStorage, "itemdisplayinfo"); printf("itemdisplayinfo.."); OutSCP(SCPDIR "/itemdisplayinfo.scp",ItemDisplayInfoStorage, "itemdisplayinfo");
printf("creaturemodeldata.."); OutSCP(SCPDIR "/creaturemodeldata.scp",CreatureModelStorage,"creaturemodeldata");
printf("creaturedisplayinfo.."); OutSCP(SCPDIR "/creaturedisplayinfo.scp",CreatureDisplayInfoStorage,"creaturedisplayinfo");
printf("npcsound.."); OutSCP(SCPDIR "/npcsound.scp",NPCSoundStorage,"npcsound");
//... //...
printf("DONE!\n"); printf("DONE!\n");