/** * \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 * . */ #include #include "storage_object.h" // STL includes #include // NeL includes // #include // Project includes #include "storage_stream.h" #include "storage_chunks.h" // using namespace std; // using namespace NLMISC; // #define NL_DEBUG_STORAGE namespace PIPELINE { namespace MAX { //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// IStorageObject::IStorageObject() { } IStorageObject::~IStorageObject() { } 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; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// CStorageContainer::CStorageContainer() : m_ChunksOwnsPointers(true) { } CStorageContainer::~CStorageContainer() { if (m_ChunksOwnsPointers) { for (TStorageObjectContainer::iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { delete it->second; } } m_Chunks.clear(); } std::string CStorageContainer::className() const // why is this not const in IClassable? { return "StorageContainer"; } void CStorageContainer::serial(NLMISC::IStream &stream) { if (stream.isReading()) { nlassert(m_ChunksOwnsPointers); nlassert(m_Chunks.empty()); } if (stream.getPos() == 0) { CStorageStream *storageStream = dynamic_cast(&stream); if (storageStream) { #ifdef NL_DEBUG_STORAGE nldebug("Implicitly assume the entire stream is the container"); #endif CStorageChunks chunks(stream, stream.isReading() ? storageStream->size() : 0); serial(chunks); return; } } #ifdef NL_DEBUG_STORAGE nldebug("Wrap the container inside a chunk, as the size is not known"); #endif { // Use dummy size value so the system can at least read the header CStorageChunks chunks(stream, stream.isReading() ? 0xFF : 0); bool ok = chunks.enterChunk(0x4352, true); nlassert(ok); serial(chunks); chunks.leaveChunk(); } } void CStorageContainer::serial(NLMISC::IStream &stream, uint size) { CStorageChunks chunks(stream, size); serial(chunks); return; } void CStorageContainer::toString(std::ostream &ostream, const std::string &pad) const { // note: only use pad when multi-lining // like Blah: (Something) "SingleValue" // Blahblah: (Container) { // Moo: (Foo) "What" } // only increase pad when multi-lining sub-items nlassert(m_ChunksOwnsPointers); ostream << "(" << className() << ") [" << m_Chunks.size() << "] { "; std::string padpad = pad + "\t"; sint i = 0; for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { std::stringstream ss; ss << std::hex << std::setfill('0'); ss << std::setw(4) << it->first; ostream << "\n" << pad << i << " 0x" << ss.str() << ": "; it->second->toString(ostream, padpad); ++i; } ostream << "} "; } void CStorageContainer::parse(uint16 version, uint filter) { nlassert(m_ChunksOwnsPointers); // Can only use this when m_Chunks still has ownership. for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { if (it->second->isContainer()) { static_cast(it->second)->parse(version); } } } void CStorageContainer::clean() { nlassert(m_ChunksOwnsPointers); // Can only use the default when m_Chunks retains ownership. for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { if (it->second->isContainer()) { static_cast(it->second)->clean(); } } } void CStorageContainer::build(uint16 version, uint filter) { for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { if (it->second->isContainer()) { static_cast(it->second)->build(version); } } } void CStorageContainer::disown() { nlassert(m_ChunksOwnsPointers); // Can only use this when m_Chunks has been given ownership. for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { if (it->second->isContainer()) { static_cast(it->second)->disown(); } } } IStorageObject *CStorageContainer::findStorageObject(uint16 id, uint nb) const { uint c = 0; for (TStorageObjectContainer::const_iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { if (it->first == id) { if (c == nb) return it->second; else ++c; } } return NULL; } IStorageObject *CStorageContainer::findLastStorageObject(uint16 id) const { for (TStorageObjectContainer::const_reverse_iterator it = m_Chunks.rbegin(), end = m_Chunks.rend(); it != end; ++it) { if (it->first == id) return it->second; } return NULL; } bool CStorageContainer::isContainer() const { return true; } void CStorageContainer::serial(CStorageChunks &chunks) { if (chunks.stream().isReading()) { #ifdef NL_DEBUG_STORAGE nldebug("Reading container chunk"); #endif nlassert(m_ChunksOwnsPointers); nlassert(m_Chunks.empty()); while (chunks.enterChunk()) { uint16 id = chunks.getChunkId(); bool cont = chunks.isChunkContainer(); IStorageObject *storageObject = createChunkById(id, cont); storageObject->setSize(chunks.getChunkSize()); if (storageObject->isContainer()) static_cast(storageObject)->serial(chunks); else storageObject->serial(chunks.stream()); m_Chunks.push_back(TStorageObjectWithId(id, storageObject)); if (chunks.leaveChunk()) // bytes were skipped while reading throw EStorage(); /*TStorageObjectContainer::iterator soit = m_Chunks.end(); --soit; serialized(soit, cont);*/ } } else { #ifdef NL_DEBUG_STORAGE nldebug("Writing container chunk"); #endif for (TStorageObjectContainer::iterator it = m_Chunks.begin(), end = m_Chunks.end(); it != end; ++it) { chunks.enterChunk(it->first, it->second->isContainer()); IStorageObject *storageObject = it->second; if (storageObject->isContainer()) static_cast(storageObject)->serial(chunks); else storageObject->serial(chunks.stream()); chunks.leaveChunk(); } } } IStorageObject *CStorageContainer::createChunkById(uint16 id, bool container) { if (container) { return new CStorageContainer(); } else { return new CStorageRaw(); } } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// CStorageRaw::CStorageRaw() { } CStorageRaw::~CStorageRaw() { } std::string CStorageRaw::className() const { return "StorageRaw"; } void CStorageRaw::serial(NLMISC::IStream &stream) { stream.serialBuffer(&Value[0], Value.size()); } void CStorageRaw::toString(std::ostream &ostream, const std::string &pad) const { // 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 << "(" << className() << ") { "; ostream << "\n" << pad << "Size: " << Value.size(); bool isString = true; ostream << "\n" << pad << "String: "; for (TType::size_type i = 0; i < Value.size(); ++i) { char c = Value[i]; if (c == 0 || c == 123 || c == 125) ostream << "."; else if (c >= 32 && c <= 126) ostream << c; else { ostream << "."; isString = false; } } ostream << " "; if (!isString || Value.size() <= 4) { ostream << "\n" << pad << "Hex: "; for (TType::size_type i = 0; i < Value.size(); ++i) { std::stringstream ss; ss << std::hex << std::setfill('0') << std::setw(2) << (int)Value[i]; ostream << ss.str() << " "; } } if (Value.size() == 4) { ostream << "\n" << pad << "Int: " << (*(sint32 *)(void *)(&Value[0])) << " "; ostream << "\n" << pad << "Float: " << (*(float *)(void *)(&Value[0])) << " "; } 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 */