Added: #1440 Simple library for serializing max storage through an IStream

--HG--
branch : build_pipeline_v3
hg/feature/build_pipeline_v3
kaetemi 12 years ago
parent 9beb31bac4
commit 635092b0db

@ -0,0 +1,189 @@
/**
* \file storage_chunks.cpp
* \brief CStorageChunks
* \date 2012-08-18 09:20GMT
* \author Jan Boon (Kaetemi)
* CStorageChunks
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE PIPELINE.
* RYZOM CORE PIPELINE is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* RYZOM CORE PIPELINE is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE PIPELINE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <nel/misc/types_nl.h>
#include "storage_chunks.h"
// STL includes
// NeL includes
#include <nel/misc/debug.h>
// Project includes
// using namespace std;
// using namespace NLMISC;
// #define NL_DEBUG_STORAGE
namespace PIPELINE {
namespace MAX {
CStorageChunks::CStorageChunks(NLMISC::IStream &stream, sint64 size) : m_Stream(stream), m_Is64Bit(false)
{
if (size >= 2147483647L)
throw NLMISC::EStream("64bit chunks not supported");
m_Chunks.reserve(64);
m_Chunks.resize(1);
m_Chunks[0].HeaderSize = 0;
m_Chunks[0].OffsetBegin = stream.getPos();
if (stream.isReading())
{
m_Chunks[0].Id = 0;
m_Chunks[0].Size = 0x80000000 | (uint32)(size);
}
else
{
m_Chunks[0].Id = 1;
m_Chunks[0].Size = 0;
}
}
CStorageChunks::~CStorageChunks()
{
#ifdef NL_DEBUG_STORAGE
if (m_Chunks.size() != 1)
nldebug("Not all chunks were closed");
#endif
}
bool CStorageChunks::enterChunk()
{
if (m_Stream.isReading())
{
// input logic
if (!isChunkContainer())
{
#ifdef NL_DEBUG_STORAGE
nldebug("Current chunk is not a container, cannot enter");
#endif
return false;
}
if (endOfChunk())
{
#ifdef NL_DEBUG_STORAGE
nldebug("End of chunk, cannot enter");
#endif
return false;
}
m_Chunks.resize(m_Chunks.size() + 1);
CChunk *chunk = currentChunk();
chunk->OffsetBegin = m_Stream.getPos();
m_Stream.serial(chunk->Id);
m_Stream.serial(chunk->Size);
chunk->HeaderSize = 6;
if (chunk->Size == 0)
{
// this is a 64bit chunk
uint64 size64;
m_Stream.serial(size64);
chunk->HeaderSize += 8;
bool iscont = (size64 & 0x8000000000000000) == 0x8000000000000000;
size64 &= 0x7FFFFFFFFFFFFFFF;
if (size64 >= 2147483647L)
throw NLMISC::EStream("64bit chunks not supported");
// downgrade to 32 bit chunk
chunk->Size = (uint32)size64;
if (iscont) chunk->Size |= 0x80000000;
m_Is64Bit = true; // it's true
}
return true;
}
else
{
#ifdef NL_DEBUG_STORAGE
nldebug("No input, this function cannot output, throw exception");
#endif
throw NLMISC::EStream();
}
}
bool CStorageChunks::enterChunk(uint16 id, bool container)
{
if (!m_Stream.isReading())
{
if (m_Is64Bit)
throw NLMISC::EStream("64bit chunks not supported");
// enter the new chunk
m_Chunks.resize(m_Chunks.size() + 1);
CChunk *chunk = currentChunk();
uint32 sizeDummy = 0xFFFFFFFF;
chunk->Id = container ? 1 : 0;
chunk->OffsetBegin = m_Stream.getPos(); // store current pos
// write header
m_Stream.serial(id); // write the id
m_Stream.serial(sizeDummy); // write 32 bit size placeholder
return true;
}
else // input or exception
{
while (enterChunk())
{
if (getChunkId() == id)
return true;
leaveChunk(); // skip data
}
return false;
}
}
sint32 CStorageChunks::leaveChunk()
{
if (m_Stream.isReading())
{
// input logic
sint32 skipped = currentChunk()->endOfChunk() - m_Stream.getPos();
if (skipped)
{
m_Stream.seek(currentChunk()->endOfChunk(), NLMISC::IStream::begin);
#ifdef NL_DEBUG_STORAGE
nldebug("Skipped %i bytes in the current chunk", skipped);
#endif
}
m_Chunks.resize(m_Chunks.size() - 1);
return skipped;
}
else
{
sint32 pos = m_Stream.getPos();
sint32 sizeWithHeader = pos - currentChunk()->OffsetBegin;
sint32 sizePos = currentChunk()->OffsetBegin + 2;
m_Stream.seek(sizePos, NLMISC::IStream::begin); // hopefully this correctly overwrites!!!
uint32 sizeField = (uint32)sizeWithHeader | (uint32)currentChunk()->Id << 31; // add container flag
m_Stream.serial(sizeField);
m_Stream.seek(pos, NLMISC::IStream::begin);
m_Chunks.resize(m_Chunks.size() - 1);
return sizeWithHeader;
}
}
} /* namespace MAX */
} /* namespace PIPELINE */
/* end of file */

