tge/lib/dtsSDK/DTSOutputStream.h
2017-04-17 06:17:10 -06:00

438 lines
14 KiB
C++
Executable File

#ifndef __DTSOUTPUTSTREAM_H
#define __DTSOUTPUTSTREAM_H
#include <cassert>
#include <vector>
#include <fstream>
#include "DTSShape.h"
#include "DTSQuaternion.h"
#include "DTSMatrix.h"
#include "DTSEndian.h"
using namespace std;
namespace DTS
{
// -------------------------------------------------------------------------
// class OutputStream
// --------------------------------------------------------------------------
/*! The DTS file format stores in separate buffers values of 8, 16 or 32
bits of length, in order to speed up loading in non-Intel platforms.
In order to create a DTS file you should create the three buffers in
memory and then begin writting the data using helper functions. This
mimics the way the data is retrieved in the engine.
This class provides the needed functionality. Simply create a
OutputStream object and write the data using the "write" method,
that stores the values in the correct buffer. The "flush" method
creates finally the file in the format expected by the V12 engine.
The class knowns about all DTS namespace simple objects and can
save them (operator <<) in the expected format.
*** WARNING *** THIS CODE IS NOT PORTABLE
This code assumes a little-endian 32 bits machine
*/
class OutputStream
{
public:
//! Initialize the stream. The version number is written in the header
OutputStream (std::ostream &, int DTSVersion = 24) ;
~OutputStream () ;
//! Wite "count" dwords (= count x 4 bytes)
void write (const int *, int count) ;
//! Write "count" words (= count x 2 bytes)
void write (const short *, int count) ;
//! Write "count" bytes
void write (const char *, int count) ;
//! Write one dword
OutputStream & operator << (const int value) { write(&value, 1) ; return *this ; }
//! Write one word
OutputStream & operator << (const short value) { write(&value, 1) ; return *this ; }
OutputStream & operator << (const unsigned short value) { write((const short*)&value, 1) ; return *this ; }
//! Write one byte
OutputStream & operator << (const char value) { write(&value, 1) ; return *this ; }
//! Write one dword (1 float == 32 bits)
OutputStream & operator << (const float value)
{
write((int *)&value, 1) ;
return *this ;
}
//! Write one byte (provided for convenience)
OutputStream & operator << (const unsigned char value)
{
write((char *)&value, 1) ;
return *this ;
}
//! Write a string (1 byte len first, then n byte-sized characters)
OutputStream & operator << (const std::string &s) ;
// Operators to write some usual structs
OutputStream & operator << (const Point &) ;
OutputStream & operator << (const Point2D &) ;
OutputStream & operator << (const Box &) ;
OutputStream & operator << (const Quaternion &) ;
OutputStream & operator << (const Node &) ;
OutputStream & operator << (const Object &) ;
OutputStream & operator << (const Decal &) ;
OutputStream & operator << (const IFLMaterial &) ;
OutputStream & operator << (const DecalState &) ;
OutputStream & operator << (const DetailLevel &) ;
OutputStream & operator << (const ObjectState &) ;
OutputStream & operator << (const Subshape &) ;
OutputStream & operator << (const Trigger &) ;
OutputStream & operator << (const Primitive &) ;
OutputStream & operator << (const Mesh &) ;
OutputStream & operator << (const Cluster &) ;
OutputStream & operator << (const Matrix<4,4> &) ;
//! Stores checkpoint values in the streams
void storeCheck (int checkPoint = -1) ;
//! When you're finished, call this method to create the file
//! You'll still need to write the sequence data to the stream, next
void flush () ;
private:
std::ostream & out ;
int DTSVersion ;
int checkCount ;
// Pointers to the 3 memory buffers
int * Buffer32 ;
short * Buffer16 ;
char * Buffer8 ;
// Values allocated in each buffer
unsigned long Allocated32 ;
unsigned long Allocated16 ;
unsigned long Allocated8 ;
// Values actually used in each buffer
unsigned long Used32 ;
unsigned long Used16 ;
unsigned long Used8 ;
};
// --------------------------------------------------------------------------
// Construction and destruction
// --------------------------------------------------------------------------
inline OutputStream::OutputStream (std::ostream & s, int version)
: out(s), DTSVersion(version)
{
// Versions 17 and down don't use the buffers
assert (version >= 18) ;
// Create buffers with some unused space in
Buffer32 = new int [64] ;
Buffer16 = new short [128] ;
Buffer8 = new char [256] ;
Allocated32 = 64 ;
Allocated16 = 128 ;
Allocated8 = 256 ;
Used32 = 0 ;
Used16 = 0 ;
Used8 = 0 ;
checkCount = 0 ;
}
inline OutputStream::~OutputStream()
{
delete [] Buffer8 ;
delete [] Buffer16 ;
delete [] Buffer32 ;
}
// --------------------------------------------------------------------------
// Write functions
// --------------------------------------------------------------------------
/*! All 32 bits dwords must be written to the internal 32-bits buffer
so they can be reversed in a single pass in big-endian machines */
inline void OutputStream::write (const int * data, int count)
{
assert (count > 0) ;
assert (data != 0) ;
if (Used32 + count > Allocated32)
{
// Not enough space allocated. Alloc a bigger buffer,
// with a multiple of 64 number of elements, but enough
// to add the new data, and swap the old buffer for it
Allocated32 = ((Used32 + count) & ~63) + 64 ;
assert (Allocated32 > Used32 + count) ;
int * NewBuffer32 = new int[Allocated32] ;
memcpy (NewBuffer32, Buffer32, sizeof(int) * Used32) ;
delete [] Buffer32 ;
Buffer32 = NewBuffer32 ;
}
// Add the new data to the buffer
memcpy (Buffer32 + Used32, data, sizeof(int) * count) ;
Used32 += count ;
}
// The other write methods are exactly the same, except for the buffer used
inline void OutputStream::write (const short * data, int count)
{
assert (count > 0) ;
assert (data != 0) ;
if (Used16 + count > Allocated16)
{
Allocated16 = ((Used16 + count) & ~63) + 64 ;
assert (Allocated16 > Used16 + count) ;
short * NewBuffer16 = new short[Allocated16] ;
memcpy (NewBuffer16, Buffer16, sizeof(short) * Used16) ;
delete [] Buffer16 ;
Buffer16 = NewBuffer16 ;
}
memcpy (Buffer16 + Used16, data, sizeof(short) * count) ;
Used16 += count ;
}
inline void OutputStream::write (const char * data, int count)
{
assert (count > 0) ;
assert (data != 0) ;
if (Used8 + count > Allocated8)
{
Allocated8 = ((Used8 + count) & ~63) + 64 ;
assert (Allocated8 > Used8 + count) ;
char * NewBuffer8 = new char[Allocated8] ;
memcpy (NewBuffer8, Buffer8, sizeof(char) * Used8) ;
delete [] Buffer8 ;
Buffer8 = NewBuffer8 ;
}
memcpy (Buffer8 + Used8, data, sizeof(char) * count) ;
Used8 += count ;
}
// --------------------------------------------------------------------------
// Write operators
// --------------------------------------------------------------------------
inline OutputStream & OutputStream::operator << (const Point &p)
{
return (*this) << p.x() << p.y() << p.z() ;
}
inline OutputStream & OutputStream::operator << (const Point2D &p)
{
return (*this) << p.x() << p.y() ;
}
inline OutputStream & OutputStream::operator << (const Box &b)
{
return (*this) << b.min << b.max ;
}
inline OutputStream & OutputStream::operator << (const Quaternion &q)
{
return (*this) << (short)(q.x() * 32767.0f)
<< (short)(q.y() * 32767.0f)
<< (short)(q.z() * 32767.0f)
<< (short)(q.w() * 32767.0f) ;
}
inline OutputStream & OutputStream::operator << (const Node &n)
{
return (*this) << n.name << n.parent << n.firstObject
<< n.child << n.sibling ;
}
inline OutputStream & OutputStream::operator << (const Object &o)
{
return (*this) << o.name << o.numMeshes << o.firstMesh
<< o.node << o.sibling << o.firstDecal ;
}
inline OutputStream & OutputStream::operator << (const Decal &d)
{
return (*this) << d.name << d.numMeshes << d.firstMesh
<< d.object << d.sibling ;
}
inline OutputStream & OutputStream::operator << (const IFLMaterial &m)
{
return (*this) << m.name << m.slot << m.firstFrame
<< m.time << m.numFrames ;
}
inline OutputStream & OutputStream::operator << (const Subshape &s)
{
assert (0 && "Subshapes should be written in block") ;
}
inline OutputStream & OutputStream::operator << (const Trigger &d)
{
return (*this) << d.state << d.pos ;
}
inline OutputStream & OutputStream::operator << (const DecalState &d)
{
return (*this) << d.frame ;
}
inline OutputStream & OutputStream::operator << (const ObjectState &o)
{
return (*this) << o.vis << o.frame << o.matFrame ;
}
inline OutputStream & OutputStream::operator << (const DetailLevel &d)
{
return (*this) << d.name << d.subshape << d.objectDetail << d.size
<< d.avgError << d.maxError << d.polyCount ;
}
inline OutputStream & OutputStream::operator << (const Primitive &p)
{
return (*this) << p.firstElement << p.numElements << p.type ;
}
inline OutputStream & OutputStream::operator << (const Mesh &m)
{
m.save(*this) ;
return *this ;
}
inline OutputStream & OutputStream::operator << (const Cluster &c)
{
return *this << c.startPrimitive << c.endPrimitive << c.normal << c.k << c.frontCluster << c.backCluster;
}
inline OutputStream & OutputStream::operator << (const std::string &s)
{
std::string::const_iterator pos = s.begin() ;
while (pos != s.end()) (*this) << *pos++ ;
(*this) << '\x00' ;
return *this ;
}
template <class type>
inline OutputStream & operator << (OutputStream & out, const std::vector<type> &v)
{
typename vector<type>::const_iterator pos = v.begin() ;
while (pos != v.end())
{
out << *pos++ ;
}
return out ;
}
inline OutputStream & OutputStream::operator << (const Matrix<4,4> &m)
{
for (int r = 0 ; r < 4 ; r++)
for (int c = 0 ; c < 4 ; c++)
(*this) << m[r][c];
return *this;
}
// --------------------------------------------------------------------------
// Checkpoints and alignment
// --------------------------------------------------------------------------
/*! Sometimes during the storing process, a checkpoint value is
stored to all the buffers at the same time. This check helps
to determine if the files are not corrupted. */
inline void OutputStream::storeCheck(int checkPoint)
{
if (checkPoint >= 0)
assert (checkPoint == checkCount) ;
(*this) << (int) checkCount ;
(*this) << (short) checkCount ;
(*this) << (char) checkCount ;
checkCount++ ;
}
// --------------------------------------------------------------------------
// Flush method
// --------------------------------------------------------------------------
/*! The file format wants a header with the version number, total size
of all three buffers (in dwords), and the offsets of the 16-bits
and 8-bits buffers, followed with the buffer data itself.
There is additional information that is stored after this data,
but we don't know about it here */
inline void OutputStream::flush ()
{
int totalSize ;
int offset16 ;
int offset8 ;
int i;
// Force all buffers to have a size multiple of 4 bytes
if (Used16 & 0x0001) (*this) << (short)0 ;
while (Used8 & 0x0003) (*this) << (char) 0 ;
// Compute the header values (in dwords)
offset16 = Used32 ;
offset8 = Used32 + Used16/2 ;
totalSize = Used32 + Used16/2 + Used8/4 ;
// Fix endian...
if (!isLittleEndian())
{
for (i=0; i<Used16; i++)
Buffer16[i] = FIX_ENDIAN(Buffer16[i]);
for (i=0; i<Used32; i++)
Buffer32[i] = FIX_ENDIAN(Buffer32[i]);
}
// Write the resulting data to the file
std::streampos pos = out.tellp() ;
out.write ((char *) &FIX_ENDIAN(DTSVersion), 4) ;
out.write ((char *) &FIX_ENDIAN(totalSize), 4) ;
out.write ((char *) &FIX_ENDIAN(offset16), 4) ;
out.write ((char *) &FIX_ENDIAN(offset8), 4) ;
out.write ((char *) Buffer32, 4 * Used32) ;
out.write ((char *) Buffer16, 2 * Used16) ;
out.write (Buffer8, Used8) ;
std::streampos endpos = out.tellp() ;
assert ((endpos - pos) == (std::streampos)(4*Used32 + 2*Used16 + 1*Used8 + 16)) ;
}
}
#endif