void Scene::save(stream::OutputStream<stream::SerializationEndian> &strm) const { PROFILE; strm.write(ENGINE_VERSION_MAJOR); strm.write(ENGINE_VERSION_MINOR); strm.write(ENGINE_VERSION_PATCH); strm.write(entities.size()); for (unsigned i = 0; i < entities.size(); ++i) { entities[i]->save(strm); } }
/** Continuous decompression process. Not all compressor support this (in that case, it's probably emulated or might return false). @param outStream The output stream to write to. @param inStream The input stream to read from. @param amountToProcess The number of decompressed bytes to reach. Set to 0 to process the whole stream. @return true on success, false if not supported or an error occurred on the input stream */ bool BSCLib::decompressStream(Stream::OutputStream & outStream, const Stream::InputStream & inStream, const uint32 amountToProcess) { // We need to first read the number of blocks if (!memBuffer || !outBuffer) return setError(NotEnoughMemory); const size_t minBlockHeaderSize = (sizeof(dataSize) + sizeof(decHeader.recordSize) + sizeof(decHeader.sortingContext) + BSC::HeaderSize); if (memBuffer->total <= outBuffer->total) { // If we were compressing, let's enlarge that resizeBuffer(minBlockHeaderSize); decHeader.curBlock = 0; } uint32 nBlocks = (uint32)dataSize; #define POP(X, S) if (inStream.read(X, (uint64)S) != (uint64)S) return setError(Success); // No more data to read if (dataSize <= 0) { POP(&nBlocks, sizeof(nBlocks)); dataSize = nBlocks; // Crude off estimation memBuffer->empty(); } if (!nBlocks) return setError(BadFormat); // Check if we have enough data in our memory block to answer directly uint64 outSize = amountToProcess ? min((uint64)amountToProcess, outStream.fullSize()) : outStream.fullSize(); if (outSize > 0xFFFFFFFF) outSize = 0xFFFFFFFF; // We can only deal with 4GB at a time // If we reach here, we have not enough data in the memory block (we need to refill it), int64 inPos = 0; do { // Check if we have a valid header if (!decHeader.valid && decHeader.curBlock < nBlocks) { if (memBuffer->available() < minBlockHeaderSize && (memBuffer->refill(inStream, minBlockHeaderSize - memBuffer->available()) == (size_t)-1 || memBuffer->available() < minBlockHeaderSize)) return setError(UnexpectedEOD); // The decode the header #define POPM(X, S) memcpy(X, *memBuffer, S); memBuffer->use(S); #ifndef BreakCompatibility POPM(&inPos, sizeof(inPos)); POPM(&decHeader.recordSize, sizeof(decHeader.recordSize)); POPM(&decHeader.sortingContext, sizeof(decHeader.sortingContext)); #endif // Unsupported features ? if (decHeader.recordSize < 1 || decHeader.sortingContext < 1 || decHeader.sortingContext > 2) return setError(BadFormat); // Figure out the block informations if (opaque->getBlockInfo(*memBuffer, BSC::HeaderSize, decHeader.blockSize, decHeader.dataSize) != BSC::Success) return setError(BadFormat); //memBuffer->empty(); decHeader.valid = true; } // Check if we have decompressed data ready for output stream direct write if (outBuffer->available()) { // Data is ready in the buffer, let's extract it size_t doneSize = min(outBuffer->available(), (size_t)outSize); if (outStream.write(*outBuffer, doneSize) != (uint64)doneSize) return setError(UnexpectedEOD); outBuffer->use(doneSize); if (doneSize == outSize) break; // Success outSize -= doneSize; // If we reach here, then the outBuffer is empty anyway, let's mark as is outBuffer->empty(); // Need to refill a header now continue; } // We need to fetch a complete buffer from the input stream size_t need = decHeader.blockSize - memBuffer->available(); if (memBuffer->refill(inStream, need) != need) { // Could not read enough from the input stream to fill a block. // Let's output the amount we succeed to write from it. if (amountToProcess - outSize > 0) return setError(Success); return setError(UnexpectedEOD); } if (decHeader.dataSize != 0 && decHeader.curBlock < dataSize) { // Now decompress the cruft BSC::Error err = opaque->decompress(*memBuffer, decHeader.blockSize, *outBuffer, decHeader.dataSize); if (err != BSC::Success) return err == BSC::NotEnoughMemory ? setError(NotEnoughMemory) : setError(DataCorrupt); // Depending on the sorting context, we might need to reverse the blocks err = opaque->postProcess(*outBuffer, decHeader.dataSize, decHeader.sortingContext, decHeader.recordSize); if (err != BSC::Success) return err == BSC::NotEnoughMemory ? setError(NotEnoughMemory) : setError(DataCorrupt); outBuffer->setFill(decHeader.dataSize); memBuffer->empty(); // Done with this data buffer decHeader.valid = false; // The block header is now invalid too decHeader.curBlock++; } else break; // No more data to decompress } while (true); #undef POP // Check if we've finished sending the complete decompressed input stream if (decHeader.curBlock == dataSize && outBuffer->available() == 0) { // We can reset the decompression stream if it needs to be re-used decHeader.curBlock = 0; dataSize = 0; } if (outBuffer->available() == 0) outBuffer->empty(); return setError(Success); }
bool BSCLib::compressStream(Stream::OutputStream & outStream, const Stream::InputStream & inStream, const uint32 amountToProcess, const bool lastCall) { if (!memBuffer || !outBuffer) return setError(NotEnoughMemory); // If we were decompressing, let's change that if (decHeader.valid) { resizeBuffer(); decHeader.valid = false; } // Detect end of buffer // Try to be smart, else try to read as much as possible uint64 inSize = amountToProcess ? min((uint64)amountToProcess, inStream.fullSize()) : inStream.fullSize(); if (inSize == 0) { if (!dataSize && !memBuffer->available()) return true; // Need to process the remaining block, if any if (!processBlock(outStream)) return false; // End of stream, let's rewind and write the header if (outStream.setPosition(0)) { uint32 nBlocks = (uint32)((dataSize + getBufferSize() - 1) / getBufferSize()); PUSH(&nBlocks, sizeof(nBlocks)); dataSize = 0; headerWritten = false; } return true; } // Check if we need to write the number of blocks now (or not) if (!headerWritten) { // Safety first if (dataSize == 0 && !outStream.setPosition(outStream.currentPosition())) { // If you don't provide the input size and the output stream can not be rewind to save // the header in the end, then it'll not work. Assert(dataSize == 0 && outStream.setPosition(outStream.currentPosition())); return false; } uint32 nBlocks = (uint32)((-dataSize + getBufferSize() - 1) / getBufferSize()); //blockSize > 0 ? (uint32)((inSize + blockSize - 1) / blockSize) : 0; PUSH(&nBlocks, sizeof(nBlocks)); dataSize = 0; headerWritten = true; } // Check if we can cache the data to avoid small compression block if (!lastCall && memBuffer->canFit(inSize)) { // Accumulate the input stream return memBuffer->refill(inStream, inSize) == inSize; } while ((int64)inSize > 0) { // Read as much as possible to the memory buffer, and save that size_t readSize = memBuffer->available(); if (!memBuffer->full()) { readSize = memBuffer->refill(inStream, inSize); inSize -= (uint64)readSize; } if (memBuffer->available() >= getBufferSize() || lastCall) { if (!processBlock(outStream)) return false; } } return setError(Success); }
void Handle::save(stream::OutputStream<stream::SerializationEndian> &strm) const { strm.write(id); }