@ -0,0 +1,107 @@
/**
* \file storage_chunks.h
* \brief CStorageChunks
* \date 2012-08-18 09:20GMT
* \author Jan Boon (Kaetemi)
* CStorageChunks
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE PIPELINE.
* RYZOM CORE PIPELINE is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* RYZOM CORE PIPELINE is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE PIPELINE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef PIPELINE_STORAGE_CHUNKS_H
#define PIPELINE_STORAGE_CHUNKS_H
#include <nel/misc/types_nl.h>
// STL includes
// NeL includes
#include <nel/misc/stream.h>
// Project includes
namespace PIPELINE {
namespace MAX {
/**
* \brief CStorageChunks
* \date 2012-08-18 09:20GMT
* \author Jan Boon (Kaetemi)
* CStorageChunks
*/
class CStorageChunks
{
private:
struct CChunk
{
// Size of the chunk header, 6 for 32 bit, 14 for 64 bit
uint8 HeaderSize;
// Where the header starts
sint32 OffsetBegin;
// Identifier
uint16 Id;
// Size including header size
uint32 Size;
inline sint32 getSizeWithHeader() const { return (sint32)(Size & 0x7FFFFFFF); }
inline sint32 getSize() const { return getSizeWithHeader() - (sint32)HeaderSize; }
inline bool isContainer() const { return (Size & 0x80000000) == 0x80000000; }
inline sint32 endOfChunk() const { return OffsetBegin + getSizeWithHeader(); }
inline sint32 getDataBegin() const { return OffsetBegin + (sint32)HeaderSize; }
};
public:
CStorageChunks(NLMISC::IStream &stream, sint64 size = 0);
virtual ~CStorageChunks();
// Returns true if there's another chunk, false if no more chunks in this container or if the current chunk is not a container
bool enterChunk();
// Reads and skips chunks until the one with given id is found, or writes a chunk with this id
bool enterChunk(uint16 id, bool container);
// Returns the number of skipped bytes in read more, returns chunk size including header in write mode
sint32 leaveChunk();
inline bool is64Bit() const { return m_Is64Bit; }
inline void set64Bit(bool enabled = true) { m_Is64Bit = enabled; }
inline uint16 getChunkId() const { return currentChunk()->Id; }
inline sint32 getChunkSize() const { return currentChunk()->getSize(); }
inline bool isChunkContainer() const { return currentChunk()->isContainer(); }
inline bool endOfChunk() const { return /*m_Chunks.size() == 1 ? eof() :*/ m_Stream.getPos() >= currentChunk()->endOfChunk(); }
inline NLMISC::IStream &stream() { return m_Stream; }
private:
inline const CChunk *currentChunk() const { return &m_Chunks[m_Chunks.size() - 1]; }
inline CChunk *currentChunk() { return &m_Chunks[m_Chunks.size() - 1]; }
private:
NLMISC::IStream &m_Stream;
std::vector<CChunk> m_Chunks;
bool m_Is64Bit;
}; /* class CStorageChunks */
} /* namespace MAX */
} /* namespace PIPELINE */
#endif /* #ifndef PIPELINE_STORAGE_CHUNKS_H */
/* end of file */

