#include #include "renderware.h" using namespace std; namespace rw { uint32 version; /* You can write ONE header per block using these macros */ #define SKIP_HEADER()\ uint32 bytesWritten = 0;\ uint32 headerPos = dff.tellp();\ dff.seekp(0x0C, ios::cur); #define WRITE_HEADER(chunkType)\ uint32 oldPos = dff.tellp();\ dff.seekp(headerPos, ios::beg);\ header.type = (chunkType);\ header.length = bytesWritten;\ bytesWritten += header.write(dff);\ writtenBytesReturn = bytesWritten;\ dff.seekp(oldPos, ios::beg); /* * Clump */ void Clump::write(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; /* * writtenBytesReturn will always contain the number of bytes * written in a sub-block, it is used like a return value. */ // Clump SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(atomicList.size(), dff); if (version != GTA3_1 && version != GTA3_2 && version != GTA3_3 && version != GTA3_4) { bytesWritten += writeUInt32(0, dff); bytesWritten += writeUInt32(0, dff); } WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Frame List { SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(frameList.size(), dff); for (uint32 i = 0; i < frameList.size(); i++) bytesWritten += frameList[i].writeStruct(dff); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Extensions for (uint32 i = 0; i < frameList.size(); i++) bytesWritten += frameList[i].writeExtension(dff); WRITE_HEADER(CHUNK_FRAMELIST); } bytesWritten += writtenBytesReturn; // Geometry List { SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(geometryList.size(), dff); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Geometries for (uint32 i = 0; i < geometryList.size(); i++) bytesWritten += geometryList[i].write(dff); WRITE_HEADER(CHUNK_GEOMETRYLIST); } bytesWritten += writtenBytesReturn; // Atomics for (uint32 i = 0; i < atomicList.size(); i++) bytesWritten += atomicList[i].write(dff); // Extension { SKIP_HEADER(); WRITE_HEADER(CHUNK_EXTENSION); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_CLUMP); } /* * Atomic */ uint32 Atomic::write(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; // Atomic SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(frameIndex, dff); bytesWritten += writeUInt32(geometryIndex, dff); bytesWritten += writeUInt32(5, dff); bytesWritten += writeUInt32(0, dff); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Extension { SKIP_HEADER(); // Right To Render writtenBytesReturn = 0; if (hasRightToRender) { SKIP_HEADER(); bytesWritten += writeUInt32(rightToRenderVal1, dff); bytesWritten += writeUInt32(rightToRenderVal2, dff); WRITE_HEADER(CHUNK_RIGHTTORENDER); } bytesWritten += writtenBytesReturn; // Particles writtenBytesReturn = 0; if (hasParticles) { SKIP_HEADER(); bytesWritten += writeUInt32(particlesVal, dff); WRITE_HEADER(CHUNK_PARTICLES); } bytesWritten += writtenBytesReturn; // Pipeline set writtenBytesReturn = 0; if (hasPipelineSet) { SKIP_HEADER(); bytesWritten += writeUInt32(pipelineSetVal, dff); WRITE_HEADER(CHUNK_PIPELINESET); } bytesWritten += writtenBytesReturn; // Material Fx writtenBytesReturn = 0; if (hasMaterialFx) { SKIP_HEADER(); bytesWritten += writeUInt32(materialFxVal, dff); WRITE_HEADER(CHUNK_MATERIALEFFECTS); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_EXTENSION); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_ATOMIC); return bytesWritten; } /* * Frame */ uint32 Frame::writeStruct(ofstream &dff) { uint32 bytesWritten = 0; dff.write(reinterpret_cast (rotationMatrix), 9*sizeof(float32)); bytesWritten += 9*sizeof(float32); dff.write(reinterpret_cast (position), 3*sizeof(float32)); bytesWritten += 3*sizeof(float32); bytesWritten += writeInt32(parent, dff); /* matrix creation flags; not used, only written */ bytesWritten += writeInt32(0, dff); return bytesWritten; } uint32 Frame::writeExtension(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; // Extension SKIP_HEADER(); // Frame { SKIP_HEADER(); dff.write(name.c_str(), name.length()); bytesWritten += name.length(); WRITE_HEADER(CHUNK_FRAME); } bytesWritten += writtenBytesReturn; // HAnim writtenBytesReturn = 0; if (hasHAnim) { SKIP_HEADER(); bytesWritten += writeUInt32(hAnimUnknown1, dff); bytesWritten += writeInt32(hAnimBoneId, dff); bytesWritten += writeUInt32(hAnimBoneCount, dff); if (hAnimBoneCount != 0) { bytesWritten += writeUInt32(hAnimUnknown2, dff); bytesWritten += writeUInt32(hAnimUnknown3, dff); } for (uint32 i = 0; i < hAnimBoneCount; i++) { bytesWritten += writeInt32(hAnimBoneIds[i], dff); bytesWritten += writeUInt32(hAnimBoneNumbers[i], dff); bytesWritten += writeUInt32(hAnimBoneTypes[i], dff); } WRITE_HEADER(CHUNK_HANIM); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_EXTENSION); return bytesWritten; } /* * Geometry */ uint32 Geometry::write(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; // Geometry SKIP_HEADER(); // Struct { SKIP_HEADER(); if (faces.size() == 0) generateFaces(); bytesWritten += writeUInt16(flags, dff); bytesWritten += writeUInt8(numUVs, dff); /* no, we can't write native geometry */ bytesWritten += writeUInt8(0, dff); uint32 triangleCount = faces.size() / 4; uint32 vertexCount = vertices.size() / 3; bytesWritten += writeUInt32(triangleCount, dff); bytesWritten += writeUInt32(vertexCount, dff); /* morph targets are always just 1 */ bytesWritten += writeUInt32(1, dff); if (header.version == GTA3_1 || header.version == GTA3_2 || header.version == GTA3_3 || header.version == GTA3_4 || header.version == VCPS2) { bytesWritten += writeFloat32(1.0f, dff); bytesWritten += writeFloat32(1.0f, dff); bytesWritten += writeFloat32(1.0f, dff); } if (flags & FLAGS_PRELIT) { dff.write(reinterpret_cast (&vertexColors[0]), 4*vertexCount*sizeof(uint8)); bytesWritten += 4*vertexCount*sizeof(uint8); } if (flags & FLAGS_TEXTURED) { dff.write(reinterpret_cast (&texCoords1[0]), 2*vertexCount*sizeof(float32)); bytesWritten += 2*vertexCount*sizeof(float32); } if (flags & FLAGS_TEXTURED2) { dff.write(reinterpret_cast (&texCoords1[0]), 2*vertexCount*sizeof(float32)); dff.write(reinterpret_cast (&texCoords2[0]), 2*vertexCount*sizeof(float32)); bytesWritten += 4*vertexCount*sizeof(float32); } dff.write(reinterpret_cast (&faces[0]), 4*triangleCount*sizeof(uint16)); bytesWritten += 4*triangleCount*sizeof(uint16); // Bounding Sphere dff.write(reinterpret_cast (boundingSphere), 4*sizeof(float32)); bytesWritten += 4*sizeof(float32); bytesWritten += writeUInt32(bsPos, dff); bytesWritten += writeUInt32(bsNor, dff); // Morph Targets (always 1) dff.write(reinterpret_cast (&vertices[0]), 3*vertexCount*sizeof(float32)); bytesWritten += 3*vertexCount*sizeof(float32); if (flags & FLAGS_NORMALS) { dff.write(reinterpret_cast (&normals[0]), 3*vertexCount*sizeof(float32)); bytesWritten += 3*vertexCount*sizeof(float32); } WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Material List { SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(materialList.size(), dff); for (uint32 i = 0; i < materialList.size(); i++) bytesWritten += writeInt32(-1, dff); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Materials for (uint32 i = 0; i < materialList.size(); i++) bytesWritten += materialList[i].write(dff); WRITE_HEADER(CHUNK_MATLIST); } bytesWritten += writtenBytesReturn; // Extensions { SKIP_HEADER(); // Bin Mesh { SKIP_HEADER(); bytesWritten += writeUInt32(faceType, dff); bytesWritten += writeUInt32(splits.size(), dff); bytesWritten += writeUInt32(numIndices, dff); for (uint32 i = 0; i < splits.size(); i++) { uint32 indexCount = splits[i].indices.size(); bytesWritten += writeUInt32(indexCount, dff); bytesWritten += writeUInt32(splits[i].matIndex, dff); for (uint32 j = 0; j < indexCount; j++) bytesWritten += writeUInt32( splits[i].indices[j], dff); } WRITE_HEADER(CHUNK_BINMESH); } bytesWritten += writtenBytesReturn; // Mesh extension if (hasMeshExtension) bytesWritten += writeMeshExtension(dff); // Night vertex Colors writtenBytesReturn = 0; if (hasNightColors) { SKIP_HEADER(); bytesWritten += writeUInt32(nightColorsUnknown, dff); if (nightColorsUnknown != 0) { dff.write(reinterpret_cast (&nightColors[0]), nightColors.size()*sizeof(uint8)); bytesWritten+= nightColors.size()*sizeof(uint8); } WRITE_HEADER(CHUNK_NIGHTVERTEXCOLOR); } bytesWritten += writtenBytesReturn; // 2dfx // Skin writtenBytesReturn = 0; if (hasSkin) { SKIP_HEADER(); bytesWritten += writeUInt8(boneCount, dff); bytesWritten += writeUInt8(specialIndexCount, dff); bytesWritten += writeUInt8(unknown1, dff); bytesWritten += writeUInt8(unknown2, dff); dff.write(reinterpret_cast (&specialIndices[0]), specialIndexCount*sizeof(uint8)); bytesWritten += specialIndexCount*sizeof(uint8); dff.write(reinterpret_cast (&vertexBoneIndices[0]), vertexCount*sizeof(uint32)); bytesWritten += vertexCount*sizeof(uint32); dff.write(reinterpret_cast (&vertexBoneWeights[0]), vertexCount*4*sizeof(float32)); bytesWritten += vertexCount*4*sizeof(float32); for (uint32 i = 0; i < boneCount; i++) { if (specialIndexCount == 0) bytesWritten += writeUInt32(0xdeaddead, dff); dff.write(reinterpret_cast (&inverseMatrices[i*16]), 16*sizeof(float32)); bytesWritten += 16*sizeof(float32); } if (specialIndexCount != 0) dff.seekp(0x0C, ios::cur); WRITE_HEADER(CHUNK_SKIN); } bytesWritten += writtenBytesReturn; // Morph writtenBytesReturn = 0; if (hasMorph) { SKIP_HEADER(); bytesWritten += writeUInt32(0, dff); WRITE_HEADER(CHUNK_MORPH); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_EXTENSION); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_GEOMETRY); return bytesWritten; } uint32 Geometry::writeMeshExtension(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; SKIP_HEADER(); bytesWritten += writeUInt32(meshExtension->unknown, dff); if (meshExtension->unknown != 0) { uint32 vertexCount = meshExtension->vertices.size() / 3; uint32 faceCount = meshExtension->faces.size() / 3; uint32 materialCount = meshExtension->textureName.size(); bytesWritten += writeUInt32(1, dff); bytesWritten += writeUInt32(vertexCount, dff); dff.seekp(0xC, ios::cur); bytesWritten += 0xC; bytesWritten += writeUInt32(faceCount, dff); dff.seekp(0x8, ios::cur); bytesWritten += 0x8; bytesWritten += writeUInt32(materialCount, dff); dff.seekp(0x10, ios::cur); bytesWritten += 0x10; dff.write(reinterpret_cast (&meshExtension->vertices[0]), 3*vertexCount*sizeof(float32)); bytesWritten += 3*vertexCount*sizeof(float32); dff.write(reinterpret_cast (&meshExtension->texCoords[0]), 2*vertexCount*sizeof(float32)); bytesWritten += 2*vertexCount*sizeof(float32); dff.write(reinterpret_cast (&meshExtension->vertexColors[0]), 4*vertexCount*sizeof(uint8)); bytesWritten += 4*vertexCount*sizeof(uint8); dff.write(reinterpret_cast (&meshExtension->faces[0]), 3*faceCount*sizeof(uint16)); bytesWritten += 3*faceCount*sizeof(uint16); dff.write(reinterpret_cast (&meshExtension->assignment[0]), faceCount*sizeof(uint16)); bytesWritten += faceCount*sizeof(uint16); char buffer[0x20]; for (uint32 i = 0; i < materialCount; i++) { memset(buffer, 0, 0x20); strncpy(buffer, meshExtension->textureName[i].c_str(), 0x20); dff.write(buffer, 0x20); bytesWritten += 0x20; memset(buffer, 0, 0x20); strncpy(buffer, meshExtension->maskName[i].c_str(), 0x20); dff.write(buffer, 0x20); bytesWritten += 0x20; bytesWritten += writeFloat32(meshExtension->unknowns[i*3+0], dff); bytesWritten += writeFloat32(meshExtension->unknowns[i*3+1], dff); bytesWritten += writeFloat32(meshExtension->unknowns[i*3+2], dff); } } WRITE_HEADER(CHUNK_MESHEXTENSION); return bytesWritten; } /* * Material */ uint32 Material::write(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; // Material SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt32(flags, dff); dff.write(reinterpret_cast (color), 4*sizeof(uint8)); bytesWritten += 4*sizeof(uint8); bytesWritten += writeInt32(unknown, dff); bytesWritten += writeInt32(hasTex, dff); dff.write(reinterpret_cast (surfaceProps), 3*sizeof(float32)); bytesWritten += 3*sizeof(float32); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // Texture if (hasTex) bytesWritten += texture.write(dff); // Extensions { SKIP_HEADER(); // Right To Render writtenBytesReturn = 0; if (hasRightToRender) { SKIP_HEADER(); bytesWritten += writeUInt32(rightToRenderVal1, dff); bytesWritten += writeUInt32(rightToRenderVal2, dff); WRITE_HEADER(CHUNK_RIGHTTORENDER); } bytesWritten += writtenBytesReturn; // Mat fx // Reflection Mat writtenBytesReturn = 0; if (hasReflectionMat) { SKIP_HEADER(); bytesWritten += writeFloat32(reflectionChannelAmount[0], dff); bytesWritten += writeFloat32(reflectionChannelAmount[1], dff); bytesWritten += writeFloat32(reflectionChannelAmount[2], dff); bytesWritten += writeFloat32(reflectionChannelAmount[3], dff); bytesWritten += writeFloat32(reflectionIntensity, dff); bytesWritten += writeFloat32(0, dff); WRITE_HEADER(CHUNK_REFLECTIONMAT); } bytesWritten += writtenBytesReturn; // Specular Mat writtenBytesReturn = 0; if (hasSpecularMat) { SKIP_HEADER(); bytesWritten += writeFloat32(specularLevel, dff); uint32 len = specularName.length()+1; dff.write(specularName.c_str(), len); bytesWritten += len; if (len % 4 != 0) { dff.seekp(4 - len % 4, ios::cur); bytesWritten += 4 - len % 4; } dff.seekp(4, ios::cur); WRITE_HEADER(CHUNK_SPECULARMAT); } bytesWritten += writtenBytesReturn; // UV Anim WRITE_HEADER(CHUNK_EXTENSION); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_MATERIAL); return bytesWritten; } /* * Texture */ uint32 Texture::write(ofstream &dff) { HeaderInfo header; header.version = version; uint32 writtenBytesReturn; // Material SKIP_HEADER(); // Struct { SKIP_HEADER(); bytesWritten += writeUInt16(filterFlags, dff); bytesWritten += writeUInt16(0, dff); WRITE_HEADER(CHUNK_STRUCT); } bytesWritten += writtenBytesReturn; // String -- Texture name { SKIP_HEADER(); uint32 len = name.length()+1; dff.write(name.c_str(), len); bytesWritten += len; if (len % 4 != 0) { dff.seekp(4 - len % 4, ios::cur); bytesWritten += 4 - len % 4; } WRITE_HEADER(CHUNK_STRING); } bytesWritten += writtenBytesReturn; // String -- Mask name { SKIP_HEADER(); uint32 len = maskName.length()+1; dff.write(maskName.c_str(), len); bytesWritten += len; if (len % 4 != 0) { dff.seekp(4 - len % 4, ios::cur); bytesWritten += 4 - len % 4; } WRITE_HEADER(CHUNK_STRING); } bytesWritten += writtenBytesReturn; // Extensions { SKIP_HEADER(); // Sky Mipmap Val writtenBytesReturn = 0; if (hasSkyMipmap) { SKIP_HEADER(); bytesWritten += writeUInt32(0xFC0, dff); WRITE_HEADER(CHUNK_SKYMIPMAP); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_EXTENSION); } bytesWritten += writtenBytesReturn; WRITE_HEADER(CHUNK_TEXTURE); return bytesWritten; } };