diff --git a/README.md b/README.md index 53a9fef..c0a728e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ librw ===== -New implementation of some parts of Renderware +This still very much work in progress. +The basics of DFFs are implemented, no plugins yet. diff --git a/clump.cpp b/clump.cpp new file mode 100644 index 0000000..91f6197 --- /dev/null +++ b/clump.cpp @@ -0,0 +1,495 @@ +#include +#include +#include + +#include +#include + +#include "rwbase.h" +#include "rwplugin.h" +#include "rw.h" + +using namespace std; + +namespace Rw { + +static int32 +findFrame(Frame *f, Frame **frameList, int32 numFrames) +{ + int frm; + for(frm = 0; frm < numFrames; frm++) + if(frameList[frm] == f) + goto foundfrm; + return -1; +foundfrm: + return frm; +} + +Frame::Frame(void) +{ + this->child = NULL; + this->next = NULL; + this->root = NULL; + constructPlugins(); +} + +Frame::Frame(Frame *f) +{ + // TODO + copyPlugins(f); +} + +Frame::~Frame(void) +{ + destructPlugins(); +} + +Frame* +Frame::addChild(Frame *child) +{ + if(this->child == NULL) + this->child = child; + else{ + Frame *f; + for(f = this->child; f->next; f = f->next); + f->next = child; + } + child->next = NULL; + child->parent = this; + child->root = this->root; + return this; +} + +Frame* +Frame::forAllChildren(Callback cb, void *data) +{ + for(Frame *f = this->child; f; f = f->next) + cb(f, data); + return this; +} + +static Frame* +countCB(Frame *f, void *count) +{ + (*(int32*)count)++; + f->forAllChildren(countCB, count); + return f; +} + +int32 +Frame::count(void) +{ + int32 count = 1; + this->forAllChildren(countCB, (void*)&count); + return count; +} + +static Frame* +sizeCB(Frame *f, void *size) +{ + *(int32*)size += f->streamGetPluginSize(); + f->forAllChildren(sizeCB, size); + return f; +} + +static Frame** +makeFrameList(Frame *frame, Frame **flist) +{ + *flist++ = frame; + if(frame->next) + flist = makeFrameList(frame->next, flist); + if(frame->child) + flist = makeFrameList(frame->child, flist); + return flist; +} + + + +Clump::Clump(void) +{ + this->numAtomics = 0; + this->numLights = 0; + this->numCameras = 0; + constructPlugins(); +} + +Clump::Clump(Clump *c) +{ + this->numAtomics = c->numAtomics; + this->numLights = c->numLights; + this->numCameras = c->numCameras; + copyPlugins(c); +} + +Clump::~Clump(void) +{ + destructPlugins(); +} + +Clump* +Clump::streamRead(istream &stream) +{ + uint32 length, version; + int32 buf[3]; + Clump *clump; + if(!FindChunk(stream, ID_STRUCT, &length, &version)) + return NULL; + clump = new Clump; + stream.read((char*)buf, length); + clump->numAtomics = buf[0]; + clump->numLights = 0; + clump->numCameras = 0; + if(version >= 0x33000){ + clump->numLights = buf[1]; + clump->numCameras = buf[2]; + } + + // Frame list + Frame **frameList; + int32 numFrames; + clump->frameListStreamRead(stream, &frameList, &numFrames); + clump->parent = (void*)frameList[0]; + + // Geometry list + int32 numGeometries = 0; + if(!FindChunk(stream, ID_GEOMETRYLIST, NULL, NULL)) + return NULL; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + numGeometries = readInt32(stream); + Geometry **geometryList = new Geometry*[numGeometries]; + for(int32 i = 0; i < numGeometries; i++){ + if(!FindChunk(stream, ID_GEOMETRY, NULL, NULL)) + return NULL; + geometryList[i] = Geometry::streamRead(stream); + } + + // Atomics + clump->atomicList = new Atomic*[clump->numAtomics]; + for(int32 i = 0; i < clump->numAtomics; i++){ + if(!FindChunk(stream, ID_ATOMIC, NULL, NULL)) + return NULL; + clump->atomicList[i] = Atomic::streamReadClump(stream, + frameList, geometryList); + clump->atomicList[i]->clump = clump; + } + + // Lights + clump->lightList = new Light*[clump->numLights]; + for(int32 i = 0; i < clump->numLights; i++){ + int32 frm; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + frm = readInt32(stream); + if(!FindChunk(stream, ID_LIGHT, NULL, NULL)) + return NULL; + clump->lightList[i] = Light::streamRead(stream); + clump->lightList[i]->frame = frameList[frm]; + clump->lightList[i]->clump = clump; + } + + delete[] frameList; + + clump->streamReadPlugins(stream); + return clump; +} + +bool +Clump::streamWrite(ostream &stream) +{ + int size = this->streamGetSize(); + WriteChunkHeader(stream, ID_CLUMP, size); + int buf[3] = { this->numAtomics, this->numLights, this->numCameras }; + size = Version >= 0x33000 ? 12 : 4; + WriteChunkHeader(stream, ID_STRUCT, size); + stream.write((char*)buf, size); + + int32 numFrames = ((Frame*)this->parent)->count(); + Frame **flist = new Frame*[numFrames]; + makeFrameList((Frame*)this->parent, flist); + + this->frameListStreamWrite(stream, flist, numFrames); + + size = 12+4; + for(int32 i = 0; i < this->numAtomics; i++) + size += 12 + this->atomicList[i]->geometry->streamGetSize(); + WriteChunkHeader(stream, ID_GEOMETRYLIST, size); + WriteChunkHeader(stream, ID_STRUCT, 4); + writeInt32(this->numAtomics, stream); // same as numGeometries + for(int32 i = 0; i < this->numAtomics; i++) + this->atomicList[i]->geometry->streamWrite(stream); + + for(int32 i = 0; i < this->numAtomics; i++) + this->atomicList[i]->streamWriteClump(stream, flist, numFrames); + + for(int32 i = 0; i < this->numLights; i++){ + Light *l = this->lightList[i]; + int frm = findFrame(l->frame, flist, numFrames); + if(frm < 0) + return false; + WriteChunkHeader(stream, ID_STRUCT, 4); + writeInt32(frm, stream); + l->streamWrite(stream); + } + + delete[] flist; + + this->streamWritePlugins(stream); + return true; +} + +struct FrameStreamData +{ + float32 mat[9]; + float32 pos[3]; + int32 parent; + int32 matflag; +}; + +uint32 +Clump::streamGetSize(void) +{ + uint32 size = 0; + size += 12; // Struct + size += 4; // numAtomics + if(Version >= 0x33000) + size += 8; // numLights, numCameras + + // frame list + int32 numFrames = ((Frame*)this->parent)->count(); + size += 12 + 12 + 4 + numFrames*(sizeof(FrameStreamData)+12); + sizeCB((Frame*)this->parent, (void*)&size); + + // geometry list + size += 12 + 12 + 4; + for(int32 i = 0; i < this->numAtomics; i++) + size += 12 + this->atomicList[i]->geometry->streamGetSize(); + + // atomics + for(int32 i = 0; i < this->numAtomics; i++) + size += 12 + this->atomicList[i]->streamGetSize(); + + // light + for(int32 i = 0; i < this->numAtomics; i++) + size += 16 + 12 + this->lightList[i]->streamGetSize(); + + size += 12 + this->streamGetPluginSize(); + return size; +} + + +void +Clump::frameListStreamRead(istream &stream, Frame ***flp, int32 *nf) +{ + FrameStreamData buf; + int32 numFrames = 0; + if(!FindChunk(stream, ID_FRAMELIST, NULL, NULL)) + return; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return; + numFrames = readInt32(stream); + Frame **frameList = new Frame*[numFrames]; + for(int32 i = 0; i < numFrames; i++){ + Frame *f; + frameList[i] = f = new Frame; + stream.read((char*)&buf, sizeof(buf)); + f->matrix[0] = buf.mat[0]; + f->matrix[1] = buf.mat[1]; + f->matrix[2] = buf.mat[2]; + f->matrix[3] = 0.0f; + f->matrix[4] = buf.mat[3]; + f->matrix[5] = buf.mat[4]; + f->matrix[6] = buf.mat[5]; + f->matrix[7] = 0.0f; + f->matrix[8] = buf.mat[6]; + f->matrix[9] = buf.mat[7]; + f->matrix[10] = buf.mat[8]; + f->matrix[11] = 0.0f; + f->matrix[12] = buf.pos[0]; + f->matrix[13] = buf.pos[1]; + f->matrix[14] = buf.pos[2]; + f->matrix[15] = 1.0f; + if(buf.parent >= 0) + frameList[buf.parent]->addChild(f); + f->matflag = buf.matflag; + } + for(int32 i = 0; i < numFrames; i++) + frameList[i]->streamReadPlugins(stream); + *nf = numFrames; + *flp = frameList; +} + +void +Clump::frameListStreamWrite(ostream &stream, Frame **frameList, int32 numFrames) +{ + FrameStreamData buf; + + int size = 0, structsize = 0; + structsize = 4 + numFrames*sizeof(FrameStreamData); + size += 12 + structsize; + for(int32 i = 0; i < numFrames; i++) + size += 12 + frameList[i]->streamGetPluginSize(); + + WriteChunkHeader(stream, ID_FRAMELIST, size); + WriteChunkHeader(stream, ID_STRUCT, structsize); + writeUInt32(numFrames, stream); + for(int32 i = 0; i < numFrames; i++){ + Frame *f = frameList[i]; + buf.mat[0] = f->matrix[0]; + buf.mat[1] = f->matrix[1]; + buf.mat[2] = f->matrix[2]; + buf.mat[3] = f->matrix[4]; + buf.mat[4] = f->matrix[5]; + buf.mat[5] = f->matrix[6]; + buf.mat[6] = f->matrix[8]; + buf.mat[7] = f->matrix[9]; + buf.mat[8] = f->matrix[10]; + buf.pos[0] = f->matrix[12]; + buf.pos[1] = f->matrix[13]; + buf.pos[2] = f->matrix[14]; + buf.parent = findFrame(f, frameList, numFrames); + buf.matflag = f->matflag; + stream.write((char*)&buf, sizeof(buf)); + } + for(int32 i = 0; i < numFrames; i++) + frameList[i]->streamWritePlugins(stream); +} + + +Atomic::Atomic(void) +{ + this->frame = NULL; + this->geometry = NULL; + constructPlugins(); +} + +Atomic::Atomic(Atomic *a) +{ + //TODO + copyPlugins(a); +} + +Atomic::~Atomic(void) +{ + //TODO + destructPlugins(); +} + +Atomic* +Atomic::streamReadClump(istream &stream, + Frame **frameList, Geometry **geometryList) +{ + int32 buf[4]; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + stream.read((char*)buf, 16); + Atomic *atomic = new Atomic; + atomic->frame = frameList[buf[0]]; + atomic->geometry = geometryList[buf[1]]; + atomic->streamReadPlugins(stream); + return atomic; +} + +bool +Atomic::streamWriteClump(ostream &stream, Frame **frameList, int32 numFrames) +{ + int32 buf[4] = { 0, 0, 5, 0 }; + Clump *c = this->clump; + if(c == NULL) + return false; + WriteChunkHeader(stream, ID_ATOMIC, this->streamGetSize()); + WriteChunkHeader(stream, ID_STRUCT, 16); + buf[0] = findFrame(this->frame, frameList, numFrames); + +// TODO + for(buf[1] = 0; buf[1] < c->numAtomics; buf[1]++) + if(c->atomicList[buf[1]]->geometry == this->geometry) + goto foundgeo; + return false; +foundgeo: + + stream.write((char*)buf, sizeof(buf)); + this->streamWritePlugins(stream); + return true; +} + +uint32 +Atomic::streamGetSize(void) +{ + return 12 + 16 + 12 + this->streamGetPluginSize(); +} + + +Light::Light(void) +{ + this->frame = NULL; + constructPlugins(); +} + +Light::Light(Light *l) +{ + // TODO + copyPlugins(l); +} + +Light::~Light(void) +{ + destructPlugins(); +} + +struct LightChunkData +{ + float32 radius; + float32 red, green, blue; + float32 minusCosAngle; + uint16 flags; + uint16 type; +}; + +Light* +Light::streamRead(istream &stream) +{ + LightChunkData buf; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + Light *light = new Light; + stream.read((char*)&buf, sizeof(LightChunkData)); + light->radius = buf.radius; + light->color[0] = buf.red; + light->color[1] = buf.green; + light->color[2] = buf.blue; + light->color[3] = 1.0f; + light->minusCosAngle = buf.minusCosAngle; + light->flags = (uint8)buf.flags; + light->subType = (uint8)buf.type; + + light->streamReadPlugins(stream); + return light; +} + +bool +Light::streamWrite(ostream &stream) +{ + LightChunkData buf; + WriteChunkHeader(stream, ID_LIGHT, this->streamGetSize()); + WriteChunkHeader(stream, ID_STRUCT, sizeof(LightChunkData)); + buf.radius = this->radius; + buf.red = this->color[0]; + buf.green = this->color[1]; + buf.blue = this->color[2]; + buf.minusCosAngle = this->minusCosAngle; + buf.flags = this->flags; + buf.type = this->subType; + stream.write((char*)&buf, sizeof(LightChunkData)); + + this->streamWritePlugins(stream); + return true; +} + +uint32 +Light::streamGetSize(void) +{ + return 12 + sizeof(LightChunkData) + 12 + this->streamGetPluginSize(); +} + +} diff --git a/geometry.cpp b/geometry.cpp new file mode 100644 index 0000000..6859665 --- /dev/null +++ b/geometry.cpp @@ -0,0 +1,407 @@ +#include +#include +#include + +#include +#include + +#include "rwbase.h" +#include "rwplugin.h" +#include "rw.h" + +using namespace std; + +namespace Rw { + +Geometry::Geometry(void) +{ + geoflags = 0; + numTriangles = 0; + numVertices = 0; + numMorphTargets = 0; + numTexCoordSets = 0; + triangles = 0; + colors = 0; + for(int i = 0; i < 8; i++) + texCoords[i] = 0; + morphTargets = NULL; + constructPlugins(); +} + +Geometry::Geometry(Geometry *g) +{ + // TODO + geoflags = g->geoflags; + numTriangles = g->numTriangles; + numVertices = g->numVertices; + numMorphTargets = g->numMorphTargets; + numTexCoordSets = g->numTexCoordSets; + copyPlugins(g); +} + +Geometry::~Geometry(void) +{ + destructPlugins(); +} + +struct GeoStreamData +{ + uint32 flags; + int32 numTriangles; + int32 numVertices; + int32 numMorphTargets; +}; + +Geometry* +Geometry::streamRead(istream &stream) +{ + uint32 version; + GeoStreamData buf; + if(!FindChunk(stream, ID_STRUCT, NULL, &version)) + return NULL; + Geometry *geo = new Geometry; + stream.read((char*)&buf, sizeof(buf)); + geo->geoflags = buf.flags & 0xFF00FFFF; + geo->numTexCoordSets = (buf.flags & 0xFF0000) >> 16; + if(geo->numTexCoordSets == 0 && (geo->geoflags & TEXTURED)) + geo->numTexCoordSets = 1; + geo->numTriangles = buf.numTriangles; + geo->numVertices = buf.numVertices; + geo->numMorphTargets = buf.numMorphTargets; + // skip surface properties + if(version < 0x34000) + stream.seekg(12, ios::cur); + + if(!(geo->geoflags & 0xFF000000)){ + if(geo->geoflags & PRELIT){ + geo->colors = new uint8[4*geo->numVertices]; + stream.read((char*)geo->colors, 4*geo->numVertices); + } + if((geo->geoflags & TEXTURED) || (geo->geoflags & TEXTURED2)) + for(int32 i = 0; i < geo->numTexCoordSets; i++){ + geo->texCoords[i] = + new float32[2*geo->numVertices]; + stream.read((char*)geo->texCoords[i], + 2*geo->numVertices*4); + } + geo->triangles = new uint16[4*geo->numTriangles]; + stream.read((char*)geo->triangles, 4*geo->numTriangles*2); + } + + geo->morphTargets = new MorphTarget[geo->numMorphTargets]; + for(int32 i = 0; i < geo->numMorphTargets; i++){ + MorphTarget *m = &geo->morphTargets[i]; + stream.read((char*)m->boundingSphere, 4*4); + int32 hasVertices = readInt32(stream); + int32 hasNormals = readInt32(stream); + if(hasVertices){ + m->vertices = new float32[3*geo->numVertices]; + stream.read((char*)m->vertices, 3*geo->numVertices*4); + } + if(hasNormals){ + m->normals = new float32[3*geo->numVertices]; + stream.read((char*)m->normals, 3*geo->numVertices*4); + } + } + + if(!FindChunk(stream, ID_MATLIST, NULL, NULL)) + return geo; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return geo; + geo->numMaterials = readInt32(stream); + geo->materialList = new Material*[geo->numMaterials]; + stream.seekg(geo->numMaterials*4, ios::cur); // unused (-1) + for(int32 i = 0; i < geo->numMaterials; i++){ + if(!FindChunk(stream, ID_MATERIAL, NULL, NULL)) + return geo; + geo->materialList[i] = Material::streamRead(stream); + } + + geo->streamReadPlugins(stream); + + return geo; +} + +static uint32 +geoStructSize(Geometry *geo) +{ + uint32 size = 0; + size += sizeof(GeoStreamData); + if(Version < 0x34000) + size += 12; // surface properties + if(!(geo->geoflags & 0xFF000000)){ + if(geo->geoflags&geo->PRELIT) + size += 4*geo->numVertices; + if((geo->geoflags & geo->TEXTURED) || + (geo->geoflags & geo->TEXTURED2)) + for(int32 i = 0; i < geo->numTexCoordSets; i++) + size += 2*geo->numVertices*4; + size += 4*geo->numTriangles*2; + } + for(int32 i = 0; i < geo->numMorphTargets; i++){ + MorphTarget *m = &geo->morphTargets[i]; + size += 4*4 + 2*4; // bounding sphere and bools + if(m->vertices) + size += 3*geo->numVertices*4; + if(m->normals) + size += 3*geo->numVertices*4; + } + return size; +} + +bool +Geometry::streamWrite(ostream &stream) +{ + GeoStreamData buf; + uint32 size; + static float32 fbuf[3] = { 1.0f, 1.0f, 1.0f }; + + WriteChunkHeader(stream, ID_GEOMETRY, this->streamGetSize()); + WriteChunkHeader(stream, ID_STRUCT, geoStructSize(this)); + + buf.flags = this->geoflags | this->numTexCoordSets << 16; + buf.numTriangles = this->numTriangles; + buf.numVertices = this->numVertices; + buf.numMorphTargets = this->numMorphTargets; + stream.write((char*)&buf, sizeof(buf)); + if(Version < 0x34000) + stream.write((char*)fbuf, sizeof(fbuf)); + + if(!(this->geoflags & 0xFF000000)){ + if(this->geoflags & PRELIT) + stream.write((char*)this->colors, 4*this->numVertices); + if((this->geoflags & TEXTURED) || (this->geoflags & TEXTURED2)) + for(int32 i = 0; i < this->numTexCoordSets; i++) + stream.write((char*)this->texCoords[i], + 2*this->numVertices*4); + stream.write((char*)this->triangles, 4*this->numTriangles*2); + } + + for(int32 i = 0; i < this->numMorphTargets; i++){ + MorphTarget *m = &this->morphTargets[i]; + stream.write((char*)m->boundingSphere, 4*4); + writeInt32(m->vertices != NULL, stream); + writeInt32(m->normals != NULL, stream); + if(m->vertices) + stream.write((char*)m->vertices, 3*this->numVertices*4); + if(m->normals) + stream.write((char*)m->normals, 3*this->numVertices*4); + } + + size = 12 + 4; + for(int32 i = 0; i < this->numMaterials; i++) + size += 4 + 12 + this->materialList[i]->streamGetSize(); + WriteChunkHeader(stream, ID_MATLIST, size); + WriteChunkHeader(stream, ID_STRUCT, 4 + this->numMaterials*4); + writeInt32(this->numMaterials, stream); + for(int32 i = 0; i < this->numMaterials; i++) + writeInt32(-1, stream); + for(int32 i = 0; i < this->numMaterials; i++) + this->materialList[i]->streamWrite(stream); + + this->streamWritePlugins(stream); + return true; +} + +uint32 +Geometry::streamGetSize(void) +{ + uint32 size = 0; + size += 12 + geoStructSize(this); + size += 12 + 12 + 4; + for(int32 i = 0; i < this->numMaterials; i++) + size += 4 + 12 + this->materialList[i]->streamGetSize(); + size += 12 + this->streamGetPluginSize(); + return size; +} + + + + + +Material::Material(void) +{ + this->texture = NULL; + color[0] = color[1] = color[2] = color[3] = 0xFF; + surfaceProps[0] = surfaceProps[1] = surfaceProps[2] = 1.0f; + constructPlugins(); +} + +Material::Material(Material *m) +{ + m->color[0] = this->color[0]; + m->color[1] = this->color[1]; + m->color[2] = this->color[2]; + m->color[3] = this->color[3]; + m->surfaceProps[0] = this->surfaceProps[0]; + m->surfaceProps[1] = this->surfaceProps[1]; + m->surfaceProps[2] = this->surfaceProps[2]; + copyPlugins(m); +} + +Material::~Material(void) +{ + destructPlugins(); +} + +struct MatStreamData +{ + int32 flags; // unused according to RW + uint8 color[4]; + int32 unused; + int32 textured; + float32 surfaceProps[3]; +}; + +Material* +Material::streamRead(istream &stream) +{ + uint32 length; + MatStreamData buf; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + stream.read((char*)&buf, sizeof(buf)); + Material *mat = new Material; + mat->color[0] = buf.color[0]; + mat->color[1] = buf.color[1]; + mat->color[2] = buf.color[2]; + mat->color[3] = buf.color[3]; + mat->surfaceProps[0] = buf.surfaceProps[0]; + mat->surfaceProps[1] = buf.surfaceProps[1]; + mat->surfaceProps[2] = buf.surfaceProps[2]; + + if(buf.textured){ + if(!FindChunk(stream, ID_TEXTURE, &length, NULL)) + return NULL; + mat->texture = Texture::streamRead(stream); + } + + mat->streamReadPlugins(stream); + + return mat; +} + +bool +Material::streamWrite(ostream &stream) +{ + MatStreamData buf; + + WriteChunkHeader(stream, ID_MATERIAL, this->streamGetSize()); + WriteChunkHeader(stream, ID_STRUCT, sizeof(MatStreamData)); + + buf.color[0] = this->color[0]; + buf.color[1] = this->color[1]; + buf.color[2] = this->color[2]; + buf.color[3] = this->color[3]; + buf.surfaceProps[0] = this->surfaceProps[0]; + buf.surfaceProps[1] = this->surfaceProps[1]; + buf.surfaceProps[2] = this->surfaceProps[2]; + buf.flags = 0; + buf.unused = 0; + buf.textured = this->texture != NULL; + stream.write((char*)&buf, sizeof(buf)); + + if(this->texture) + this->texture->streamWrite(stream); + + this->streamWritePlugins(stream); + return true; +} + +uint32 +Material::streamGetSize(void) +{ + uint32 size = 0; + size += 12 + sizeof(MatStreamData); + if(this->texture) + size += 12 + this->texture->streamGetSize(); + size += 12 + this->streamGetPluginSize(); + return size; +} + + + +Texture::Texture(void) +{ + memset(this->name, 0, 32); + memset(this->mask, 0, 32); + this->filterAddressing = 0; + constructPlugins(); +} + +Texture::Texture(Texture *t) +{ + memcpy(this->name, t->name, 32); + memcpy(this->mask, t->name, 32); + this->filterAddressing = t->filterAddressing; + copyPlugins(t); +} + +Texture::~Texture(void) +{ + destructPlugins(); +} + +Texture* +Texture::streamRead(istream &stream) +{ + uint32 length; + if(!FindChunk(stream, ID_STRUCT, NULL, NULL)) + return NULL; + Texture *tex = new Texture; + tex->filterAddressing = readUInt16(stream); + stream.seekg(2, ios::cur); + + if(!FindChunk(stream, ID_STRING, &length, NULL)) + return NULL; + stream.read(tex->name, length); + + if(!FindChunk(stream, ID_STRING, &length, NULL)) + return NULL; + stream.read(tex->mask, length); + + tex->streamReadPlugins(stream); + + return tex; +} + +bool +Texture::streamWrite(ostream &stream) +{ + int size; + WriteChunkHeader(stream, ID_TEXTURE, this->streamGetSize()); + WriteChunkHeader(stream, ID_STRUCT, 4); + writeUInt32(this->filterAddressing, stream); + + size = strnlen(this->name, 32)+3 & ~3; + if(size < 4) + size = 4; + WriteChunkHeader(stream, ID_STRING, size); + stream.write(this->name, size); + + size = strnlen(this->mask, 32)+3 & ~3; + if(size < 4) + size = 4; + WriteChunkHeader(stream, ID_STRING, size); + stream.write(this->mask, size); + + this->streamWritePlugins(stream); + return true; +} + +uint32 +Texture::streamGetSize(void) +{ + uint32 size = 0; + int strsize; + size += 12 + 4; + size += 12 + 12; + strsize = strnlen(this->name, 32)+3 & ~3; + size += strsize < 4 ? 4 : strsize; + strsize = strnlen(this->mask, 32)+3 & ~3; + size += strsize < 4 ? 4 : strsize; + size += 12 + this->streamGetPluginSize(); + return size; +} + +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fd85821 --- /dev/null +++ b/main.cpp @@ -0,0 +1,95 @@ +#include +#include +#include + +#include +#include +#include + +#include "rwbase.h" +#include "rwplugin.h" +#include "rw.h" + +using namespace std; + +// +// X - Plugin of Clump +// +struct X +{ + Rw::int32 a; + Rw::int32 b; +}; + +void* +ctorX(void *object, int offset, int) +{ + X *x = PLUGINOFFSET(X, object, offset); + x->a = 123; + x->b = 789; + return object; +} + +void* +dtorX(void *object, int, int) +{ + return object; +} + +void* +cctorX(void *dst, void *src, int offset, int) +{ + X *srcx = PLUGINOFFSET(X, src, offset); + X *dstx = PLUGINOFFSET(X, dst, offset); + dstx->a = srcx->a; + dstx->b = srcx->b; + return dst; +} + +Rw::int32 +getSizeX(void *, int, int) +{ + return 8; +} + +void +writeX(ostream &stream, Rw::int32, void *object, int offset, int) +{ + X *x = PLUGINOFFSET(X, object, offset); + Rw::writeInt32(x->a, stream); + Rw::writeInt32(x->b, stream); +} + +void +readX(istream &stream, Rw::int32, void *object, int offset, int) +{ + X *x = PLUGINOFFSET(X, object, offset); + x->a = Rw::readInt32(stream); + x->b = Rw::readInt32(stream); +} + +int +main(int argc, char *argv[]) +{ +// Rw::Version = 0x31000; +// Rw::Build = 0; + +// int Xoff = Rw::Clump::registerPlugin(8, 0x123, ctorX, dtorX, cctorX); +// Xoff = Rw::Clump::registerPluginStream(0x123, readX, writeX, getSizeX); + Rw::Clump *c; +// X *x; + + ifstream in(argv[1], ios::binary); + Rw::FindChunk(in, Rw::ID_CLUMP, NULL, NULL); + c = Rw::Clump::streamRead(in); + in.close(); + + ofstream out("out.dff", ios::binary); + c->streamWrite(out); + out.close(); + + delete c; + + return 0; + +} diff --git a/rw.h b/rw.h new file mode 100644 index 0000000..eed690a --- /dev/null +++ b/rw.h @@ -0,0 +1,161 @@ +namespace Rw { + +struct Object +{ + uint8 type; + uint8 subType; + uint8 flags; + void *parent; +}; + +struct Texture : PluginBase +{ + char name[32]; + char mask[32]; + uint32 filterAddressing; + + Texture(void); + Texture(Texture *t); + ~Texture(void); + static Texture *streamRead(std::istream &stream); + bool streamWrite(std::ostream &stream); + uint32 streamGetSize(void); +}; + +struct Material : PluginBase +{ + Texture *texture; + uint8 color[4]; + float32 surfaceProps[3]; + + Material(void); + Material(Material *m); + ~Material(void); + static Material *streamRead(std::istream &stream); + bool streamWrite(std::ostream &stream); + uint32 streamGetSize(void); +}; + +struct MorphTarget +{ + float32 boundingSphere[4]; + float32 *vertices; + float32 *normals; +}; + +struct Geometry : PluginBase, Object +{ + uint32 geoflags; + int32 numTriangles; + int32 numVertices; + int32 numMorphTargets; + int32 numTexCoordSets; + + uint16 *triangles; + uint8 *colors; + float32 *texCoords[8]; + + MorphTarget *morphTargets; + + int32 numMaterials; + Material **materialList; + + Geometry(void); + Geometry(Geometry *g); + ~Geometry(void); + static Geometry *streamRead(std::istream &stream); + bool streamWrite(std::ostream &stream); + uint32 streamGetSize(void); + + enum Flags + { + TRISTRIP = 0x01, + POSITIONS = 0x02, + TEXTURED = 0x04, + PRELIT = 0x08, + NORMALS = 0x10, + LIGHT = 0x20, + MODULATE = 0x40, + TEXTURED2 = 0x80, + NATIVE = 0x01000000, + NATIVEINSTANCE = 0x02000000 + }; +}; + +struct Frame : PluginBase, Object +{ + typedef Frame *(*Callback)(Frame *f, void *data); + float32 matrix[16]; + float32 ltm[16]; + + Frame *child; + Frame *next; + Frame *root; + + // temporary + int32 matflag; + + Frame(void); + Frame(Frame *f); + ~Frame(void); + Frame *addChild(Frame *f); + Frame *forAllChildren(Callback cb, void *data); + int32 count(void); +}; + +struct Clump; + +struct Light : PluginBase, Object +{ + Frame *frame; + float32 radius; + float32 color[4]; + float32 minusCosAngle; + Clump *clump; + + Light(void); + Light(Light *l); + ~Light(void); + static Light *streamRead(std::istream &stream); + bool streamWrite(std::ostream &stream); + uint32 streamGetSize(void); +}; + +struct Atomic : PluginBase, Object +{ + Frame *frame; + Geometry *geometry; + Clump *clump; + + Atomic(void); + Atomic(Atomic *a); + ~Atomic(void); + static Atomic *streamReadClump(std::istream &stream, + Frame **frameList, Geometry **geometryList); + bool streamWriteClump(std::ostream &stream, + Frame **frameList, int32 numFrames); + uint32 streamGetSize(void); +}; + +struct Clump : PluginBase, Object +{ + int32 numAtomics; + Atomic **atomicList; + int32 numLights; + Light **lightList; + int32 numCameras; + // cameras not implemented + + Clump(void); + Clump(Clump *c); + ~Clump(void); + static Clump *streamRead(std::istream &stream); + bool streamWrite(std::ostream &stream); + uint32 streamGetSize(void); + +private: + void frameListStreamRead(std::istream &stream, Frame ***flp, int32 *nf); + void frameListStreamWrite(std::ostream &stream, Frame **flp, int32 nf); +}; + +} diff --git a/rwbase.cpp b/rwbase.cpp new file mode 100644 index 0000000..f04d96f --- /dev/null +++ b/rwbase.cpp @@ -0,0 +1,163 @@ +#include +#include +#include + +#include +#include + +#include "rwbase.h" +#include "rwplugin.h" + +using namespace std; + +namespace Rw { + +int Version = 0x36003; +int Build = 0xFFFF; + +uint32 +writeInt8(int8 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(int8)); + return sizeof(int8); +} + +uint32 +writeUInt8(uint8 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(uint8)); + return sizeof(uint8); +} + +uint32 +writeInt16(int16 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(int16)); + return sizeof(int16); +} + +uint32 +writeUInt16(uint16 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(uint16)); + return sizeof(uint16); +} + +uint32 +writeInt32(int32 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(int32)); + return sizeof(int32); +} + +uint32 +writeUInt32(uint32 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(uint32)); + return sizeof(uint32); +} + +uint32 +writeFloat32(float32 tmp, ostream &rw) +{ + rw.write((char*)&tmp, sizeof(float32)); + return sizeof(float32); +} + +uint8 +readUInt8(istream &rw) +{ + uint8 tmp; + rw.read((char*)&tmp, sizeof(uint8)); + return tmp; +} + +int16 +readInt16(istream &rw) +{ + int16 tmp; + rw.read((char*)&tmp, sizeof(int16)); + return tmp; +} + +uint16 +readUInt16(istream &rw) +{ + uint16 tmp; + rw.read((char*)&tmp, sizeof(uint16)); + return tmp; +} + +int32 +readInt32(istream &rw) +{ + int32 tmp; + rw.read((char*)&tmp, sizeof(int32)); + return tmp; +} + +uint32 +readUInt32(istream &rw) +{ + uint32 tmp; + rw.read((char*)&tmp, sizeof(uint32)); + return tmp; +} + +float32 +readFloat32(istream &rw) +{ + float32 tmp; + rw.read((char*)&tmp, sizeof(float32)); + return tmp; +} + +bool +WriteChunkHeader(ostream &s, int32 type, int32 size) +{ + struct { + int32 type, size; + uint32 id; + } buf = { type, size, LibraryIDPack(Version, Build) }; + s.write((char*)&buf, 12); + return true; +} + +bool +ReadChunkHeaderInfo(istream &s, ChunkHeaderInfo *header) +{ + struct { + int32 type, size; + uint32 id; + } buf; + s.read((char*)&buf, 12); + if(s.eof()) + return false; + assert(header != NULL); + header->type = buf.type; + header->length = buf.size; + header->version = LibraryIDUnpackVersion(buf.id); + header->build = LibraryIDUnpackBuild(buf.id); + return true; +} + +bool +FindChunk(istream &s, uint32 type, uint32 *length, uint32 *version) +{ + ChunkHeaderInfo header; + while(ReadChunkHeaderInfo(s, &header)){ + if(header.type == ID_NAOBJECT) + return false; + if(header.type == type){ + if(length) + *length = header.length; + if(version) + *version = header.version; + return true; + } + s.seekg(header.length, ios::cur); + } + return false; +} + +} diff --git a/rwbase.h b/rwbase.h new file mode 100644 index 0000000..127d00d --- /dev/null +++ b/rwbase.h @@ -0,0 +1,100 @@ +namespace Rw { + +typedef char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; +typedef float float32; +typedef uint8 byte; +typedef uint32 uint; + +uint32 writeInt8(int8 tmp, std::ostream &rw); +uint32 writeUInt8(uint8 tmp, std::ostream &rw); +uint32 writeInt16(int16 tmp, std::ostream &rw); +uint32 writeUInt16(uint16 tmp, std::ostream &rw); +uint32 writeInt32(int32 tmp, std::ostream &rw); +uint32 writeUInt32(uint32 tmp, std::ostream &rw); +uint32 writeFloat32(float32 tmp, std::ostream &rw); +int8 readInt8(std::istream &rw); +uint8 readUInt8(std::istream &rw); +int16 readInt16(std::istream &rw); +uint16 readUInt16(std::istream &rw); +int32 readInt32(std::istream &rw); +uint32 readUInt32(std::istream &rw); +float32 readFloat32(std::istream &rw); + +enum PluginID +{ + // Core + ID_NAOBJECT = 0x0, + ID_STRUCT = 0x1, + ID_STRING = 0x2, + ID_EXTENSION = 0x3, + ID_CAMERA = 0x5, + ID_TEXTURE = 0x6, + ID_MATERIAL = 0x7, + ID_MATLIST = 0x8, + ID_FRAMELIST = 0xE, + ID_GEOMETRY = 0xF, + ID_CLUMP = 0x10, + ID_LIGHT = 0x12, + ID_ATOMIC = 0x14, + ID_TEXTURENATIVE = 0x15, + ID_TEXDICTIONARY = 0x16, + ID_GEOMETRYLIST = 0x1A, + ID_ANIMANIMATION = 0x1B, + ID_RIGHTTORENDER = 0x1F, + ID_UVANIMDICT = 0x2B, +}; + +extern int Version; +extern int Build; + +inline uint32 +LibraryIDPack(int version, int build) +{ + if(build){ + version -= 0x30000; + return (version&0xFFC0) << 14 | (version&0x3F) << 16 | + (build & 0xFFFF); + } + return version>>8; +} + +inline int +LibraryIDUnpackVersion(uint32 libid) +{ + if(libid & 0xFFFF0000) + return (libid>>14 & 0x3FF00) | + (libid>>16 & 0x3F) | + 0x30000; + else + return libid<<8; +} + +inline int +LibraryIDUnpackBuild(uint32 libid) +{ + if(libid & 0xFFFF0000) + return libid & 0xFFFF; + else + return 0; +} + +struct ChunkHeaderInfo +{ + uint32 type; + uint32 length; + uint32 version, build; +}; + +// TODO?: make these methods of ChunkHeaderInfo? +bool WriteChunkHeader(std::ostream &s, int32 type, int32 size); +bool ReadChunkHeaderInfo(std::istream &s, ChunkHeaderInfo *header); +bool FindChunk(std::istream &s, uint32 type, uint32 *length, uint32 *version); + +} diff --git a/rwplugin.h b/rwplugin.h new file mode 100644 index 0000000..2373652 --- /dev/null +++ b/rwplugin.h @@ -0,0 +1,186 @@ +namespace Rw { + +#define PLUGINOFFSET(type, base, offset) \ + ((type*)((char*)(base) + (offset))) + +typedef void *(*Constructor)(void *object, int offset, int size); +typedef void *(*Destructor)(void *object, int offset, int size); +typedef void *(*CopyConstructor)(void *dst, void *src, int offset, int size); +typedef void (*StreamRead)(std::istream &stream, int length, void *object, int offset, int size); +typedef void (*StreamWrite)(std::ostream &stream, int length, void *object, int offset, int size); +typedef int32 (*StreamGetSize)(void *object, int offset, int size); + +struct Plugin +{ + int offset; + int size; + uint id; + Constructor constructor; + Destructor destructor; + CopyConstructor copy; + StreamRead read; + StreamWrite write; + StreamGetSize getSize; + Plugin *next; +}; + +template +struct PluginBase +{ + static int s_defaultSize; + static int s_size; + static Plugin *s_plugins; + + void constructPlugins(void); + void destructPlugins(void); + void copyPlugins(T *t); + void streamReadPlugins(std::istream &stream); + void streamWritePlugins(std::ostream &stream); + int streamGetPluginSize(void); + + static int registerPlugin(int size, uint id, + Constructor, Destructor, CopyConstructor); + static int registerPluginStream(uint id, + StreamRead, StreamWrite, StreamGetSize); + static int getPluginOffset(uint id); + static void *operator new(size_t size); + static void operator delete(void *p); +}; +template +int PluginBase::s_defaultSize = sizeof(T); +template +int PluginBase::s_size = sizeof(T); +template +Plugin *PluginBase::s_plugins = 0; + +template void +PluginBase::constructPlugins(void) +{ + for(Plugin *p = this->s_plugins; p; p = p->next) + if(p->constructor) + p->constructor((void*)this, p->offset, p->size); +} + +template void +PluginBase::destructPlugins(void) +{ + for(Plugin *p = this->s_plugins; p; p = p->next) + if(p->destructor) + p->destructor((void*)this, p->offset, p->size); +} + +template void +PluginBase::copyPlugins(T *t) +{ + for(Plugin *p = this->s_plugins; p; p = p->next) + if(p->copy) + p->copy((void*)this, (void*)t, p->offset, p->size); +} + +template void +PluginBase::streamReadPlugins(std::istream &stream) +{ + uint32 length; + Rw::ChunkHeaderInfo header; + if(!Rw::FindChunk(stream, Rw::ID_EXTENSION, &length, NULL)) + return; + while(length){ + Rw::ReadChunkHeaderInfo(stream, &header); + length -= 12; + for(Plugin *p = this->s_plugins; p; p = p->next) + if(p->id == header.type){ + p->read(stream, header.length, + (void*)this, p->offset, p->size); + goto cont; + } + stream.seekg(header.length, std::ios::cur); +cont: + length -= header.length; + } +} + +template void +PluginBase::streamWritePlugins(std::ostream &stream) +{ + int size = this->streamGetPluginSize(); + Rw::WriteChunkHeader(stream, Rw::ID_EXTENSION, size); + for(Plugin *p = this->s_plugins; p; p = p->next){ + if((size = p->getSize(this, p->offset, p->size)) < 0) + continue; + Rw::WriteChunkHeader(stream, p->id, size); + p->write(stream, size, this, p->offset, p->size); + } +} + +template int +PluginBase::streamGetPluginSize(void) +{ + int size = 0; + int plgsize; + for(Plugin *p = this->s_plugins; p; p = p->next) + if(p->getSize && + (plgsize = p->getSize(this, p->offset, p->size)) >= 0) + size += 12 + plgsize; + return size; +} + +template int +PluginBase::registerPlugin(int size, uint id, + Constructor ctor, Destructor dtor, CopyConstructor cctor) +{ + Plugin *p = new Plugin; + p->offset = s_size; + s_size += size; + + p->size = size; + p->id = id; + p->constructor = ctor; + p->copy = cctor; + p->destructor = dtor; + p->read = NULL; + p->write = NULL; + p->getSize = NULL; + + p->next = s_plugins; + s_plugins = p; + return p->offset; +} + +template int +PluginBase::registerPluginStream(uint id, + StreamRead read, StreamWrite write, StreamGetSize getSize) +{ + for(Plugin *p = PluginBase::s_plugins; p; p = p->next) + if(p->id == id){ + p->read = read; + p->write = write; + p->getSize = getSize; + return p->offset; + } + return -1; +} + +template int +PluginBase::getPluginOffset(uint id) +{ + for(Plugin *p = PluginBase::s_plugins; p; p = p->next) + if(p->id == id) + return p->offset; + return -1; +} + +template void* +PluginBase::operator new(size_t) +{ + void *m = malloc(T::s_size); + if(!m) + throw std::bad_alloc(); + return m; +} + +template void +PluginBase::operator delete(void *p) +{ + free(p); +} +}