@ -0,0 +1,236 @@
/**
* \file storage_object.cpp
* \brief CStorageObject
* \date 2012-08-18 09:02GMT
* \author Jan Boon (Kaetemi)
* CStorageObject
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE PIPELINE.
* RYZOM CORE PIPELINE is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* RYZOM CORE PIPELINE is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE PIPELINE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <nel/misc/types_nl.h>
#include "storage_object.h"
// STL includes
#include <iomanip>
// NeL includes
// #include <nel/misc/debug.h>
// Project includes
#include "storage_stream.h"
#include "storage_chunks.h"
// using namespace std;
// using namespace NLMISC;
#define NL_DEBUG_STORAGE
namespace PIPELINE {
namespace MAX {
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
std::string IStorageObject::toString()
{
std::stringstream ss;
toString(ss);
return ss.str();
}
void IStorageObject::setSize(sint32 size)
{
// ignore
}
bool IStorageObject::getSize(sint32 &size) const
{
return false;
}
bool IStorageObject::isContainer() const
{
return false;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
std::string CStorageContainer::getClassName() // why is this not const in IClassable?
{
return "StorageContainer";
}
void CStorageContainer::serial(NLMISC::IStream &stream)
{
if (stream.getPos() == 0)
{
CStorageStream *storageStream = dynamic_cast<CStorageStream *>(&stream);
if (storageStream)
{
// implicitly assume the entire stream is the container
CStorageChunks chunks(stream, stream.isReading() ? storageStream->size() : 0);
serial(chunks);
return;
}
}
// wrapping the container inside a stream with necessary size markers
{
const uint32 magic = 0xC0C01473;
stream.serialCheck(magic);
uint version = stream.serialVersion(1);
sint32 sizePos;
bool reading = stream.isReading();
if (!reading)
sizePos = stream.getPos();
sint64 size = 0;
stream.serial(size);
CStorageChunks chunks(stream, size);
serial(chunks);
if (!reading)
{
sint32 returnPos = stream.getPos();
stream.seek(sizePos, NLMISC::IStream::begin);
stream.serial(size);
stream.seek(returnPos, NLMISC::IStream::begin);
}
return;
}
}
void CStorageContainer::toString(std::ostream &ostream, const std::string &pad)
{
// note: only use pad when multi-lining
// like Blah: (Something) "SingleValue"
// Blahblah: (Container) {
// Moo: (Foo) "What" }
// only increase pad when multi-lining sub-items
ostream << "(" << getClassName() << ") { ";
std::string padpad = pad + "\t";
for (TStorageObjectContainer::const_iterator it = Chunks.begin(), end = Chunks.end(); it != end; ++it)
{
std::stringstream ss;
ss << std::hex << std::setfill('0');
ss << std::setw(4) << it->first;
ostream << "\n" << pad << "0x" << ss.str() << ": ";
it->second->toString(ostream, padpad);
}
ostream << "} ";
}
bool CStorageContainer::isContainer() const
{
return true;
}
void CStorageContainer::serial(CStorageChunks &chunks)
{
if (chunks.stream().isReading())
{
#ifdef NL_DEBUG_STORAGE
if (Chunks.size())
{
nldebug("Storage container not empty, clearing");
}
#endif
Chunks.clear();
while (chunks.enterChunk())
{
uint16 id = chunks.getChunkId();
IStorageObject *storageObject = createChunkById(id, chunks.isChunkContainer());
storageObject->setSize(chunks.getChunkSize());
if (storageObject->isContainer()) static_cast<CStorageContainer *>(storageObject)->serial(chunks);
else storageObject->serial(chunks.stream());
Chunks.push_back(TStorageObjectWithId(id, storageObject));
if (chunks.leaveChunk()) // bytes were skipped while reading
throw EStorage();
}
}
else
{
for (TStorageObjectContainer::iterator it = Chunks.begin(), end = Chunks.end(); it != end; ++it)
{
chunks.enterChunk(it->first, it->second->isContainer());
it->second->serial(chunks.stream());
chunks.leaveChunk();
}
}
}
IStorageObject *CStorageContainer::createChunkById(uint16 id, bool container)
{
if (container)
{
return new CStorageContainer();
}
else
{
return new CStorageRaw();
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
std::string CStorageRaw::getClassName()
{
return "StorageRaw";
}
void CStorageRaw::serial(NLMISC::IStream &stream)
{
stream.serialBuffer(&Value[0], Value.size());
}
void CStorageRaw::toString(std::ostream &ostream, const std::string &pad)
{
// note: only use pad when multi-lining
// like Blah: (Something) "SingleValue"
// Blahblah: (Container) {
// Moo: (Foo) "What" }
// only increase pad when multi-lining sub-items
ostream << "(" << getClassName() << ") { ";
ostream << "} ";
}
void CStorageRaw::setSize(sint32 size)
{
Value.resize(size);
}
bool CStorageRaw::getSize(sint32 &size) const
{
size = Value.size();
return true;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
} /* namespace MAX */
} /* namespace PIPELINE */
/* end of file */

