Files
FC1/CryCommon/CryCompiledFile.h
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

600 lines
18 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// Crytek (C) 2002
//
// CryCommon Source Code
//
// File:CryCompiledFile.h
// Description: Cry Compiled File Format declarations: necessary structures,
// chunk descriptions.
//
// Notes on Cry Compiled File Format.
// Generally, compiled format is used for faster load times in the game.
// The source files exported from Max and otherwise created are compiled
// into compiled files that are fast to read, but whose format can be changed
// frequently and be inflexible.
//
// The compiled format is essentially a bitstream structured with light-weight chunks.
// THe compiled format file consists of several pieces (chunks) that can either contain some
// binary data, or other chunks. Each chunk begins with CCFHeader structure.
//
// If you need chunk identification (like ChunkId in the CGF/CAF), write another DWORD after the CCFChunkHeader
//
// If you need chunk type version, write the version number as another DWORD after the CCFChunkHeader
//
// If you need ANYTHING else, just write it after the CCFChunkHeader. DO NOT expand CCFChunkHeader.
// If you need, use your own convention to write some other structure right after the CCFChunkHeader,
// but everybody must be able to skip chunks using the nSize field in the CCFChunkHeader
//
// The CCF format is for one-time stream-like reading, not for random access.
// There's no point in having any chunk table or other dictionary structures.
//
// History:
// - Jan 16, 2003 - Created by Sergiy Migdalskiy
//
// Prerequisities:
// #include <stdio.h>
//
//////////////////////////////////////////////////////////////////////
#ifndef __CRY_COMMON_CRY_COMPILED_FILE_FORMAT_HDR__
#define __CRY_COMMON_CRY_COMPILED_FILE_FORMAT_HDR__
// the required header for file i/o operations
#include <stdio.h>
// Cry Compiled File format chunk header
// Each chunk in the file begins with this structure
struct CCFChunkHeader
{
// the chunk size, in bytes, including this header.
// this must always be 4-byte aligned
unsigned nSize;
// the chunk type, must be one of the CCFChunkTypeEnum constants
unsigned nType;
};
enum CCFChunkTypeEnum
{
CCF_NOT_CHUNK = -1, // this is used to signal absence of chunk
CCF_EMPTY_CHUNK = 0,
// the Compiled Cry Geometry header, contains one structure CCFCCGHeader
CCF_HEADER_CCG,
// information about geometry included into a compiled CGF file
// this chunk is followed by the structure CCFAnimGeomInfo
// and contains also subchunks:
// CCF_GI_PRIMITIVE_GROUPS
// CCF_GI_INDEX_BUFFER
// CCF_GI_EXT_TO_INT_MAP
// CCF_GI_EXT_UVS
// CCF_GI_EXT_TANGENTS
// CCF_GI_EXT_COLORS {optionally}
CCF_GEOMETRY_INFO,
// This is a pure array of material groups in CCFMaterialGroup structures
CCF_GI_PRIMITIVE_GROUPS,
// this is purely an array of unsigned shorts representing the index buffer
CCF_GI_INDEX_BUFFER,
// external to internal indexation map, again, an array of unsigned shorts.
CCF_GI_EXT_TO_INT_MAP,
// an array of CryUV's - in external indexation, for immediate copying into the vertex buffer
CCF_GI_EXT_UVS,
// an array of tangent bases in TangData structures (external indexation)
CCF_GI_EXT_TANGENTS,
// an array of DWORDs representing colors - in internal indexation
CCF_GI_INT_COLORS,
// First, struct CCFBoneDescArray followed by
// the linearized hierarchy of bones: each bone represented by serialized by CryBoneDesc data
CCF_BONE_DESC_ARRAY,
// this is an array of vertices, internal indexation; must be the exact size to contain
// number of vertices specified in CCF_GEOMETRY_INFO chunk
CCF_VERTICES,
// this is an array of normals, internal indexation; must be the exact size to contain
// all the normals (specified in CCF_GEOMETRY_INFO chunk)
CCF_NORMALS,
// this is the serialized connectivity info, without any header
CCF_STENCIL_SHADOW_CONNECTIVITY,
// this is the vertex skin, serialized CrySkinFull
CCF_SKIN_VERTICES,
// the normal skin, serialized CrySkinFull
CCF_SKIN_NORMALS,
// the skin representing tangents,
CCF_SKIN_TANGENTS,
// the array of materials: it's just followed by an integral number of structures MAT_ENTITY
CCF_MATERIALS,
// these are the faces, in internal indexation. The chunk data is a set of triples of
// ushorts (CCFIntFace) representing each face's vertices (internal indexation) and
// a set of unsigned chars (CCFIntFaceMtlID) representing the face materials
CCF_GI_INT_FACES,
// This is compound chunk containing physical info for bones of the corresponding LOD
// (The LOD is the # of occurence of this chunk in the file; there must be at most 2 such chunks)
// It's followed by a CCFBoneGeometry with the number of subsequent CCF_BG_BONE chunks
CCF_BONE_GEOMETRY,
// This is one bone geometry info for a bone. Followed by struct CCFBGBone
// and the necessary info arrays (see CCFBGBone comments)
CCF_BG_BONE,
// This is the morph target set for the geometry in this file. It contains
// ALL the morph targets for ALL LODs
// The header is CCFMorphTargetSet, followed by the specified number of
// CCF_MORPH_TARGET chunks
CCF_MORPH_TARGET_SET,
// This is the morph target. The header is CCFMorphTarget, followed by
// the raw morph target skin (serialized from the CrySkinMorph) and then the name
// All the serialized skins and the name are padded for 4 byte alignment
CCF_MORPH_TARGET,
// This is the array of light description for a character
// this chunk has the header CCFCharLightDesc and then
// the corresponding number of serialized CBoneLightBindInfo's
// all of them must be 4-byte-aligned.
CCF_CHAR_LIGHT_DESC,
// this is the compiled CAL file, that contains information about the
// animation names, file names and directives
// in case there's no CAL, the Resource Compiler finds all the proper files
// in the current directory and forms a simple CAL script from them.
// this chunk consists of subchunks that define animations or commands (CCF_ANIM_SCRIPT_*)
CCF_ANIM_SCRIPT,
// this subchunk registers a dummy animation (animation that's ignored if it's started)
// It's followed by the name of the animation
CCF_ANIM_SCRIPT_DUMMYANIM,
// the subchunk of CCF_ANIM_SCRIPT, defines the animation name and path, delimited by \0
// it contains CCFAnimInfo structure with the base info about the animation (needed to avoid loading animation
// at the beginning of the frame), which is followed by the animation name, then \0, then the path
CCF_ANIM_SCRIPT_ANIMINFO,
// the subchunk of CCF_ANIM_SCRIPT, defines a directive AnimationDir, contains the directory itself
// this should be concatenated with the last directory (new current dir relative to the current directory)
CCF_ANIM_SCRIPT_ANIMDIR,
// the subchunks of CCF_ANIM_SCRIPT, define directives ModelOffsetX, ModelOffsetY, ModelOffsetZ
// contains 3 floats with the values of offsets
CCF_ANIM_SCRIPT_MODELOFFSET,
// chunk with a set of string pairs representing the name and value of
// user-defined scene properties. It can be inside a LOD (geometry) chunk
// or as a top-level ccg chunk
CCF_USER_PROPERTIES
};
// THis is the header of CCF_ANIM_SCRIPT_ANIMINFO
struct CCFAnimInfo
{
// combination of GlobalAnimation internal flags
unsigned nAnimFlags;
// timing data, retrieved from the timing_chunk_desc
int nTicksPerFrame;
float fSecsPerTick;
// the start/end of the animation, in ticks
int nRangeStart;
int nRangeEnd;
// number of controllers in the file
unsigned numControllers;
// initializes the values in the structure to some sensible defaults
void init()
{
nAnimFlags = 0;
nTicksPerFrame = 160;
fSecsPerTick = 0.000208f;
nRangeStart = 0;
nRangeEnd = 900; // this is in ticks, means 30 frames
numControllers = 0;
}
};
// this is the header of CCF_CHAR_LIGHT_DESC chunk and is followed by
// the corresponding number of serialized CBoneLightBindInfo's
// all of them must be 4-byte-aligned.
struct CCFCharLightDesc
{
// total number of lights
unsigned numLights;
// the first numLocalLights lights are local
unsigned numLocalLights;
};
// This is the header of CCF_MORPH_TARGET_SET chunk, followed by the specified number of
// CCF_MORPH_TARGET chunks
struct CCFMorphTargetSet
{
// the number of morph targets in this set.
unsigned numMorphTargets;
};
// Represents one morph target; header of CCF_MORPH_TARGET chunk
// followed by the raw morph target skin (serialized from the CrySkinMorph) and then the name
// All the serialized skins and the name are padded for 4 byte alignment
struct CCFMorphTarget
{
// number of LODs (skins) in this morph target. Must be 1 by now
unsigned numLODs;
};
// this is the bone geometry for physics chunk header
struct CCFBoneGeometry
{
// the number of CCF_BG_BONE chunks inside this chunk
unsigned numBGBones;
};
//////////////////////////////////////////////////////////////////////////
// The header of the physical bone chunk, containing the whole info to initialize
// physical geometry of the bone
struct CCFBGBone
{
// the index of the bone, in LOD0 indexation (to be directly plugged into the bone array)
unsigned nBone;
// the geometry data
// the number of vertices in the mesh. This structure is immediately followed by
// a Vec3d array of this size
unsigned numVertices;
// the number of faces. The number of CCFIntFace structures immediately follow the
// vertex array. After them, the same number of unsigned chars follow, that describe each face material
unsigned numFaces;
};
#pragma pack(push,2)
struct CCFIntFace
{
unsigned short v[3];
CCFIntFace (){}
CCFIntFace (const unsigned short* p)
{
v[0] = p[0]; v[1] = p[1]; v[2] = p[2];
}
void operator = (const CryFace& face)
{
v[0] = face.v0;
v[1] = face.v1;
v[2] = face.v2;
}
};
#pragma pack(pop)
typedef unsigned char CCFIntFaceMtlID;
// this structure appears in the CCF_HEADER_CCG chunk at the top of the CCG file
// and contains general information about the model being loaded
struct CCFCCGHeader
{
// number of lods in this file
unsigned numLODs;
};
struct CCFAnimGeomInfo
{
// number of vertices and normals in internal indexation
unsigned numVertices;
// number of vertices, normals, UVs and Tangent Bases (external indexation)
unsigned numExtTangents;
// number of faces
unsigned numFaces;
// number of indices in the index buffer (currently always 3*numFaces)
unsigned numIndices;
// number of material/primitive groups
unsigned numPrimGroups;
};
struct CCFBoneDescArrayHeader
{
// number of bones in the hierarchy; each bone should be read with Serialize function of CryBoneDesc
unsigned numBones;
};
// array of these structures represents the groups of indices in the index buffer:
// each group has its own material id and number of elements (indices, i.e. number of faces * 3 in case of strip stripification)
struct CCFMaterialGroup
{
// material index in the original indexation of CGF
unsigned nMaterial;
// the first index in the final index buffer
unsigned short nIndexBase;
// number of indices in the final index buffer
unsigned short numIndices;
};
// This is utility that can be used to easily write CCF format files
// just open the file, create this object on stack and add the chunk with
// AddChunk(nType)
// Then write the chunk directly to the file, and then add new chunk,
// repeat the process..
// Destroy (or call CloseChunk()) this object before you close the file.
// To have nested chunks, create another instance of CCFWriter and use it for some time.
// When you destruct it, you can use the upper-level writer. And so on.
class CCFFileWriter
{
public:
CCFFileWriter (FILE* f):
m_pFile (f), m_nLastChunk (-1)
{
}
CCFFileWriter ():
m_pFile(NULL), m_nLastChunk(-1)
{
}
~CCFFileWriter()
{
CloseChunk();
}
// changes the file handle
void SetFile (FILE* f)
{
CloseChunk();
m_pFile = f;
}
// adds a new chunk of the specified type
// returns 1 if successfully wrote the chunk header or 0 if an error occurred
size_t AddChunk(CCFChunkTypeEnum nType)
{
// first, end the previous chunk
CloseChunk();
// remember the current position
m_nLastChunk = ftell (m_pFile);
// write the chunk header
CCFChunkHeader header;
header.nType = nType;
header.nSize = 0;
return fwrite (&header, sizeof(header), 1, m_pFile);
}
// Signals the end of the chunk that was added with AddChunk
// Automatically aligns the chunk data by 4-byte boundary, if needed
void CloseChunk()
{
if (m_nLastChunk < 0)
return; // no last chunk, or the last chunk was closed
long nNewChunkPos = ftell (m_pFile);
if (nNewChunkPos&3)
{
// align by 4-byte boundary
int nPad = 0;
fwrite (&nPad, 1, 4-(nNewChunkPos&3), m_pFile);
nNewChunkPos = ftell (m_pFile);
}
// write the size of the chunk to the chunk header
fseek (m_pFile, (INT_PTR)(&((CCFChunkHeader*)0)->nSize)+m_nLastChunk, SEEK_SET);
unsigned nSize = nNewChunkPos - m_nLastChunk;
fwrite (&nSize, sizeof(nSize), 1, m_pFile);
// set the file pointer back where it was
fseek (m_pFile, nNewChunkPos, SEEK_SET);
// forget about the last chunk
m_nLastChunk = -1;
}
protected:
// the file to which the chunks are written
FILE* m_pFile;
// the last chunk position within the file (used to update the chunk)
long m_nLastChunk;
};
//////////////////////////////////////////////////////////////////////////
// This class is used to read the CCF files.
// Just open the file, and pass it to an instance of this class CCFReader.
// Immediately after construction, check IsEnd(). If it's false, the chunk header will be available via
// getType(), getChunkSize(), getDataSize() and readData()
// Call Skip() until there are no more available data (in which case Skip() will return false)
class CCFFileReader
{
public:
// ASSUMES: the file pointer is exactly at some chunk header
CCFFileReader (FILE* f):
m_pFile (f)
{
Reset();
}
// starts all over again
void Reset()
{
m_nNextChunk = 0;
Skip();
}
// indicates the end of data stream, or error
bool IsEnd()
{
return m_Header.nType == CCF_NOT_CHUNK;
}
CCFChunkTypeEnum GetChunkType()
{
return (CCFChunkTypeEnum)m_Header.nType;
}
// returns the whole chunk size, including the chunk header, in bytes
unsigned GetChunkSize()const
{
return m_Header.nSize;
}
// returns the chunk data size, in bytes
unsigned GetDataSize ()const
{
return m_Header.nSize - sizeof(m_Header);
}
// reads the data into the supplied buffer (must be at least GetDataSize() bytes)
// returns true when successful
bool ReadData (void* pBuffer)
{
return 1 == fread (pBuffer, GetDataSize(), 1, m_pFile);
}
// skips the current chunk and goes no to the next one.
// returns true if successful
bool Skip ()
{
// be pessimistic - we won't read it
m_Header.nType = CCF_NOT_CHUNK;
m_Header.nSize = -1;
if (!m_pFile)
return false;
if (fseek (m_pFile, m_nNextChunk, SEEK_SET))
return false; // couldn't seek
if (1 != fread (&m_Header, sizeof(m_Header),1, m_pFile))
return false; // couldn't read
m_nNextChunk += m_Header.nSize;
return true;// read it
}
protected:
// current file
FILE* m_pFile;
// current chunk header (the file pointer is located after this chunk)
CCFChunkHeader m_Header;
// the next chunk absolute position
int m_nNextChunk;
};
//////////////////////////////////////////////////////////////////////////
// This class is used to read the CCF-formatted memory mapped files.
// Just pass the block of memory representing contents of CCF file to an instance of this class.
// Immediately after construction, check IsEnd(). If it's false, the chunk header will be available via
// getChunkType(), getChunkSize(), getDataSize() and getData()
// Call Skip() until there are no more available data (in which case Skip() will return false)
class CCFMemReader
{
public:
// we don't support chunks of > this size
enum {g_nMaxChunkSize = 0x40000000};
// ASSUMES: the file pointer is exactly at some chunk header
CCFMemReader (const void* pData, unsigned nSize):
m_pData ((const char*)pData), m_pEnd ((const char*)pData + nSize)
{
Reset();
}
// starts all over again
void Reset ()
{
if (m_pEnd - m_pData > sizeof(CCFChunkHeader) && ((unsigned)(m_pEnd - m_pData)) <= g_nMaxChunkSize)
{
m_pHeader = (CCFChunkHeader*)m_pData;
if (((const char*)m_pHeader)+m_pHeader->nSize > m_pEnd || m_pHeader->nSize > g_nMaxChunkSize)
m_pHeader = NULL; // Error: data truncated
}
else
m_pHeader = NULL; // Error: header truncated
}
// indicates the end of data stream, or error
bool IsEnd()
{
return !m_pHeader;
}
CCFChunkTypeEnum GetChunkType()
{
return (CCFChunkTypeEnum)m_pHeader->nType;
}
// returns the whole chunk size, including the chunk header, in bytes
unsigned GetChunkSize()const
{
return m_pHeader->nSize;
}
// returns the chunk data size, in bytes
unsigned GetDataSize ()const
{
return m_pHeader->nSize - sizeof(*m_pHeader);
}
// reads the data into the supplied buffer (must be at least GetDataSize() bytes)
// returns true when successful
const void* GetData ()
{
return m_pHeader+1;
}
// skips the current chunk and goes no to the next one.
// returns true if successful
bool Skip ()
{
// be optimistic - assume we will end up with successful step to the next chunk
m_pHeader = (const CCFChunkHeader*)(((const char*)m_pHeader) + m_pHeader->nSize);
if ((const char*)(m_pHeader+1) > m_pEnd)
{
// there's no next chunk, or its header is truncated
m_pHeader = NULL;
return false;
}
if (m_pHeader->nSize < sizeof(CCFChunkHeader)
|| (unsigned)m_pHeader->nSize > g_nMaxChunkSize)
{
// the header size is unsupported (most probably a noisy header)
m_pHeader = NULL;
return false;
}
if (((const char*)m_pHeader)+m_pHeader->nSize > m_pEnd)
{
// the chunk header is maybe ok, but the chunk data is truncated; or maybe
// the chunk header contains noise
m_pHeader = NULL;
return false;
}
return true;
}
protected:
// the whole data
const char* m_pData;
// the end of the whole data
const char* m_pEnd;
// current chunk header (the file pointer is located after this chunk)
const CCFChunkHeader* m_pHeader;
};
#endif