@ -0,0 +1,143 @@
/**
* \file storage_object.h
* \brief CStorageObject
* \date 2012-08-18 09:02GMT
* \author Jan Boon (Kaetemi)
* CStorageObject
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE PIPELINE.
* RYZOM CORE PIPELINE is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* RYZOM CORE PIPELINE is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE PIPELINE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef PIPELINE_STORAGE_OBJECT_H
#define PIPELINE_STORAGE_OBJECT_H
#include <nel/misc/types_nl.h>
// STL includes
#include <sstream>
// NeL includes
#include <nel/misc/stream.h>
// Project includes
namespace PIPELINE {
namespace MAX {
class CStorageChunks;
struct EStorage : public NLMISC::Exception
{
EStorage() : NLMISC::Exception("PIPELINE::MAX::EStorage") { }
virtual ~EStorage() throw() { }
};
// IStorageObject : exposes serial(CStorageStream &stream) and dump(const std::string &pad)
class IStorageObject : public NLMISC::IStreamable
{
public:
// virtual std::string getClassName() = 0; // inherited from NLMISC::IClassable through NLMISC::IStreamable
// virtual void serial(NLMISC::IStream &stream); // inherited from NLMISC::IStreamable
std::string toString();
virtual void toString(std::ostream &ostream, const std::string &pad = "") = 0;
public: // should be protected but that doesn't compile, nice c++!
// Sets size when reading
virtual void setSize(sint32 size);
// Gets the size when writing, return false if unknown
virtual bool getSize(sint32 &size) const;
// Only true when inherting from CStorageContainer
virtual bool isContainer() const;
};
// CStorageContainer : serializes a container chunk
class CStorageContainer : public IStorageObject
{
public:
// public data
typedef std::pair<uint16, IStorageObject *> TStorageObjectWithId;
typedef std::vector<TStorageObjectWithId> TStorageObjectContainer;
TStorageObjectContainer Chunks;
// inherited
virtual std::string getClassName();
virtual void serial(NLMISC::IStream &stream); // only used to wrap a container inside another stream
virtual void toString(std::ostream &ostream, const std::string &pad = "");
public: // should be protected but that doesn't compile, nice c++!
// inherited
virtual bool isContainer() const;
protected:
// override in subclasses, default to parent if not handled
virtual void serial(CStorageChunks &chunks);
virtual IStorageObject *createChunkById(uint16 id, bool container);
};
// CStorageRaw : serializes raw data, use for unknown data
class CStorageRaw : public IStorageObject
{
public:
// public data
typedef std::vector<uint8> TType;
TType Value;
// inherited
virtual std::string getClassName();
virtual void serial(NLMISC::IStream &stream);
virtual void toString(std::ostream &ostream, const std::string &pad = "");
public: // should be protected but that doesn't compile, nice c++!
// Sets size when reading
virtual void setSize(sint32 size);
// Gets the size when writing, return false if unknown
virtual bool getSize(sint32 &size) const;
};
/*
// CStorageUCString : serializes an ucstring chunk
class CStorageUCString : public ucstring, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
// CStorageString : serializes a string chunk
class CStorageString : public std::string, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
template<typename T>
class CStorageValue : public IStorageObject
{
public:
T Value;
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
*/
} /* namespace MAX */
} /* namespace PIPELINE */
#endif /* #ifndef PIPELINE_STORAGE_OBJECT_H */
/* end of file */

@ -39,30 +39,22 @@
// Project includes // Project includes
using namespace std; // using namespace std;
// using namespace NLMISC; // using namespace NLMISC;
// #define NL_DEBUG_STORAGE_STREAM // #define NL_DEBUG_STORAGE
namespace PIPELINE { namespace PIPELINE {
namespace MAX { namespace MAX {
CStorageStream::CStorageStream(GsfInput *input) : NLMISC::IStream(true), m_Input(input), m_Output(NULL), m_Is64Bit(false) CStorageStream::CStorageStream(GsfInput *input) : NLMISC::IStream(true), m_Input(input), m_Output(NULL)
{ {
m_Chunks.reserve(64);
m_Chunks.resize(1);
m_Chunks[0].OffsetBegin = -6;
m_Chunks[0].Id = 0;
m_Chunks[0].Size = 0x80000000;
} }
CStorageStream::CStorageStream(GsfOutput *output) : NLMISC::IStream(true), m_Input(NULL), m_Output(output), m_Is64Bit(false) CStorageStream::CStorageStream(GsfOutput *output) : NLMISC::IStream(false), m_Input(NULL), m_Output(output)
{ {
m_Chunks.reserve(64);
m_Chunks.resize(1);
m_Chunks[0].OffsetBegin = -6;
m_Chunks[0].Id = 1; // in write mode, id flags a container
m_Chunks[0].Size = 0;
} }
CStorageStream::~CStorageStream() CStorageStream::~CStorageStream()
@ -120,7 +112,7 @@ void CStorageStream::serialBuffer(uint8 *buf, uint len)
{ {
if (!len) if (!len)
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("Serial 0 size buffer"); nldebug("Serial 0 size buffer");
#endif #endif
return; return;
@ -129,7 +121,7 @@ void CStorageStream::serialBuffer(uint8 *buf, uint len)
{ {
if (!gsf_input_read(m_Input, len, buf)) if (!gsf_input_read(m_Input, len, buf))
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("Cannot read from input, throw exception"); nldebug("Cannot read from input, throw exception");
#endif #endif
throw NLMISC::EStream(); throw NLMISC::EStream();
@ -139,7 +131,7 @@ void CStorageStream::serialBuffer(uint8 *buf, uint len)
{ {
if (!gsf_output_write(m_Output, len, buf)) if (!gsf_output_write(m_Output, len, buf))
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("Cannot write to output, throw exception"); nldebug("Cannot write to output, throw exception");
#endif #endif
throw NLMISC::EStream(); throw NLMISC::EStream();
@ -147,7 +139,7 @@ void CStorageStream::serialBuffer(uint8 *buf, uint len)
} }
else else
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("No input or output, should not happen, throw exception"); nldebug("No input or output, should not happen, throw exception");
#endif #endif
throw NLMISC::EStream(); throw NLMISC::EStream();
@ -169,133 +161,30 @@ bool CStorageStream::eof()
} }
else else
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("No input, this function cannot output, throw exception"); nldebug("No input, this function cannot output, throw exception");
#endif #endif
throw NLMISC::EStream(); throw NLMISC::EStream();
} }
} }
bool CStorageStream::enterChunk() sint32 CStorageStream::size()
{ {
if (m_Input) if (m_Input)
{ {
// input logic gsf_off_t res = gsf_input_size(m_Input);
if (!isChunkContainer()) if (res < 2147483647L) // exception when larger
{ return (sint32)res;
#ifdef NL_DEBUG_STORAGE_STREAM
nldebug("Current chunk is not a container, cannot enter");
#endif
return false;
}
if (endOfChunk())
{
#ifdef NL_DEBUG_STORAGE_STREAM
nldebug("End of chunk, cannot enter");
#endif
return false;
}
m_Chunks.resize(m_Chunks.size() + 1);
CChunk *chunk = currentChunk();
chunk->OffsetBegin = CStorageStream::getPos();
serial(chunk->Id);
serial(chunk->Size);
chunk->HeaderSize = 6;
if (chunk->Size == 0)
{
// this is a 64bit chunk
uint64 size64;
serial(size64);
chunk->HeaderSize += 8;
bool iscont = (size64 & 0x8000000000000000) == 0x8000000000000000;
size64 &= 0x7FFFFFFFFFFFFFFF;
if (size64 >= 2147483647L)
throw NLMISC::EStream("64bit chunks not supported");
// downgrade to 32 bit chunk
chunk->Size = (uint32)size64;
if (iscont) chunk->Size |= 0x80000000;
m_Is64Bit = true; // it's true
}
return true;
} }
else else
{ {
#ifdef NL_DEBUG_STORAGE_STREAM #ifdef NL_DEBUG_STORAGE
nldebug("No input, this function cannot output, throw exception"); nldebug("No input, this function cannot output, throw exception");
#endif #endif
throw NLMISC::EStream(); throw NLMISC::EStream();
} }
}
bool CStorageStream::enterChunk(uint16 id)
{
if (m_Output)
{
if (m_Is64Bit)
throw NLMISC::EStream("64bit chunks not supported");
// current chunk is a container
currentChunk()->Id = 1; // in write mode, id flags a container
// enter the new chunk
m_Chunks.resize(m_Chunks.size() + 1);
CChunk *chunk = currentChunk();
chunk->Id = 0; // don't know if it's a container
uint32 sizeDummy = 0xFFFFFFFF;
chunk->OffsetBegin = getPos(); // store current pos
// write header
serial(id); // write the id
serial(sizeDummy); // write 32 bit size placeholder
}
else // input or exception
{
while (enterChunk())
{
if (getChunkId() == id)
return true;
leaveChunk(); // skip data
}
return false;
}
}
sint32 CStorageStream::leaveChunk()
{
if (m_Input)
{
// input logic
sint32 skipped = currentChunk()->endOfChunk() - CStorageStream::getPos();
if (skipped)
{
CStorageStream::seek(currentChunk()->endOfChunk(), begin);
#ifdef NL_DEBUG_STORAGE_STREAM
nldebug("Skipped %i bytes in the current chunk", skipped);
#endif
}
m_Chunks.resize(m_Chunks.size() - 1);
return skipped;
}
else if (m_Output)
{
sint32 pos = getPos();
sint32 sizeWithHeader = pos - currentChunk()->OffsetBegin;
sint32 sizePos = currentChunk()->OffsetBegin + 2;
seek(sizePos, begin); // hopefully this correctly overwrites!!!
uint32 sizeField = (uint32)sizeWithHeader | (uint32)currentChunk()->Id << 31; // add container flag
serial(sizeField);
seek(pos, begin);
m_Chunks.resize(m_Chunks.size() - 1);
return sizeWithHeader;
}
else
{
#ifdef NL_DEBUG_STORAGE_STREAM
nldebug("No input or output, should not happen, throw exception");
#endif
throw NLMISC::EStream(); throw NLMISC::EStream();
} }
}
} /* namespace MAX */ } /* namespace MAX */
} /* namespace PIPELINE */ } /* namespace PIPELINE */

@ -52,26 +52,6 @@ namespace MAX {
*/ */
class CStorageStream : public NLMISC::IStream class CStorageStream : public NLMISC::IStream
{ {
private:
struct CChunk
{
// Size of the chunk header, 6 for 32 bit, 14 for 64 bit
uint8 HeaderSize;
// Where the header starts
sint32 OffsetBegin;
// Identifier
uint16 Id;
// Size including header size
uint32 Size;
inline sint32 getSizeWithHeader() { return (sint32)(Size & 0x7FFFFFFF); }
inline sint32 getSize() { return getSizeWithHeader() - (sint32)HeaderSize; }
inline bool isContainer() { return (Size & 0x80000000) == 0x80000000; }
inline sint32 endOfChunk() { return OffsetBegin + getSizeWithHeader(); }
inline sint32 getDataBegin() { return OffsetBegin + (sint32)HeaderSize; }
};
public: public:
CStorageStream(GsfInput *input); CStorageStream(GsfInput *input);
CStorageStream(GsfOutput *output); CStorageStream(GsfOutput *output);
@ -83,31 +63,12 @@ public:
virtual void serialBuffer(uint8 *buf, uint len); virtual void serialBuffer(uint8 *buf, uint len);
virtual void serialBit(bool &bit); virtual void serialBit(bool &bit);
sint32 size();
bool eof(); bool eof();
// Returns true if there's another chunk, false if no more chunks in this container or if the current chunk is not a container
bool enterChunk();
// Reads and skips chunks until the one with given id is found, or writes a chunk with this id
bool enterChunk(uint16 id);
// Returns the number of skipped bytes in read more, returns chunk size including header in write mode
sint32 leaveChunk();
inline bool is64Bit() const { return m_Is64Bit; }
inline void set64Bit(bool enabled = true) { m_Is64Bit = enabled; }
inline uint16 getChunkId() { return currentChunk()->Id; }
inline sint32 getChunkSize() { return currentChunk()->getSize(); }
inline bool isChunkContainer() { return currentChunk()->isContainer(); }
inline bool endOfChunk() { return m_Chunks.size() == 1 ? eof() : CStorageStream::getPos() >= currentChunk()->endOfChunk(); }
private:
inline CChunk *currentChunk() { return &m_Chunks[m_Chunks.size() - 1]; }
private: private:
GsfInput *m_Input; GsfInput *m_Input;
GsfOutput *m_Output; GsfOutput *m_Output;
std::vector<CChunk> m_Chunks;
bool m_Is64Bit;
/* there exist compressed max files, so maybe we will need this at some point /* there exist compressed max files, so maybe we will need this at some point
GsfInput * gsf_input_uncompress (GsfInput *src); GsfInput * gsf_input_uncompress (GsfInput *src);

@ -11,11 +11,13 @@
#include <glib/gi18n.h> #include <glib/gi18n.h>
#include <string.h> #include <string.h>
#include <cstdio> #include <cstdio>
#include <iostream>
#include <vector> #include <vector>
#include <utility> #include <utility>
#include "../max/storage_stream.h" #include "../max/storage_stream.h"
#include "../max/storage_object.h"
//static const char *filename = "/srv/work/database/interfaces/anims_max/cp_fy_hof_species.max"; //static const char *filename = "/srv/work/database/interfaces/anims_max/cp_fy_hof_species.max";
static const char *filename = "/home/kaetemi/source/minimax/GE_Acc_MikotoBaniere.max"; static const char *filename = "/home/kaetemi/source/minimax/GE_Acc_MikotoBaniere.max";
@ -32,75 +34,7 @@ inline uint8 cleanChar(uint8 c)
namespace PIPELINE { namespace PIPELINE {
namespace MAX { namespace MAX {
struct EStorage : public NLMISC::Exception /*
{
EStorage() : NLMISC::Exception("PIPELINE::MAX::EStorage") { }
virtual ~EStorage() throw() { }
};
// IStorageObject : exposes serial(CStorageStream *stream) and dump(const std::string &pad)
class IStorageObject
{
public:
virtual void serial(CStorageStream *stream) = 0;
virtual void dump(const std::string &pad) = 0;
};
class IStorageStreamable : public NLMISC::IStreamable, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void serial(NLMISC::IStream &stream) = 0;
};
void IStorageStreamable::serial(CStorageStream *stream)
{
serial(*((NLMISC::IStream *)stream));
}
// CStorageContainer : serializes a container chunk
class CStorageContainer : public std::vector<std::pair<uint16, IStorageObject *> >, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
// override in subclasses, call parent if not handled
virtual IStorageObject *serialChunk(CStorageStream *stream);
};
// CStorageRaw : serializes raw data, use for unknown data
class CStorageRaw : public std::vector<uint8>, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
// CStorageUCString : serializes an ucstring chunk
class CStorageUCString : public ucstring, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
// CStorageString : serializes a string chunk
class CStorageString : public std::string, public IStorageObject
{
public:
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
template<typename T>
class CStorageValue : public IStorageObject
{
public:
T Value;
virtual void serial(CStorageStream *stream);
virtual void dump(const std::string &pad);
};
struct CClass_ID : public NLMISC::IStreamable struct CClass_ID : public NLMISC::IStreamable
{ {
uint32 A; uint32 A;
@ -145,21 +79,7 @@ std::string CClassDirectoryHeader::getClassName()
void CStorageContainer::serial(CStorageStream *stream) void CStorageContainer::serial(CStorageStream *stream)
{ {
if (stream->isReading())
{
while (stream->enterChunk())
{
uint16 id = stream->getChunkId();
IStorageObject *storageObject = serialChunk(stream);
push_back(std::pair<uint16, IStorageObject *>(id, storageObject));
if (stream->leaveChunk()) // bytes were skipped while reading
throw EStorage();
}
}
else
{
throw EStorage();
}
} }
IStorageObject *CStorageContainer::serialChunk(CStorageStream *stream) IStorageObject *CStorageContainer::serialChunk(CStorageStream *stream)
{ {
@ -291,10 +211,10 @@ void CStorageValue<T>::dump(const std::string &pad)
std::string valstr = NLMISC::toString(Value); std::string valstr = NLMISC::toString(Value);
printf("%s%s\n", pad.c_str(), valstr.c_str()); printf("%s%s\n", pad.c_str(), valstr.c_str());
} }
*/
} }
} }
/*
static void dumpData(PIPELINE::MAX::CStorageStream *in, const std::string &pad) static void dumpData(PIPELINE::MAX::CStorageStream *in, const std::string &pad)
{ {
sint32 size = in->getChunkSize(); sint32 size = in->getChunkSize();
@ -336,7 +256,7 @@ static void dumpContainer(PIPELINE::MAX::CStorageStream *in, const std::string &
printf("%sSKIPPED: %i\n", pad.c_str(), skipped); printf("%sSKIPPED: %i\n", pad.c_str(), skipped);
} }
} }
*/
// int __stdcall WinMain(void *, void *, void *, int) // int __stdcall WinMain(void *, void *, void *, int)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -382,13 +302,16 @@ int main(int argc, char **argv)
g_print("%s\n", streamname); g_print("%s\n", streamname);
GsfInput *input = gsf_infile_child_by_name(infile, streamname); GsfInput *input = gsf_infile_child_by_name(infile, streamname);
{
//gsf_input_dump(input, 1); // just a regular hex dump of this input stream //gsf_input_dump(input, 1); // just a regular hex dump of this input stream
PIPELINE::MAX::CStorageStream *instream = new PIPELINE::MAX::CStorageStream(input); PIPELINE::MAX::CStorageStream instream(input);
dumpContainer(instream, ""); //dumpContainer(instream, "");
//PIPELINE::MAX::CStorageContainer ctr; PIPELINE::MAX::CStorageContainer ctr;
//ctr.serial(instream); ctr.serial(instream);
ctr.toString(std::cout);
//ctr.dump(""); //ctr.dump("");
delete instream; }
g_object_unref(input); g_object_unref(input);
g_object_unref(infile); g_object_unref(infile);

Loading…
Cancel
Save