From 202c18861a3417fe06b15a135f51bf5d9f095d7d Mon Sep 17 00:00:00 2001 From: Martin Gerhardy Date: Tue, 7 May 2024 20:00:30 +0200 Subject: [PATCH] VOXELFORMAT: refactored interface to work with archives and not on streams this allows us to support multi file formats in a better way Fixes issue #460 --- src/modules/io/Archive.cpp | 24 +- src/modules/io/Archive.h | 2 - src/modules/io/FilesystemArchive.cpp | 23 +- src/modules/io/FilesystemArchive.h | 2 + src/modules/io/MemoryArchive.cpp | 1 + src/modules/io/MemoryArchive.h | 11 +- src/modules/io/StreamArchive.h | 32 ++ src/modules/io/ZipArchive.cpp | 10 + src/modules/io/ZipArchive.h | 2 + .../voxelcollection/CollectionManager.cpp | 5 +- src/modules/voxelformat/Format.cpp | 43 ++- src/modules/voxelformat/Format.h | 25 +- src/modules/voxelformat/VolumeFormat.cpp | 60 ++-- src/modules/voxelformat/VolumeFormat.h | 8 +- .../private/aceofspades/AoSVXLFormat.cpp | 32 +- .../private/aceofspades/AoSVXLFormat.h | 6 +- .../private/animatoon/AnimaToonFormat.cpp | 14 +- .../private/animatoon/AnimaToonFormat.h | 4 +- .../private/binvox/BinVoxFormat.cpp | 46 ++- .../voxelformat/private/binvox/BinVoxFormat.h | 4 +- .../private/chronovox/CSMFormat.cpp | 45 +-- .../voxelformat/private/chronovox/CSMFormat.h | 4 +- .../private/commandconquer/HVAFormat.cpp | 27 +- .../private/commandconquer/HVAFormat.h | 5 +- .../private/commandconquer/VXLFormat.cpp | 98 +++--- .../private/commandconquer/VXLFormat.h | 11 +- .../private/cubeworld/CubFormat.cpp | 64 ++-- .../voxelformat/private/cubeworld/CubFormat.h | 6 +- .../private/cubzh/CubzhB64Format.cpp | 43 +-- .../private/cubzh/CubzhB64Format.h | 12 +- .../voxelformat/private/cubzh/CubzhFormat.cpp | 90 ++++-- .../voxelformat/private/cubzh/CubzhFormat.h | 8 +- .../private/cubzh/PCubesFormat.cpp | 33 +- .../voxelformat/private/cubzh/PCubesFormat.h | 2 +- .../voxelformat/private/goxel/GoxFormat.cpp | 91 +++--- .../voxelformat/private/goxel/GoxFormat.h | 8 +- .../private/magicavoxel/VoxFormat.cpp | 32 +- .../private/magicavoxel/VoxFormat.h | 6 +- .../private/magicavoxel/XRawFormat.cpp | 95 +++--- .../private/magicavoxel/XRawFormat.h | 6 +- .../voxelformat/private/mesh/FBXFormat.cpp | 37 ++- .../voxelformat/private/mesh/FBXFormat.h | 4 +- .../voxelformat/private/mesh/GLTFFormat.cpp | 23 +- .../voxelformat/private/mesh/GLTFFormat.h | 4 +- .../voxelformat/private/mesh/MeshFormat.cpp | 11 +- .../voxelformat/private/mesh/MeshFormat.h | 9 +- .../voxelformat/private/mesh/OBJFormat.cpp | 85 ++--- .../voxelformat/private/mesh/OBJFormat.h | 4 +- .../voxelformat/private/mesh/PLYFormat.cpp | 70 +++-- .../voxelformat/private/mesh/PLYFormat.h | 4 +- .../voxelformat/private/mesh/STLFormat.cpp | 42 ++- .../voxelformat/private/mesh/STLFormat.h | 4 +- .../private/mesh/quake/MD2Format.cpp | 51 +-- .../private/mesh/quake/MD2Format.h | 4 +- .../private/mesh/quake/QuakeBSPFormat.cpp | 20 +- .../private/mesh/quake/QuakeBSPFormat.h | 4 +- .../private/minecraft/DatFormat.cpp | 29 +- .../voxelformat/private/minecraft/DatFormat.h | 4 +- .../private/minecraft/MCRFormat.cpp | 46 ++- .../voxelformat/private/minecraft/MCRFormat.h | 4 +- .../private/minecraft/MTSFormat.cpp | 28 +- .../voxelformat/private/minecraft/MTSFormat.h | 4 +- .../private/minecraft/SchematicFormat.cpp | 18 +- .../private/minecraft/SchematicFormat.h | 4 +- .../private/qubicle/QBCLFormat.cpp | 137 ++++---- .../voxelformat/private/qubicle/QBCLFormat.h | 9 +- .../voxelformat/private/qubicle/QBFormat.cpp | 64 ++-- .../voxelformat/private/qubicle/QBFormat.h | 6 +- .../voxelformat/private/qubicle/QBTFormat.cpp | 72 +++-- .../voxelformat/private/qubicle/QBTFormat.h | 6 +- .../voxelformat/private/qubicle/QEFFormat.cpp | 45 ++- .../voxelformat/private/qubicle/QEFFormat.h | 4 +- .../voxelformat/private/rooms/ThingFormat.cpp | 17 +- .../voxelformat/private/rooms/ThingFormat.h | 4 +- .../voxelformat/private/sandbox/VXAFormat.cpp | 51 +-- .../voxelformat/private/sandbox/VXAFormat.h | 5 +- .../voxelformat/private/sandbox/VXCFormat.cpp | 18 +- .../voxelformat/private/sandbox/VXCFormat.h | 4 +- .../voxelformat/private/sandbox/VXMFormat.cpp | 296 +++++++++--------- .../voxelformat/private/sandbox/VXMFormat.h | 6 +- .../voxelformat/private/sandbox/VXRFormat.cpp | 124 ++++---- .../voxelformat/private/sandbox/VXRFormat.h | 28 +- .../voxelformat/private/sandbox/VXTFormat.cpp | 24 +- .../voxelformat/private/sandbox/VXTFormat.h | 4 +- .../voxelformat/private/slab6/KV6Format.cpp | 188 ++++++----- .../voxelformat/private/slab6/KV6Format.h | 10 +- .../voxelformat/private/slab6/KVXFormat.cpp | 103 +++--- .../voxelformat/private/slab6/KVXFormat.h | 4 +- .../private/slab6/SLAB6VoxFormat.cpp | 62 ++-- .../private/slab6/SLAB6VoxFormat.h | 6 +- .../private/sproxel/SproxelFormat.cpp | 55 ++-- .../private/sproxel/SproxelFormat.h | 6 +- .../voxelformat/private/starmade/SMFormat.cpp | 27 +- .../voxelformat/private/starmade/SMFormat.h | 6 +- .../private/starmade/SMTPLFormat.cpp | 127 ++++---- .../private/starmade/SMTPLFormat.h | 6 +- .../voxelformat/private/vengi/VENGIFormat.cpp | 23 +- .../voxelformat/private/vengi/VENGIFormat.h | 4 +- .../voxelformat/private/voxel3d/V3AFormat.cpp | 41 ++- .../voxelformat/private/voxel3d/V3AFormat.h | 4 +- .../private/voxelbuilder/VBXFormat.cpp | 25 +- .../private/voxelbuilder/VBXFormat.h | 6 +- .../private/voxelmax/VMaxFormat.cpp | 31 +- .../voxelformat/private/voxelmax/VMaxFormat.h | 8 +- .../voxelformat/tests/AbstractFormatTest.cpp | 225 +++++-------- .../voxelformat/tests/AbstractFormatTest.h | 12 +- .../voxelformat/tests/AoSVXLFormatTest.cpp | 11 +- src/modules/voxelformat/tests/ConvertTest.cpp | 4 +- .../voxelformat/tests/CubFormatTest.cpp | 2 +- .../voxelformat/tests/CubzhFormatTest.cpp | 2 +- .../voxelformat/tests/FormatPaletteTest.cpp | 59 ++-- .../voxelformat/tests/MeshFormatTest.cpp | 3 +- .../voxelformat/tests/VolumeFormatTest.cpp | 11 +- .../voxelformat/tests/VoxFormatTest.cpp | 28 +- src/modules/voxelgenerator/LUAApi.cpp | 11 +- .../tests/ShapeGeneratorTest.cpp | 12 +- .../voxelpathtracer/tests/PathTracerTest.cpp | 15 +- src/tools/thumbnailer/Thumbnailer.cpp | 21 +- .../modules/voxbrowser-ui/MainWindow.cpp | 8 +- src/tools/voxconvert/VoxConvert.cpp | 228 +++++++------- src/tools/voxconvert/VoxConvert.h | 4 +- .../modules/voxedit-util/SceneManager.cpp | 39 +-- .../modifier/brush/StampBrush.cpp | 10 +- 123 files changed, 2149 insertions(+), 1775 deletions(-) create mode 100644 src/modules/io/StreamArchive.h diff --git a/src/modules/io/Archive.cpp b/src/modules/io/Archive.cpp index db86e97955..f89dcef2cd 100644 --- a/src/modules/io/Archive.cpp +++ b/src/modules/io/Archive.cpp @@ -36,27 +36,11 @@ bool isSupportedArchive(const core::String &filename) { } ArchivePtr openArchive(const io::FilesystemPtr &fs, const core::String &path, io::SeekableReadStream *stream) { - if (fs->isReadableDir(path)) { - auto archive = core::make_shared(fs); - if (!archive->init(path, stream)) { - return ArchivePtr{}; - } - return archive; + const core::String &ext = core::string::extractExtension(path); + if (ext == "zip" || ext == "pk3" || ext == "thing") { + return openZipArchive(stream); } - const core::String ext = core::string::extractExtension(path); - if (ext == "zip" || ext == "pk3" || ext == "thing" || (stream != nullptr && ZipArchive::validStream(*stream))) { - auto archive = core::make_shared(); - if (!archive->init(path, stream)) { - return ArchivePtr{}; - } - return archive; - } - const core::String &directory = core::string::extractPath(path); - auto archive = core::make_shared(fs); - if (!archive->init(directory, stream)) { - return ArchivePtr{}; - } - return archive; + return openFilesystemArchive(fs, path); } } // namespace io diff --git a/src/modules/io/Archive.h b/src/modules/io/Archive.h index efd986f80f..1dd7ae1029 100644 --- a/src/modules/io/Archive.h +++ b/src/modules/io/Archive.h @@ -14,8 +14,6 @@ namespace io { class Filesystem; using ArchiveFiles = core::DynamicArray; -using SeekableReadStreamPtr = core::SharedPtr; -using SeekableWriteStreamPtr = core::SharedPtr; using FilesystemPtr = core::SharedPtr; /** diff --git a/src/modules/io/FilesystemArchive.cpp b/src/modules/io/FilesystemArchive.cpp index 52ea54d6c6..a7476cdfd1 100644 --- a/src/modules/io/FilesystemArchive.cpp +++ b/src/modules/io/FilesystemArchive.cpp @@ -4,6 +4,7 @@ #include "FilesystemArchive.h" #include "core/Log.h" +#include "core/SharedPtr.h" #include "core/StringUtil.h" #include "io/File.h" #include "io/FileStream.h" @@ -47,6 +48,7 @@ static FileMode convertMode(const core::String &path, FileMode mode) { } io::FilePtr FilesystemArchive::open(const core::String &path, FileMode mode) const { + Log::debug("searching for %s in %i files", path.c_str(), (int)_files.size()); for (const auto &e : _files) { // TODO: implement case insensitive search if (core::string::endsWith(e.fullPath, path)) { @@ -69,8 +71,17 @@ io::FilePtr FilesystemArchive::open(const core::String &path, FileMode mode) con return {}; } -bool FilesystemArchive::exists(const core::String &filePath) const { - return open(filePath, FileMode::Read); +bool FilesystemArchive::exists(const core::String &path) const { + for (const auto &e : _files) { + // TODO: implement case insensitive search + if (core::string::endsWith(e.fullPath, path)) { + return true; + } + } + if (_files.empty()) { + return _filesytem->exists(path); + } + return false; } SeekableReadStream* FilesystemArchive::readStream(const core::String &filePath) { @@ -91,4 +102,12 @@ SeekableWriteStream* FilesystemArchive::writeStream(const core::String &filePath return stream; } +ArchivePtr openFilesystemArchive(const io::FilesystemPtr &fs, const core::String &path) { + core::SharedPtr fa = core::make_shared(fs); + if (!path.empty() && fs->isReadableDir(path)) { + fa->init(path); + } + return fa; +} + } // namespace io diff --git a/src/modules/io/FilesystemArchive.h b/src/modules/io/FilesystemArchive.h index bb403f5ab6..fa1dd52a3b 100644 --- a/src/modules/io/FilesystemArchive.h +++ b/src/modules/io/FilesystemArchive.h @@ -28,4 +28,6 @@ class FilesystemArchive : public Archive { SeekableWriteStream* writeStream(const core::String &filePath) override; }; +ArchivePtr openFilesystemArchive(const io::FilesystemPtr &fs, const core::String &path = ""); + } // namespace io diff --git a/src/modules/io/MemoryArchive.cpp b/src/modules/io/MemoryArchive.cpp index 72737c5050..6a81cccf31 100644 --- a/src/modules/io/MemoryArchive.cpp +++ b/src/modules/io/MemoryArchive.cpp @@ -58,6 +58,7 @@ SeekableReadStream *MemoryArchive::readStream(const core::String &filePath) { if (iter == _entries.end()) { return nullptr; } + iter->second->seek(0); return new SeekableReadWriteStreamWrapper((io::SeekableReadStream*)iter->second); } diff --git a/src/modules/io/MemoryArchive.h b/src/modules/io/MemoryArchive.h index 50f25618f1..80330fc511 100644 --- a/src/modules/io/MemoryArchive.h +++ b/src/modules/io/MemoryArchive.h @@ -4,14 +4,13 @@ #pragma once +#include "core/SharedPtr.h" #include "core/collection/StringMap.h" #include "io/Archive.h" -#include "io/BufferedReadWriteStream.h" -#include "core/SharedPtr.h" namespace io { -using BufferedReadWriteStreamPtr = core::SharedPtr; +class BufferedReadWriteStream; /** * Archive that stores files in memory. @@ -34,4 +33,10 @@ class MemoryArchive : public Archive { SeekableWriteStream* writeStream(const core::String &filePath) override; }; +using MemoryArchivePtr = core::SharedPtr; + +inline MemoryArchivePtr openMemoryArchive() { + return core::make_shared(); +} + } // namespace io diff --git a/src/modules/io/StreamArchive.h b/src/modules/io/StreamArchive.h new file mode 100644 index 0000000000..b8dda0ba9a --- /dev/null +++ b/src/modules/io/StreamArchive.h @@ -0,0 +1,32 @@ +/** + * @file + */ + +#include "io/Archive.h" +#include "io/Stream.h" + +namespace io { + +/** + * @ingroup IO + */ +class StreamArchive : public Archive { +private: + io::SeekableReadStream *_readStream; + io::SeekableWriteStream *_writeStream; + +public: + StreamArchive(io::SeekableReadStream *stream) : _readStream(stream) { + } + StreamArchive(io::SeekableWriteStream *stream) : _writeStream(stream) { + } + ~StreamArchive() override = default; + SeekableReadStream *readStream(const core::String &filePath) override { + return _readStream; + } + SeekableWriteStream *writeStream(const core::String &filePath) override { + return _writeStream; + } +}; + +} // namespace io diff --git a/src/modules/io/ZipArchive.cpp b/src/modules/io/ZipArchive.cpp index ecb0be3419..36664a871c 100644 --- a/src/modules/io/ZipArchive.cpp +++ b/src/modules/io/ZipArchive.cpp @@ -4,6 +4,7 @@ #include "ZipArchive.h" #include "core/Log.h" +#include "core/SharedPtr.h" #include "core/StandardLib.h" #include "core/StringUtil.h" #include "io/BufferedReadWriteStream.h" @@ -174,4 +175,13 @@ SeekableWriteStream* ZipArchive::writeStream(const core::String &filePath) { return nullptr; } +ArchivePtr openZipArchive(io::SeekableReadStream *stream) { + if (!stream || !ZipArchive::validStream(*stream)) { + return ArchivePtr{}; + } + core::SharedPtr za = core::make_shared(); + za->init("", stream); + return za; +} + } // namespace io diff --git a/src/modules/io/ZipArchive.h b/src/modules/io/ZipArchive.h index bbc7da3c8d..66bec2e007 100644 --- a/src/modules/io/ZipArchive.h +++ b/src/modules/io/ZipArchive.h @@ -29,4 +29,6 @@ class ZipArchive : public Archive { void shutdown() override; }; +ArchivePtr openZipArchive(io::SeekableReadStream *stream); + } // namespace io diff --git a/src/modules/voxelcollection/CollectionManager.cpp b/src/modules/voxelcollection/CollectionManager.cpp index f0b95184ff..ed4ea47aa8 100644 --- a/src/modules/voxelcollection/CollectionManager.cpp +++ b/src/modules/voxelcollection/CollectionManager.cpp @@ -7,6 +7,8 @@ #include "core/Log.h" #include "core/StringUtil.h" #include "http/HttpCacheStream.h" +#include "io/Archive.h" +#include "io/FilesystemArchive.h" #include "voxelcollection/Downloader.h" #include "voxelformat/VolumeFormat.h" @@ -126,8 +128,9 @@ void CollectionManager::loadThumbnail(const VoxelFile &voxelFile) { return; } http::HttpCacheStream stream(_filesystem, voxelFile.fullPath, voxelFile.url); + const io::ArchivePtr &archive = io::openFilesystemArchive(_filesystem); voxelformat::LoadContext loadCtx; - image::ImagePtr thumbnailImage = voxelformat::loadScreenshot(voxelFile.fullPath, stream, loadCtx); + image::ImagePtr thumbnailImage = voxelformat::loadScreenshot(voxelFile.fullPath, archive, loadCtx); if (!thumbnailImage || !thumbnailImage->isLoaded()) { Log::debug("Failed to load given input file: %s", voxelFile.fullPath.c_str()); return; diff --git a/src/modules/voxelformat/Format.cpp b/src/modules/voxelformat/Format.cpp index f781143580..eed9c118bc 100644 --- a/src/modules/voxelformat/Format.cpp +++ b/src/modules/voxelformat/Format.cpp @@ -12,6 +12,7 @@ #include "core/Var.h" #include "core/collection/DynamicArray.h" #include "image/Image.h" +#include "io/Archive.h" #include "math/Math.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -86,18 +87,18 @@ void Format::calcMinsMaxs(const voxel::Region ®ion, const glm::ivec3 &maxSize Log::debug("maxs(%i:%i:%i)", maxs.x, maxs.y, maxs.z); } -size_t Format::loadPalette(const core::String &, io::SeekableReadStream &, palette::Palette &, const LoadContext &) { +size_t Format::loadPalette(const core::String &, const io::ArchivePtr &, palette::Palette &, const LoadContext &) { return 0; } -size_t PaletteFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t PaletteFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { scenegraph::SceneGraph sceneGraph; - loadGroupsPalette(filename, stream, sceneGraph, palette, ctx); + loadGroupsPalette(filename, archive, sceneGraph, palette, ctx); return palette.size(); } -image::ImagePtr Format::loadScreenshot(const core::String &filename, io::SeekableReadStream &, const LoadContext &) { +image::ImagePtr Format::loadScreenshot(const core::String &filename, const io::ArchivePtr &, const LoadContext &) { Log::debug("%s doesn't have a supported embedded screenshot", filename.c_str()); return image::ImagePtr(); } @@ -111,7 +112,7 @@ bool Format::singleVolume() const { } bool Format::save(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { bool needsSplit = false; const glm::ivec3 maxsize = maxSize(); if (maxsize.x > 0 && maxsize.y > 0 && maxsize.z > 0) { @@ -143,14 +144,14 @@ bool Format::save(const scenegraph::SceneGraph &sceneGraph, const core::String & mergedNode.setVolume(merged.first, true); mergedNode.setPalette(merged.second); mergedSceneGraph.emplace(core::move(mergedNode)); - return saveGroups(mergedSceneGraph, filename, stream, ctx); + return saveGroups(mergedSceneGraph, filename, archive, ctx); } if (needsSplit) { Log::debug("Split volumes before saving as the target format only supports smaller volume sizes"); scenegraph::SceneGraph newSceneGraph; scenegraph::splitVolumes(sceneGraph, newSceneGraph, false, false, saveVisibleOnly, maxsize); - return saveGroups(newSceneGraph, filename, stream, ctx); + return saveGroups(newSceneGraph, filename, archive, ctx); } if (saveVisibleOnly) { @@ -166,14 +167,14 @@ bool Format::save(const scenegraph::SceneGraph &sceneGraph, const core::String & for (int nodeId : nodes) { newSceneGraph.removeNode(nodeId, false); } - return saveGroups(newSceneGraph, filename, stream, ctx); + return saveGroups(newSceneGraph, filename, archive, ctx); } - return saveGroups(sceneGraph, filename, stream, ctx); + return saveGroups(sceneGraph, filename, archive, ctx); } -bool Format::load(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, +bool Format::load(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { - if (!loadGroups(filename, stream, sceneGraph, ctx)) { + if (!loadGroups(filename, archive, sceneGraph, ctx)) { return false; } if (!sceneGraph.validate()) { @@ -191,10 +192,10 @@ bool Format::stopExecution() { return app::App::getInstance()->shouldQuit(); } -bool PaletteFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool PaletteFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { palette::Palette palette; - if (!loadGroupsPalette(filename, stream, sceneGraph, palette, ctx)) { + if (!loadGroupsPalette(filename, archive, sceneGraph, palette, ctx)) { return false; } sceneGraph.updateTransforms(); @@ -202,7 +203,7 @@ bool PaletteFormat::loadGroups(const core::String &filename, io::SeekableReadStr } bool PaletteFormat::save(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { if (onlyOnePalette() && sceneGraph.hasMoreThanOnePalette()) { Log::debug("Need to merge palettes before saving"); const palette::Palette &palette = sceneGraph.mergePalettes(true, emptyPaletteIndex()); @@ -213,7 +214,7 @@ bool PaletteFormat::save(const scenegraph::SceneGraph &sceneGraph, const core::S node.remapToPalette(palette); node.setPalette(palette); } - return Format::save(newSceneGraph, filename, stream, ctx); + return Format::save(newSceneGraph, filename, archive, ctx); } else if (emptyPaletteIndex() >= 0 && emptyPaletteIndex() < palette::PaletteMaxColors) { Log::debug("Need to convert voxels to a palette that has %i as an empty slot", emptyPaletteIndex()); scenegraph::SceneGraph newSceneGraph; @@ -258,9 +259,9 @@ bool PaletteFormat::save(const scenegraph::SceneGraph &sceneGraph, const core::S node.remapToPalette(node.palette(), emptyPaletteIndex()); } } - return Format::save(newSceneGraph, filename, stream, ctx); + return Format::save(newSceneGraph, filename, archive, ctx); } - return Format::save(sceneGraph, filename, stream, ctx); + return Format::save(sceneGraph, filename, archive, ctx); } Format::Format() { @@ -275,20 +276,18 @@ core::RGBA Format::flattenRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t a) const return core::Color::flattenRGB(r, g, b, a, _flattenFactor); } -bool RGBAFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool RGBAFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { palette::Palette palette; const bool createPalette = core::Var::get(cfg::VoxelCreatePalette); if (createPalette) { - const int64_t resetToPos = stream.pos(); - if (loadPalette(filename, stream, palette, ctx) <= 0) { + if (loadPalette(filename, archive, palette, ctx) <= 0) { palette = voxel::getPalette(); } - stream.seek(resetToPos); } else { palette = voxel::getPalette(); } - if (!loadGroupsRGBA(filename, stream, sceneGraph, palette, ctx)) { + if (!loadGroupsRGBA(filename, archive, sceneGraph, palette, ctx)) { return false; } sceneGraph.updateTransforms(); diff --git a/src/modules/voxelformat/Format.h b/src/modules/voxelformat/Format.h index 7327d92c1c..1d02e6d610 100644 --- a/src/modules/voxelformat/Format.h +++ b/src/modules/voxelformat/Format.h @@ -10,6 +10,7 @@ #include "core/collection/DynamicMap.h" #include "image/Image.h" +#include "io/Archive.h" #include "io/Stream.h" #include "voxel/RawVolume.h" #include "voxelformat/FormatThumbnail.h" @@ -127,12 +128,12 @@ class Format { * @todo don't use a stream, but an archive for formats that are split over several files */ virtual bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) = 0; + const io::ArchivePtr &archive, const SaveContext &ctx) = 0; /** * @brief If the format supports multiple models or groups, this method load them into the scene graph * @todo don't use a stream, but an archive for formats that are split over several files */ - virtual bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, + virtual bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) = 0; public: @@ -153,7 +154,7 @@ class Format { * @note Not supported by many formats. * @todo don't use a stream, but an archive for formats that are split over several files */ - virtual image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + virtual image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx); /** @@ -166,18 +167,18 @@ class Format { * @todo don't use a stream, but an archive for formats that are split over several files * @return the amount of colors found in the palette */ - virtual size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + virtual size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx); /** * @todo don't use a stream, but an archive for formats that are split over several files */ - virtual bool load(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + virtual bool load(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx); /** * @todo don't use a stream, but an archive for formats that are split over several files */ virtual bool save(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx); + const io::ArchivePtr &archive, const SaveContext &ctx); }; /** @@ -194,7 +195,7 @@ class NoColorFormat : public Format {}; */ class PaletteFormat : public Format { protected: - virtual bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + virtual bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) = 0; @@ -211,13 +212,13 @@ class PaletteFormat : public Format { virtual int emptyPaletteIndex() const { return -1; } - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override final; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; - bool save(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, io::SeekableWriteStream &stream, + bool save(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, const io::ArchivePtr &archive, const SaveContext &ctx) override final; }; @@ -229,10 +230,10 @@ class PaletteFormat : public Format { */ class RGBAFormat : public Format { protected: - virtual bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + virtual bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) = 0; - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/VolumeFormat.cpp b/src/modules/voxelformat/VolumeFormat.cpp index 3d67140bb2..196a461c36 100644 --- a/src/modules/voxelformat/VolumeFormat.cpp +++ b/src/modules/voxelformat/VolumeFormat.cpp @@ -5,10 +5,13 @@ #include "VolumeFormat.h" #include "app/App.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/SharedPtr.h" #include "core/StringUtil.h" #include "core/Trace.h" #include "core/collection/DynamicArray.h" +#include "io/Archive.h" +#include "io/FilesystemArchive.h" #include "io/File.h" #include "io/FileStream.h" #include "io/Filesystem.h" @@ -488,9 +491,14 @@ static core::SharedPtr getFormat(const io::FormatDescription &desc, uint return {}; } -image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, const LoadContext &ctx) { +image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { core_trace_scoped(LoadVolumeScreenshot); - const uint32_t magic = loadMagic(stream); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::warn("Failed to open file for %s", filename.c_str()); + return image::ImagePtr(); + } + const uint32_t magic = loadMagic(*stream); const io::FormatDescription *desc = io::getDescription(filename, magic, voxelLoad()); if (desc == nullptr) { @@ -503,8 +511,7 @@ image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStr } const core::SharedPtr &f = getFormat(*desc, magic); if (f) { - stream.seek(0); - return f->loadScreenshot(filename, stream, ctx); + return f->loadScreenshot(filename, archive, ctx); } Log::error("Failed to load model screenshot from file %s - " "unsupported file format", @@ -517,15 +524,9 @@ bool importPalette(const core::String &filename, palette::Palette &palette) { return palette.load(filename.c_str()); } if (io::isA(filename, voxelformat::voxelLoad())) { - const io::FilesystemPtr &fs = io::filesystem(); - const io::FilePtr &palFile = fs->open(filename); - if (!palFile->validHandle()) { - Log::warn("Failed to open palette file at %s", filename.c_str()); - return false; - } - io::FileStream stream(palFile); + const io::ArchivePtr &archive = io::openFilesystemArchive(io::filesystem()); LoadContext loadCtx; - if (voxelformat::loadPalette(filename, stream, palette, loadCtx) <= 0) { + if (voxelformat::loadPalette(filename, archive, palette, loadCtx) <= 0) { Log::warn("Failed to load palette from %s", filename.c_str()); return false; } @@ -535,10 +536,15 @@ bool importPalette(const core::String &filename, palette::Palette &palette) { return false; } -size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { core_trace_scoped(LoadVolumePalette); - const uint32_t magic = loadMagic(stream); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::warn("Failed to open palette file at %s", filename.c_str()); + return 0; + } + const uint32_t magic = loadMagic(*stream); const io::FormatDescription *desc = io::getDescription(filename, magic, voxelLoad()); if (desc == nullptr) { Log::warn("Format %s isn't supported", filename.c_str()); @@ -549,8 +555,7 @@ size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, return 0; } if (const core::SharedPtr &f = getFormat(*desc, magic)) { - stream.seek(0); - const size_t n = f->loadPalette(filename, stream, palette, ctx); + const size_t n = f->loadPalette(filename, archive, palette, ctx); palette.markDirty(); return n; } @@ -560,10 +565,15 @@ size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, return 0; } -bool loadFormat(const io::FileDescription &fileDesc, io::SeekableReadStream &stream, +bool loadFormat(const io::FileDescription &fileDesc, const io::ArchivePtr &archive, scenegraph::SceneGraph &newSceneGraph, const LoadContext &ctx) { core_trace_scoped(LoadVolumeFormat); - const uint32_t magic = loadMagic(stream); + core::ScopedPtr stream(archive->readStream(fileDesc.name)); + if (!stream) { + Log::warn("Failed to open file at %s", fileDesc.name.c_str()); + return 0; + } + const uint32_t magic = loadMagic(*stream); const io::FormatDescription *desc = io::getDescription(fileDesc, magic, voxelLoad()); if (desc == nullptr) { return false; @@ -571,7 +581,7 @@ bool loadFormat(const io::FileDescription &fileDesc, io::SeekableReadStream &str const core::String &filename = fileDesc.name; const core::SharedPtr &f = getFormat(*desc, magic); if (f) { - if (!f->load(filename, stream, newSceneGraph, ctx)) { + if (!f->load(filename, archive, newSceneGraph, ctx)) { Log::error("Error while loading %s", filename.c_str()); newSceneGraph.clear(); } @@ -629,7 +639,7 @@ bool isModelFormat(const core::String &filename) { } bool saveFormat(scenegraph::SceneGraph &sceneGraph, const core::String &filename, const io::FormatDescription *desc, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { if (sceneGraph.empty()) { Log::error("Failed to save model file %s - no volumes given", filename.c_str()); return false; @@ -643,7 +653,7 @@ bool saveFormat(scenegraph::SceneGraph &sceneGraph, const core::String &filename if (desc != nullptr) { core::SharedPtr f = getFormat(*desc, 0u); if (f) { - if (f->save(sceneGraph, filename, stream, ctx)) { + if (f->save(sceneGraph, filename, archive, ctx)) { Log::debug("Saved file for format '%s' (ext: '%s')", desc->name.c_str(), ext.c_str()); metric::count("save", 1, {{"type", ext}}); return true; @@ -656,7 +666,7 @@ bool saveFormat(scenegraph::SceneGraph &sceneGraph, const core::String &filename if (desc->matchesExtension(ext)) { core::SharedPtr f = getFormat(*desc, 0u); if (f) { - if (f->save(sceneGraph, filename, stream, ctx)) { + if (f->save(sceneGraph, filename, archive, ctx)) { Log::debug("Saved file for format '%s' (ext: '%s')", desc->name.c_str(), ext.c_str()); metric::count("save", 1, {{"type", ext}}); return true; @@ -676,8 +686,8 @@ bool saveFormat(const io::FilePtr &filePtr, const io::FormatDescription *desc, s return false; } - io::FileStream stream(filePtr); - if (!saveFormat(sceneGraph, filePtr->name(), desc, stream, ctx)) { + const io::ArchivePtr &archive = io::openFilesystemArchive(io::filesystem()); + if (!saveFormat(sceneGraph, filePtr->name(), desc, archive, ctx)) { if (!useVengiAsFallback) { Log::error("Failed to save file %s", filePtr->name().c_str()); return false; @@ -686,7 +696,7 @@ bool saveFormat(const io::FilePtr &filePtr, const io::FormatDescription *desc, s VENGIFormat vengiFormat; const core::String &newName = core::string::replaceExtension(filePtr->name(), "vengi"); io::FileStream newStream(io::filesystem()->open(newName, io::FileMode::SysWrite)); - return vengiFormat.save(sceneGraph, newName, newStream, ctx); + return vengiFormat.save(sceneGraph, newName, archive, ctx); } return true; } diff --git a/src/modules/voxelformat/VolumeFormat.h b/src/modules/voxelformat/VolumeFormat.h index bc7bac9dcf..66f912a3e0 100644 --- a/src/modules/voxelformat/VolumeFormat.h +++ b/src/modules/voxelformat/VolumeFormat.h @@ -37,10 +37,10 @@ bool importPalette(const core::String &filename, palette::Palette &palette); * @brief Tries to load the embedded palette from the given file. If the format doesn't have a palette embedded, this * returns @c 0 */ -size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx); -image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, const LoadContext &ctx); -bool loadFormat(const io::FileDescription &fileDesc, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, +image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx); +bool loadFormat(const io::FileDescription &fileDesc, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx); /** @@ -49,7 +49,7 @@ bool loadFormat(const io::FileDescription &fileDesc, io::SeekableReadStream &str bool saveFormat(const io::FilePtr &filePtr, const io::FormatDescription *desc, scenegraph::SceneGraph &sceneGraph, const SaveContext &ctx, bool useVengiAsFallback = true); bool saveFormat(scenegraph::SceneGraph &sceneGraph, const core::String &filename, const io::FormatDescription *desc, - io::SeekableWriteStream &stream, const SaveContext &ctx); + const io::ArchivePtr &archive, const SaveContext &ctx); bool isMeshFormat(const core::String &filename, bool save); bool isMeshFormat(const io::FormatDescription &desc); diff --git a/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.cpp b/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.cpp index d7048b9e45..5dcf034905 100644 --- a/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.cpp +++ b/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.cpp @@ -4,6 +4,7 @@ #include "AoSVXLFormat.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/collection/DynamicMap.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -40,12 +41,17 @@ static inline uint8_t vxl_red(uint32_t c) { return (c >> 16) & 0xFF; } -bool AoSVXLFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool AoSVXLFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { - const int64_t size = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + const int64_t size = stream->size(); uint8_t *data = (uint8_t *)core_malloc(size); - if (stream.read(data, size) == -1) { + if (stream->read(data, size) == -1) { Log::error("Failed to read vxl stream for %s of size %i", filename.c_str(), (int)size); core_free(data); return false; @@ -96,11 +102,16 @@ bool AoSVXLFormat::loadGroupsRGBA(const core::String &filename, io::SeekableRead return true; } -size_t AoSVXLFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t AoSVXLFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { - const int64_t size = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + const int64_t size = stream->size(); uint8_t *data = (uint8_t *)core_malloc(size); - if (stream.read(data, size) == -1) { + if (stream->read(data, size) == -1) { Log::error("Failed to read vxl stream for %s of size %i", filename.c_str(), (int)size); core_free(data); return 0; @@ -154,7 +165,12 @@ glm::ivec3 AoSVXLFormat::maxSize() const { } bool AoSVXLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } const voxel::Region ®ion = sceneGraph.region(); glm::ivec3 size = region.getDimensionsInVoxels(); glm::ivec3 targetSize(512, size.y, 512); @@ -195,7 +211,7 @@ bool AoSVXLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co if (read == 0) { break; } - if (stream.write(buf, read) == -1) { + if (stream->write(buf, read) == -1) { Log::error("Could not write AoE vxl file to stream"); libvxl_stream_free(&s); libvxl_free(&map); diff --git a/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.h b/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.h index ffa04da9b7..94e597babe 100644 --- a/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.h +++ b/src/modules/voxelformat/private/aceofspades/AoSVXLFormat.h @@ -17,15 +17,15 @@ namespace voxelformat { */ class AoSVXLFormat : public RGBASinglePaletteFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; glm::ivec3 maxSize() const override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/animatoon/AnimaToonFormat.cpp b/src/modules/voxelformat/private/animatoon/AnimaToonFormat.cpp index 6b80fc1b86..7bea8f28de 100644 --- a/src/modules/voxelformat/private/animatoon/AnimaToonFormat.cpp +++ b/src/modules/voxelformat/private/animatoon/AnimaToonFormat.cpp @@ -5,9 +5,12 @@ #include "AnimaToonFormat.h" #include "core/GLMConst.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/collection/DynamicArray.h" +#include "io/Archive.h" #include "io/Base64.h" #include "io/BufferedReadWriteStream.h" +#include "io/Stream.h" #include "io/ZipReadStream.h" #include "palette/Palette.h" #include "scenegraph/SceneGraph.h" @@ -24,12 +27,17 @@ namespace voxelformat { #error "GLM_FORCE_QUAT_DATA_WXYZ is not supported here" #endif -bool AnimaToonFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool AnimaToonFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { - const int64_t size = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + const int64_t size = stream->size(); core::String str(size, ' '); - if (!stream.readString((int)str.size(), str.c_str())) { + if (!stream->readString((int)str.size(), str.c_str())) { Log::error("Failed to read string from stream"); return false; } diff --git a/src/modules/voxelformat/private/animatoon/AnimaToonFormat.h b/src/modules/voxelformat/private/animatoon/AnimaToonFormat.h index 3d8b62b456..12b5c5a7c7 100644 --- a/src/modules/voxelformat/private/animatoon/AnimaToonFormat.h +++ b/src/modules/voxelformat/private/animatoon/AnimaToonFormat.h @@ -44,11 +44,11 @@ class AnimaToonFormat : public RGBAFormat { core::DynamicArray voxels; }; - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override { + const io::ArchivePtr &archive, const SaveContext &ctx) override { return false; } }; diff --git a/src/modules/voxelformat/private/binvox/BinVoxFormat.cpp b/src/modules/voxelformat/private/binvox/BinVoxFormat.cpp index 16ebe88a09..929e65ffe5 100644 --- a/src/modules/voxelformat/private/binvox/BinVoxFormat.cpp +++ b/src/modules/voxelformat/private/binvox/BinVoxFormat.cpp @@ -5,7 +5,9 @@ #include "BinVoxFormat.h" #include "core/Color.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" +#include "io/Archive.h" #include "io/Stream.h" #include "scenegraph/SceneGraph.h" #include "palette/Palette.h" @@ -68,10 +70,17 @@ bool BinVoxFormat::readData(State &state, const core::String &filename, io::Seek return true; } -bool BinVoxFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool BinVoxFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { char line[512]; - wrapBool(stream.readLine(sizeof(line), line)) + + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + + wrapBool(stream->readLine(sizeof(line), line)) if (0 != strcmp(line, "#binvox 1")) { Log::error("Expected to get '#binvox 1', but got '%s'", line); return false; @@ -84,7 +93,7 @@ bool BinVoxFormat::loadGroups(const core::String &filename, io::SeekableReadStre } for (;;) { - wrapBool(stream.readLine(sizeof(line), line)) + wrapBool(stream->readLine(sizeof(line), line)) if (core::string::startsWith(line, "dim ")) { if (SDL_sscanf(line, "dim %u %u %u", &state._d, &state._h, &state._w) != 3) { Log::error("Failed to parse binvox dimensions"); @@ -111,7 +120,7 @@ bool BinVoxFormat::loadGroups(const core::String &filename, io::SeekableReadStre return false; } } - if (!readData(state, filename, stream, sceneGraph)) { + if (!readData(state, filename, *stream, sceneGraph)) { Log::warn("Could not load the data from %s", filename.c_str()); return false; } @@ -120,7 +129,12 @@ bool BinVoxFormat::loadGroups(const core::String &filename, io::SeekableReadStre } bool BinVoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); const voxel::Region ®ion = node->region(); @@ -134,11 +148,11 @@ bool BinVoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co const glm::ivec3 &offset = -mins; const float scale = 1.0f; - wrapBool(stream.writeString("#binvox 1\n", false)) - stream.writeStringFormat(false, "dim %u %u %u\n", width, depth, height); - stream.writeStringFormat(false, "translate %i %i %i\n", offset.x, offset.y, offset.z); - stream.writeStringFormat(false, "scale %f\n", scale); - wrapBool(stream.writeString("data\n", false)) + wrapBool(stream->writeString("#binvox 1\n", false)) + stream->writeStringFormat(false, "dim %u %u %u\n", width, depth, height); + stream->writeStringFormat(false, "translate %i %i %i\n", offset.x, offset.y, offset.z); + stream->writeStringFormat(false, "scale %f\n", scale); + wrapBool(stream->writeString("data\n", false)) uint8_t count = 0u; uint8_t value = 0u; @@ -159,8 +173,8 @@ bool BinVoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co if (isAir(voxel.getMaterial())) { if (value != 0u || count == 255u) { if (count > 0u) { - wrapBool(stream.writeUInt8(value)) - wrapBool(stream.writeUInt8(count)) + wrapBool(stream->writeUInt8(value)) + wrapBool(stream->writeUInt8(count)) } voxels += count; count = 0u; @@ -174,8 +188,8 @@ bool BinVoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co } if (value != v || count == 255u) { if (count > 0u) { - wrapBool(stream.writeUInt8(value)) - wrapBool(stream.writeUInt8(count)) + wrapBool(stream->writeUInt8(value)) + wrapBool(stream->writeUInt8(count)) } voxels += count; count = 0u; @@ -195,8 +209,8 @@ bool BinVoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co } } core_assert_msg(count > 0u, "Expected to have at least one voxel left: %i", (int)count); - wrapBool(stream.writeUInt8(value)) - wrapBool(stream.writeUInt8(count)) + wrapBool(stream->writeUInt8(value)) + wrapBool(stream->writeUInt8(count)) voxels += count; const uint32_t expectedVoxels = width * height * depth; if (voxels != expectedVoxels) { diff --git a/src/modules/voxelformat/private/binvox/BinVoxFormat.h b/src/modules/voxelformat/private/binvox/BinVoxFormat.h index 501a60a94d..db166d95c2 100644 --- a/src/modules/voxelformat/private/binvox/BinVoxFormat.h +++ b/src/modules/voxelformat/private/binvox/BinVoxFormat.h @@ -33,10 +33,10 @@ class BinVoxFormat : public NoColorFormat { scenegraph::SceneGraph &sceneGraph); protected: - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: bool singleVolume() const override { return true; diff --git a/src/modules/voxelformat/private/chronovox/CSMFormat.cpp b/src/modules/voxelformat/private/chronovox/CSMFormat.cpp index 95ad143daf..11834ce3a3 100644 --- a/src/modules/voxelformat/private/chronovox/CSMFormat.cpp +++ b/src/modules/voxelformat/private/chronovox/CSMFormat.cpp @@ -5,6 +5,8 @@ #include "CSMFormat.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" +#include "io/Archive.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" #include "palette/PaletteLookup.h" @@ -72,15 +74,20 @@ static core::String makeNameUnique(const scenegraph::SceneGraph &sceneGraph, cor return name; } -bool CSMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool CSMFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } uint32_t magic, version, blank, matrixCount; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) const bool isNVM = magic == FourCC('.', 'N', 'V', 'M'); - wrap(stream.readUInt32(version)) - wrap(stream.readUInt32(blank)) - wrap(stream.readUInt32(matrixCount)) + wrap(stream->readUInt32(version)) + wrap(stream->readUInt32(blank)) + wrap(stream->readUInt32(matrixCount)) Log::debug("CSM version: %i", version); if (isNVM && version > 2) { @@ -96,19 +103,19 @@ bool CSMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr for (uint16_t i = 0u; (uint16_t)i < matrixCount; ++i) { core::String name; core::String parent; - wrapBool(readString(stream, name, readStringAsInt)) + wrapBool(readString(*stream, name, readStringAsInt)) if (version > 1) { - wrapBool(readString(stream, parent, readStringAsInt)) + wrapBool(readString(*stream, parent, readStringAsInt)) } int16_t posx, posy, posz; - wrap(stream.readInt16(posx)) - wrap(stream.readInt16(posy)) - wrap(stream.readInt16(posz)) + wrap(stream->readInt16(posx)) + wrap(stream->readInt16(posy)) + wrap(stream->readInt16(posz)) uint16_t sizex, sizey, sizez; - wrap(stream.readUInt16(sizex)) - wrap(stream.readUInt16(sizey)) - wrap(stream.readUInt16(sizez)) + wrap(stream->readUInt16(sizex)) + wrap(stream->readUInt16(sizey)) + wrap(stream->readUInt16(sizez)) if (sizex > MaxRegionSize || sizey > MaxRegionSize || sizez > MaxRegionSize) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", sizex, sizey, sizez); @@ -130,15 +137,15 @@ bool CSMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr while (matrixIndex < voxels) { uint8_t count; - wrap(stream.readUInt8(count)) + wrap(stream->readUInt8(count)) uint8_t r; - wrap(stream.readUInt8(r)) + wrap(stream->readUInt8(r)) uint8_t g; - wrap(stream.readUInt8(g)) + wrap(stream->readUInt8(g)) uint8_t b; - wrap(stream.readUInt8(b)) + wrap(stream->readUInt8(b)) uint8_t interactiontype; - wrap(stream.readUInt8(interactiontype)) + wrap(stream->readUInt8(interactiontype)) if (interactiontype == 0u) { matrixIndex += count; continue; @@ -185,7 +192,7 @@ bool CSMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr #undef wrapBool bool CSMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { return false; } diff --git a/src/modules/voxelformat/private/chronovox/CSMFormat.h b/src/modules/voxelformat/private/chronovox/CSMFormat.h index 01bef83782..bc1f9fc4e8 100644 --- a/src/modules/voxelformat/private/chronovox/CSMFormat.h +++ b/src/modules/voxelformat/private/chronovox/CSMFormat.h @@ -15,11 +15,11 @@ namespace voxelformat { */ class CSMFormat : public RGBAFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/commandconquer/HVAFormat.cpp b/src/modules/voxelformat/private/commandconquer/HVAFormat.cpp index bde82c272d..4f4353c6bd 100644 --- a/src/modules/voxelformat/private/commandconquer/HVAFormat.cpp +++ b/src/modules/voxelformat/private/commandconquer/HVAFormat.cpp @@ -5,6 +5,8 @@ #include "HVAFormat.h" #include "app/App.h" #include "core/Log.h" +#include "core/ScopedPtr.h" +#include "io/Archive.h" #include "io/FileStream.h" #include "scenegraph/SceneGraphNode.h" @@ -86,18 +88,16 @@ static void convertHVARead(glm::mat4 &vengiMatrix, const vxl::VXLLayerInfo &foot translation.z *= footer.scale * sectionScale.y; } -bool HVAFormat::loadHVA(const core::String &filename, const vxl::VXLModel &mdl, scenegraph::SceneGraph &sceneGraph) { +bool HVAFormat::loadHVA(const core::String &filename, const io::ArchivePtr &archive, const vxl::VXLModel &mdl, scenegraph::SceneGraph &sceneGraph) { vxl::HVAModel file; { - const io::FilesystemPtr &filesystem = io::filesystem(); - const io::FilePtr &hvaFile = filesystem->open(filename); - if (!hvaFile->validHandle()) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { // if there is no hva file, we still don't show an error return true; } - io::FileStream stream(hvaFile); - wrapBool(readHVAHeader(stream, file.header)); - wrapBool(readHVAFrames(stream, mdl, file)); + wrapBool(readHVAHeader(*stream, file.header)); + wrapBool(readHVAFrames(*stream, mdl, file)); } Log::debug("load %u frames", file.header.numFrames); for (uint32_t keyFrameIdx = 0; keyFrameIdx < file.header.numFrames; ++keyFrameIdx) { @@ -188,15 +188,14 @@ bool HVAFormat::writeHVAFrames(io::SeekableWriteStream &stream, const scenegraph return true; } -bool HVAFormat::saveHVA(const core::String &filename, const scenegraph::SceneGraph &sceneGraph) { - const io::FilesystemPtr &filesystem = io::filesystem(); - io::FilePtr hvaFile = filesystem->open(filename, io::FileMode::SysWrite); - if (!hvaFile->validHandle()) { +bool HVAFormat::saveHVA(const core::String &filename, const io::ArchivePtr &archive, const scenegraph::SceneGraph &sceneGraph) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); return false; } - io::FileStream stream(hvaFile); - wrapBool(writeHVAHeader(stream, sceneGraph)); - wrapBool(writeHVAFrames(stream, sceneGraph)); + wrapBool(writeHVAHeader(*stream, sceneGraph)); + wrapBool(writeHVAFrames(*stream, sceneGraph)); return true; } diff --git a/src/modules/voxelformat/private/commandconquer/HVAFormat.h b/src/modules/voxelformat/private/commandconquer/HVAFormat.h index fd19f423f1..aeb65422fb 100644 --- a/src/modules/voxelformat/private/commandconquer/HVAFormat.h +++ b/src/modules/voxelformat/private/commandconquer/HVAFormat.h @@ -5,6 +5,7 @@ #pragma once #include "VXLShared.h" +#include "io/Archive.h" #include "io/Stream.h" #include "scenegraph/SceneGraph.h" @@ -23,8 +24,8 @@ class HVAFormat { bool readHVAFrames(io::SeekableReadStream &stream, const vxl::VXLModel &mdl, vxl::HVAModel &file) const; public: - bool loadHVA(const core::String &filename, const vxl::VXLModel &mdl, scenegraph::SceneGraph &sceneGraph); - bool saveHVA(const core::String &filename, const scenegraph::SceneGraph &sceneGraph); + bool loadHVA(const core::String &filename, const io::ArchivePtr &archive, const vxl::VXLModel &mdl, scenegraph::SceneGraph &sceneGraph); + bool saveHVA(const core::String &filename, const io::ArchivePtr &archive, const scenegraph::SceneGraph &sceneGraph); }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/commandconquer/VXLFormat.cpp b/src/modules/voxelformat/private/commandconquer/VXLFormat.cpp index 027b75c158..9edae13ab3 100644 --- a/src/modules/voxelformat/private/commandconquer/VXLFormat.cpp +++ b/src/modules/voxelformat/private/commandconquer/VXLFormat.cpp @@ -8,11 +8,13 @@ #include "core/Common.h" #include "core/GameConfig.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/Var.h" #include "core/collection/Buffer.h" #include "core/collection/DynamicArray.h" #include "core/collection/StringSet.h" +#include "io/Archive.h" #include "io/File.h" #include "io/FileStream.h" #include "io/Filesystem.h" @@ -276,15 +278,20 @@ bool VXLFormat::writeHeader(io::SeekableWriteStream &stream, uint32_t numNodes, bool VXLFormat::saveVXL(const scenegraph::SceneGraph &sceneGraph, core::DynamicArray &nodes, const core::String &filename, - io::SeekableWriteStream &stream) { + const io::ArchivePtr &archive) { if (nodes.empty()) { return false; } - const uint32_t numLayers = (uint32_t)nodes.size(); - wrapBool(writeHeader(stream, numLayers, nodes[0]->palette())) + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + const uint32_t numLayers = nodes.size(); + wrapBool(writeHeader(*stream, numLayers, nodes[0]->palette())) for (uint32_t i = 0; i < numLayers; ++i) { const scenegraph::SceneGraphNode *node = nodes[(int)i]; - wrapBool(writeLayerHeader(stream, *node, i)) + wrapBool(writeLayerHeader(*stream, *node, i)) } { @@ -299,36 +306,36 @@ bool VXLFormat::saveVXL(const scenegraph::SceneGraph &sceneGraph, } core::Buffer layerOffsets(numLayers); - const uint64_t bodyStart = stream.pos(); + const uint64_t bodyStart = stream->pos(); for (uint32_t i = 0; i < numLayers; ++i) { const scenegraph::SceneGraphNode *node = nodes[(int)i]; - wrapBool(writeLayer(stream, sceneGraph, *node, layerOffsets[i], bodyStart)) + wrapBool(writeLayer(*stream, sceneGraph, *node, layerOffsets[i], bodyStart)) } - const uint64_t afterBodyPos = stream.pos(); + const uint64_t afterBodyPos = stream->pos(); const uint64_t bodySize = afterBodyPos - bodyStart; Log::debug("write %u bytes as body size", (uint32_t)bodySize); - if (stream.seek(vxl::HeaderBodySizeOffset) == -1) { + if (stream->seek(vxl::HeaderBodySizeOffset) == -1) { Log::error("Failed to seek to body size"); return false; } - wrapBool(stream.writeUInt32((uint32_t)bodySize)) - if (stream.seek(afterBodyPos) == -1) { + wrapBool(stream->writeUInt32((uint32_t)bodySize)) + if (stream->seek(afterBodyPos) == -1) { Log::error("Failed to seek to after body"); return false; } - core_assert((uint64_t)stream.pos() == (uint64_t)(vxl::HeaderSize + vxl::LayerHeaderSize * numLayers + bodySize)); + core_assert((uint64_t)stream->pos() == (uint64_t)(vxl::HeaderSize + vxl::LayerHeaderSize * numLayers + bodySize)); for (uint32_t i = 0; i < numLayers; ++i) { const scenegraph::SceneGraphNode *node = nodes[(int)i]; - wrapBool(writeLayerInfo(stream, sceneGraph, *node, layerOffsets[i])) + wrapBool(writeLayerInfo(*stream, sceneGraph, *node, layerOffsets[i])) } return true; } bool VXLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { core::DynamicArray body; core::DynamicArray barrel; core::DynamicArray turret; @@ -352,25 +359,23 @@ bool VXLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const core::String &basename = core::string::stripExtension(filename); - if (!saveVXL(sceneGraph, body, filename, stream)) { + if (!saveVXL(sceneGraph, body, filename, archive)) { return false; } if (!barrel.empty()) { const core::String &extFilename = basename + "barl.vxl"; - io::FileStream extStream(io::filesystem()->open(extFilename, io::FileMode::SysWrite)); - if (extStream.valid() && !saveVXL(sceneGraph, barrel, extFilename, extStream)) { + if (!saveVXL(sceneGraph, barrel, extFilename, archive)) { Log::warn("Failed to write %s", extFilename.c_str()); } } if (!turret.empty()) { const core::String &extFilename = basename + "tur.vxl"; - io::FileStream extStream(io::filesystem()->open(extFilename, io::FileMode::SysWrite)); - if (extStream.valid() && !saveVXL(sceneGraph, turret, extFilename, extStream)) { + if (!saveVXL(sceneGraph, turret, extFilename, archive)) { Log::warn("Failed to write %s", extFilename.c_str()); } } HVAFormat hva; - return hva.saveHVA(basename + ".hva", sceneGraph); + return hva.saveHVA(basename + ".hva", archive, sceneGraph); } bool VXLFormat::readLayer(io::SeekableReadStream &stream, vxl::VXLModel &mdl, uint32_t nodeIdx, @@ -630,55 +635,62 @@ bool VXLFormat::prepareModel(vxl::VXLModel &mdl) const { return true; } -bool VXLFormat::loadFromFile(const core::String &filename, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, - const LoadContext &ctx) { - const io::FilePtr &file = io::filesystem()->open(filename); - if (file && file->validHandle()) { - io::FileStream stream(file); - return loadGroupsPalette(filename, stream, sceneGraph, palette, ctx); - } - return true; -} - -size_t VXLFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t VXLFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } vxl::VXLModel mdl; - if (!readHeader(stream, mdl, palette)) { + if (!readHeader(*stream, mdl, palette)) { return false; } return palette.colorCount(); } -bool VXLFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool VXLFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { vxl::VXLModel mdl; + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } - wrapBool(readHeader(stream, mdl, palette)) + wrapBool(readHeader(*stream, mdl, palette)) wrapBool(prepareModel(mdl)) - wrapBool(readLayerHeaders(stream, mdl)) - const int64_t bodyPos = stream.pos(); - if (stream.skip(mdl.header.dataSize) == -1) { + wrapBool(readLayerHeaders(*stream, mdl)) + const int64_t bodyPos = stream->pos(); + if (stream->skip(mdl.header.dataSize) == -1) { Log::error("Failed to skip %u bytes", mdl.header.dataSize); return false; } - wrapBool(readLayerInfos(stream, mdl)) + wrapBool(readLayerInfos(*stream, mdl)) - if (stream.seek(bodyPos) == -1) { + if (stream->seek(bodyPos) == -1) { Log::error("Failed to seek"); return false; } - wrapBool(readLayers(stream, mdl, sceneGraph, palette)) + wrapBool(readLayers(*stream, mdl, sceneGraph, palette)) const core::String &basename = core::string::stripExtension(filename); - HVAFormat hva; - wrapBool(hva.loadHVA(basename + ".hva", mdl, sceneGraph)) + + if (archive->exists(basename + ".hva")) { + HVAFormat hva; + wrapBool(hva.loadHVA(basename + ".hva", archive, mdl, sceneGraph)) + } if (!core::string::endsWith(filename, "barl.vxl")) { - wrapBool(loadFromFile(basename + "barl.vxl", sceneGraph, palette, ctx)); + if (archive->exists(basename + "barl.vxl")) { + wrapBool(loadGroupsPalette(basename + "barl.vxl", archive, sceneGraph, palette, ctx)) + } } if (!core::string::endsWith(filename, "tur.vxl")) { - wrapBool(loadFromFile(basename + "tur.vxl", sceneGraph, palette, ctx)); + if (archive->exists(basename + "tur.vxl")) { + wrapBool(loadGroupsPalette(basename + "tur.vxl", archive, sceneGraph, palette, ctx)) + } } return true; diff --git a/src/modules/voxelformat/private/commandconquer/VXLFormat.h b/src/modules/voxelformat/private/commandconquer/VXLFormat.h index 87c75a1ebc..97e8ff75a2 100644 --- a/src/modules/voxelformat/private/commandconquer/VXLFormat.h +++ b/src/modules/voxelformat/private/commandconquer/VXLFormat.h @@ -47,24 +47,21 @@ class VXLFormat : public PaletteFormat { bool saveVXL(const scenegraph::SceneGraph &sceneGraph, core::DynamicArray &nodes, const core::String &filename, - io::SeekableWriteStream &stream); + const io::ArchivePtr &archive); bool prepareModel(vxl::VXLModel &mdl) const; bool readHeader(io::SeekableReadStream &stream, vxl::VXLModel &mdl, palette::Palette &palette); - bool loadFromFile(const core::String &filename, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, - const LoadContext &ctx); - protected: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/cubeworld/CubFormat.cpp b/src/modules/voxelformat/private/cubeworld/CubFormat.cpp index 340cc7ca67..1bd22aa888 100644 --- a/src/modules/voxelformat/private/cubeworld/CubFormat.cpp +++ b/src/modules/voxelformat/private/cubeworld/CubFormat.cpp @@ -4,6 +4,7 @@ #include "CubFormat.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "scenegraph/SceneGraph.h" #include "palette/PaletteLookup.h" @@ -22,12 +23,17 @@ namespace voxelformat { return false; \ } -size_t CubFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t CubFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } uint32_t width, depth, height; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); @@ -38,9 +44,9 @@ size_t CubFormat::loadPalette(const core::String &filename, io::SeekableReadStre for (uint32_t d = 0u; d < depth; ++d) { for (uint32_t w = 0u; w < width; ++w) { uint8_t r, g, b; - wrap(stream.readUInt8(r)) - wrap(stream.readUInt8(g)) - wrap(stream.readUInt8(b)) + wrap(stream->readUInt8(r)) + wrap(stream->readUInt8(g)) + wrap(stream->readUInt8(b)) if (r == 0u && g == 0u && b == 0u) { // empty voxel continue; @@ -53,13 +59,18 @@ size_t CubFormat::loadPalette(const core::String &filename, io::SeekableReadStre return palette.size(); } -bool CubFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool CubFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } uint32_t width, depth, height; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); @@ -79,9 +90,9 @@ bool CubFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr for (uint32_t d = 0u; d < depth; ++d) { for (uint32_t w = 0u; w < width; ++w) { uint8_t r, g, b; - wrap(stream.readUInt8(r)) - wrap(stream.readUInt8(g)) - wrap(stream.readUInt8(b)) + wrap(stream->readUInt8(r)) + wrap(stream->readUInt8(g)) + wrap(stream->readUInt8(b)) if (r == 0u && g == 0u && b == 0u) { // empty voxel continue; @@ -103,7 +114,12 @@ bool CubFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr #undef wrap bool CubFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -116,9 +132,9 @@ bool CubFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const uint32_t depth = region.getDepthInVoxels(); // we have to flip depth with height for our own coordinate system - wrapBool(stream.writeUInt32(width)) - wrapBool(stream.writeUInt32(depth)) - wrapBool(stream.writeUInt32(height)) + wrapBool(stream->writeUInt32(width)) + wrapBool(stream->writeUInt32(depth)) + wrapBool(stream->writeUInt32(height)) const palette::Palette &palette = node->palette(); for (uint32_t y = 0u; y < height; ++y) { @@ -127,9 +143,9 @@ bool CubFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: core_assert_always(sampler.setPosition(lower.x + x, lower.y + y, lower.z + z)); const voxel::Voxel &voxel = sampler.voxel(); if (voxel.getMaterial() == voxel::VoxelType::Air) { - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) continue; } @@ -137,9 +153,9 @@ bool CubFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: if (rgba.r == 0u && rgba.g == 0u && rgba.b == 0u) { rgba = palette.color(palette.findReplacement(voxel.getColor())); } - wrapBool(stream.writeUInt8(rgba.r)) - wrapBool(stream.writeUInt8(rgba.g)) - wrapBool(stream.writeUInt8(rgba.b)) + wrapBool(stream->writeUInt8(rgba.r)) + wrapBool(stream->writeUInt8(rgba.g)) + wrapBool(stream->writeUInt8(rgba.b)) } } } diff --git a/src/modules/voxelformat/private/cubeworld/CubFormat.h b/src/modules/voxelformat/private/cubeworld/CubFormat.h index f4c70f584a..cbf551279f 100644 --- a/src/modules/voxelformat/private/cubeworld/CubFormat.h +++ b/src/modules/voxelformat/private/cubeworld/CubFormat.h @@ -18,14 +18,14 @@ namespace voxelformat { */ class CubFormat : public RGBASinglePaletteFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/cubzh/CubzhB64Format.cpp b/src/modules/voxelformat/private/cubzh/CubzhB64Format.cpp index b6803d6821..2d8e8223ec 100644 --- a/src/modules/voxelformat/private/cubzh/CubzhB64Format.cpp +++ b/src/modules/voxelformat/private/cubzh/CubzhB64Format.cpp @@ -10,6 +10,7 @@ #include "core/ScopedPtr.h" #include "core/StandardLib.h" #include "core/StringUtil.h" +#include "io/Archive.h" #include "io/Base64ReadStream.h" #include "io/FilesystemArchive.h" #include "scenegraph/SceneGraph.h" @@ -153,19 +154,14 @@ bool CubzhB64Format::readBlocks(io::ReadStream &stream, scenegraph::SceneGraph & #define CHECK_ID(field, id) core_memcmp((field), (id), 2) == 0 -bool CubzhB64Format::load3zh(io::FilesystemArchive &archive, const core::String &filename, +bool CubzhB64Format::load3zh(const io::ArchivePtr &archive, const core::String &filename, scenegraph::SceneGraph &modelScene, const palette::Palette &palette, const LoadContext &ctx) { - core::ScopedPtr stream(archive.readStream(filename)); - if (!stream) { - Log::error("Failed to open file: %s", filename.c_str()); - return false; - } CubzhFormat format; - return format.load(filename, *stream, modelScene, ctx); + return format.load(filename, archive, modelScene, ctx); } -bool CubzhB64Format::readObjects(const core::String &filename, io::ReadStream &stream, +bool CubzhB64Format::readObjects(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx, int version) { if (version == 3 || version == 2) { @@ -183,10 +179,10 @@ bool CubzhB64Format::readObjects(const core::String &filename, io::ReadStream &s const core::String &path = core::string::extractPath(filename); // e.g. the hubmap.b64 is in bundle/misc, the 3zh files in bundle/cache - io::FilesystemArchive archive(io::filesystem()); - archive.add(core::string::path(path, "..", "cache"), "*.3zh", 1); - archive.add(path, "*.3zh", 1); - archive.add(core::string::path(path, "cache"), "*.3zh", 1); + // TODO: re-add the cache dir + // archive->add(core::string::path(path, "..", "cache"), "*.3zh", 1); + // archive->add(path, "*.3zh", 1); + // archive->add(core::string::path(path, "cache"), "*.3zh", 1); while (instanceCount < numObjects) { core::String fullname3zh; @@ -339,7 +335,7 @@ bool CubzhB64Format::loadVersion1(const core::String &filename, io::ReadStream & return false; } -bool CubzhB64Format::loadVersion2(const core::String &filename, io::ReadStream &stream, +bool CubzhB64Format::loadVersion2(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { Ambience ambience; @@ -354,7 +350,7 @@ bool CubzhB64Format::loadVersion2(const core::String &filename, io::ReadStream & wrapBool(readAmbience(stream, sceneGraph, palette, ctx, ambience)) break; case 2: - wrapBool(readObjects(filename, stream, sceneGraph, palette, ctx, 2)) + wrapBool(readObjects(filename, archive, stream, sceneGraph, palette, ctx, 2)) break; case 3: wrapBool(readBlocks(stream, sceneGraph, palette, ctx)) @@ -368,7 +364,7 @@ bool CubzhB64Format::loadVersion2(const core::String &filename, io::ReadStream & return true; } -bool CubzhB64Format::loadVersion3(const core::String &filename, io::ReadStream &stream, +bool CubzhB64Format::loadVersion3(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { Ambience ambience; @@ -384,7 +380,7 @@ bool CubzhB64Format::loadVersion3(const core::String &filename, io::ReadStream & wrapBool(readAmbience(stream, sceneGraph, palette, ctx, ambience)) break; case 2: - wrapBool(readObjects(filename, stream, sceneGraph, palette, ctx, 3)) + wrapBool(readObjects(filename, archive, stream, sceneGraph, palette, ctx, 3)) break; case 3: wrapBool(readBlocks(stream, sceneGraph, palette, ctx)) @@ -398,10 +394,15 @@ bool CubzhB64Format::loadVersion3(const core::String &filename, io::ReadStream & return true; } -bool CubzhB64Format::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool CubzhB64Format::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { - io::Base64ReadStream base64Stream(stream); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Failed to open stream for file: %s", filename.c_str()); + return false; + } + io::Base64ReadStream base64Stream(*stream); uint8_t version; wrap(base64Stream.readUInt8(version)) Log::debug("Found version %i", (int)version); @@ -410,18 +411,18 @@ bool CubzhB64Format::loadGroupsRGBA(const core::String &filename, io::SeekableRe return false; } } else if (version == 2) { - if (!loadVersion2(filename, base64Stream, sceneGraph, palette, ctx)) { + if (!loadVersion2(filename, archive, base64Stream, sceneGraph, palette, ctx)) { return false; } } else if (version == 3) { - if (!loadVersion3(filename, base64Stream, sceneGraph, palette, ctx)) { + if (!loadVersion3(filename, archive, base64Stream, sceneGraph, palette, ctx)) { return false; } } else { Log::error("Unsupported version found: %i", (int)version); return false; } - Log::debug("%i bytes left in the stream", (int)stream.remaining()); + Log::debug("%i bytes left in the stream", (int)stream->remaining()); return true; } diff --git a/src/modules/voxelformat/private/cubzh/CubzhB64Format.h b/src/modules/voxelformat/private/cubzh/CubzhB64Format.h index 0da2c2fe82..d2c96eea12 100644 --- a/src/modules/voxelformat/private/cubzh/CubzhB64Format.h +++ b/src/modules/voxelformat/private/cubzh/CubzhB64Format.h @@ -50,9 +50,9 @@ class CubzhB64Format : public RGBAFormat { core::String txt; }; void setAmbienceProperties(scenegraph::SceneGraph &sceneGraph, const Ambience &ambience) const; - bool load3zh(io::FilesystemArchive &archive, const core::String &filename, scenegraph::SceneGraph &modelScene, + bool load3zh(const io::ArchivePtr &archive, const core::String &filename, scenegraph::SceneGraph &modelScene, const palette::Palette &palette, const LoadContext &ctx); - bool readObjects(const core::String &filename, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool readObjects(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx, int version); bool readChunkMap(io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx); @@ -62,14 +62,14 @@ class CubzhB64Format : public RGBAFormat { const LoadContext &ctx); bool loadVersion1(const core::String &filename, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx); - bool loadVersion2(const core::String &filename, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadVersion2(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx); - bool loadVersion3(const core::String &filename, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadVersion3(const core::String &filename, const io::ArchivePtr &archive, io::ReadStream &stream, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx); - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx); bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { return false; } }; diff --git a/src/modules/voxelformat/private/cubzh/CubzhFormat.cpp b/src/modules/voxelformat/private/cubzh/CubzhFormat.cpp index 8ec8201c9a..47d851a211 100644 --- a/src/modules/voxelformat/private/cubzh/CubzhFormat.cpp +++ b/src/modules/voxelformat/private/cubzh/CubzhFormat.cpp @@ -4,7 +4,9 @@ #include "CubzhFormat.h" #include "CubzhShared.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" +#include "io/Archive.h" #include "io/ZipReadStream.h" #include "palette/Palette.h" #include "scenegraph/SceneGraph.h" @@ -694,65 +696,80 @@ bool CubzhFormat::loadVersion6(const core::String &filename, const Header &heade return true; } -bool CubzhFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool CubzhFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } Header header; - wrapBool(loadHeader(stream, header)) + wrapBool(loadHeader(*stream, header)) Log::debug("Found version %d", header.version); if (header.legacy) { - return loadPCubes(filename, header, stream, sceneGraph, palette, ctx); + return loadPCubes(filename, header, *stream, sceneGraph, palette, ctx); } if (header.version == 5) { - return loadVersion5(filename, header, stream, sceneGraph, palette, ctx); + return loadVersion5(filename, header, *stream, sceneGraph, palette, ctx); } - return loadVersion6(filename, header, stream, sceneGraph, palette, ctx); + return loadVersion6(filename, header, *stream, sceneGraph, palette, ctx); } -size_t CubzhFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t CubzhFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } Header header; - wrapBool(loadHeader(stream, header)) - while (!stream.eos()) { + wrapBool(loadHeader(*stream, header)) + while (!stream->eos()) { Chunk chunk; - wrapBool(loadChunkHeader(header, stream, chunk)) - ChunkChecker check(stream, chunk); + wrapBool(loadChunkHeader(header, *stream, chunk)) + ChunkChecker check(*stream, chunk); if (header.version == 5u && chunk.chunkId == priv::CHUNK_ID_PALETTE_V5) { - wrapBool(loadPalettePCubes(stream, palette)) + wrapBool(loadPalettePCubes(*stream, palette)) return palette.size(); } else if (header.version == 6u && chunk.chunkId == priv::CHUNK_ID_PALETTE_V6) { - CubzhReadStream zhs(header, chunk, stream); + CubzhReadStream zhs(header, chunk, *stream); wrapBool(loadPalette6(zhs, palette)) return palette.size(); } else if (header.version == 6u && chunk.chunkId == priv::CHUNK_ID_PALETTE_LEGACY_V6) { - CubzhReadStream zhs(header, chunk, stream); + CubzhReadStream zhs(header, chunk, *stream); wrapBool(loadPalettePCubes(zhs, palette)) return palette.size(); } else { - wrapBool(loadSkipChunk(header, chunk, stream)) + wrapBool(loadSkipChunk(header, chunk, *stream)) } } return palette.size(); } -image::ImagePtr CubzhFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr CubzhFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } Header header; - if (!loadHeader(stream, header)) { + if (!loadHeader(*stream, header)) { Log::error("Failed to read header"); return image::ImagePtr(); } - while (!stream.eos()) { + while (!stream->eos()) { Chunk chunk; - if (!loadChunkHeader(header, stream, chunk)) { + if (!loadChunkHeader(header, *stream, chunk)) { return image::ImagePtr(); } if (chunk.chunkId == priv::CHUNK_ID_PREVIEW) { image::ImagePtr img = image::createEmptyImage(core::string::extractFilename(filename) + ".png"); - img->load(stream, chunk.chunkSize); + img->load(*stream, chunk.chunkSize); return img; } - if (!loadSkipChunk(header, chunk, stream)) { + if (!loadSkipChunk(header, chunk, *stream)) { Log::error("Failed to skip chunk %d with size %d", chunk.chunkId, chunk.chunkSize); break; } @@ -770,24 +787,29 @@ image::ImagePtr CubzhFormat::loadScreenshot(const core::String &filename, io::Se } bool CubzhFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { - stream.write("CUBZH!", 6); - wrapBool(stream.writeUInt32(6)) // version - wrapBool(stream.writeUInt8(1)) // zip compression - const int64_t totalSizePos = stream.pos(); - wrapBool(stream.writeUInt32(0)) // total size is written at the end - const int64_t afterHeaderPos = stream.pos(); + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + stream->write("CUBZH!", 6); + wrapBool(stream->writeUInt32(6)) // version + wrapBool(stream->writeUInt8(1)) // zip compression + const int64_t totalSizePos = stream->pos(); + wrapBool(stream->writeUInt32(0)) // total size is written at the end + const int64_t afterHeaderPos = stream->pos(); ThumbnailContext thumbnailCtx; thumbnailCtx.outputSize = glm::ivec2(128); const image::ImagePtr &image = createThumbnail(sceneGraph, ctx.thumbnailCreator, thumbnailCtx); if (image) { - WriteChunkStream ws(priv::CHUNK_ID_PREVIEW, stream); + WriteChunkStream ws(priv::CHUNK_ID_PREVIEW, *stream); image->writePng(ws); } { - WriteChunkStream ws(priv::CHUNK_ID_PALETTE_V6, stream); + WriteChunkStream ws(priv::CHUNK_ID_PALETTE_V6, *stream); const palette::Palette &palette = sceneGraph.firstPalette(); const uint8_t colorCount = palette.colorCount(); ws.writeUInt8(colorCount); @@ -807,7 +829,7 @@ bool CubzhFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor if (!node.isAnyModelNode()) { continue; } - WriteChunkStream ws(priv::CHUNK_ID_SHAPE_V6, stream); + WriteChunkStream ws(priv::CHUNK_ID_SHAPE_V6, *stream); { WriteSubChunkStream sub(priv::CHUNK_ID_SHAPE_ID_V6, ws); wrapBool(sub.writeUInt16(node.id())) @@ -926,13 +948,13 @@ bool CubzhFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor } } - const uint32_t totalSize = stream.size() - afterHeaderPos; - if (stream.seek(totalSizePos) == -1) { + const uint32_t totalSize = stream->size() - afterHeaderPos; + if (stream->seek(totalSizePos) == -1) { Log::error("Failed to seek to the total size position in the header"); return false; } - wrapBool(stream.writeUInt32(totalSize)) - stream.seek(0, SEEK_END); + wrapBool(stream->writeUInt32(totalSize)) + stream->seek(0, SEEK_END); return true; } diff --git a/src/modules/voxelformat/private/cubzh/CubzhFormat.h b/src/modules/voxelformat/private/cubzh/CubzhFormat.h index 5942d1e6b2..1796724fb7 100644 --- a/src/modules/voxelformat/private/cubzh/CubzhFormat.h +++ b/src/modules/voxelformat/private/cubzh/CubzhFormat.h @@ -316,7 +316,7 @@ class CubzhFormat : public PaletteFormat { bool empty() const; }; - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool loadChunkHeader(const Header &header, io::ReadStream &stream, Chunk &chunk) const; @@ -326,7 +326,7 @@ class CubzhFormat : public PaletteFormat { bool loadSkipSubChunk(const Chunk &chunk, io::ReadStream &stream) const; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; bool loadVersion6(const core::String &filename, const Header &header, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) const; @@ -346,10 +346,10 @@ class CubzhFormat : public PaletteFormat { scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) const; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; int emptyPaletteIndex() const override { diff --git a/src/modules/voxelformat/private/cubzh/PCubesFormat.cpp b/src/modules/voxelformat/private/cubzh/PCubesFormat.cpp index f397546dd0..6115e0a093 100644 --- a/src/modules/voxelformat/private/cubzh/PCubesFormat.cpp +++ b/src/modules/voxelformat/private/cubzh/PCubesFormat.cpp @@ -4,6 +4,7 @@ #include "PCubesFormat.h" #include "CubzhShared.h" +#include "core/ScopedPtr.h" #include "palette/Palette.h" #include "scenegraph/SceneGraph.h" @@ -16,18 +17,24 @@ namespace voxelformat { } bool PCubesFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); - stream.write("PARTICUBES!", 11); - wrapBool(stream.writeUInt32(6)) // version - wrapBool(stream.writeUInt8(1)) // zip compression - const int64_t totalSizePos = stream.pos(); - wrapBool(stream.writeUInt32(0)) // total size is written at the end - const int64_t afterHeaderPos = stream.pos(); + stream->write("PARTICUBES!", 11); + wrapBool(stream->writeUInt32(6)) // version + wrapBool(stream->writeUInt8(1)) // zip compression + const int64_t totalSizePos = stream->pos(); + wrapBool(stream->writeUInt32(0)) // total size is written at the end + const int64_t afterHeaderPos = stream->pos(); { - WriteChunkStream sub(priv::CHUNK_ID_PALETTE_LEGACY_V6, stream); + WriteChunkStream sub(priv::CHUNK_ID_PALETTE_LEGACY_V6, *stream); const palette::Palette &palette = node->palette(); const uint8_t colorCount = palette.colorCount(); wrapBool(sub.writeUInt8(1)) @@ -47,7 +54,7 @@ bool PCubesFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co } } { - WriteChunkStream ws(priv::CHUNK_ID_SHAPE_V6, stream); + WriteChunkStream ws(priv::CHUNK_ID_SHAPE_V6, *stream); { WriteSubChunkStream sub(priv::CHUNK_ID_SHAPE_SIZE_V6, ws); const glm::ivec3 &dimensions = node->region().getDimensionsInVoxels(); @@ -82,13 +89,13 @@ bool PCubesFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const co } } - const uint32_t totalSize = (uint32_t)(stream.size() - afterHeaderPos); - if (stream.seek(totalSizePos) == -1) { + const uint32_t totalSize = (uint32_t)(stream->size() - afterHeaderPos); + if (stream->seek(totalSizePos) == -1) { Log::error("Failed to seek to the total size position in the header"); return false; } - wrapBool(stream.writeUInt32(totalSize)) - stream.seek(0, SEEK_END); + wrapBool(stream->writeUInt32(totalSize)) + stream->seek(0, SEEK_END); return true; } diff --git a/src/modules/voxelformat/private/cubzh/PCubesFormat.h b/src/modules/voxelformat/private/cubzh/PCubesFormat.h index 9812697e9a..3893b44b43 100644 --- a/src/modules/voxelformat/private/cubzh/PCubesFormat.h +++ b/src/modules/voxelformat/private/cubzh/PCubesFormat.h @@ -14,7 +14,7 @@ namespace voxelformat { class PCubesFormat : public CubzhFormat { protected: bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/goxel/GoxFormat.cpp b/src/modules/voxelformat/private/goxel/GoxFormat.cpp index 535e969020..0889e237cf 100644 --- a/src/modules/voxelformat/private/goxel/GoxFormat.cpp +++ b/src/modules/voxelformat/private/goxel/GoxFormat.cpp @@ -6,6 +6,7 @@ #include "core/Common.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "image/Image.h" #include "io/MemoryReadStream.h" @@ -144,10 +145,15 @@ bool GoxFormat::loadChunk_DictEntry(const GoxChunk &c, io::SeekableReadStream &s return true; } -image::ImagePtr GoxFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr GoxFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } uint32_t magic; - wrapImg(stream.readUInt32(magic)) + wrapImg(stream->readUInt32(magic)) if (magic != FourCC('G', 'O', 'X', ' ')) { Log::error("Invalid magic"); @@ -155,7 +161,7 @@ image::ImagePtr GoxFormat::loadScreenshot(const core::String &filename, io::Seek } uint32_t version; - wrapImg(stream.readUInt32(version)) + wrapImg(stream->readUInt32(version)) if (version != 2) { Log::error("Unknown gox format version found: %u", version); @@ -163,15 +169,15 @@ image::ImagePtr GoxFormat::loadScreenshot(const core::String &filename, io::Seek } GoxChunk c; - while (loadChunk_Header(c, stream)) { + while (loadChunk_Header(c, *stream)) { if (c.type == FourCC('P', 'R', 'E', 'V')) { image::ImagePtr img = image::createEmptyImage(core::string::extractFilename(filename) + ".png"); - img->load(stream, c.length); + img->load(*stream, c.length); return img; } else { - stream.seek(c.length, SEEK_CUR); + stream->seek(c.length, SEEK_CUR); } - loadChunk_ValidateCRC(stream); + loadChunk_ValidateCRC(*stream); } return image::ImagePtr(); } @@ -517,10 +523,15 @@ bool GoxFormat::loadChunk_LIGH(State &state, const GoxChunk &c, io::SeekableRead return true; } -size_t GoxFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t GoxFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('G', 'O', 'X', ' ')) { Log::error("Invalid magic"); @@ -528,7 +539,7 @@ size_t GoxFormat::loadPalette(const core::String &filename, io::SeekableReadStre } State state; - wrap(stream.readInt32(state.version)) + wrap(stream->readInt32(state.version)) if (state.version > 2) { Log::error("Unknown gox format version found: %u", state.version); @@ -536,13 +547,13 @@ size_t GoxFormat::loadPalette(const core::String &filename, io::SeekableReadStre } GoxChunk c; - while (loadChunk_Header(c, stream)) { + while (loadChunk_Header(c, *stream)) { if (c.type == FourCC('B', 'L', '1', '6')) { - wrapBool(loadChunk_BL16(state, c, stream)) + wrapBool(loadChunk_BL16(state, c, *stream)) } else { - stream.seek(c.length, SEEK_CUR); + stream->seek(c.length, SEEK_CUR); } - loadChunk_ValidateCRC(stream); + loadChunk_ValidateCRC(*stream); } for (image::ImagePtr &img : state.images) { @@ -560,11 +571,16 @@ size_t GoxFormat::loadPalette(const core::String &filename, io::SeekableReadStre return palette.size(); } -bool GoxFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool GoxFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('G', 'O', 'X', ' ')) { Log::error("Invalid magic"); @@ -572,7 +588,7 @@ bool GoxFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr } State state; - wrap(stream.readInt32(state.version)) + wrap(stream->readInt32(state.version)) if (state.version > 2) { Log::error("Unknown gox format version found: %u", state.version); @@ -580,23 +596,23 @@ bool GoxFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr } GoxChunk c; - while (loadChunk_Header(c, stream)) { + while (loadChunk_Header(c, *stream)) { if (c.type == FourCC('B', 'L', '1', '6')) { - wrapBool(loadChunk_BL16(state, c, stream)) + wrapBool(loadChunk_BL16(state, c, *stream)) } else if (c.type == FourCC('L', 'A', 'Y', 'R')) { - wrapBool(loadChunk_LAYR(state, c, stream, sceneGraph, palette)) + wrapBool(loadChunk_LAYR(state, c, *stream, sceneGraph, palette)) } else if (c.type == FourCC('C', 'A', 'M', 'R')) { - wrapBool(loadChunk_CAMR(state, c, stream, sceneGraph)) + wrapBool(loadChunk_CAMR(state, c, *stream, sceneGraph)) } else if (c.type == FourCC('M', 'A', 'T', 'E')) { - wrapBool(loadChunk_MATE(state, c, stream, sceneGraph)) + wrapBool(loadChunk_MATE(state, c, *stream, sceneGraph)) } else if (c.type == FourCC('I', 'M', 'G', ' ')) { - wrapBool(loadChunk_IMG(state, c, stream, sceneGraph)) + wrapBool(loadChunk_IMG(state, c, *stream, sceneGraph)) } else if (c.type == FourCC('L', 'I', 'G', 'H')) { - wrapBool(loadChunk_LIGH(state, c, stream, sceneGraph)) + wrapBool(loadChunk_LIGH(state, c, *stream, sceneGraph)) } else { - stream.seek(c.length, SEEK_CUR); + stream->seek(c.length, SEEK_CUR); } - loadChunk_ValidateCRC(stream); + loadChunk_ValidateCRC(*stream); } return !sceneGraph.empty(); } @@ -851,17 +867,22 @@ bool GoxFormat::saveChunk_BL16(io::SeekableWriteStream &stream, const scenegraph } bool GoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { - wrapSave(stream.writeUInt32(FourCC('G', 'O', 'X', ' '))) - wrapSave(stream.writeUInt32(2)) + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + wrapSave(stream->writeUInt32(FourCC('G', 'O', 'X', ' '))) + wrapSave(stream->writeUInt32(2)) - wrapBool(saveChunk_PREV(sceneGraph, stream, ctx)) + wrapBool(saveChunk_PREV(sceneGraph, *stream, ctx)) int blocks = 0; - wrapBool(saveChunk_BL16(stream, sceneGraph, blocks)) - wrapBool(saveChunk_MATE(stream, sceneGraph)) - wrapBool(saveChunk_LAYR(stream, sceneGraph, blocks)) - wrapBool(saveChunk_CAMR(stream, sceneGraph)) - wrapBool(saveChunk_LIGH(stream)) + wrapBool(saveChunk_BL16(*stream, sceneGraph, blocks)) + wrapBool(saveChunk_MATE(*stream, sceneGraph)) + wrapBool(saveChunk_LAYR(*stream, sceneGraph, blocks)) + wrapBool(saveChunk_CAMR(*stream, sceneGraph)) + wrapBool(saveChunk_LIGH(*stream)) return true; } diff --git a/src/modules/voxelformat/private/goxel/GoxFormat.h b/src/modules/voxelformat/private/goxel/GoxFormat.h index c8d4974d26..cbbf5b2428 100644 --- a/src/modules/voxelformat/private/goxel/GoxFormat.h +++ b/src/modules/voxelformat/private/goxel/GoxFormat.h @@ -126,16 +126,16 @@ class GoxFormat : public RGBAFormat { bool saveChunk_MATE(io::SeekableWriteStream &stream, const scenegraph::SceneGraph &sceneGraph); // Write all the layers. bool saveChunk_LAYR(io::SeekableWriteStream &stream, const scenegraph::SceneGraph &sceneGraph, int numBlocks); - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/magicavoxel/VoxFormat.cpp b/src/modules/voxelformat/private/magicavoxel/VoxFormat.cpp index ba79117968..7c97ca0e5e 100644 --- a/src/modules/voxelformat/private/magicavoxel/VoxFormat.cpp +++ b/src/modules/voxelformat/private/magicavoxel/VoxFormat.cpp @@ -5,6 +5,7 @@ #include "VoxFormat.h" #include "core/GameConfig.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/Var.h" #include "scenegraph/SceneGraph.h" @@ -31,11 +32,16 @@ int VoxFormat::emptyPaletteIndex() const { return EMPTY_PALETTE; } -size_t VoxFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t VoxFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { - const size_t size = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + const size_t size = stream->size(); uint8_t *buffer = (uint8_t *)core_malloc(size); - if (stream.read(buffer, size) == -1) { + if (stream->read(buffer, size) == -1) { core_free(buffer); return 0; } @@ -154,11 +160,16 @@ bool VoxFormat::loadGroup(const ogt_vox_scene *scene, uint32_t ogt_groupIdx, sce return true; } -bool VoxFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool VoxFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { - const size_t size = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + const size_t size = stream->size(); uint8_t *buffer = (uint8_t *)core_malloc(size); - if (stream.read(buffer, size) == -1) { + if (stream->read(buffer, size) == -1) { core_free(buffer); return false; } @@ -367,7 +378,12 @@ void VoxFormat::saveNode(const scenegraph::SceneGraph &sceneGraph, scenegraph::S } bool VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &savectx) { + const io::ArchivePtr &archive, const SaveContext &savectx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } MVSceneContext ctx; const scenegraph::SceneGraphNode &root = sceneGraph.root(); saveNode(sceneGraph, sceneGraph.node(root.id()), ctx, k_invalid_group_index, 0); @@ -493,7 +509,7 @@ bool VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: Log::error("Failed to write the scene"); return false; } - if (stream.write(buffer, buffersize) == -1) { + if (stream->write(buffer, buffersize) == -1) { Log::error("Failed to write to the stream"); return false; } diff --git a/src/modules/voxelformat/private/magicavoxel/VoxFormat.h b/src/modules/voxelformat/private/magicavoxel/VoxFormat.h index 59e1474b4d..016b5d7ac6 100644 --- a/src/modules/voxelformat/private/magicavoxel/VoxFormat.h +++ b/src/modules/voxelformat/private/magicavoxel/VoxFormat.h @@ -38,17 +38,17 @@ class VoxFormat : public PaletteFormat { bool loadGroup(const ogt_vox_scene *scene, uint32_t ogt_parentGroupIdx, scenegraph::SceneGraph &sceneGraph, int parent, core::DynamicArray &models, core::Set &addedInstances, const palette::Palette &palette); - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; void saveNode(const scenegraph::SceneGraph &sceneGraph, scenegraph::SceneGraphNode &node, MVSceneContext &ctx, uint32_t parentGroupIdx, uint32_t layerIdx); bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: VoxFormat(); - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/magicavoxel/XRawFormat.cpp b/src/modules/voxelformat/private/magicavoxel/XRawFormat.cpp index 68b375d2ee..2160685544 100644 --- a/src/modules/voxelformat/private/magicavoxel/XRawFormat.cpp +++ b/src/modules/voxelformat/private/magicavoxel/XRawFormat.cpp @@ -5,6 +5,8 @@ #include "XRawFormat.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" +#include "io/Stream.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" #include "voxel/MaterialColor.h" @@ -69,10 +71,15 @@ static int readVoxel(io::SeekableReadStream &stream, const palette::Palette &pal return -1; } -size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t XRawFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('X', 'R', 'A', 'W')) { Log::error("Could not load xraw file: Invalid magic number"); @@ -80,22 +87,22 @@ size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStr } ColorChannelDataType colorChannelDataType; - wrap(stream.readUInt8((uint8_t &)colorChannelDataType)) + wrap(stream->readUInt8((uint8_t &)colorChannelDataType)) ColorChannelCount colorChannelCount; - wrap(stream.readUInt8((uint8_t &)colorChannelCount)) + wrap(stream->readUInt8((uint8_t &)colorChannelCount)) uint8_t bitsPerColorChannel; - wrap(stream.readUInt8(bitsPerColorChannel)) + wrap(stream->readUInt8(bitsPerColorChannel)) uint8_t bitsPerIndex; - wrap(stream.readUInt8(bitsPerIndex)) + wrap(stream->readUInt8(bitsPerIndex)) // address = x + y * width + z * (width * height) uint32_t width, depth, height; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); @@ -104,7 +111,7 @@ size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStr // 256, 32768 uint32_t paletteSize; - wrap(stream.readUInt32(paletteSize)) + wrap(stream->readUInt32(paletteSize)) if (colorChannelDataType != ColorChannelDataType::TypeUnsignedInteger) { Log::error("Could not load xraw file: Unsupported color channel data type: %i", (int)colorChannelDataType); @@ -127,7 +134,7 @@ size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStr return 0; } - if (stream.skip(width * height * depth * bitsPerIndex / 8u) == -1) { + if (stream->skip(width * height * depth * bitsPerIndex / 8u) == -1) { Log::error("Could not load xraw file: Not enough data in stream"); return 0; } @@ -135,7 +142,7 @@ size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStr // palette buffer for (uint32_t i = 0u; i < paletteSize; ++i) { - const core::RGBA rgba = readColor(stream); + const core::RGBA rgba = readColor(*stream); const core::RGBA color = flattenRGB(rgba); palette.tryAdd(color, false); } @@ -143,11 +150,16 @@ size_t XRawFormat::loadPalette(const core::String &filename, io::SeekableReadStr return palette.size(); } -bool XRawFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool XRawFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('X', 'R', 'A', 'W')) { Log::error("Could not load xraw file: Invalid magic number"); @@ -155,34 +167,34 @@ bool XRawFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadSt } ColorChannelDataType colorChannelDataType; - wrap(stream.readUInt8((uint8_t &)colorChannelDataType)) + wrap(stream->readUInt8((uint8_t &)colorChannelDataType)) if (colorChannelDataType != ColorChannelDataType::TypeUnsignedInteger) { Log::error("Could not load xraw file: Unsupported color channel data type: %i", (int)colorChannelDataType); return 0; } ColorChannelCount colorChannelCount; - wrap(stream.readUInt8((uint8_t &)colorChannelCount)) + wrap(stream->readUInt8((uint8_t &)colorChannelCount)) if (colorChannelCount != ColorChannelCount::RGBA) { Log::error("Could not load xraw file: Unsupported color channel count: %i", (int)colorChannelCount); return 0; } uint8_t bitsPerColorChannel; - wrap(stream.readUInt8(bitsPerColorChannel)) + wrap(stream->readUInt8(bitsPerColorChannel)) if (bitsPerColorChannel != 8) { Log::error("Could not load xraw file: Unsupported bits per color channel: %i", bitsPerColorChannel); return 0; } uint8_t bitsPerIndex; - wrap(stream.readUInt8(bitsPerIndex)) + wrap(stream->readUInt8(bitsPerIndex)) // address = x + y * width + z * (width * height) uint32_t width, depth, height; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); @@ -191,7 +203,7 @@ bool XRawFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadSt // 256, 32768 uint32_t paletteSize; - wrap(stream.readUInt32(paletteSize)) + wrap(stream->readUInt32(paletteSize)) // end of header @@ -204,7 +216,7 @@ bool XRawFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadSt for (uint32_t h = 0u; h < height; ++h) { for (uint32_t d = 0u; d < depth; ++d) { for (uint32_t w = 0u; w < width; ++w) { - const int index = readVoxel(stream, palette, paletteSize, bitsPerIndex); + const int index = readVoxel(*stream, palette, paletteSize, bitsPerIndex); if (index == 0 || index == ~(uint16_t)0) { continue; } @@ -225,24 +237,29 @@ bool XRawFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadSt #undef wrap bool XRawFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); const voxel::Region ®ion = node->region(); - wrapBool(stream.writeUInt32(FourCC('X', 'R', 'A', 'W'))) + wrapBool(stream->writeUInt32(FourCC('X', 'R', 'A', 'W'))) - wrapBool(stream.writeUInt8((uint8_t)ColorChannelDataType::TypeUnsignedInteger)) - wrapBool(stream.writeUInt8((uint8_t)ColorChannelCount::RGBA)) - wrapBool(stream.writeUInt8(8)) // bitsPerColorChannel - wrapBool(stream.writeUInt8(8)) // bitsPerIndex + wrapBool(stream->writeUInt8((uint8_t)ColorChannelDataType::TypeUnsignedInteger)) + wrapBool(stream->writeUInt8((uint8_t)ColorChannelCount::RGBA)) + wrapBool(stream->writeUInt8(8)) // bitsPerColorChannel + wrapBool(stream->writeUInt8(8)) // bitsPerIndex // we have to flip depth with height for our own coordinate system - wrapBool(stream.writeUInt32(region.getWidthInVoxels())) - wrapBool(stream.writeUInt32(region.getDepthInVoxels())) - wrapBool(stream.writeUInt32(region.getHeightInVoxels())) + wrapBool(stream->writeUInt32(region.getWidthInVoxels())) + wrapBool(stream->writeUInt32(region.getDepthInVoxels())) + wrapBool(stream->writeUInt32(region.getHeightInVoxels())) - wrapBool(stream.writeUInt32(palette::PaletteMaxColors)) + wrapBool(stream->writeUInt32(palette::PaletteMaxColors)) palette::Palette palette = node->palette(); uint8_t replacement = palette.findReplacement(emptyPaletteIndex()); @@ -255,23 +272,23 @@ bool XRawFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core *node->volume(), [&stream, replacement](int, int, int, const voxel::Voxel &voxel) { if (voxel.getMaterial() == voxel::VoxelType::Air) { - wrapBool(stream.writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) } else if (voxel.getColor() == 0) { - wrapBool(stream.writeUInt8(replacement)) + wrapBool(stream->writeUInt8(replacement)) } else { - wrapBool(stream.writeUInt8(voxel.getColor())) + wrapBool(stream->writeUInt8(voxel.getColor())) } return true; }, voxelutil::VisitAll(), voxelutil::VisitorOrder::YZmX); - wrapBool(stream.writeUInt32(0)) // first palette entry is always 0 - empty voxel + wrapBool(stream->writeUInt32(0)) // first palette entry is always 0 - empty voxel for (int i = 1; i < palette.colorCount(); ++i) { const core::RGBA rgba = palette.color(i); - wrapBool(stream.writeUInt32(rgba.rgba)) + wrapBool(stream->writeUInt32(rgba.rgba)) } for (int i = palette.colorCount(); i < palette::PaletteMaxColors; ++i) { - wrapBool(stream.writeUInt32(0)) + wrapBool(stream->writeUInt32(0)) } return true; diff --git a/src/modules/voxelformat/private/magicavoxel/XRawFormat.h b/src/modules/voxelformat/private/magicavoxel/XRawFormat.h index ca3c1bc192..b9d71ab118 100644 --- a/src/modules/voxelformat/private/magicavoxel/XRawFormat.h +++ b/src/modules/voxelformat/private/magicavoxel/XRawFormat.h @@ -13,16 +13,16 @@ namespace voxelformat { */ class XRawFormat : public RGBASinglePaletteFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; virtual int emptyPaletteIndex() { return 0; } public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/mesh/FBXFormat.cpp b/src/modules/voxelformat/private/mesh/FBXFormat.cpp index 97598297b9..f643789b6d 100644 --- a/src/modules/voxelformat/private/mesh/FBXFormat.cpp +++ b/src/modules/voxelformat/private/mesh/FBXFormat.cpp @@ -6,9 +6,11 @@ #include "app/App.h" #include "core/Color.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StandardLib.h" #include "core/String.h" #include "engine-config.h" +#include "io/Archive.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" #include "voxel/Mesh.h" @@ -27,14 +29,20 @@ namespace voxelformat { } bool FBXFormat::saveMeshes(const core::Map &, const scenegraph::SceneGraph &sceneGraph, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) { - return saveMeshesAscii(meshes, filename, stream, scale, quad, withColor, withTexCoords, sceneGraph); + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + return saveMeshesAscii(meshes, filename, *stream, scale, quad, withColor, withTexCoords, sceneGraph); } +// TODO: unused class FBXScopedHeader { private: - io::SeekableWriteStream &_stream; + io::SeekableWriteStream *_stream; /** * @brief EndOffset is the distance from the beginning of the file to the end of the node record (i.e. the first * byte of whatever comes next). This can be used to easily skip over unknown or not required records. @@ -42,16 +50,16 @@ class FBXScopedHeader { int64_t _endOffsetPos; public: - FBXScopedHeader(io::SeekableWriteStream &stream) : _stream(stream) { - _endOffsetPos = stream.pos(); - stream.writeUInt32(0u); + FBXScopedHeader(io::SeekableWriteStream *stream) : _stream(stream) { + _endOffsetPos = stream->pos(); + stream->writeUInt32(0u); } ~FBXScopedHeader() { - const int64_t currentPos = _stream.pos(); - _stream.seek(_endOffsetPos); - _stream.writeUInt32(currentPos); - _stream.seek(currentPos); + const int64_t currentPos = _stream->pos(); + _stream->seek(_endOffsetPos); + _stream->writeUInt32(currentPos); + _stream->seek(currentPos); } }; @@ -462,11 +470,16 @@ int FBXFormat::addNode_r(const ufbx_scene *scene, const ufbx_node *node, const c return nodeId; } -bool FBXFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool FBXFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } ufbx_stream ufbxstream; core_memset(&ufbxstream, 0, sizeof(ufbxstream)); - ufbxstream.user = &stream; + ufbxstream.user = stream; ufbxstream.read_fn = priv::_ufbx_read_fn; ufbxstream.skip_fn = priv::_ufbx_skip_fn; diff --git a/src/modules/voxelformat/private/mesh/FBXFormat.h b/src/modules/voxelformat/private/mesh/FBXFormat.h index 59fc155f52..1c629fc897 100644 --- a/src/modules/voxelformat/private/mesh/FBXFormat.h +++ b/src/modules/voxelformat/private/mesh/FBXFormat.h @@ -28,7 +28,7 @@ class FBXFormat : public MeshFormat { bool saveMeshesAscii(const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords, const scenegraph::SceneGraph &sceneGraph); - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; int addNode_r(const ufbx_scene *scene, const ufbx_node *node, const core::String &filename, scenegraph::SceneGraph &sceneGraph, const core::StringMap &textures, @@ -41,7 +41,7 @@ class FBXFormat : public MeshFormat { public: bool saveMeshes(const core::Map &meshIdxNodeMap, const scenegraph::SceneGraph &sceneGraph, - const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, + const Meshes &meshes, const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override; }; diff --git a/src/modules/voxelformat/private/mesh/GLTFFormat.cpp b/src/modules/voxelformat/private/mesh/GLTFFormat.cpp index 1d43ce6da0..307525da5b 100644 --- a/src/modules/voxelformat/private/mesh/GLTFFormat.cpp +++ b/src/modules/voxelformat/private/mesh/GLTFFormat.cpp @@ -9,6 +9,7 @@ #include "core/GameConfig.h" #include "core/Log.h" #include "core/RGBA.h" +#include "core/ScopedPtr.h" #include "core/String.h" #include "core/StringUtil.h" #include "core/Var.h" @@ -664,8 +665,13 @@ void GLTFFormat::generateMaterials(bool withTexCoords, tinygltf::Model &gltfMode } bool GLTFFormat::saveMeshes(const core::Map &meshIdxNodeMap, const scenegraph::SceneGraph &sceneGraph, - const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, + const Meshes &meshes, const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } const core::String &ext = core::string::extractExtension(filename); const bool writeBinary = ext == "glb"; @@ -784,7 +790,7 @@ bool GLTFFormat::saveMeshes(const core::Map &meshIdxNodeMap, const sce gltfModel.cameras.push_back(gltfCamera); } - io::StdOStreamBuf buf(stream); + io::StdOStreamBuf buf(*stream); std::ostream gltfStream(&buf); if (!gltf.WriteGltfSceneToStream(&gltfModel, gltfStream, false, writeBinary)) { Log::error("Could not save to file"); @@ -1630,13 +1636,18 @@ bool GLTFFormat::loadNode_r(const core::String &filename, scenegraph::SceneGraph return true; } -bool GLTFFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool GLTFFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } uint32_t magic; - stream.peekUInt32(magic); - const int64_t size = stream.size(); + stream->peekUInt32(magic); + const int64_t size = stream->size(); uint8_t *data = (uint8_t *)core_malloc(size); - if (stream.read(data, size) == -1) { + if (stream->read(data, size) == -1) { Log::error("Failed to read gltf stream for %s of size %i", filename.c_str(), (int)size); core_free(data); return false; diff --git a/src/modules/voxelformat/private/mesh/GLTFFormat.h b/src/modules/voxelformat/private/mesh/GLTFFormat.h index 4ce0c13828..b0c62cade5 100644 --- a/src/modules/voxelformat/private/mesh/GLTFFormat.h +++ b/src/modules/voxelformat/private/mesh/GLTFFormat.h @@ -140,12 +140,12 @@ class GLTFFormat : public MeshFormat { size_t accessorSize(const tinygltf::Accessor &gltfAccessor) const; const tinygltf::Accessor *getAccessor(const tinygltf::Model &gltfModel, int id) const; - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; public: bool saveMeshes(const core::Map &meshIdxNodeMap, const scenegraph::SceneGraph &sceneGraph, - const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, + const Meshes &meshes, const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override; }; diff --git a/src/modules/voxelformat/private/mesh/MeshFormat.cpp b/src/modules/voxelformat/private/mesh/MeshFormat.cpp index 35da46b005..bcd850bbbf 100644 --- a/src/modules/voxelformat/private/mesh/MeshFormat.cpp +++ b/src/modules/voxelformat/private/mesh/MeshFormat.cpp @@ -16,6 +16,7 @@ #include "core/collection/DynamicMap.h" #include "core/collection/Map.h" #include "core/concurrent/Lock.h" +#include "io/Archive.h" #include "io/FormatDescription.h" #include "palette/PaletteLookup.h" #include "scenegraph/SceneGraph.h" @@ -472,14 +473,14 @@ MeshFormat::MeshExt::MeshExt(voxel::ChunkMesh *_mesh, const scenegraph::SceneGra pivot(node.pivot()), nodeId(node.id()) { } -bool MeshFormat::loadGroups(const core::String &filename, io::SeekableReadStream &file, +bool MeshFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { - const bool retVal = voxelizeGroups(filename, file, sceneGraph, ctx); + const bool retVal = voxelizeGroups(filename, archive, sceneGraph, ctx); sceneGraph.updateTransforms(); return retVal; } -bool MeshFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &, scenegraph::SceneGraph &, +bool MeshFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &, scenegraph::SceneGraph &, const LoadContext &) { Log::debug("Mesh %s can't get voxelized yet", filename.c_str()); return false; @@ -554,7 +555,7 @@ core::String MeshFormat::lookupTexture(const core::String &meshFilename, const c } bool MeshFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { const bool mergeQuads = core::Var::getSafe(cfg::VoxformatMergequads)->boolVal(); const bool reuseVertices = core::Var::getSafe(cfg::VoxformatReusevertices)->boolVal(); const bool ambientOcclusion = core::Var::getSafe(cfg::VoxformatAmbientocclusion)->boolVal(); @@ -619,7 +620,7 @@ bool MeshFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core state = false; } else { Log::debug("Save meshes"); - state = saveMeshes(meshIdxNodeMap, sceneGraph, nonEmptyMeshes, filename, stream, {1.0f, 1.0f, 1.0f}, + state = saveMeshes(meshIdxNodeMap, sceneGraph, nonEmptyMeshes, filename, archive, {1.0f, 1.0f, 1.0f}, type == voxel::SurfaceExtractionType::Cubic ? quads : false, withColor, withTexCoords); } for (MeshExt &meshext : meshes) { diff --git a/src/modules/voxelformat/private/mesh/MeshFormat.h b/src/modules/voxelformat/private/mesh/MeshFormat.h index 9ae0a66827..6b95c1a11c 100644 --- a/src/modules/voxelformat/private/mesh/MeshFormat.h +++ b/src/modules/voxelformat/private/mesh/MeshFormat.h @@ -4,6 +4,7 @@ #pragma once +#include "io/Archive.h" #include "voxelformat/Format.h" #include "core/collection/DynamicArray.h" #include "core/collection/Map.h" @@ -53,7 +54,7 @@ class MeshFormat : public Format { }; using Meshes = core::DynamicArray; virtual bool saveMeshes(const core::Map &meshIdxNodeMap, const scenegraph::SceneGraph &sceneGraph, - const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, + const Meshes &meshes, const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale = glm::vec3(1.0f), bool quad = false, bool withColor = true, bool withTexCoords = true) = 0; @@ -66,7 +67,7 @@ class MeshFormat : public Format { * Convert your input mesh into @c voxelformat::TexturedTri instances and use the methods of this class to help voxelizing those. * @see voxelizeNode() */ - virtual bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &file, + virtual bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx); /** @@ -142,10 +143,10 @@ class MeshFormat : public Format { static core::String lookupTexture(const core::String &meshFilename, const core::String &in); MeshFormat(); - bool loadGroups(const core::String &filename, io::SeekableReadStream &file, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/mesh/OBJFormat.cpp b/src/modules/voxelformat/private/mesh/OBJFormat.cpp index 882bac0d17..49118b97ad 100644 --- a/src/modules/voxelformat/private/mesh/OBJFormat.cpp +++ b/src/modules/voxelformat/private/mesh/OBJFormat.cpp @@ -3,17 +3,15 @@ */ #include "OBJFormat.h" -#include "app/App.h" #include "core/Color.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/collection/DynamicArray.h" #include "core/collection/StringMap.h" #include "engine-config.h" #include "image/Image.h" -#include "io/File.h" -#include "io/FileStream.h" -#include "io/Filesystem.h" +#include "io/Archive.h" #include "io/StdStreamBuf.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -57,25 +55,25 @@ bool OBJFormat::writeMtlFile(io::SeekableWriteStream &stream, const core::String } bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneGraph &sceneGraph, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) { - stream.writeStringFormat(false, "# version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n"); - wrapBool(stream.writeStringFormat(false, "\n")) - wrapBool(stream.writeStringFormat(false, "g Model\n")) + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + stream->writeStringFormat(false, "# version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n"); + wrapBool(stream->writeStringFormat(false, "\n")) + wrapBool(stream->writeStringFormat(false, "g Model\n")) Log::debug("Exporting %i layers", (int)meshes.size()); const core::String &mtlname = core::string::replaceExtension(filename, "mtl"); Log::debug("Use mtl file: %s", mtlname.c_str()); - const io::FilePtr &file = io::filesystem()->open(mtlname, io::FileMode::SysWrite); - if (!file->validHandle()) { - Log::error("Failed to create mtl file at %s", file->name().c_str()); - return false; - } - io::FileStream matlstream(file); - wrapBool(matlstream.writeString("# version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n", false)) - wrapBool(matlstream.writeString("\n", false)) + core::ScopedPtr matlstream(archive->writeStream(mtlname)); + wrapBool(matlstream->writeString("# version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n", false)) + wrapBool(matlstream->writeString("\n", false)) core::Map paletteMaterialIndices((int)sceneGraph.size()); @@ -109,9 +107,9 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG if (objectName[0] == '\0') { objectName = "Noname"; } - stream.writeStringFormat(false, "o %s\n", objectName); - stream.writeStringFormat(false, "mtllib %s\n", core::string::extractFilenameWithExtension(mtlname).c_str()); - if (!stream.writeStringFormat(false, "usemtl %s\n", hashId.c_str())) { + stream->writeStringFormat(false, "o %s\n", objectName); + stream->writeStringFormat(false, "mtllib %s\n", core::string::extractFilenameWithExtension(mtlname).c_str()); + if (!stream->writeStringFormat(false, "usemtl %s\n", hashId.c_str())) { Log::error("Failed to write obj usemtl %s\n", hashId.c_str()); return false; } @@ -126,17 +124,17 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG pos = v.position; } pos *= scale; - stream.writeStringFormat(false, "v %.04f %.04f %.04f", pos.x, pos.y, pos.z); + stream->writeStringFormat(false, "v %.04f %.04f %.04f", pos.x, pos.y, pos.z); if (withColor) { const glm::vec4 &color = core::Color::fromRGBA(palette.color(v.colorIndex)); - stream.writeStringFormat(false, " %.03f %.03f %.03f", color.r, color.g, color.b); + stream->writeStringFormat(false, " %.03f %.03f %.03f", color.r, color.g, color.b); } - wrapBool(stream.writeStringFormat(false, "\n")) + wrapBool(stream->writeStringFormat(false, "\n")) } if (withNormals) { for (int i = 0; i < nv; ++i) { const glm::vec3 &norm = normals[i]; - stream.writeStringFormat(false, "vn %.04f %.04f %.04f\n", norm.x, norm.y, norm.z); + stream->writeStringFormat(false, "vn %.04f %.04f %.04f\n", norm.x, norm.y, norm.z); } } @@ -145,10 +143,10 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG for (int i = 0; i < ni; i += 6) { const voxel::VoxelVertex &v = vertices[indices[i]]; const glm::vec2 &uv = paletteUV(v.colorIndex); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); } } @@ -160,19 +158,19 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG const uint32_t four = idxOffset + indices[i + 5] + 1; if (withTexCoords) { if (withNormals) { - stream.writeStringFormat(false, "f %i/%i/%i %i/%i/%i %i/%i/%i %i/%i/%i\n", (int)one, + stream->writeStringFormat(false, "f %i/%i/%i %i/%i/%i %i/%i/%i %i/%i/%i\n", (int)one, uvi + 1, (int)one, (int)two, uvi + 2, (int)two, (int)three, uvi + 3, (int)three, (int)four, uvi + 4, (int)four); } else { - stream.writeStringFormat(false, "f %i/%i %i/%i %i/%i %i/%i\n", (int)one, uvi + 1, (int)two, + stream->writeStringFormat(false, "f %i/%i %i/%i %i/%i %i/%i\n", (int)one, uvi + 1, (int)two, uvi + 2, (int)three, uvi + 3, (int)four, uvi + 4); } } else { if (withNormals) { - stream.writeStringFormat(false, "f %i//%i %i//%i %i//%i %i//%i\n", (int)one, (int)two, + stream->writeStringFormat(false, "f %i//%i %i//%i %i//%i %i//%i\n", (int)one, (int)two, (int)three, (int)four, (int)one, (int)two, (int)three, (int)four); } else { - stream.writeStringFormat(false, "f %i %i %i %i\n", (int)one, (int)two, (int)three, + stream->writeStringFormat(false, "f %i %i %i %i\n", (int)one, (int)two, (int)three, (int)four); } } @@ -183,9 +181,9 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG for (int i = 0; i < ni; i += 3) { const voxel::VoxelVertex &v = vertices[indices[i]]; const glm::vec2 &uv = paletteUV(v.colorIndex); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); - stream.writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); + stream->writeStringFormat(false, "vt %f %f\n", uv.x, uv.y); } } @@ -195,20 +193,20 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG const uint32_t three = idxOffset + indices[i + 2] + 1; if (withTexCoords) { if (withNormals) { - stream.writeStringFormat(false, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", (int)one, + stream->writeStringFormat(false, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", (int)one, texcoordOffset + i + 1, (int)one, (int)two, texcoordOffset + i + 2, (int)two, (int)three, texcoordOffset + i + 3, (int)three); } else { - stream.writeStringFormat(false, "f %i/%i %i/%i %i/%i\n", (int)one, texcoordOffset + i + 1, + stream->writeStringFormat(false, "f %i/%i %i/%i %i/%i\n", (int)one, texcoordOffset + i + 1, (int)two, texcoordOffset + i + 2, (int)three, texcoordOffset + i + 3); } } else { if (withNormals) { - stream.writeStringFormat(false, "f %i//%i %i//%i %i//%i\n", (int)one, (int)two, (int)three, + stream->writeStringFormat(false, "f %i//%i %i//%i %i//%i\n", (int)one, (int)two, (int)three, (int)one, (int)two, (int)three); } else { - stream.writeStringFormat(false, "f %i %i %i\n", (int)one, (int)two, (int)three); + stream->writeStringFormat(false, "f %i %i %i\n", (int)one, (int)two, (int)three); } } } @@ -222,7 +220,7 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG palettename.append(".png"); paletteMaterialIndices.put(palette.hash(), 1); const core::String &mapKd = core::string::extractFilenameWithExtension(palettename); - if (!writeMtlFile(matlstream, hashId, mapKd)) { + if (!writeMtlFile(*matlstream, hashId, mapKd)) { return false; } if (!palette.save(palettename.c_str())) { @@ -236,15 +234,20 @@ bool OBJFormat::saveMeshes(const core::Map &, const scenegraph::SceneG #undef wrapBool -bool OBJFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool OBJFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string warn; std::string err; const core::String &mtlbasedir = core::string::extractPath(filename); - io::StdIStreamBuf stdStreamBuf(stream); + io::StdIStreamBuf stdStreamBuf(*stream); std::istream inputStream(&stdStreamBuf); tinyobj::MaterialFileReader matFileReader(mtlbasedir.c_str()); Log::debug("Load obj %s", filename.c_str()); diff --git a/src/modules/voxelformat/private/mesh/OBJFormat.h b/src/modules/voxelformat/private/mesh/OBJFormat.h index 08cf271f00..0d7fd70050 100644 --- a/src/modules/voxelformat/private/mesh/OBJFormat.h +++ b/src/modules/voxelformat/private/mesh/OBJFormat.h @@ -25,12 +25,12 @@ class OBJFormat : public MeshFormat { private: bool writeMtlFile(io::SeekableWriteStream &stream, const core::String &mtlId, const core::String &mapKd) const; protected: - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; public: bool saveMeshes(const core::Map &, const scenegraph::SceneGraph &, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, bool quad, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/mesh/PLYFormat.cpp b/src/modules/voxelformat/private/mesh/PLYFormat.cpp index 260dd03dab..1163435336 100644 --- a/src/modules/voxelformat/private/mesh/PLYFormat.cpp +++ b/src/modules/voxelformat/private/mesh/PLYFormat.cpp @@ -6,10 +6,12 @@ #include "core/Color.h" #include "core/GameConfig.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/Var.h" #include "core/collection/DynamicArray.h" #include "engine-config.h" +#include "io/Archive.h" #include "io/EndianStreamReadWrapper.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -767,17 +769,22 @@ bool PLYFormat::parseMesh(const core::String &filename, io::SeekableReadStream & return voxelizeNode(filename, sceneGraph, tris); } -bool PLYFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool PLYFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } core::String line; - wrapBool(stream.readLine(line)) + wrapBool(stream->readLine(line)) if (line != "ply") { Log::error("Invalid ply header"); return false; } Header header; - if (!parseHeader(stream, header)) { + if (!parseHeader(*stream, header)) { return false; } @@ -785,18 +792,23 @@ bool PLYFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStr // element is available too, this is a mesh auto predicate = [](const Element &e) { return e.name == "face"; }; if (core::find_if(header.elements.begin(), header.elements.end(), predicate) == header.elements.end()) { - return parsePointCloud(filename, stream, sceneGraph, ctx, header); + return parsePointCloud(filename, *stream, sceneGraph, ctx, header); } - return parseMesh(filename, stream, sceneGraph, ctx, header); + return parseMesh(filename, *stream, sceneGraph, ctx, header); } #undef wrapBool #undef wrap bool PLYFormat::saveMeshes(const core::Map &, const scenegraph::SceneGraph &sceneGraph, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } int elements = 0; int indices = 0; for (const auto &meshExt : meshes) { @@ -815,23 +827,23 @@ bool PLYFormat::saveMeshes(const core::Map &, const scenegraph::SceneG } const core::String paletteName = core::string::replaceExtension(voxel::getPalette().name(), "png"); - stream.writeStringFormat(false, "ply\nformat ascii 1.0\n"); - stream.writeStringFormat(false, "comment version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n"); - stream.writeStringFormat(false, "comment TextureFile %s\n", paletteName.c_str()); - - stream.writeStringFormat(false, "element vertex %i\n", elements); - stream.writeStringFormat(false, "property float x\n"); - stream.writeStringFormat(false, "property float z\n"); - stream.writeStringFormat(false, "property float y\n"); + stream->writeStringFormat(false, "ply\nformat ascii 1.0\n"); + stream->writeStringFormat(false, "comment version " PROJECT_VERSION " github.com/vengi-voxel/vengi\n"); + stream->writeStringFormat(false, "comment TextureFile %s\n", paletteName.c_str()); + + stream->writeStringFormat(false, "element vertex %i\n", elements); + stream->writeStringFormat(false, "property float x\n"); + stream->writeStringFormat(false, "property float z\n"); + stream->writeStringFormat(false, "property float y\n"); if (withTexCoords) { - stream.writeStringFormat(false, "property float s\n"); - stream.writeStringFormat(false, "property float t\n"); + stream->writeStringFormat(false, "property float s\n"); + stream->writeStringFormat(false, "property float t\n"); } if (withColor) { - stream.writeStringFormat(false, "property uchar red\n"); - stream.writeStringFormat(false, "property uchar green\n"); - stream.writeStringFormat(false, "property uchar blue\n"); - stream.writeStringFormat(false, "property uchar alpha\n"); + stream->writeStringFormat(false, "property uchar red\n"); + stream->writeStringFormat(false, "property uchar green\n"); + stream->writeStringFormat(false, "property uchar blue\n"); + stream->writeStringFormat(false, "property uchar alpha\n"); } int faces; @@ -841,9 +853,9 @@ bool PLYFormat::saveMeshes(const core::Map &, const scenegraph::SceneG faces = indices / 3; } - stream.writeStringFormat(false, "element face %i\n", faces); - stream.writeStringFormat(false, "property list uchar uint vertex_indices\n"); - stream.writeStringFormat(false, "end_header\n"); + stream->writeStringFormat(false, "element face %i\n", faces); + stream->writeStringFormat(false, "property list uchar uint vertex_indices\n"); + stream->writeStringFormat(false, "end_header\n"); for (const auto &meshExt : meshes) { for (int i = 0; i < voxel::ChunkMesh::Meshes; ++i) { @@ -867,16 +879,16 @@ bool PLYFormat::saveMeshes(const core::Map &, const scenegraph::SceneG pos = v.position; } pos *= scale; - stream.writeStringFormat(false, "%f %f %f", pos.x, pos.y, pos.z); + stream->writeStringFormat(false, "%f %f %f", pos.x, pos.y, pos.z); if (withTexCoords) { const glm::vec2 &uv = paletteUV(v.colorIndex); - stream.writeStringFormat(false, " %f %f", uv.x, uv.y); + stream->writeStringFormat(false, " %f %f", uv.x, uv.y); } if (withColor) { const core::RGBA color = palette.color(v.colorIndex); - stream.writeStringFormat(false, " %u %u %u %u", color.r, color.g, color.b, color.a); + stream->writeStringFormat(false, " %u %u %u %u", color.r, color.g, color.b, color.a); } - stream.writeStringFormat(false, "\n"); + stream->writeStringFormat(false, "\n"); } } } @@ -901,14 +913,14 @@ bool PLYFormat::saveMeshes(const core::Map &, const scenegraph::SceneG const uint32_t two = idxOffset + indices[j + 1]; const uint32_t three = idxOffset + indices[j + 2]; const uint32_t four = idxOffset + indices[j + 5]; - stream.writeStringFormat(false, "4 %i %i %i %i\n", (int)one, (int)two, (int)three, (int)four); + stream->writeStringFormat(false, "4 %i %i %i %i\n", (int)one, (int)two, (int)three, (int)four); } } else { for (int j = 0; j < ni; j += 3) { const uint32_t one = idxOffset + indices[j + 0]; const uint32_t two = idxOffset + indices[j + 1]; const uint32_t three = idxOffset + indices[j + 2]; - stream.writeStringFormat(false, "3 %i %i %i\n", (int)one, (int)two, (int)three); + stream->writeStringFormat(false, "3 %i %i %i\n", (int)one, (int)two, (int)three); } } idxOffset += nv; diff --git a/src/modules/voxelformat/private/mesh/PLYFormat.h b/src/modules/voxelformat/private/mesh/PLYFormat.h index 5759939f7d..172e091536 100644 --- a/src/modules/voxelformat/private/mesh/PLYFormat.h +++ b/src/modules/voxelformat/private/mesh/PLYFormat.h @@ -113,12 +113,12 @@ class PLYFormat : public MeshFormat { bool parseMesh(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx, const Header &header); - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; public: bool saveMeshes(const core::Map &, const scenegraph::SceneGraph &, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, bool quad, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/mesh/STLFormat.cpp b/src/modules/voxelformat/private/mesh/STLFormat.cpp index eb87b8666d..81903818a0 100644 --- a/src/modules/voxelformat/private/mesh/STLFormat.cpp +++ b/src/modules/voxelformat/private/mesh/STLFormat.cpp @@ -6,7 +6,9 @@ #include "core/Color.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" +#include "io/Archive.h" #include "scenegraph/SceneGraph.h" #include "voxel/Mesh.h" @@ -114,22 +116,27 @@ bool STLFormat::parseBinary(io::SeekableReadStream &stream, TriCollection &tris) return true; } -bool STLFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool STLFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } uint32_t magic; - wrap(stream.readUInt32(magic)); + wrap(stream->readUInt32(magic)); const bool ascii = FourCC('s', 'o', 'l', 'i') == magic; TriCollection tris; if (ascii) { Log::debug("found ascii format"); - if (!parseAscii(stream, tris)) { + if (!parseAscii(*stream, tris)) { Log::error("Failed to parse ascii stl file %s", filename.c_str()); return false; } } else { Log::debug("found binary format"); - if (!parseBinary(stream, tris)) { + if (!parseBinary(*stream, tris)) { Log::error("Failed to parse binary stl file %s", filename.c_str()); return false; } @@ -162,14 +169,19 @@ bool STLFormat::writeVertex(io::SeekableWriteStream &stream, const MeshExt &mesh } bool STLFormat::saveMeshes(const core::Map &, const scenegraph::SceneGraph &sceneGraph, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) { - stream.writeStringFormat(false, "github.com/vengi-voxel/vengi"); - const size_t delta = priv::BinaryHeaderSize - stream.pos(); + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return 0; + } + stream->writeStringFormat(false, "github.com/vengi-voxel/vengi"); + const size_t delta = priv::BinaryHeaderSize - stream->pos(); for (size_t i = 0; i < delta; ++i) { - stream.writeUInt8(0); + stream->writeUInt8(0); } - core_assert(stream.pos() == priv::BinaryHeaderSize); + core_assert(stream->pos() == priv::BinaryHeaderSize); int faceCount = 0; for (const auto &meshExt : meshes) { @@ -186,7 +198,7 @@ bool STLFormat::saveMeshes(const core::Map &, const scenegraph::SceneG faceCount += ni / 3; } } - stream.writeUInt32(faceCount); + stream->writeUInt32(faceCount); for (const auto &meshExt : meshes) { for (int i = 0; i < voxel::ChunkMesh::Meshes; ++i) { @@ -216,24 +228,24 @@ bool STLFormat::saveMeshes(const core::Map &, const scenegraph::SceneG const glm::vec3 edge2 = glm::vec3(v3.position - v1.position); const glm::vec3 normal = glm::normalize(glm::cross(edge1, edge2)); for (int j = 0; j < 3; ++j) { - if (!stream.writeFloat(normal[j])) { + if (!stream->writeFloat(normal[j])) { return false; } } - if (!writeVertex(stream, meshExt, v1, transform, scale)) { + if (!writeVertex(*stream, meshExt, v1, transform, scale)) { return false; } - if (!writeVertex(stream, meshExt, v2, transform, scale)) { + if (!writeVertex(*stream, meshExt, v2, transform, scale)) { return false; } - if (!writeVertex(stream, meshExt, v3, transform, scale)) { + if (!writeVertex(*stream, meshExt, v3, transform, scale)) { return false; } - stream.writeUInt16(0); + stream->writeUInt16(0); } } } diff --git a/src/modules/voxelformat/private/mesh/STLFormat.h b/src/modules/voxelformat/private/mesh/STLFormat.h index 7ca8142363..ad6b0a2445 100644 --- a/src/modules/voxelformat/private/mesh/STLFormat.h +++ b/src/modules/voxelformat/private/mesh/STLFormat.h @@ -42,12 +42,12 @@ class STLFormat : public MeshFormat { bool parseBinary(io::SeekableReadStream &stream, TriCollection &tris); bool parseAscii(io::SeekableReadStream &stream, TriCollection &tris); - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; public: bool saveMeshes(const core::Map &, const scenegraph::SceneGraph &, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, bool quad, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/mesh/quake/MD2Format.cpp b/src/modules/voxelformat/private/mesh/quake/MD2Format.cpp index 596166f31b..8d9aa61841 100644 --- a/src/modules/voxelformat/private/mesh/quake/MD2Format.cpp +++ b/src/modules/voxelformat/private/mesh/quake/MD2Format.cpp @@ -5,7 +5,9 @@ #include "MD2Format.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/collection/DynamicArray.h" +#include "io/Archive.h" #include "io/Stream.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -25,37 +27,42 @@ namespace voxelformat { return false; \ } -bool MD2Format::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool MD2Format::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } MD2Header hdr; - const int64_t startOffset = stream.pos(); + const int64_t startOffset = stream->pos(); - wrap(stream.readUInt32(hdr.magic)) + wrap(stream->readUInt32(hdr.magic)) if (hdr.magic != FourCC('I', 'D', 'P', '2')) { Log::error("Could not load md2 file: Invalid magic"); return false; } - wrap(stream.readUInt32(hdr.version)) + wrap(stream->readUInt32(hdr.version)) if (hdr.version != MD2_VERSION) { Log::error("Could not load md2 file: Invalid version"); return false; } - wrap(stream.readUInt32(hdr.skinWidth)) - wrap(stream.readUInt32(hdr.skinHeight)) - wrap(stream.readUInt32(hdr.frameSize)) - wrap(stream.readUInt32(hdr.numSkins)) - wrap(stream.readUInt32(hdr.numVerts)) - wrap(stream.readUInt32(hdr.numST)) - wrap(stream.readUInt32(hdr.numTris)) - wrap(stream.readUInt32(hdr.numGLCmds)) - wrap(stream.readUInt32(hdr.numFrames)) - wrap(stream.readUInt32(hdr.offsetSkins)) - wrap(stream.readUInt32(hdr.offsetST)) - wrap(stream.readUInt32(hdr.offsetTris)) - wrap(stream.readUInt32(hdr.offsetFrames)) - wrap(stream.readUInt32(hdr.offsetGLCmds)) - wrap(stream.readUInt32(hdr.offsetEnd)) + wrap(stream->readUInt32(hdr.skinWidth)) + wrap(stream->readUInt32(hdr.skinHeight)) + wrap(stream->readUInt32(hdr.frameSize)) + wrap(stream->readUInt32(hdr.numSkins)) + wrap(stream->readUInt32(hdr.numVerts)) + wrap(stream->readUInt32(hdr.numST)) + wrap(stream->readUInt32(hdr.numTris)) + wrap(stream->readUInt32(hdr.numGLCmds)) + wrap(stream->readUInt32(hdr.numFrames)) + wrap(stream->readUInt32(hdr.offsetSkins)) + wrap(stream->readUInt32(hdr.offsetST)) + wrap(stream->readUInt32(hdr.offsetTris)) + wrap(stream->readUInt32(hdr.offsetFrames)) + wrap(stream->readUInt32(hdr.offsetGLCmds)) + wrap(stream->readUInt32(hdr.offsetEnd)) if (hdr.numVerts >= MD2_MAX_VERTS) { Log::error("Max verts exceeded"); @@ -72,10 +79,10 @@ bool MD2Format::voxelizeGroups(const core::String &filename, io::SeekableReadStr core::StringMap textures; - stream.seek(startOffset + hdr.offsetSkins); + stream->seek(startOffset + hdr.offsetSkins); for (uint32_t i = 0; i < hdr.numSkins; ++i) { core::String skinname; - if (!stream.readString(MD2_MAX_SKINNAME, skinname)) { + if (!stream->readString(MD2_MAX_SKINNAME, skinname)) { Log::error("Failed to read skin name"); return false; } @@ -87,7 +94,7 @@ bool MD2Format::voxelizeGroups(const core::String &filename, io::SeekableReadStr textures.put(skinname, image::loadImage(lookupTexture(filename, skinname))); } - if (!loadFrame(filename, stream, startOffset, hdr, 0, sceneGraph, textures)) { + if (!loadFrame(filename, *stream, startOffset, hdr, 0, sceneGraph, textures)) { Log::error("Failed to load frame"); return false; } diff --git a/src/modules/voxelformat/private/mesh/quake/MD2Format.h b/src/modules/voxelformat/private/mesh/quake/MD2Format.h index 00e1c1650a..dcbf0fcf78 100644 --- a/src/modules/voxelformat/private/mesh/quake/MD2Format.h +++ b/src/modules/voxelformat/private/mesh/quake/MD2Format.h @@ -52,7 +52,7 @@ class MD2Format : public MeshFormat { static_assert(sizeof(MD2FrameHeader) == 40, "MD2FrameHeader size is wrong"); private: - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool loadFrame(const core::String &filename, io::SeekableReadStream &stream, int64_t startOffset, const MD2Header &hdr, uint32_t frameIndex, scenegraph::SceneGraph &sceneGraph, @@ -60,7 +60,7 @@ class MD2Format : public MeshFormat { public: bool saveMeshes(const core::Map &meshIdxNodeMap, const scenegraph::SceneGraph &sceneGraph, - const Meshes &meshes, const core::String &filename, io::SeekableWriteStream &stream, + const Meshes &meshes, const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale = glm::vec3(1.0f), bool quad = false, bool withColor = true, bool withTexCoords = true) override { return false; diff --git a/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.cpp b/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.cpp index 332fb88228..93830510d8 100644 --- a/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.cpp +++ b/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.cpp @@ -6,6 +6,7 @@ #include "core/Common.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StandardLib.h" #include "core/StringUtil.h" #include "core/collection/Buffer.h" @@ -720,28 +721,33 @@ bool QuakeBSPFormat::voxelize(const core::DynamicArray &textures, const return voxelizeNode(name, sceneGraph, tris) > 0; } -bool QuakeBSPFormat::voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, +bool QuakeBSPFormat::voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } static const uint32_t q1Version = FourCC('\x1d', '\0', '\0', '\0'); static const uint32_t bspMagic = FourCC('I', 'B', 'S', 'P'); BspHeader header; - wrap(stream.readUInt32(header.magic)) + wrap(stream->readUInt32(header.magic)) if (header.magic == q1Version) { header.version = 29; } else { - wrap(stream.readUInt32(header.version)) + wrap(stream->readUInt32(header.version)) } for (int i = 0; i < lengthof(header.lumps); ++i) { - wrap(stream.readUInt32(header.lumps[i].offset)) - wrap(stream.readUInt32(header.lumps[i].len)) + wrap(stream->readUInt32(header.lumps[i].offset)) + wrap(stream->readUInt32(header.lumps[i].len)) } if (header.version == 79 && header.magic == bspMagic) { - return loadUFOAlienInvasionBsp(filename, stream, sceneGraph, header); + return loadUFOAlienInvasionBsp(filename, *stream, sceneGraph, header); } if (header.magic == q1Version) { - return loadQuake1Bsp(filename, stream, sceneGraph, header); + return loadQuake1Bsp(filename, *stream, sceneGraph, header); } uint8_t buf[4]; diff --git a/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.h b/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.h index 67d084778e..45ce260dd2 100644 --- a/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.h +++ b/src/modules/voxelformat/private/mesh/quake/QuakeBSPFormat.h @@ -149,12 +149,12 @@ class QuakeBSPFormat : public MeshFormat { bool loadUFOAlienInvasionModels(io::SeekableReadStream &stream, const BspHeader &header, core::DynamicArray &models); - bool voxelizeGroups(const core::String &filename, io::SeekableReadStream &stream, + bool voxelizeGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; public: bool saveMeshes(const core::Map &, const scenegraph::SceneGraph &, const Meshes &meshes, - const core::String &filename, io::SeekableWriteStream &stream, const glm::vec3 &scale, bool quad, + const core::String &filename, const io::ArchivePtr &archive, const glm::vec3 &scale, bool quad, bool withColor, bool withTexCoords) override { return false; } diff --git a/src/modules/voxelformat/private/minecraft/DatFormat.cpp b/src/modules/voxelformat/private/minecraft/DatFormat.cpp index 6c8813d9df..c74b329efc 100644 --- a/src/modules/voxelformat/private/minecraft/DatFormat.cpp +++ b/src/modules/voxelformat/private/minecraft/DatFormat.cpp @@ -7,6 +7,7 @@ #include "core/Color.h" #include "core/Common.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/collection/DynamicArray.h" #include "io/File.h" @@ -24,11 +25,16 @@ namespace voxelformat { -bool DatFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool DatFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &loadctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } palette.minecraft(); - io::ZipReadStream zipStream(stream); + io::ZipReadStream zipStream(*stream); priv::NamedBinaryTagContext ctx; ctx.stream = &zipStream; const priv::NamedBinaryTag &root = priv::NamedBinaryTag::parse(ctx); @@ -90,28 +96,19 @@ bool DatFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead continue; } const core::String ®ionFilename = core::string::path(baseName, "region", e.name); - const io::FilePtr &file = io::filesystem()->open(regionFilename); - if (!file->validHandle()) { - Log::warn("Could not open %s", regionFilename.c_str()); - continue; - } - futures.emplace_back(app::async([file, &loadctx]() { - io::FileStream fileStream(file); + futures.emplace_back(app::async([regionFilename, &archive, &loadctx]() { + MCRFormat mcrFormat; scenegraph::SceneGraph newSceneGraph; - if (fileStream.size() <= 2l * MCRFormat::SECTOR_BYTES) { - Log::debug("Skip empty region file %s", file->name().c_str()); + if (!mcrFormat.load(regionFilename, archive, newSceneGraph, loadctx)) { + Log::debug("Could not load %s", regionFilename.c_str()); return core::move(newSceneGraph); } - MCRFormat mcrFormat; - if (!mcrFormat.load(file->name(), fileStream, newSceneGraph, loadctx)) { - Log::warn("Could not load %s", file->name().c_str()); - } const scenegraph::SceneGraph::MergedVolumePalette &merged = newSceneGraph.merge(); newSceneGraph.clear(); if (merged.first == nullptr) { return core::move(newSceneGraph); } - scenegraph::SceneGraphNode node; + scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); node.setVolume(merged.first, true); node.setPalette(merged.second); newSceneGraph.emplace(core::move(node)); diff --git a/src/modules/voxelformat/private/minecraft/DatFormat.h b/src/modules/voxelformat/private/minecraft/DatFormat.h index 93c201a04f..ec90f4d933 100644 --- a/src/modules/voxelformat/private/minecraft/DatFormat.h +++ b/src/modules/voxelformat/private/minecraft/DatFormat.h @@ -17,11 +17,11 @@ namespace voxelformat { */ class DatFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override { + const io::ArchivePtr &archive, const SaveContext &ctx) override { return false; } }; diff --git a/src/modules/voxelformat/private/minecraft/MCRFormat.cpp b/src/modules/voxelformat/private/minecraft/MCRFormat.cpp index b83663630d..b32dc306f6 100644 --- a/src/modules/voxelformat/private/minecraft/MCRFormat.cpp +++ b/src/modules/voxelformat/private/minecraft/MCRFormat.cpp @@ -6,8 +6,10 @@ #include "core/Color.h" #include "core/Common.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/collection/DynamicArray.h" +#include "io/Stream.h" #include "io/ZipReadStream.h" #include "io/ZipWriteStream.h" #include "scenegraph/SceneGraph.h" @@ -31,9 +33,14 @@ namespace voxelformat { } \ } while (0) -bool MCRFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool MCRFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { - const int64_t length = stream.size(); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } + const int64_t length = stream->size(); if (length < SECTOR_BYTES) { Log::error("File does not contain enough data"); return false; @@ -52,7 +59,7 @@ bool MCRFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead switch (type) { case 'r': // Region file format case 'a': { // Anvil file format - const int64_t fileSize = stream.remaining(); + const int64_t fileSize = stream->remaining(); if (fileSize < 2l * SECTOR_BYTES) { Log::error("This region file has not enough data for the 8kb header"); return false; @@ -60,26 +67,26 @@ bool MCRFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead for (int i = 0; i < SECTOR_INTS; ++i) { uint8_t raw[3]; - wrap(stream.readUInt8(raw[0])); - wrap(stream.readUInt8(raw[1])); - wrap(stream.readUInt8(raw[2])); - wrap(stream.readUInt8(_offsets[i].sectorCount)); + wrap(stream->readUInt8(raw[0])); + wrap(stream->readUInt8(raw[1])); + wrap(stream->readUInt8(raw[2])); + wrap(stream->readUInt8(_offsets[i].sectorCount)); _offsets[i].offset = ((raw[0] << 16) + (raw[1] << 8) + raw[2]) * SECTOR_BYTES; } for (int i = 0; i < SECTOR_INTS; ++i) { uint32_t lastModValue; - wrap(stream.readUInt32BE(lastModValue)); + wrap(stream->readUInt32BE(lastModValue)); } // might be an empty region file - if (stream.eos()) { + if (stream->eos()) { Log::warn("Empty region file"); return false; } - const bool success = loadMinecraftRegion(sceneGraph, stream, palette); + const bool success = loadMinecraftRegion(sceneGraph, *stream, palette); return success; } } @@ -527,24 +534,29 @@ bool MCRFormat::parsePaletteList(int dataVersion, const priv::NamedBinaryTag &pa } bool MCRFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } for (int i = 0; i < SECTOR_INTS; ++i) { uint8_t raw[3] = {0, 0, 0}; // TODO _offsets[i].sectorCount = 0; // TODO core_assert(_offsets[i].offset < sizeof(_offsets)); - wrapBool(stream.writeUInt8(raw[0])); - wrapBool(stream.writeUInt8(raw[1])); - wrapBool(stream.writeUInt8(raw[2])); - wrapBool(stream.writeUInt8(_offsets[i].sectorCount)); + wrapBool(stream->writeUInt8(raw[0])); + wrapBool(stream->writeUInt8(raw[1])); + wrapBool(stream->writeUInt8(raw[2])); + wrapBool(stream->writeUInt8(_offsets[i].sectorCount)); } for (int i = 0; i < SECTOR_INTS; ++i) { uint32_t lastModValue = 0u; - wrapBool(stream.writeUInt32BE(lastModValue)); + wrapBool(stream->writeUInt32BE(lastModValue)); } - return saveMinecraftRegion(sceneGraph, stream); + return saveMinecraftRegion(sceneGraph, *stream); } bool MCRFormat::saveMinecraftRegion(const scenegraph::SceneGraph &sceneGraph, io::SeekableWriteStream &stream) { diff --git a/src/modules/voxelformat/private/minecraft/MCRFormat.h b/src/modules/voxelformat/private/minecraft/MCRFormat.h index 99708366e2..77e16fd3ba 100644 --- a/src/modules/voxelformat/private/minecraft/MCRFormat.h +++ b/src/modules/voxelformat/private/minecraft/MCRFormat.h @@ -117,11 +117,11 @@ class MCRFormat : public PaletteFormat { bool saveMinecraftRegion(const scenegraph::SceneGraph &sceneGraph, io::SeekableWriteStream &stream); protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/minecraft/MTSFormat.cpp b/src/modules/voxelformat/private/minecraft/MTSFormat.cpp index ef9db265c7..7b7e428cb7 100644 --- a/src/modules/voxelformat/private/minecraft/MTSFormat.cpp +++ b/src/modules/voxelformat/private/minecraft/MTSFormat.cpp @@ -6,6 +6,7 @@ #include "core/Common.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/collection/Array3DView.h" #include "core/collection/Buffer.h" #include "core/collection/DynamicArray.h" @@ -24,25 +25,30 @@ namespace voxelformat { return false; \ } -bool MTSFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool MTSFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('M', 'T', 'S', 'M')) { Log::error("Invalid magic"); return false; } uint16_t version; - wrap(stream.readUInt16BE(version)) + wrap(stream->readUInt16BE(version)) if (version > 4u) { Log::error("Unsupported version: %i", (int)version); return false; } glm::i16vec3 size; - wrap(stream.readInt16BE(size.x)) - wrap(stream.readInt16BE(size.y)) - wrap(stream.readInt16BE(size.z)) + wrap(stream->readInt16BE(size.x)) + wrap(stream->readInt16BE(size.y)) + wrap(stream->readInt16BE(size.z)) Log::debug("Size: %i:%i:%i", size.x, size.y, size.z); @@ -51,21 +57,21 @@ bool MTSFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead if (version >= 3) { for (int16_t y = 0; y < size.y; ++y) { - wrap(stream.readUInt8(probs[y])) + wrap(stream->readUInt8(probs[y])) } } else { probs.fill(0xFF); } uint16_t idmapcount; - wrap(stream.readUInt16BE(idmapcount)) + wrap(stream->readUInt16BE(idmapcount)) Log::debug("idmapcount: %i", idmapcount); core::DynamicArray names; names.reserve(idmapcount); for (uint16_t i = 0; i < idmapcount; ++i) { core::String name; - if (!stream.readPascalStringUInt16BE(name)) { + if (!stream->readPascalStringUInt16BE(name)) { Log::error("Failed to read material name"); return false; } @@ -86,7 +92,7 @@ bool MTSFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead core::Buffer databuf; databuf.resize(nodecount); - io::ZipReadStream zipStream(stream); + io::ZipReadStream zipStream(*stream); for (int i = 0; i < nodecount; ++i) { wrap(zipStream.readUInt16BE(databuf[i].param0)) } @@ -122,7 +128,7 @@ bool MTSFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead #undef wrap bool MTSFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &) { + const io::ArchivePtr &archive, const SaveContext &) { return false; } diff --git a/src/modules/voxelformat/private/minecraft/MTSFormat.h b/src/modules/voxelformat/private/minecraft/MTSFormat.h index a60624716d..9adcc23b3a 100644 --- a/src/modules/voxelformat/private/minecraft/MTSFormat.h +++ b/src/modules/voxelformat/private/minecraft/MTSFormat.h @@ -17,11 +17,11 @@ namespace voxelformat { */ class MTSFormat : public PaletteFormat { public: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/minecraft/SchematicFormat.cpp b/src/modules/voxelformat/private/minecraft/SchematicFormat.cpp index 7a2ee76284..c66a92e622 100644 --- a/src/modules/voxelformat/private/minecraft/SchematicFormat.cpp +++ b/src/modules/voxelformat/private/minecraft/SchematicFormat.cpp @@ -49,11 +49,16 @@ static glm::ivec3 parsePosList(const priv::NamedBinaryTag &compound, const core: return glm::ivec3(x, y, z); } -bool SchematicFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool SchematicFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &loadctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } palette.minecraft(); - io::ZipReadStream zipStream(stream); + io::ZipReadStream zipStream(*stream); priv::NamedBinaryTagContext ctx; ctx.stream = &zipStream; const priv::NamedBinaryTag &schematic = priv::NamedBinaryTag::parse(ctx); @@ -499,7 +504,12 @@ void SchematicFormat::addMetadata_r(const core::String &key, const priv::NamedBi } bool SchematicFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } // save as sponge-3 const scenegraph::SceneGraph::MergedVolumePalette &merged = sceneGraph.merge(); if (merged.first == nullptr) { @@ -511,7 +521,7 @@ bool SchematicFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const const glm::ivec3 &size = region.getDimensionsInVoxels(); const glm::ivec3 &mins = region.getLowerCorner(); - io::ZipWriteStream zipStream(stream); + io::ZipWriteStream zipStream(*stream); priv::NBTCompound compound; compound.put("Width", (int16_t)size.x); diff --git a/src/modules/voxelformat/private/minecraft/SchematicFormat.h b/src/modules/voxelformat/private/minecraft/SchematicFormat.h index 8f1be64657..dc1417ca09 100644 --- a/src/modules/voxelformat/private/minecraft/SchematicFormat.h +++ b/src/modules/voxelformat/private/minecraft/SchematicFormat.h @@ -47,11 +47,11 @@ class SchematicFormat : public PaletteFormat { void parseMetadata(const priv::NamedBinaryTag &schematic, scenegraph::SceneGraph &sceneGraph, scenegraph::SceneGraphNode &node); int parsePalette(const priv::NamedBinaryTag &schematic, core::Buffer &mcpal) const; - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/qubicle/QBCLFormat.cpp b/src/modules/voxelformat/private/qubicle/QBCLFormat.cpp index 6a183f582d..38ec4e747b 100644 --- a/src/modules/voxelformat/private/qubicle/QBCLFormat.cpp +++ b/src/modules/voxelformat/private/qubicle/QBCLFormat.cpp @@ -10,6 +10,7 @@ #include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "image/Image.h" +#include "io/Archive.h" #include "io/BufferedReadWriteStream.h" #include "io/Stream.h" #include "io/ZipReadStream.h" @@ -41,7 +42,7 @@ class ScopedQBCLHeader { public: ScopedQBCLHeader(io::SeekableWriteStream &stream, uint32_t nodeType) : _stream(stream) { Log::debug("Write node type %u", nodeType); - if (!_stream.writeUInt32(nodeType)) { + if (!stream.writeUInt32(nodeType)) { Log::error("Failed to write the node type %u", nodeType); _success = false; } @@ -66,7 +67,7 @@ class ScopedQBCLHeader { break; } - if (!_stream.writeUInt32(nodeType)) { + if (!stream.writeUInt32(nodeType)) { Log::error("Failed to write the node type %u", nodeType); _success = false; } @@ -138,7 +139,7 @@ static bool writeRLE(io::WriteStream &stream, core::RGBA color, uint8_t count) { return true; } -bool QBCLFormat::saveMatrix(io::SeekableWriteStream &outStream, const scenegraph::SceneGraph &sceneGraph, +bool QBCLFormat::saveMatrix(io::SeekableWriteStream &stream, const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node) const { const voxel::Region ®ion = sceneGraph.resolveRegion(node); const scenegraph::SceneGraphTransform &transform = node.transform(0); @@ -147,29 +148,29 @@ bool QBCLFormat::saveMatrix(io::SeekableWriteStream &outStream, const scenegraph const glm::ivec3 &maxs = region.getUpperCorner(); const glm::ivec3 size = region.getDimensionsInVoxels(); - wrapSave(outStream.writeUInt32(1)) // unknown - wrapSave(outStream.writePascalStringUInt32LE(node.name())) - wrapSave(outStream.writeBool(node.visible())) - wrapSave(outStream.writeBool(true)) // unknown - wrapSave(outStream.writeBool(node.locked())) + wrapSave(stream.writeUInt32(1)) // unknown + wrapSave(stream.writePascalStringUInt32LE(node.name())) + wrapSave(stream.writeBool(node.visible())) + wrapSave(stream.writeBool(true)) // unknown + wrapSave(stream.writeBool(node.locked())) - wrapSave(outStream.writeUInt32(size.x)) - wrapSave(outStream.writeUInt32(size.y)) - wrapSave(outStream.writeUInt32(size.z)) + wrapSave(stream.writeUInt32(size.x)) + wrapSave(stream.writeUInt32(size.y)) + wrapSave(stream.writeUInt32(size.z)) - wrapSave(outStream.writeInt32(translation.x)) - wrapSave(outStream.writeInt32(translation.y)) - wrapSave(outStream.writeInt32(translation.z)) + wrapSave(stream.writeInt32(translation.x)) + wrapSave(stream.writeInt32(translation.y)) + wrapSave(stream.writeInt32(translation.z)) const glm::vec3 &normalizedPivot = node.pivot(); - wrapSave(outStream.writeFloat(/* TODO mins.x +*/ normalizedPivot.x * size.x)) - wrapSave(outStream.writeFloat(/* TODO mins.y +*/ normalizedPivot.y * size.y)) - wrapSave(outStream.writeFloat(/* TODO mins.z +*/ normalizedPivot.z * size.z)) + wrapSave(stream.writeFloat(/* TODO mins.x +*/ normalizedPivot.x * size.x)) + wrapSave(stream.writeFloat(/* TODO mins.y +*/ normalizedPivot.y * size.y)) + wrapSave(stream.writeFloat(/* TODO mins.z +*/ normalizedPivot.z * size.z)) constexpr voxel::Voxel Empty; - uint32_t voxelDataSizePos = outStream.pos(); - wrapSave(outStream.writeUInt32(0)); + uint32_t voxelDataSizePos = stream.pos(); + wrapSave(stream.writeUInt32(0)); io::BufferedReadWriteStream rleDataStream(size.x * size.y * size.z * 32); @@ -214,16 +215,16 @@ bool QBCLFormat::saveMatrix(io::SeekableWriteStream &outStream, const scenegraph } } - io::ZipWriteStream zipStream(outStream); + io::ZipWriteStream zipStream(stream); if (zipStream.write(rleDataStream.getBuffer(), rleDataStream.size()) == -1) { Log::error("Could not write compressed data"); return false; } wrapSave(zipStream.flush()) const int64_t compressedDataSize = zipStream.size(); - wrapSaveNegative(outStream.seek(voxelDataSizePos)) - wrapSave(outStream.writeUInt32(compressedDataSize)) - wrapSaveNegative(outStream.seek(0, SEEK_END)) + wrapSaveNegative(stream.seek(voxelDataSizePos)) + wrapSave(stream.writeUInt32(compressedDataSize)) + wrapSaveNegative(stream.seek(0, SEEK_END)) return true; } @@ -282,10 +283,15 @@ bool QBCLFormat::saveNode(io::SeekableWriteStream &stream, const scenegraph::Sce } bool QBCLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &savectx) { - wrapBool(stream.writeUInt32(FourCC('Q', 'B', 'C', 'L'))) - wrapBool(stream.writeUInt32(131331)) - wrapBool(stream.writeUInt32(qbcl::VERSION)) + const io::ArchivePtr &archive, const SaveContext &savectx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } + wrapBool(stream->writeUInt32(FourCC('Q', 'B', 'C', 'L'))) + wrapBool(stream->writeUInt32(131331)) + wrapBool(stream->writeUInt32(qbcl::VERSION)) bool imageAdded = false; ThumbnailContext ctx; ctx.outputSize = glm::ivec2(128); @@ -293,15 +299,15 @@ bool QBCLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core if (image) { const int size = image->width() * image->height() * image->depth(); if (size > 0) { - wrapBool(stream.writeUInt32(image->width())) - wrapBool(stream.writeUInt32(image->height())) + wrapBool(stream->writeUInt32(image->width())) + wrapBool(stream->writeUInt32(image->height())) for (int x = 0; x < image->width(); ++x) { for (int y = 0; y < image->height(); ++y) { const core::RGBA color = image->colorAt(x, y); - stream.writeUInt8(color.b); - stream.writeUInt8(color.g); - stream.writeUInt8(color.r); - stream.writeUInt8(color.a); + stream->writeUInt8(color.b); + stream->writeUInt8(color.g); + stream->writeUInt8(color.r); + stream->writeUInt8(color.a); } } imageAdded = true; @@ -311,31 +317,36 @@ bool QBCLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core } if (!imageAdded) { - wrapBool(stream.writeUInt32(0)) // thumbnail w/h - wrapBool(stream.writeUInt32(0)) // thumbnail w/h + wrapBool(stream->writeUInt32(0)) // thumbnail w/h + wrapBool(stream->writeUInt32(0)) // thumbnail w/h } const scenegraph::SceneGraphNode &rootNode = sceneGraph.root(); - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Title"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Description"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Metadata"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Author"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Company"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Website"))) - wrapBool(stream.writePascalStringUInt32LE(rootNode.property("Copyright"))) - wrapBool(stream.writeUInt64(0)) // timestamp1 - wrapBool(stream.writeUInt64(0)) // timestamp2 - return saveNode(stream, sceneGraph, sceneGraph.root()); + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Title"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Description"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Metadata"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Author"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Company"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Website"))) + wrapBool(stream->writePascalStringUInt32LE(rootNode.property("Copyright"))) + wrapBool(stream->writeUInt64(0)) // timestamp1 + wrapBool(stream->writeUInt64(0)) // timestamp2 + return saveNode(*stream, sceneGraph, sceneGraph.root()); } -size_t QBCLFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t QBCLFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } Header header; - wrapBool(readHeader(stream, header)) + wrapBool(readHeader(*stream, header)) header.loadPalette = true; scenegraph::SceneGraph sceneGraph; - wrapBool(readNodes(filename, stream, sceneGraph, sceneGraph.root().id(), palette, header)) + wrapBool(readNodes(filename, *stream, sceneGraph, sceneGraph.root().id(), palette, header)) Log::debug("qbcl: loaded %i colors", palette.colorCount()); return palette.colorCount(); @@ -604,14 +615,19 @@ bool QBCLFormat::readHeader(io::SeekableReadStream &stream, Header &header) { return true; } -bool QBCLFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool QBCLFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } Header header; - wrapBool(readHeader(stream, header)) + wrapBool(readHeader(*stream, header)) palette::Palette palCopy = palette; - wrapBool(readNodes(filename, stream, sceneGraph, -1, palCopy, header)) + wrapBool(readNodes(filename, *stream, sceneGraph, -1, palCopy, header)) scenegraph::SceneGraphNode &rootNode = sceneGraph.node(sceneGraph.root().id()); rootNode.setProperty("Title", header.title); @@ -625,28 +641,33 @@ bool QBCLFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadSt return true; } -image::ImagePtr QBCLFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr QBCLFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t magic; - wrapImg(stream.readUInt32(magic)) + wrapImg(stream->readUInt32(magic)) if (magic != FourCC('Q', 'B', 'C', 'L')) { Log::error("Invalid magic found - no qbcl file"); return image::ImagePtr(); } uint32_t version; - wrapImg(stream.readUInt32(version)) + wrapImg(stream->readUInt32(version)) uint32_t flags; - wrapImg(stream.readUInt32(flags)) + wrapImg(stream->readUInt32(flags)) uint32_t thumbWidth; - wrapImg(stream.readUInt32(thumbWidth)) + wrapImg(stream->readUInt32(thumbWidth)) uint32_t thumbHeight; - wrapImg(stream.readUInt32(thumbHeight)) + wrapImg(stream->readUInt32(thumbHeight)) if (thumbWidth <= 0 || thumbHeight <= 0) { Log::debug("No embedded screenshot found in %s", filename.c_str()); return image::ImagePtr(); } image::ImagePtr img = image::createEmptyImage(core::string::extractFilename(filename)); - if (!img->loadBGRA(stream, thumbWidth, thumbHeight)) { + if (!img->loadBGRA(*stream, thumbWidth, thumbHeight)) { Log::error("Failed to read the qbcl thumbnail buffer of width %u and height %u", thumbWidth, thumbHeight); return image::ImagePtr(); } diff --git a/src/modules/voxelformat/private/qubicle/QBCLFormat.h b/src/modules/voxelformat/private/qubicle/QBCLFormat.h index bff86e3dea..2047445855 100644 --- a/src/modules/voxelformat/private/qubicle/QBCLFormat.h +++ b/src/modules/voxelformat/private/qubicle/QBCLFormat.h @@ -4,6 +4,7 @@ #pragma once +#include "io/Stream.h" #include "voxelformat/Format.h" namespace voxelformat { @@ -64,16 +65,16 @@ class QBCLFormat : public RGBAFormat { const NodeHeader &nodeHeader); bool readNodes(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int parent, palette::Palette &palette, Header &header); - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/qubicle/QBFormat.cpp b/src/modules/voxelformat/private/qubicle/QBFormat.cpp index c8e07ab771..8496a2dd54 100644 --- a/src/modules/voxelformat/private/qubicle/QBFormat.cpp +++ b/src/modules/voxelformat/private/qubicle/QBFormat.cpp @@ -193,19 +193,24 @@ bool QBFormat::saveMatrix(io::SeekableWriteStream &stream, const scenegraph::Sce } bool QBFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { - wrapSave(stream.writeUInt32(257)) // version - wrapSave(stream.writeUInt32((uint32_t)ColorFormat::RGBA)) + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } + wrapSave(stream->writeUInt32(257)) // version + wrapSave(stream->writeUInt32((uint32_t)ColorFormat::RGBA)) const bool leftHanded = core::Var::getSafe(cfg::VoxformatQBSaveLeftHanded)->boolVal(); const ZAxisOrientation orientation = leftHanded ? ZAxisOrientation::LeftHanded : ZAxisOrientation::RightHanded; const bool rleCompressed = core::Var::getSafe(cfg::VoxformatQBSaveCompressed)->boolVal(); - wrapSave(stream.writeUInt32((uint32_t)orientation)) - wrapSave(stream.writeUInt32(rleCompressed ? (uint32_t)Compression::RLE : (uint32_t)Compression::None)) - wrapSave(stream.writeUInt32((uint32_t)VisibilityMask::AlphaChannelVisibleByValue)) - wrapSave(stream.writeUInt32((uint32_t)sceneGraph.size(scenegraph::SceneGraphNodeType::AllModels))) + wrapSave(stream->writeUInt32((uint32_t)orientation)) + wrapSave(stream->writeUInt32(rleCompressed ? (uint32_t)Compression::RLE : (uint32_t)Compression::None)) + wrapSave(stream->writeUInt32((uint32_t)VisibilityMask::AlphaChannelVisibleByValue)) + wrapSave(stream->writeUInt32((uint32_t)sceneGraph.size(scenegraph::SceneGraphNodeType::AllModels))) for (auto iter = sceneGraph.beginAllModels(); iter != sceneGraph.end(); ++iter) { const scenegraph::SceneGraphNode &node = *iter; - if (!saveMatrix(stream, sceneGraph, node, leftHanded, rleCompressed)) { + if (!saveMatrix(*stream, sceneGraph, node, leftHanded, rleCompressed)) { return false; } } @@ -448,25 +453,31 @@ bool QBFormat::readPalette(State &state, io::SeekableReadStream &stream, RGBAMap return true; } -size_t QBFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t QBFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } + State state; - wrap(stream.readUInt32(state._version)) + wrap(stream->readUInt32(state._version)) uint32_t colorFormat; - wrap(stream.readUInt32(colorFormat)) + wrap(stream->readUInt32(colorFormat)) state._colorFormat = (ColorFormat)colorFormat; uint32_t zAxisOrientation; - wrap(stream.readUInt32(zAxisOrientation)) + wrap(stream->readUInt32(zAxisOrientation)) state._zAxisOrientation = (ZAxisOrientation)zAxisOrientation; uint32_t compressed; - wrap(stream.readUInt32(compressed)) + wrap(stream->readUInt32(compressed)) state._compressed = (Compression)compressed; uint32_t visibilityMaskEncoded; - wrap(stream.readUInt32(visibilityMaskEncoded)) + wrap(stream->readUInt32(visibilityMaskEncoded)) state._visibilityMaskEncoded = (VisibilityMask)visibilityMaskEncoded; uint32_t numMatrices; - wrap(stream.readUInt32(numMatrices)) + wrap(stream->readUInt32(numMatrices)) if (numMatrices > 16384) { Log::error("Max allowed matrices exceeded: %u", numMatrices); return 0; @@ -474,7 +485,7 @@ size_t QBFormat::loadPalette(const core::String &filename, io::SeekableReadStrea RGBAMap colors; for (uint32_t i = 0; i < numMatrices; i++) { Log::debug("Loading matrix colors: %u", i); - if (!readPalette(state, stream, colors)) { + if (!readPalette(state, *stream, colors)) { Log::error("Failed to load the matrix colors %u", i); break; } @@ -490,26 +501,31 @@ size_t QBFormat::loadPalette(const core::String &filename, io::SeekableReadStrea return palette.colorCount(); } -bool QBFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool QBFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } State state; - wrap(stream.readUInt32(state._version)) + wrap(stream->readUInt32(state._version)) uint32_t colorFormat; - wrap(stream.readUInt32(colorFormat)) + wrap(stream->readUInt32(colorFormat)) state._colorFormat = (ColorFormat)colorFormat; uint32_t zAxisOrientation; - wrap(stream.readUInt32(zAxisOrientation)) + wrap(stream->readUInt32(zAxisOrientation)) state._zAxisOrientation = (ZAxisOrientation)zAxisOrientation; uint32_t compressed; - wrap(stream.readUInt32(compressed)) + wrap(stream->readUInt32(compressed)) state._compressed = (Compression)compressed; uint32_t visibilityMaskEncoded; - wrap(stream.readUInt32(visibilityMaskEncoded)) + wrap(stream->readUInt32(visibilityMaskEncoded)) state._visibilityMaskEncoded = (VisibilityMask)visibilityMaskEncoded; uint32_t numMatrices; - wrap(stream.readUInt32(numMatrices)) + wrap(stream->readUInt32(numMatrices)) if (numMatrices > 16384) { Log::error("Max allowed matrices exceeded: %u", numMatrices); return false; @@ -526,7 +542,7 @@ bool QBFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStre palette::PaletteLookup palLookup(palette); for (uint32_t i = 0; i < numMatrices; i++) { Log::debug("Loading matrix: %u", i); - if (!readMatrix(state, stream, sceneGraph, palLookup)) { + if (!readMatrix(state, *stream, sceneGraph, palLookup)) { Log::error("Failed to load the matrix %u", i); break; } diff --git a/src/modules/voxelformat/private/qubicle/QBFormat.h b/src/modules/voxelformat/private/qubicle/QBFormat.h index 3af82554f5..587caa4545 100644 --- a/src/modules/voxelformat/private/qubicle/QBFormat.h +++ b/src/modules/voxelformat/private/qubicle/QBFormat.h @@ -48,16 +48,16 @@ class QBFormat : public RGBAFormat { bool readMatrix(State &state, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, palette::PaletteLookup &palLookup); bool readPalette(State &state, io::SeekableReadStream &stream, RGBAMap &colors); - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveMatrix(io::SeekableWriteStream &stream, const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node, bool leftHanded, bool rleCompressed) const; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/qubicle/QBTFormat.cpp b/src/modules/voxelformat/private/qubicle/QBTFormat.cpp index 42fa81eb7a..6895f01296 100644 --- a/src/modules/voxelformat/private/qubicle/QBTFormat.cpp +++ b/src/modules/voxelformat/private/qubicle/QBTFormat.cpp @@ -12,6 +12,7 @@ #include "core/ScopedPtr.h" #include "core/Var.h" #include "io/BufferedReadWriteStream.h" +#include "io/Stream.h" #include "io/ZipReadStream.h" #include "io/ZipWriteStream.h" #include "scenegraph/SceneGraph.h" @@ -269,7 +270,12 @@ bool QBTFormat::saveModel(io::SeekableWriteStream &stream, const scenegraph::Sce } bool QBTFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } const scenegraph::SceneGraphNode &root = sceneGraph.root(); const scenegraph::SceneGraphNodeChildren &children = root.children(); const int childCount = (int)children.size(); @@ -278,28 +284,28 @@ bool QBTFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: return false; } - wrapSave(stream.writeUInt32(FourCC('Q', 'B', ' ', '2'))) - wrapSave(stream.writeUInt8(1)); // version - wrapSave(stream.writeUInt8(0)); // version - wrapSave(stream.writeFloat(1.0f)); // globalscale - wrapSave(stream.writeFloat(1.0f)); // globalscale - wrapSave(stream.writeFloat(1.0f)); // globalscale + wrapSave(stream->writeUInt32(FourCC('Q', 'B', ' ', '2'))) + wrapSave(stream->writeUInt8(1)); // version + wrapSave(stream->writeUInt8(0)); // version + wrapSave(stream->writeFloat(1.0f)); // globalscale + wrapSave(stream->writeFloat(1.0f)); // globalscale + wrapSave(stream->writeFloat(1.0f)); // globalscale const bool colorMap = core::Var::getSafe(cfg::VoxformatQBTPaletteMode)->boolVal(); if (colorMap) { const palette::Palette &palette = sceneGraph.firstPalette(); - if (!saveColorMap(stream, palette)) { + if (!saveColorMap(*stream, palette)) { return false; } } else { palette::Palette palette; - if (!saveColorMap(stream, palette)) { + if (!saveColorMap(*stream, palette)) { return false; } } - if (!stream.writeString("DATATREE", false)) { + if (!stream->writeString("DATATREE", false)) { return false; } - return saveNode(stream, sceneGraph, sceneGraph.root(), colorMap); + return saveNode(*stream, sceneGraph, sceneGraph.root(), colorMap); } bool QBTFormat::skipNode(io::SeekableReadStream &stream) { @@ -594,24 +600,29 @@ bool QBTFormat::loadHeader(io::SeekableReadStream &stream, Header &state) { return true; } -size_t QBTFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t QBTFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } Header state; - if (!loadHeader(stream, state)) { + if (!loadHeader(*stream, state)) { Log::error("Could not load qbt file: Could not read header"); return 0u; } - const int64_t pos = stream.pos(); + const int64_t pos = stream->pos(); - while (stream.remaining() > 0) { + while (stream->remaining() > 0) { char buf[8]; - if (!stream.readString(sizeof(buf), buf)) { + if (!stream->readString(sizeof(buf), buf)) { Log::error("Could not load qbt file: Could not read chunk id"); return 0u; } if (0 == memcmp(buf, "COLORMAP", 7)) { - if (!loadColorMap(stream, palette)) { + if (!loadColorMap(*stream, palette)) { Log::error("Failed to load color map"); return 0; } @@ -621,7 +632,7 @@ size_t QBTFormat::loadPalette(const core::String &filename, io::SeekableReadStre return colorCount; } } else if (0 == memcmp(buf, "DATATREE", 8)) { - wrapBool(skipNode(stream)) + wrapBool(skipNode(*stream)) } else { Log::error("Unknown section found: %c%c%c%c%c%c%c%c", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); @@ -631,17 +642,17 @@ size_t QBTFormat::loadPalette(const core::String &filename, io::SeekableReadStre Log::debug("no palette found"); // no COLORMAP data was found - stream.seek(pos); + stream->seek(pos); - while (stream.remaining() > 0) { + while (stream->remaining() > 0) { char buf[8]; - if (!stream.readString(sizeof(buf), buf)) { + if (!stream->readString(sizeof(buf), buf)) { Log::error("Could not load qbt file: Could not read chunk id"); return 0u; } if (0 == memcmp(buf, "DATATREE", 8)) { scenegraph::SceneGraph sceneGraph; - if (!loadNode(stream, sceneGraph, sceneGraph.root().id(), palette, state)) { + if (!loadNode(*stream, sceneGraph, sceneGraph.root().id(), palette, state)) { Log::error("Failed to load node"); return 0u; } @@ -656,16 +667,21 @@ size_t QBTFormat::loadPalette(const core::String &filename, io::SeekableReadStre return 0; } -bool QBTFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool QBTFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } Header state; - wrapBool(loadHeader(stream, state)) + wrapBool(loadHeader(*stream, state)) - while (stream.remaining() > 0) { + while (stream->remaining() > 0) { char buf[8]; - wrapBool(stream.readString(sizeof(buf), buf)); + wrapBool(stream->readString(sizeof(buf), buf)); if (0 == memcmp(buf, "COLORMAP", 7)) { - if (!loadColorMap(stream, palette)) { + if (!loadColorMap(*stream, palette)) { Log::error("Failed to load color map"); return false; } @@ -677,7 +693,7 @@ bool QBTFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } } else if (0 == memcmp(buf, "DATATREE", 8)) { Log::debug("load data tree"); - if (!loadNode(stream, sceneGraph, sceneGraph.root().id(), palette, state)) { + if (!loadNode(*stream, sceneGraph, sceneGraph.root().id(), palette, state)) { Log::error("Failed to load node"); return false; } diff --git a/src/modules/voxelformat/private/qubicle/QBTFormat.h b/src/modules/voxelformat/private/qubicle/QBTFormat.h index ed0d875adf..9cf8edc605 100644 --- a/src/modules/voxelformat/private/qubicle/QBTFormat.h +++ b/src/modules/voxelformat/private/qubicle/QBTFormat.h @@ -42,7 +42,7 @@ class QBTFormat : public PaletteFormat { bool loadNode(io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int parent, palette::Palette &palette, Header &state); bool loadColorMap(io::SeekableReadStream &stream, palette::Palette &palette); - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; @@ -56,10 +56,10 @@ class QBTFormat : public PaletteFormat { bool saveModel(io::SeekableWriteStream &stream, const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node, bool colorMap) const; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/qubicle/QEFFormat.cpp b/src/modules/voxelformat/private/qubicle/QEFFormat.cpp index 70e9d5e549..7c0c760bc5 100644 --- a/src/modules/voxelformat/private/qubicle/QEFFormat.cpp +++ b/src/modules/voxelformat/private/qubicle/QEFFormat.cpp @@ -6,6 +6,7 @@ #include "core/Color.h" #include "core/GLM.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "scenegraph/SceneGraph.h" #include "voxel/MaterialColor.h" #include "voxel/Voxel.h" @@ -29,28 +30,33 @@ namespace voxelformat { return false; \ } -bool QEFFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool QEFFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } char buf[64]; - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_strcmp(buf, "Qubicle Exchange Format") != 0) { Log::error("Unexpected magic line: '%s'", buf); return false; } - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_strcmp(buf, "Version 0.2") != 0) { Log::error("Unexpected version line: '%s'", buf); return false; } - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_strcmp(buf, "www.minddesk.com") != 0) { Log::error("Unexpected url line: '%s'", buf); return false; } int width, height, depth; - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_sscanf(buf, "%i %i %i", &width, &depth, &height) != 3) { Log::error("Failed to parse dimensions"); return false; @@ -73,7 +79,7 @@ bool QEFFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } int paletteSize; - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_sscanf(buf, "%i", &paletteSize) != 1) { Log::error("Failed to parse palette size"); return false; @@ -88,7 +94,7 @@ bool QEFFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead for (int i = 0; i < paletteSize; ++i) { float r, g, b; - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) if (SDL_sscanf(buf, "%f %f %f", &r, &g, &b) != 3) { Log::error("Failed to parse palette color"); return false; @@ -103,8 +109,8 @@ bool QEFFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead node.setPalette(palette); sceneGraph.emplace(core::move(node)); - while (stream.remaining() > 0) { - wrapBool(stream.readLine(64, buf)) + while (stream->remaining() > 0) { + wrapBool(stream->readLine(64, buf)) int x, y, z, color, vismask; if (SDL_sscanf(buf, "%i %i %i %i %i", &x, &z, &y, &color, &vismask) != 5) { Log::error("Failed to parse voxel data line"); @@ -118,10 +124,15 @@ bool QEFFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } bool QEFFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { - stream.writeString("Qubicle Exchange Format\n", false); - stream.writeString("Version 0.2\n", false); - stream.writeString("www.minddesk.com\n", false); + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return false; + } + stream->writeString("Qubicle Exchange Format\n", false); + stream->writeString("Version 0.2\n", false); + stream->writeString("www.minddesk.com\n", false); const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -133,13 +144,13 @@ bool QEFFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const uint32_t width = region.getWidthInVoxels(); const uint32_t height = region.getHeightInVoxels(); const uint32_t depth = region.getDepthInVoxels(); - stream.writeStringFormat(false, "%i %i %i\n", width, depth, height); + stream->writeStringFormat(false, "%i %i %i\n", width, depth, height); const palette::Palette &palette = node->palette(); - stream.writeStringFormat(false, "%i\n", palette.colorCount()); + stream->writeStringFormat(false, "%i\n", palette.colorCount()); for (int i = 0; i < palette.colorCount(); ++i) { const core::RGBA c = palette.color(i); const glm::vec4 &cv = core::Color::fromRGBA(c); - stream.writeStringFormat(false, "%f %f %f\n", cv.r, cv.g, cv.b); + stream->writeStringFormat(false, "%f %f %f\n", cv.r, cv.g, cv.b); } for (uint32_t x = 0u; x < width; ++x) { @@ -159,7 +170,7 @@ bool QEFFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: // if (mask && 64 == 64) // back side visible const int vismask = 0x7E; // TODO: this produces voxels where every side is visible, it's up to the // importer to fix this atm - stream.writeStringFormat(false, "%i %i %i %i %i\n", x, z, y, voxel.getColor(), vismask); + stream->writeStringFormat(false, "%i %i %i %i %i\n", x, z, y, voxel.getColor(), vismask); } } } diff --git a/src/modules/voxelformat/private/qubicle/QEFFormat.h b/src/modules/voxelformat/private/qubicle/QEFFormat.h index e15b1eb32f..7518443d33 100644 --- a/src/modules/voxelformat/private/qubicle/QEFFormat.h +++ b/src/modules/voxelformat/private/qubicle/QEFFormat.h @@ -23,11 +23,11 @@ namespace voxelformat { */ class QEFFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: bool singleVolume() const override { return true; diff --git a/src/modules/voxelformat/private/rooms/ThingFormat.cpp b/src/modules/voxelformat/private/rooms/ThingFormat.cpp index 13ed2148f9..a2d49f6300 100644 --- a/src/modules/voxelformat/private/rooms/ThingFormat.cpp +++ b/src/modules/voxelformat/private/rooms/ThingFormat.cpp @@ -97,15 +97,10 @@ bool ThingFormat::loadNode(const io::ArchivePtr &archive, const NodeSpec &nodeSp Log::error("ThingFormat: Missing modelName in node spec"); return false; } - core::ScopedPtr modelStream(archive->readStream(nodeSpec.modelName)); - if (!modelStream) { - Log::error("ThingFormat: Failed to open model: %s", nodeSpec.modelName.c_str()); - return false; - } scenegraph::SceneGraph voxSceneGraph; Log::debug("ThingFormat: Load vox file: %s", nodeSpec.modelName.c_str()); VoxFormat format; - if (!format.load(nodeSpec.modelName, *modelStream, voxSceneGraph, ctx)) { + if (!format.load(nodeSpec.modelName, archive, voxSceneGraph, ctx)) { Log::error("ThingFormat: Failed to load model: %s", nodeSpec.modelName.c_str()); return false; } @@ -145,14 +140,8 @@ bool ThingFormat::loadNode(const io::ArchivePtr &archive, const NodeSpec &nodeSp return true; } -bool ThingFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool ThingFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { - io::ArchivePtr archive = io::openArchive(io::filesystem(), filename, &stream); - if (!archive) { - Log::error("ThingFormat: Failed to open archive: %s", filename.c_str()); - return false; - } - const io::ArchiveFiles &files = archive->files(); for (const io::FilesystemEntry &file : files) { if (file.isDirectory()) { @@ -179,7 +168,7 @@ bool ThingFormat::loadGroups(const core::String &filename, io::SeekableReadStrea } bool ThingFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { return false; } diff --git a/src/modules/voxelformat/private/rooms/ThingFormat.h b/src/modules/voxelformat/private/rooms/ThingFormat.h index 2044275bd6..2f6bba8178 100644 --- a/src/modules/voxelformat/private/rooms/ThingFormat.h +++ b/src/modules/voxelformat/private/rooms/ThingFormat.h @@ -32,10 +32,10 @@ class ThingFormat : public Format { const LoadContext &ctx, int parent = 0); public: - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/sandbox/VXAFormat.cpp b/src/modules/voxelformat/private/sandbox/VXAFormat.cpp index e3203c77eb..71f28e5a90 100644 --- a/src/modules/voxelformat/private/sandbox/VXAFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXAFormat.cpp @@ -9,6 +9,7 @@ #include "core/GLM.h" #include "core/Log.h" #include "core/MD5.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "io/BufferedReadWriteStream.h" #include "io/MemoryReadStream.h" @@ -246,13 +247,18 @@ bool VXAFormat::recursiveImportNodeBefore3(const core::String &filename, io::See return true; } -bool VXAFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool VXAFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint8_t magic[4]; - wrap(stream.readUInt8(magic[0])) - wrap(stream.readUInt8(magic[1])) - wrap(stream.readUInt8(magic[2])) - wrap(stream.readUInt8(magic[3])) + wrap(stream->readUInt8(magic[0])) + wrap(stream->readUInt8(magic[1])) + wrap(stream->readUInt8(magic[2])) + wrap(stream->readUInt8(magic[3])) if (magic[0] != 'V' || magic[1] != 'X' || magic[2] != 'A') { Log::error("Could not load vxa file: Invalid magic found (%c%c%c%c)", magic[0], magic[1], magic[2], magic[3]); return false; @@ -278,8 +284,8 @@ bool VXAFormat::loadGroups(const core::String &filename, io::SeekableReadStream } uint64_t md5[2]; - wrap(stream.readUInt64(md5[0])) - wrap(stream.readUInt64(md5[1])) + wrap(stream->readUInt64(md5[0])) + wrap(stream->readUInt64(md5[1])) uint64_t hash[2]{0}; vxa_priv::calculateHash(sceneGraph, hash); @@ -290,10 +296,10 @@ bool VXAFormat::loadGroups(const core::String &filename, io::SeekableReadStream } char animId[1024]; - wrapBool(stream.readString(sizeof(animId), animId, true)) + wrapBool(stream->readString(sizeof(animId), animId, true)) Log::debug("anim: '%s'", animId); int32_t rootChildren = 0; - wrap(stream.readInt32(rootChildren)) + wrap(stream->readInt32(rootChildren)) Log::debug("rootChildren: %i", rootChildren); if (rootChildren == 0) { Log::debug("No children node found in vxa - positioning might be wrong"); @@ -315,12 +321,12 @@ bool VXAFormat::loadGroups(const core::String &filename, io::SeekableReadStream const int nodeId = sceneGraph.root().children()[i]; scenegraph::SceneGraphNode &node = sceneGraph.node(nodeId); if (version <= 2) { - if (!recursiveImportNodeBefore3(filename, stream, sceneGraph, node, animId, version)) { + if (!recursiveImportNodeBefore3(filename, *stream, sceneGraph, node, animId, version)) { Log::error("VXA: failed to import children for version %i", version); return false; } } else { - if (!recursiveImportNodeSince3(filename, stream, sceneGraph, node, animId, version)) { + if (!recursiveImportNodeSince3(filename, *stream, sceneGraph, node, animId, version)) { Log::error("VXA: failed to import children for version %i", version); return false; } @@ -368,7 +374,12 @@ bool VXAFormat::saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, cons } bool VXAFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode &root = sceneGraph.root(); const scenegraph::SceneGraphNodeChildren &children = root.children(); const int childCount = (int)children.size(); @@ -385,22 +396,22 @@ bool VXAFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: } const core::String &animationId = baseFilename.substr(idx + 1); - wrapBool(stream.writeUInt32(FourCC('V', 'X', 'A', '2'))) + wrapBool(stream->writeUInt32(FourCC('V', 'X', 'A', '2'))) uint64_t hash[2]{0}; vxa_priv::calculateHash(sceneGraph, hash); - wrapBool(stream.writeUInt64(hash[0])) - wrapBool(stream.writeUInt64(hash[1])) - wrapBool(stream.writeString(animationId.c_str(), true)) + wrapBool(stream->writeUInt64(hash[0])) + wrapBool(stream->writeUInt64(hash[1])) + wrapBool(stream->writeString(animationId.c_str(), true)) Log::debug("Save animation %s", animationId.c_str()); - wrapBool(stream.writeInt32(1)) // root node has one child + wrapBool(stream->writeInt32(1)) // root node has one child if (childCount != 1 || sceneGraph.node(children[0]).name() != SANDBOX_CONTROLLER_NODE) { // add controller node (see VXRFormat) - wrapBool(stream.writeInt32(0)) // no key frames for controller node - wrapBool(stream.writeInt32(childCount)) + wrapBool(stream->writeInt32(0)) // no key frames for controller node + wrapBool(stream->writeInt32(childCount)) } for (int child : children) { const scenegraph::SceneGraphNode &node = sceneGraph.node(child); - wrapBool(saveRecursiveNode(sceneGraph, node, animationId, filename, stream)) + wrapBool(saveRecursiveNode(sceneGraph, node, animationId, filename, *stream)) } Log::debug("Save vxa to %s", filename.c_str()); return true; diff --git a/src/modules/voxelformat/private/sandbox/VXAFormat.h b/src/modules/voxelformat/private/sandbox/VXAFormat.h index 0d1ed58795..1da8a62907 100644 --- a/src/modules/voxelformat/private/sandbox/VXAFormat.h +++ b/src/modules/voxelformat/private/sandbox/VXAFormat.h @@ -4,6 +4,7 @@ #pragma once +#include "io/Archive.h" #include "voxelformat/Format.h" namespace voxelformat { @@ -29,10 +30,10 @@ class VXAFormat : public Format { bool saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node, const core::String &animation, const core::String &filename, io::SeekableWriteStream &stream); - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/sandbox/VXCFormat.cpp b/src/modules/voxelformat/private/sandbox/VXCFormat.cpp index 7f32620519..dcfa31a773 100644 --- a/src/modules/voxelformat/private/sandbox/VXCFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXCFormat.cpp @@ -6,6 +6,7 @@ #include "VXRFormat.h" #include "app/App.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "io/BufferedReadWriteStream.h" #include "io/FileStream.h" @@ -29,9 +30,14 @@ namespace voxelformat { return false; \ } -bool VXCFormat::loadGroups(const core::String &filename, io::SeekableReadStream &in, scenegraph::SceneGraph &sceneGraph, +bool VXCFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { - io::ZipReadStream stream(in, (int)in.size()); + core::ScopedPtr in(archive->readStream(filename)); + if (!in) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } + io::ZipReadStream stream(*in, (int)in->size()); uint8_t magic[4]; wrap(stream.readUInt8(magic[0])) wrap(stream.readUInt8(magic[1])) @@ -67,18 +73,14 @@ bool VXCFormat::loadGroups(const core::String &filename, io::SeekableReadStream if (!vxr.empty()) { VXRFormat f; - io::FilePtr vxrFile = io::filesystem()->open(vxr); - if (vxrFile->validHandle()) { - io::FileStream fstream(vxrFile); - f.load(vxr, fstream, sceneGraph, ctx); - } + f.load(vxr, archive, sceneGraph, ctx); } sceneGraph.updateTransforms(); return !sceneGraph.empty(); } bool VXCFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { return false; } diff --git a/src/modules/voxelformat/private/sandbox/VXCFormat.h b/src/modules/voxelformat/private/sandbox/VXCFormat.h index 82fc901420..a2fa005a82 100644 --- a/src/modules/voxelformat/private/sandbox/VXCFormat.h +++ b/src/modules/voxelformat/private/sandbox/VXCFormat.h @@ -15,10 +15,10 @@ namespace voxelformat { */ class VXCFormat : public Format { protected: - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/sandbox/VXMFormat.cpp b/src/modules/voxelformat/private/sandbox/VXMFormat.cpp index e6c24d2e33..ce3ab09c27 100644 --- a/src/modules/voxelformat/private/sandbox/VXMFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXMFormat.cpp @@ -8,6 +8,7 @@ #include "core/FourCC.h" #include "core/GLM.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/String.h" #include "core/StringUtil.h" #include "image/Image.h" @@ -64,15 +65,25 @@ bool VXMFormat::writeRLE(io::WriteStream &stream, int length, const voxel::Voxel return true; } -image::ImagePtr VXMFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr VXMFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { - const core::String imageName = filename + ".png"; - return image::loadImage(imageName); + const core::String image = filename + ".png"; + core::ScopedPtr stream(archive->readStream(image)); + if (!stream) { + Log::error("Could not load file %s", image.c_str()); + return image::ImagePtr(); + } + return image::loadImage(image, *stream, stream->size()); } bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { - wrapBool(stream.writeUInt32(FourCC('V', 'X', 'M', 'C'))); + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } + wrapBool(stream->writeUInt32(FourCC('V', 'X', 'M', 'C'))); glm::vec3 pivot(0.5f); if (const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode()) { @@ -87,15 +98,15 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const uint32_t depth = region.getDepthInVoxels(); // we have to flip depth with height for our own coordinate system - wrapBool(stream.writeUInt32(width)) - wrapBool(stream.writeUInt32(height)) - wrapBool(stream.writeUInt32(depth)) + wrapBool(stream->writeUInt32(width)) + wrapBool(stream->writeUInt32(height)) + wrapBool(stream->writeUInt32(depth)) - wrapBool(stream.writeFloat(pivot.x)); - wrapBool(stream.writeFloat(pivot.y)); - wrapBool(stream.writeFloat(pivot.z)); + wrapBool(stream->writeFloat(pivot.x)); + wrapBool(stream->writeFloat(pivot.y)); + wrapBool(stream->writeFloat(pivot.z)); - wrapBool(stream.writeBool(false)); // surface + wrapBool(stream->writeBool(false)); // surface // has surface - set to false otherwise // the following data is needed: // 3 int start @@ -103,30 +114,30 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: // 1 int normal possible values: [0,1][2,3][4,5] // followed by surface width * surface height bytes - wrapBool(stream.writeFloat(0.0f)); // lod scale - wrapBool(stream.writeFloat(0.0f)); // lod pivot x - wrapBool(stream.writeFloat(0.0f)); // lod pivot y - wrapBool(stream.writeFloat(0.0f)); // lod pivot z + wrapBool(stream->writeFloat(0.0f)); // lod scale + wrapBool(stream->writeFloat(0.0f)); // lod pivot x + wrapBool(stream->writeFloat(0.0f)); // lod pivot y + wrapBool(stream->writeFloat(0.0f)); // lod pivot z int lodLevels = 1; - wrapBool(stream.writeInt32(lodLevels)); + wrapBool(stream->writeInt32(lodLevels)); for (int lod = 0; lod < lodLevels; ++lod) { - wrapBool(stream.writeUInt32(0)); // texture dim x - wrapBool(stream.writeUInt32(0)); // texture dim y - wrapBool(stream.writeUInt32(0)); // zipped size for the rgba texture(s) + wrapBool(stream->writeUInt32(0)); // texture dim x + wrapBool(stream->writeUInt32(0)); // texture dim y + wrapBool(stream->writeUInt32(0)); // zipped size for the rgba texture(s) // followed by the compressed data for (int i = 0; i < 6; ++i) { const int quadAmount = 0; - wrapBool(stream.writeUInt32(quadAmount)); + wrapBool(stream->writeUInt32(quadAmount)); #if 0 for (int i = 0; i < quadAmount; ++i) { for (int j = 0; j < 4; ++j) { - stream.writeFloat(vertices[j].x); - stream.writeFloat(vertices[j].y); - stream.writeFloat(vertices[j].z); - stream.writeInt32(u[j]); - stream.writeInt32(v[j]); + stream->writeFloat(vertices[j].x); + stream->writeFloat(vertices[j].y); + stream->writeFloat(vertices[j].z); + stream->writeInt32(u[j]); + stream->writeInt32(v[j]); } } #endif @@ -146,61 +157,61 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: // albedo palette for (int i = 0; i < numColors; ++i) { const core::RGBA &matcolor = palette.color(i); - wrapBool(stream.writeUInt8(matcolor.r)) - wrapBool(stream.writeUInt8(matcolor.g)) - wrapBool(stream.writeUInt8(matcolor.b)) - wrapBool(stream.writeUInt8(matcolor.a)) + wrapBool(stream->writeUInt8(matcolor.r)) + wrapBool(stream->writeUInt8(matcolor.g)) + wrapBool(stream->writeUInt8(matcolor.b)) + wrapBool(stream->writeUInt8(matcolor.a)) } for (int i = numColors; i < palette::PaletteMaxColors; ++i) { - wrapBool(stream.writeUInt8(255)) - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(255)) - wrapBool(stream.writeUInt8(255)) + wrapBool(stream->writeUInt8(255)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(255)) + wrapBool(stream->writeUInt8(255)) } // emissive palette for (int i = 0; i < numColors; ++i) { const bool emissive = palette.hasEmit(i); if (emissive) { const core::RGBA &glowcolor = palette.emitColor(i); - wrapBool(stream.writeUInt8(glowcolor.r)) - wrapBool(stream.writeUInt8(glowcolor.g)) - wrapBool(stream.writeUInt8(glowcolor.b)) - wrapBool(stream.writeUInt8(glowcolor.a)) + wrapBool(stream->writeUInt8(glowcolor.r)) + wrapBool(stream->writeUInt8(glowcolor.g)) + wrapBool(stream->writeUInt8(glowcolor.b)) + wrapBool(stream->writeUInt8(glowcolor.a)) } else { - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(255)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(255)) } } for (int i = numColors; i < palette::PaletteMaxColors; ++i) { - wrapBool(stream.writeUInt8(255)) - wrapBool(stream.writeUInt8(0)) - wrapBool(stream.writeUInt8(255)) - wrapBool(stream.writeUInt8(255)) + wrapBool(stream->writeUInt8(255)) + wrapBool(stream->writeUInt8(0)) + wrapBool(stream->writeUInt8(255)) + wrapBool(stream->writeUInt8(255)) } int chunkAmount = 0; - wrapBool(stream.writeUInt8(chunkAmount)); + wrapBool(stream->writeUInt8(chunkAmount)); // always false - but the format support multiple chunks - so leave this here as a reference for (int c = 0; c < chunkAmount; ++c) { core::String id = ""; - wrapBool(stream.writeString(id, true)) + wrapBool(stream->writeString(id, true)) uint8_t offset = 0; - wrapBool(stream.writeUInt8(offset)) + wrapBool(stream->writeUInt8(offset)) uint8_t chunkLength = 0; - wrapBool(stream.writeUInt8(chunkLength)) + wrapBool(stream->writeUInt8(chunkLength)) } - wrapBool(stream.writeUInt8(numColors)) + wrapBool(stream->writeUInt8(numColors)) for (int i = 0; i < numColors; ++i) { const core::RGBA &matcolor = palette.color(i); - wrapBool(stream.writeUInt8(matcolor.b)) - wrapBool(stream.writeUInt8(matcolor.g)) - wrapBool(stream.writeUInt8(matcolor.r)) - wrapBool(stream.writeUInt8(matcolor.a)) + wrapBool(stream->writeUInt8(matcolor.b)) + wrapBool(stream->writeUInt8(matcolor.g)) + wrapBool(stream->writeUInt8(matcolor.r)) + wrapBool(stream->writeUInt8(matcolor.a)) const bool emissive = palette.hasEmit(i); - wrapBool(stream.writeBool(emissive)) + wrapBool(stream->writeBool(emissive)) } int models = (int)sceneGraph.size(scenegraph::SceneGraphNodeType::AllModels); @@ -208,13 +219,13 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: Log::warn("Failed to save to vxm - max model size exceeded"); return false; } - wrapBool(stream.writeUInt8(models)) + wrapBool(stream->writeUInt8(models)) for (auto iter = sceneGraph.beginAllModels(); iter != sceneGraph.end(); ++iter) { const scenegraph::SceneGraphNode &node = *iter; voxel::RawVolume::Sampler sampler(sceneGraph.resolveVolume(node)); - wrapBool(stream.writeString(node.name(), true)) - wrapBool(stream.writeBool(node.visible())) + wrapBool(stream->writeString(node.name(), true)) + wrapBool(stream->writeBool(node.visible())) uint32_t rleCount = 0u; voxel::Voxel prevVoxel; @@ -230,7 +241,7 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const voxel::Voxel &voxel = sampler.voxel(); if (prevVoxel.getColor() != voxel.getColor() || voxel.getMaterial() != prevVoxel.getMaterial() || rleCount >= 255) { - wrapBool(writeRLE(stream, rleCount, prevVoxel, node.palette(), palette)) + wrapBool(writeRLE(*stream, rleCount, prevVoxel, node.palette(), palette)) prevVoxel = voxel; rleCount = 0; } else if (firstLoop) { @@ -242,12 +253,12 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: } } if (rleCount > 0) { - wrapBool(writeRLE(stream, rleCount, prevVoxel, node.palette(), palette)) + wrapBool(writeRLE(*stream, rleCount, prevVoxel, node.palette(), palette)) } - wrapBool(stream.writeUInt8(0)); + wrapBool(stream->writeUInt8(0)); } - wrapBool(stream.writeBool(false)); + wrapBool(stream->writeBool(false)); // has surface - set to false otherwise // the following data is needed: // 3 int start @@ -257,13 +268,18 @@ bool VXMFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: return true; } -bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool VXMFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint8_t magic[4]; - wrap(stream.readUInt8(magic[0])) - wrap(stream.readUInt8(magic[1])) - wrap(stream.readUInt8(magic[2])) - wrap(stream.readUInt8(magic[3])) + wrap(stream->readUInt8(magic[0])) + wrap(stream->readUInt8(magic[1])) + wrap(stream->readUInt8(magic[2])) + wrap(stream->readUInt8(magic[3])) if (magic[0] != 'V' || magic[1] != 'X' || magic[2] != 'M') { Log::error("Could not load vxm file: Invalid magic found (%c%c%c%c)", magic[0], magic[1], magic[2], magic[3]); return false; @@ -288,18 +304,18 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead glm::uvec3 size(0); Log::debug("Found vxm%i", version); if (version >= 6) { - wrap(stream.readUInt32(size.x)); - wrap(stream.readUInt32(size.y)); - wrap(stream.readUInt32(size.z)); + wrap(stream->readUInt32(size.x)); + wrap(stream->readUInt32(size.y)); + wrap(stream->readUInt32(size.z)); } if (version >= 5) { - wrap(stream.readFloat(normalizedPivot.x)); - wrap(stream.readFloat(normalizedPivot.y)); - wrap(stream.readFloat(normalizedPivot.z)); + wrap(stream->readFloat(normalizedPivot.x)); + wrap(stream->readFloat(normalizedPivot.y)); + wrap(stream->readFloat(normalizedPivot.z)); } if (version >= 9) { uint8_t surface; - wrap(stream.readUInt8(surface)) + wrap(stream->readUInt8(surface)) if (surface) { uint32_t skipWidth = 0u; uint32_t skipHeight = 0u; @@ -308,16 +324,16 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead uint32_t normal; // since version 10 the start and end values are floats // but for us this fact doesn't matter - wrap(stream.readUInt32(startx)) - wrap(stream.readUInt32(starty)) - wrap(stream.readUInt32(startz)) - wrap(stream.readUInt32(endx)) - wrap(stream.readUInt32(endy)) - wrap(stream.readUInt32(endz)) - wrap(stream.readUInt32(normal)) + wrap(stream->readUInt32(startx)) + wrap(stream->readUInt32(starty)) + wrap(stream->readUInt32(startz)) + wrap(stream->readUInt32(endx)) + wrap(stream->readUInt32(endy)) + wrap(stream->readUInt32(endz)) + wrap(stream->readUInt32(normal)) if (version >= 10) { - wrap(stream.readUInt32(skipWidth)) - wrap(stream.readUInt32(skipHeight)) + wrap(stream->readUInt32(skipWidth)) + wrap(stream->readUInt32(skipHeight)) } else { switch (normal) { case 0: @@ -337,25 +353,25 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead break; } } - stream.skip(skipWidth * skipHeight); + stream->skip(skipWidth * skipHeight); } } if (version >= 8) { float dummy; // since version 'A' - wrap(stream.readFloat(dummy)); // lod scale - wrap(stream.readFloat(dummy)); // lod pivot x - wrap(stream.readFloat(dummy)); // lod pivot y - wrap(stream.readFloat(dummy)); // lod pivot z + wrap(stream->readFloat(dummy)); // lod scale + wrap(stream->readFloat(dummy)); // lod pivot x + wrap(stream->readFloat(dummy)); // lod pivot y + wrap(stream->readFloat(dummy)); // lod pivot z } uint32_t lodLevels = 1; if (version >= 7) { - wrap(stream.readUInt32(lodLevels)); + wrap(stream->readUInt32(lodLevels)); } for (uint32_t lodLevel = 0u; lodLevel < lodLevels; ++lodLevel) { glm::uvec2 textureDim; - wrap(stream.readUInt32(textureDim.x)); - wrap(stream.readUInt32(textureDim.y)); + wrap(stream->readUInt32(textureDim.x)); + wrap(stream->readUInt32(textureDim.y)); if (glm::any(glm::greaterThan(textureDim, glm::uvec2(2048)))) { Log::warn("Size of texture exceeds the max allowed value"); return false; @@ -363,19 +379,19 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead if (version >= 11) { uint32_t size; - wrap(stream.readUInt32(size)); - stream.skip(size); // zipped pixel data + wrap(stream->readUInt32(size)); + stream->skip(size); // zipped pixel data } else if (version == 3) { uint8_t byte; do { - wrap(stream.readUInt8(byte)); + wrap(stream->readUInt8(byte)); if (byte != 0u) { - stream.skip(3); + stream->skip(3); } } while (byte != 0); } else { uint32_t texAmount; - wrap(stream.readUInt32(texAmount)); + wrap(stream->readUInt32(texAmount)); if (texAmount > 0xFFFF) { Log::warn("Size of textures exceeds the max allowed value: %i", texAmount); return false; @@ -384,17 +400,17 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead Log::debug("texAmount: %i", (int)texAmount); for (uint32_t t = 0u; t < texAmount; t++) { char textureId[1024]; - wrapBool(stream.readString(sizeof(textureId), textureId, true)) + wrapBool(stream->readString(sizeof(textureId), textureId, true)) if (version >= 6) { uint32_t texZipped; - wrap(stream.readUInt32(texZipped)); - stream.skip(texZipped); + wrap(stream->readUInt32(texZipped)); + stream->skip(texZipped); } else { Log::debug("tex: %i: %s", (int)t, textureId); uint32_t px = 0u; for (;;) { uint8_t rleStride; - wrap(stream.readUInt8(rleStride)); + wrap(stream->readUInt8(rleStride)); if (rleStride == 0u) { break; } @@ -403,7 +419,7 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead glm::u8vec3h rgb; }; static_assert(sizeof(TexColor) == 3, "Unexpected TexColor size"); - stream.skip(sizeof(TexColor)); + stream->skip(sizeof(TexColor)); px += rleStride; if (px > textureDim.x * textureDim.y * sizeof(TexColor)) { Log::error("RLE texture chunk exceeds max allowed size"); @@ -415,7 +431,7 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead for (int i = 0; i < 6; ++i) { uint32_t quadAmount; - wrap(stream.readUInt32(quadAmount)); + wrap(stream->readUInt32(quadAmount)); if (quadAmount > 0x40000U) { Log::warn("Size of quads exceeds the max allowed value"); return false; @@ -425,14 +441,14 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead glm::ivec2h uv; }; static_assert(sizeof(QuadVertex) == 20, "Unexpected QuadVertex size"); - stream.skip(quadAmount * 4 * sizeof(QuadVertex)); + stream->skip(quadAmount * 4 * sizeof(QuadVertex)); } } if (version <= 5) { - wrap(stream.readUInt32(size.x)); - wrap(stream.readUInt32(size.y)); - wrap(stream.readUInt32(size.z)); + wrap(stream->readUInt32(size.x)); + wrap(stream->readUInt32(size.y)); + wrap(stream->readUInt32(size.z)); } if (glm::any(glm::greaterThan(size, glm::uvec3(MaxRegionSize)))) { @@ -448,34 +464,34 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead if (version >= 11) { // TODO: parse the material emit data - stream.skip(256l * 4l); // palette data rgba for albedo materials - stream.skip(256l * 4l); // palette data rgba for emissive materials + stream->skip(256l * 4l); // palette data rgba for albedo materials + stream->skip(256l * 4l); // palette data rgba for emissive materials uint8_t chunkAmount; // palette chunks - wrap(stream.readUInt8(chunkAmount)); + wrap(stream->readUInt8(chunkAmount)); for (int i = 0; i < (int)chunkAmount; ++i) { char chunkId[1024]; - wrapBool(stream.readString(sizeof(chunkId), chunkId, true)) - stream.skip(1); // chunk offset - stream.skip(1); // chunk length + wrapBool(stream->readString(sizeof(chunkId), chunkId, true)) + stream->skip(1); // chunk offset + stream->skip(1); // chunk length } } uint8_t materialAmount; - wrap(stream.readUInt8(materialAmount)); + wrap(stream->readUInt8(materialAmount)); Log::debug("Palette of size %i", (int)materialAmount); for (int i = 0; i < (int)materialAmount; ++i) { uint8_t blue; - wrap(stream.readUInt8(blue)); + wrap(stream->readUInt8(blue)); uint8_t green; - wrap(stream.readUInt8(green)); + wrap(stream->readUInt8(green)); uint8_t red; - wrap(stream.readUInt8(red)); + wrap(stream->readUInt8(red)); uint8_t alpha; - wrap(stream.readUInt8(alpha)); + wrap(stream->readUInt8(alpha)); if (version > 3) { uint8_t emissive; - wrap(stream.readUInt8(emissive)); + wrap(stream->readUInt8(emissive)); palette.setColor(i, core::RGBA(red, green, blue, alpha)); if (emissive) { palette.setEmit(i, 1.0f); @@ -488,7 +504,7 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead uint8_t maxModels = 1; if (version >= 12) { - wrap(stream.readUInt8(maxModels)); + wrap(stream->readUInt8(maxModels)); } for (uint8_t model = 0; model < maxModels; ++model) { @@ -496,21 +512,21 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead bool visible = true; char modelName[1024]; if (version >= 12) { - wrapBool(stream.readString(sizeof(modelName), modelName, true)) - visible = stream.readBool(); + wrapBool(stream->readString(sizeof(modelName), modelName, true)) + visible = stream->readBool(); } else { core::string::formatBuf(modelName, sizeof(modelName), "Model %i", model); } voxel::RawVolume *volume = new voxel::RawVolume(region); for (;;) { uint8_t length; - wrapDelete(stream.readUInt8(length), volume); + wrapDelete(stream->readUInt8(length), volume); if (length == 0u) { break; } uint8_t matIdx; - wrapDelete(stream.readUInt8(matIdx), volume); + wrapDelete(stream->readUInt8(matIdx), volume); if (matIdx == EMPTY_PALETTE) { idx += length; continue; @@ -547,39 +563,39 @@ bool VXMFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead if (version >= 10) { uint8_t surface; - wrap(stream.readUInt8(surface)) + wrap(stream->readUInt8(surface)) if (surface) { uint32_t startx, starty, startz; uint32_t endx, endy, endz; uint32_t normal; - wrap(stream.readUInt32(startx)) - wrap(stream.readUInt32(starty)) - wrap(stream.readUInt32(startz)) - wrap(stream.readUInt32(endx)) - wrap(stream.readUInt32(endy)) - wrap(stream.readUInt32(endz)) - wrap(stream.readUInt32(normal)) + wrap(stream->readUInt32(startx)) + wrap(stream->readUInt32(starty)) + wrap(stream->readUInt32(startz)) + wrap(stream->readUInt32(endx)) + wrap(stream->readUInt32(endy)) + wrap(stream->readUInt32(endz)) + wrap(stream->readUInt32(normal)) } // here might be another byte - but it isn't written everytime uint8_t templateModelResized; - stream.peekUInt8(templateModelResized); - if (!stream.eos() && templateModelResized != 127) { - stream.readBool(); // templateModelResized + stream->peekUInt8(templateModelResized); + if (!stream->eos() && templateModelResized != 127) { + stream->readBool(); // templateModelResized } - if (!stream.eos()) { + if (!stream->eos()) { uint8_t sentinelByte; - wrap(stream.readUInt8(sentinelByte)) + wrap(stream->readUInt8(sentinelByte)) if (sentinelByte != 127) { Log::warn("Sentinel byte is not 127"); return true; // true anyway, because the additional palette data is optional } uint8_t selectedPalette; - wrap(stream.readUInt8(selectedPalette)) + wrap(stream->readUInt8(selectedPalette)) if (selectedPalette != 255) { for (int i = 0; i < 255; ++i) { uint32_t color; - wrap(stream.readUInt32(color)) - /*bool emissive =*/ stream.readBool(); + wrap(stream->readUInt32(color)) + /*bool emissive =*/ stream->readBool(); } } } diff --git a/src/modules/voxelformat/private/sandbox/VXMFormat.h b/src/modules/voxelformat/private/sandbox/VXMFormat.h index 254a4abba4..051e74c9f1 100644 --- a/src/modules/voxelformat/private/sandbox/VXMFormat.h +++ b/src/modules/voxelformat/private/sandbox/VXMFormat.h @@ -20,14 +20,14 @@ class VXMFormat : public PaletteFormat { private: bool writeRLE(io::WriteStream &stream, int rleCount, const voxel::Voxel &voxel, const palette::Palette &nodePalette, const palette::Palette &palette) const; - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/sandbox/VXRFormat.cpp b/src/modules/voxelformat/private/sandbox/VXRFormat.cpp index 5652df308e..42df7bef09 100644 --- a/src/modules/voxelformat/private/sandbox/VXRFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXRFormat.cpp @@ -6,13 +6,13 @@ #include "VXAFormat.h" #include "VXMFormat.h" #include "app/App.h" -#include "core/Color.h" #include "core/Common.h" #include "core/FourCC.h" #include "core/GLM.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" -#include "io/FileStream.h" +#include "io/Archive.h" #include "io/Filesystem.h" #include "io/Stream.h" #include "scenegraph/SceneGraph.h" @@ -40,8 +40,8 @@ namespace voxelformat { } bool VXRFormat::saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node, - const core::String &filename, io::SeekableWriteStream &stream, - const SaveContext &ctx) { + const core::String &filename, const io::ArchivePtr &archive, + io::SeekableWriteStream &stream, const SaveContext &ctx) { core::String name = node.name(); if (name.empty()) { name = core::string::format("%i", node.id()); @@ -52,13 +52,6 @@ bool VXRFormat::saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, cons const core::String finalName = baseName + name + ".vxm"; wrapBool(stream.writeString(finalName, true)) const core::String fullPath = core::string::stripExtension(filename) + name + ".vxm"; - VXMFormat f; - io::FilePtr outputFile = io::filesystem()->open(fullPath, io::FileMode::SysWrite); - if (!outputFile) { - Log::error("Failed to open %s for writing", fullPath.c_str()); - return false; - } - io::FileStream wstream(outputFile); scenegraph::SceneGraph newSceneGraph; scenegraph::SceneGraphNode newNode(scenegraph::SceneGraphNodeType::Model); copyNode(node, newNode, false); @@ -66,7 +59,8 @@ bool VXRFormat::saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, cons newNode.setVolume(sceneGraph.resolveVolume(node)); } newSceneGraph.emplace(core::move(newNode)); - wrapBool(f.save(newSceneGraph, fullPath, wstream, ctx)) + VXMFormat f; + wrapBool(f.save(newSceneGraph, fullPath, archive, ctx)) Log::debug("Saved the model to %s", fullPath.c_str()); } else { wrapBool(stream.writeString("", true)) @@ -78,7 +72,7 @@ bool VXRFormat::saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, cons wrapBool(stream.writeInt32(childCount)); for (int child : node.children()) { const scenegraph::SceneGraphNode &cnode = sceneGraph.node(child); - wrapBool(saveRecursiveNode(sceneGraph, cnode, filename, stream, ctx)) + wrapBool(saveRecursiveNode(sceneGraph, cnode, filename, archive, stream, ctx)) } return true; } @@ -116,7 +110,12 @@ bool VXRFormat::saveNodeProperties(const scenegraph::SceneGraphNode *node, io::S } bool VXRFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode &root = sceneGraph.root(); const scenegraph::SceneGraphNodeChildren &children = root.children(); const int childCount = (int)children.size(); @@ -124,56 +123,44 @@ bool VXRFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: Log::error("Empty scene graph - can't save vxr"); return false; } - wrapBool(stream.writeUInt32(FourCC('V', 'X', 'R', '9'))) + wrapBool(stream->writeUInt32(FourCC('V', 'X', 'R', '9'))) scenegraph::SceneGraphAnimationIds animationIds = sceneGraph.animations(); if (animationIds.empty()) { animationIds.push_back("Idle"); } const core::String defaultAnim = animationIds[0]; - wrapBool(stream.writeString(defaultAnim.c_str(), true)) - wrapBool(stream.writeInt32(1)) - wrapBool(stream.writeString(stringProperty(&root, "basetemplate"), true)) - wrapBool(stream.writeBool(boolProperty(&root, "static", false))) + wrapBool(stream->writeString(defaultAnim.c_str(), true)) + wrapBool(stream->writeInt32(1)) + wrapBool(stream->writeString(stringProperty(&root, "basetemplate"), true)) + wrapBool(stream->writeBool(boolProperty(&root, "static", false))) if (childCount != 1 || sceneGraph.node(children[0]).name() != SANDBOX_CONTROLLER_NODE) { // add controller node (see VXAFormat) - wrapBool(stream.writeString(SANDBOX_CONTROLLER_NODE, true)) - wrapBool(stream.writeString("", true)) + wrapBool(stream->writeString(SANDBOX_CONTROLLER_NODE, true)) + wrapBool(stream->writeString("", true)) - wrapBool(saveNodeProperties(nullptr, stream)) + wrapBool(saveNodeProperties(nullptr, *stream)) Log::debug("add controller node with %i children", childCount); - wrapBool(stream.writeInt32(childCount)) + wrapBool(stream->writeInt32(childCount)) } for (int child : children) { const scenegraph::SceneGraphNode &node = sceneGraph.node(child); - wrapBool(saveRecursiveNode(sceneGraph, node, filename, stream, ctx)) + wrapBool(saveRecursiveNode(sceneGraph, node, filename, archive, *stream, ctx)) } const core::String &basePath = core::string::extractPath(filename); const core::String &baseName = core::string::extractFilename(filename); for (const core::String &id : animationIds) { const core::String &vxaFilename = core::string::format("%s.%s.vxa", baseName.c_str(), id.c_str()); const core::String &vxaPath = core::string::path(basePath, vxaFilename); - io::FilePtr outputFile = io::filesystem()->open(vxaPath, io::FileMode::SysWrite); - if (!outputFile) { - Log::error("Failed to open %s for writing", vxaPath.c_str()); - return false; - } - io::FileStream wstream(outputFile); - wrapBool(saveVXA(sceneGraph, vxaPath, wstream, id, ctx)) + wrapBool(saveVXA(sceneGraph, vxaPath, archive, id, ctx)) } return true; } -bool VXRFormat::loadChildVXM(const core::String &vxmPath, scenegraph::SceneGraph &sceneGraph, +bool VXRFormat::loadChildVXM(const core::String &vxmPath, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, scenegraph::SceneGraphNode &node, int version, const LoadContext &ctx) { - const io::FilePtr &file = io::filesystem()->open(vxmPath); - if (!file->validHandle()) { - Log::error("Could not open file '%s'", vxmPath.c_str()); - return false; - } - io::FileStream stream(file); VXMFormat f; scenegraph::SceneGraph childSceneGraph; - if (!f.load(vxmPath, stream, childSceneGraph, ctx)) { + if (!f.load(vxmPath, archive, childSceneGraph, ctx)) { Log::error("Failed to load '%s'", vxmPath.c_str()); return false; } @@ -311,7 +298,7 @@ bool VXRFormat::importChildVersion3AndEarlier(const core::String &filename, io:: } // the positions that were part of the previous vxr versions are now in vxa -bool VXRFormat::importChild(const core::String &vxmPath, io::SeekableReadStream &stream, +bool VXRFormat::importChild(const core::String &vxmPath, const io::ArchivePtr &archive, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, int parent, const LoadContext &ctx) { scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); char id[1024]; @@ -322,7 +309,7 @@ bool VXRFormat::importChild(const core::String &vxmPath, io::SeekableReadStream if (filename[0] != '\0') { Log::debug("load vxm %s", filename); const core::String modelPath = core::string::path(core::string::extractPath(vxmPath), filename); - if (!loadChildVXM(modelPath, sceneGraph, node, version, ctx)) { + if (!loadChildVXM(modelPath, archive, sceneGraph, node, version, ctx)) { Log::warn("Failed to attach model for id '%s' with filename %s (%s)", id, filename, modelPath.c_str()); } } @@ -390,19 +377,24 @@ bool VXRFormat::importChild(const core::String &vxmPath, io::SeekableReadStream int32_t children = 0; wrap(stream.readInt32(children)) for (int32_t i = 0; i < children; ++i) { - wrapBool(importChild(vxmPath, stream, sceneGraph, version, nodeId != InvalidNodeId ? nodeId : parent, ctx)) + wrapBool(importChild(vxmPath, archive, stream, sceneGraph, version, nodeId != InvalidNodeId ? nodeId : parent, ctx)) } } return true; } -image::ImagePtr VXRFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr VXRFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { - const core::String imageName = filename + ".png"; - return image::loadImage(imageName); + const core::String image = filename + ".png"; + core::ScopedPtr stream(archive->readStream(image)); + if (!stream) { + Log::error("Could not load file %s", image.c_str()); + return image::ImagePtr(); + } + return image::loadImage(image, *stream, stream->size()); } -bool VXRFormat::loadGroupsVersion3AndEarlier(const core::String &filename, io::SeekableReadStream &stream, +bool VXRFormat::loadGroupsVersion3AndEarlier(const core::String &filename, const io::ArchivePtr &archive, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, const LoadContext &ctx) { uint32_t childAndModelCount; wrap(stream.readUInt32(childAndModelCount)) @@ -426,7 +418,7 @@ bool VXRFormat::loadGroupsVersion3AndEarlier(const core::String &filename, io::S wrapBool(stream.readString(sizeof(vxmFilename), vxmFilename, true)) if (vxmFilename[0] != '\0') { const core::String modelPath = core::string::path(core::string::extractPath(filename), vxmFilename); - if (!loadChildVXM(modelPath, sceneGraph, *node, version, ctx)) { + if (!loadChildVXM(modelPath, archive, sceneGraph, *node, version, ctx)) { Log::warn("Failed to attach model for %s with filename %s", nodeId, modelPath.c_str()); } } @@ -475,7 +467,7 @@ bool VXRFormat::handleVersion8AndLater(io::SeekableReadStream &stream, scenegrap return true; } -bool VXRFormat::loadGroupsVersion4AndLater(const core::String &filename, io::SeekableReadStream &stream, +bool VXRFormat::loadGroupsVersion4AndLater(const core::String &filename, const io::ArchivePtr &archive, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, const LoadContext &ctx) { const int rootNodeId = sceneGraph.root().id(); scenegraph::SceneGraphNode &rootNode = sceneGraph.node(rootNodeId); @@ -496,7 +488,7 @@ bool VXRFormat::loadGroupsVersion4AndLater(const core::String &filename, io::See Log::debug("Found %i children", children); for (int32_t i = 0; i < children; ++i) { - wrapBool(importChild(filename, stream, sceneGraph, version, rootNodeId, ctx)) + wrapBool(importChild(filename, archive, stream, sceneGraph, version, rootNodeId, ctx)) } const core::String &basePath = core::string::extractPath(filename); @@ -512,7 +504,7 @@ bool VXRFormat::loadGroupsVersion4AndLater(const core::String &filename, io::See } Log::debug("Load vxa: %s", entry.name.c_str()); const core::String &vxaPath = core::string::path(basePath, entry.name); - if (!loadVXA(sceneGraph, vxaPath, ctx)) { + if (!loadVXA(sceneGraph, vxaPath, archive, ctx)) { Log::warn("Failed to load %s", vxaPath.c_str()); } } @@ -525,29 +517,29 @@ bool VXRFormat::loadGroupsVersion4AndLater(const core::String &filename, io::See } bool VXRFormat::saveVXA(const scenegraph::SceneGraph &sceneGraph, const core::String &vxaPath, - io::SeekableWriteStream &vxaStream, const core::String &animation, const SaveContext &ctx) { + const io::ArchivePtr &archive, const core::String &animation, const SaveContext &ctx) { VXAFormat f; - return f.save(sceneGraph, vxaPath, vxaStream, ctx); + return f.save(sceneGraph, vxaPath, archive, ctx); } -bool VXRFormat::loadVXA(scenegraph::SceneGraph &sceneGraph, const core::String &vxaPath, const LoadContext &ctx) { +bool VXRFormat::loadVXA(scenegraph::SceneGraph &sceneGraph, const core::String &vxaPath, const io::ArchivePtr &archive, const LoadContext &ctx) { Log::debug("Try to load a vxa file: %s", vxaPath.c_str()); - const io::FilePtr &file = io::filesystem()->open(vxaPath); - if (!file->validHandle()) { - return false; - } - io::FileStream stream(file); VXAFormat format; - return format.load(vxaPath, stream, sceneGraph, ctx); + return format.load(vxaPath, archive, sceneGraph, ctx); } -bool VXRFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool VXRFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint8_t magic[4]; - wrap(stream.readUInt8(magic[0])) - wrap(stream.readUInt8(magic[1])) - wrap(stream.readUInt8(magic[2])) - wrap(stream.readUInt8(magic[3])) + wrap(stream->readUInt8(magic[0])) + wrap(stream->readUInt8(magic[1])) + wrap(stream->readUInt8(magic[2])) + wrap(stream->readUInt8(magic[3])) if (magic[0] != 'V' || magic[1] != 'X' || magic[2] != 'R') { Log::error("Could not load vxr file: Invalid magic found (%c%c%c%c)", magic[0], magic[1], magic[2], magic[3]); return false; @@ -570,9 +562,9 @@ bool VXRFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } if (version <= 3) { - return loadGroupsVersion3AndEarlier(filename, stream, sceneGraph, version, ctx); + return loadGroupsVersion3AndEarlier(filename, archive, *stream, sceneGraph, version, ctx); } - return loadGroupsVersion4AndLater(filename, stream, sceneGraph, version, ctx); + return loadGroupsVersion4AndLater(filename, archive, *stream, sceneGraph, version, ctx); } #undef wrap diff --git a/src/modules/voxelformat/private/sandbox/VXRFormat.h b/src/modules/voxelformat/private/sandbox/VXRFormat.h index 1774f92ed0..d89db51aab 100644 --- a/src/modules/voxelformat/private/sandbox/VXRFormat.h +++ b/src/modules/voxelformat/private/sandbox/VXRFormat.h @@ -22,36 +22,38 @@ class VXRFormat : public PaletteFormat { bool onlyOnePalette() const override { return false; } - bool loadChildVXM(const core::String &vxmPath, scenegraph::SceneGraph &sceneGraph, scenegraph::SceneGraphNode &node, - int version, const LoadContext &ctx); + bool loadChildVXM(const core::String &vxmPath, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, + scenegraph::SceneGraphNode &node, int version, const LoadContext &ctx); bool handleVersion8AndLater(io::SeekableReadStream &stream, scenegraph::SceneGraphNode &node, const LoadContext &ctx); - bool importChild(const core::String &vxmPath, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, - int version, int parent, const LoadContext &ctx); + bool importChild(const core::String &vxmPath, const io::ArchivePtr &archive, io::SeekableReadStream &stream, + scenegraph::SceneGraph &sceneGraph, int version, int parent, const LoadContext &ctx); - bool loadGroupsVersion4AndLater(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsVersion4AndLater(const core::String &filename, const io::ArchivePtr &archive, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, const LoadContext &ctx); - bool loadGroupsVersion3AndEarlier(const core::String &filename, io::SeekableReadStream &stream, - scenegraph::SceneGraph &sceneGraph, int version, const LoadContext &ctx); + bool loadGroupsVersion3AndEarlier(const core::String &filename, const io::ArchivePtr &archive, + io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, + const LoadContext &ctx); bool importChildVersion3AndEarlier(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, int version, int parent, const LoadContext &ctx); bool saveRecursiveNode(const scenegraph::SceneGraph &sceneGraph, const scenegraph::SceneGraphNode &node, - const core::String &filename, io::SeekableWriteStream &stream, const SaveContext &ctx); + const core::String &filename, const io::ArchivePtr &archive, io::SeekableWriteStream &stream, + const SaveContext &ctx); bool saveNodeProperties(const scenegraph::SceneGraphNode *node, io::SeekableWriteStream &stream); bool saveVXA(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const core::String &animation, const SaveContext &ctx); - bool loadVXA(scenegraph::SceneGraph &sceneGraph, const core::String &vxaPath, const LoadContext &ctx); - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + const io::ArchivePtr &archive, const core::String &animation, const SaveContext &ctx); + bool loadVXA(scenegraph::SceneGraph &sceneGraph, const core::String &vxaPath, const io::ArchivePtr &archive, const LoadContext &ctx); + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/sandbox/VXTFormat.cpp b/src/modules/voxelformat/private/sandbox/VXTFormat.cpp index 25a89f1675..ad62cd6a19 100644 --- a/src/modules/voxelformat/private/sandbox/VXTFormat.cpp +++ b/src/modules/voxelformat/private/sandbox/VXTFormat.cpp @@ -4,11 +4,10 @@ #include "VXTFormat.h" #include "VXMFormat.h" -#include "app/App.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" -#include "io/FileStream.h" -#include "io/Filesystem.h" +#include "io/Archive.h" #include "io/ZipReadStream.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphUtil.h" @@ -30,9 +29,14 @@ namespace voxelformat { return false; \ } -bool VXTFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &in, +bool VXTFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &, const LoadContext &ctx) { - io::ZipReadStream stream(in, (int)in.size()); + core::ScopedPtr in(archive->readStream(filename)); + if (!in) { + Log::error("Could not load file %s", filename.c_str()); + return false; + } + io::ZipReadStream stream(*in, (int)in->size()); uint8_t magic[4]; wrap(stream.readUInt8(magic[0])) wrap(stream.readUInt8(magic[1])) @@ -61,15 +65,9 @@ bool VXTFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead for (int32_t i = 0; i < models; ++i) { char path[1024]; wrapBool(stream.readString(sizeof(path), path, true)) - const io::FilePtr &file = io::filesystem()->open(path); - if (!file->validHandle()) { - Log::warn("Could not load file %s", path); - continue; - } - io::FileStream childStream(file); VXMFormat f; scenegraph::SceneGraph subGraph; - if (!f.load(path, childStream, subGraph, ctx)) { + if (!f.load(path, archive, subGraph, ctx)) { Log::warn("Failed to load vxm tile %s", path); continue; } @@ -141,7 +139,7 @@ bool VXTFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } bool VXTFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { return false; } diff --git a/src/modules/voxelformat/private/sandbox/VXTFormat.h b/src/modules/voxelformat/private/sandbox/VXTFormat.h index a20bf060a6..a0225f4eb0 100644 --- a/src/modules/voxelformat/private/sandbox/VXTFormat.h +++ b/src/modules/voxelformat/private/sandbox/VXTFormat.h @@ -15,11 +15,11 @@ namespace voxelformat { */ class VXTFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; }; } // namespace voxelformat diff --git a/src/modules/voxelformat/private/slab6/KV6Format.cpp b/src/modules/voxelformat/private/slab6/KV6Format.cpp index 7cf27c47ed..a8e33462ad 100644 --- a/src/modules/voxelformat/private/slab6/KV6Format.cpp +++ b/src/modules/voxelformat/private/slab6/KV6Format.cpp @@ -12,6 +12,7 @@ #include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/collection/DynamicArray.h" +#include "io/Archive.h" #include "io/FileStream.h" #include "io/Filesystem.h" #include "io/Stream.h" @@ -172,41 +173,46 @@ const uint32_t MAXSPRITES = 1024; return false; \ } -size_t KV6Format::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t KV6Format::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('K', 'v', 'x', 'l')) { Log::error("Invalid magic"); return 0; } uint32_t xsiz_w, ysiz_d, zsiz_h; - wrap(stream.readUInt32(xsiz_w)) - wrap(stream.readUInt32(ysiz_d)) - wrap(stream.readUInt32(zsiz_h)) + wrap(stream->readUInt32(xsiz_w)) + wrap(stream->readUInt32(ysiz_d)) + wrap(stream->readUInt32(zsiz_h)) glm::vec3 pivot; - wrap(stream.readFloat(pivot.x)) - wrap(stream.readFloat(pivot.y)) - wrap(stream.readFloat(pivot.z)) + wrap(stream->readFloat(pivot.x)) + wrap(stream->readFloat(pivot.y)) + wrap(stream->readFloat(pivot.z)) uint32_t numvoxs; - wrap(stream.readUInt32(numvoxs)) + wrap(stream->readUInt32(numvoxs)) const int64_t headerSize = 32; const int64_t xLenSize = (int64_t)(xsiz_w * sizeof(uint32_t)); const int64_t yLenSize = (int64_t)((size_t)xsiz_w * (size_t)ysiz_d * sizeof(uint16_t)); const int64_t paletteOffset = headerSize + (int64_t)(numvoxs * 8) + xLenSize + yLenSize; - if (stream.seek(paletteOffset) != -1) { - if (stream.remaining() != 0) { + if (stream->seek(paletteOffset) != -1) { + if (stream->remaining() != 0) { uint32_t palMagic; - wrap(stream.readUInt32(palMagic)) + wrap(stream->readUInt32(palMagic)) if (palMagic == FourCC('S', 'P', 'a', 'l')) { // slab6 suggest palette palette.setSize(palette::PaletteMaxColors); for (int i = 0; i < palette::PaletteMaxColors; ++i) { core::RGBA color; - wrapBool(priv::readRGBScaledColor(stream, color)) + wrapBool(priv::readRGBScaledColor(*stream, color)) palette.setColor(i, color); } } @@ -215,13 +221,13 @@ size_t KV6Format::loadPalette(const core::String &filename, io::SeekableReadStre } // SPal not found, most likely slab5 - stream.seek(headerSize); + stream->seek(headerSize); for (uint32_t c = 0u; c < numvoxs; ++c) { core::RGBA color; - wrapBool(priv::readBGRColor(stream, color)); + wrapBool(priv::readBGRColor(*stream, color)); palette.tryAdd(color, false); - wrap2(stream.skip(5)); + wrap2(stream->skip(5)); } return palette.size(); @@ -234,28 +240,26 @@ size_t KV6Format::loadPalette(const core::String &filename, io::SeekableReadStre return false; \ } -bool KV6Format::loadKFA(const core::String &filename, const voxel::RawVolume *volume, +bool KV6Format::loadKFA(const core::String &filename, const io::ArchivePtr &archive, const voxel::RawVolume *volume, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette) { - const io::FilesystemPtr &filesystem = io::filesystem(); - const io::FilePtr &kfaFile = filesystem->open(filename); - if (!kfaFile->validHandle()) { - // if there is no hva file, we still don't show an error + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); return false; } - io::FileStream stream(kfaFile); uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('K', 'w', 'l', 'k')) { Log::error("Invalid magic number"); return false; } core::String kv6Name; - wrapBool(stream.readPascalStringUInt32LE(kv6Name)) + wrapBool(stream->readPascalStringUInt32LE(kv6Name)) Log::debug("kv6Name: %s", kv6Name.c_str()); priv::KFAData kfa; uint32_t numHinge; - wrap(stream.readUInt32(numHinge)) + wrap(stream->readUInt32(numHinge)) if (numHinge >= priv::MAXSPRITES) { Log::error("Max allowed hinges exceeded: %u (max is %u)", numHinge, priv::MAXSPRITES); return false; @@ -265,43 +269,43 @@ bool KV6Format::loadKFA(const core::String &filename, const voxel::RawVolume *vo for (uint32_t i = 0; i < numHinge; ++i) { priv::KFAHinge hinge; hinge.id = i; - wrap(stream.readInt32(hinge.parent)) + wrap(stream->readInt32(hinge.parent)) for (int n = 0; n < 2; ++n) { - wrap(stream.readFloat(hinge.p[n].x)) - wrap(stream.readFloat(hinge.p[n].z)) - wrap(stream.readFloat(hinge.p[n].y)) + wrap(stream->readFloat(hinge.p[n].x)) + wrap(stream->readFloat(hinge.p[n].z)) + wrap(stream->readFloat(hinge.p[n].y)) } for (int n = 0; n < 2; ++n) { - wrap(stream.readFloat(hinge.v[n].x)) - wrap(stream.readFloat(hinge.v[n].z)) - wrap(stream.readFloat(hinge.v[n].y)) + wrap(stream->readFloat(hinge.v[n].x)) + wrap(stream->readFloat(hinge.v[n].z)) + wrap(stream->readFloat(hinge.v[n].y)) } - wrap(stream.readInt16(hinge.vmin)) - wrap(stream.readInt16(hinge.vmax)) - wrap(stream.readInt8(hinge.type)) - wrap2(stream.skip(7)) + wrap(stream->readInt16(hinge.vmin)) + wrap(stream->readInt16(hinge.vmax)) + wrap(stream->readInt8(hinge.type)) + wrap2(stream->skip(7)) kfa.hinge.push_back(hinge); } uint32_t numFrames; - wrap(stream.readUInt32(numFrames)) + wrap(stream->readUInt32(numFrames)) Log::debug("numfrm: %u", numFrames); kfa.frmval.resize(numFrames); for (uint32_t i = 0; i < numFrames; ++i) { kfa.frmval[i].reserve(numHinge); for (uint32_t j = 0; j < numHinge; ++j) { int16_t angle; - wrap(stream.readInt16(angle)) + wrap(stream->readInt16(angle)) kfa.frmval[i].push_back(angle); } } uint32_t numSequences; - wrap(stream.readUInt32(numSequences)) + wrap(stream->readUInt32(numSequences)) Log::debug("numseq: %u", numSequences); kfa.seq.reserve(numSequences); for (uint32_t i = 0; i < numSequences; ++i) { priv::KFASeqTyp seq; - wrap(stream.readInt32(seq.time)) - wrap(stream.readInt32(seq.frame)) + wrap(stream->readInt32(seq.time)) + wrap(stream->readInt32(seq.frame)) kfa.seq.push_back(seq); } @@ -360,10 +364,15 @@ bool KV6Format::loadKFA(const core::String &filename, const voxel::RawVolume *vo return true; } -bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool KV6Format::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('K', 'v', 'x', 'l')) { Log::error("Invalid magic"); return false; @@ -371,9 +380,9 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead // Dimensions of voxel. (our depth is kv6 height) uint32_t width, depth, height; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 256 || depth > 256 || height > 255) { Log::error("Dimensions exceeded: w: %i, h: %i, d: %i", width, height, depth); @@ -381,9 +390,9 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } glm::vec3 pivot; - wrap(stream.readFloat(pivot.x)) // width - wrap(stream.readFloat(pivot.z)) // depth - wrap(stream.readFloat(pivot.y)) // height + wrap(stream->readFloat(pivot.x)) // width + wrap(stream->readFloat(pivot.z)) // depth + wrap(stream->readFloat(pivot.y)) // height glm::vec3 normalizedPivot = pivot / glm::vec3(width, height, depth); @@ -394,7 +403,7 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } uint32_t numvoxs; - wrap(stream.readUInt32(numvoxs)) + wrap(stream->readUInt32(numvoxs)) Log::debug("numvoxs: %u", numvoxs); if (numvoxs > priv::MAXVOXS) { Log::error("Max allowed voxels exceeded: %u (max is %u)", numvoxs, priv::MAXVOXS); @@ -407,17 +416,17 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead const int64_t paletteOffset = headerSize + (int64_t)numvoxs * (int64_t)8 + xoffsetSize + xyoffsetSize; // palette SPal (suggested palette) added in slab6 bool slab5 = true; - if (stream.seek(paletteOffset) != -1) { - if (stream.remaining() != 0) { + if (stream->seek(paletteOffset) != -1) { + if (stream->remaining() != 0) { uint32_t palMagic = 0u; - wrap(stream.readUInt32(palMagic)) + wrap(stream->readUInt32(palMagic)) if (palMagic == FourCC('S', 'P', 'a', 'l')) { Log::debug("Found embedded palette of slab6"); slab5 = false; // slab6 palette.setSize(palette::PaletteMaxColors); for (int i = 0; i < palette::PaletteMaxColors; ++i) { core::RGBA color; - wrapBool(priv::readRGBScaledColor(stream, color)); + wrapBool(priv::readRGBScaledColor(*stream, color)); palette.setColor(i, color); } } @@ -428,17 +437,17 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } else { Log::debug("Found slab6"); } - stream.seek(headerSize); + stream->seek(headerSize); core::ScopedPtr state(new priv::State()); for (uint32_t c = 0u; c < numvoxs; ++c) { core::RGBA color; - wrapBool(priv::readBGRColor(stream, color)); - wrap2(stream.skip(1)) // slab6 always 128 - wrap(stream.readUInt8(state->voxdata[c].z)) - wrap2(stream.skip(1)) // slab6 always 0 - wrap(stream.readUInt8((uint8_t &)state->voxdata[c].vis)) - wrap(stream.readUInt8(state->voxdata[c].dir)) + wrapBool(priv::readBGRColor(*stream, color)); + wrap2(stream->skip(1)) // slab6 always 128 + wrap(stream->readUInt8(state->voxdata[c].z)) + wrap2(stream->skip(1)) // slab6 always 0 + wrap(stream->readUInt8((uint8_t &)state->voxdata[c].vis)) + wrap(stream->readUInt8(state->voxdata[c].dir)) if (slab5) { palette.tryAdd(color, false, &state->voxdata[c].col, false); @@ -450,13 +459,13 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } for (uint32_t x = 0u; x < width; ++x) { - wrap(stream.readInt32(state->xoffsets[x])) + wrap(stream->readInt32(state->xoffsets[x])) Log::debug("xoffsets[%u]: %i", x, state->xoffsets[x]); } for (uint32_t x = 0u; x < width; ++x) { for (uint32_t y = 0u; y < depth; ++y) { - wrap(stream.readUInt16(state->xyoffsets[x][y])) + wrap(stream->readUInt16(state->xyoffsets[x][y])) Log::debug("xyoffsets[%u][%u]: %u", x, y, state->xyoffsets[x][y]); } } @@ -475,9 +484,11 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } const core::String &basename = core::string::stripExtension(filename); - if (loadKFA(basename + ".kfa", volume, sceneGraph, palette)) { - delete volume; - return true; + if (archive->exists(basename + ".kfa")) { + if (loadKFA(basename + ".kfa", archive, volume, sceneGraph, palette)) { + delete volume; + return true; + } } scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); node.setVolume(volume, true); @@ -500,7 +511,12 @@ bool KV6Format::loadGroupsPalette(const core::String &filename, io::SeekableRead } bool KV6Format::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -542,55 +558,55 @@ bool KV6Format::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: return false; } - wrapBool(stream.writeUInt32(FourCC('K', 'v', 'x', 'l'))) + wrapBool(stream->writeUInt32(FourCC('K', 'v', 'x', 'l'))) const int xsiz_w = dim.x; // flip y and z here const int ysiz_d = dim.z; const int zsiz_h = dim.y; - wrapBool(stream.writeUInt32(xsiz_w)) - wrapBool(stream.writeUInt32(ysiz_d)) - wrapBool(stream.writeUInt32(zsiz_h)) + wrapBool(stream->writeUInt32(xsiz_w)) + wrapBool(stream->writeUInt32(ysiz_d)) + wrapBool(stream->writeUInt32(zsiz_h)) glm::vec3 pivot = node->pivot() * glm::vec3(region.getDimensionsInVoxels()); - wrapBool(stream.writeFloat(pivot.x)) - wrapBool(stream.writeFloat(pivot.z)) - wrapBool(stream.writeFloat(pivot.y)) + wrapBool(stream->writeFloat(pivot.x)) + wrapBool(stream->writeFloat(pivot.z)) + wrapBool(stream->writeFloat(pivot.y)) - wrapBool(stream.writeUInt32(numvoxs)) + wrapBool(stream->writeUInt32(numvoxs)) for (const priv::VoxtypeKV6 &data : voxdata) { const core::RGBA color = node->palette().color(data.col); - wrapBool(priv::writeBGRColor(stream, color)) // range 0.255 - wrapBool(stream.writeUInt8(128)) // 128 as we save slab6 - wrapBool(stream.writeUInt8(data.z)) - wrapBool(stream.writeUInt8(0)) // 0 as we save slab6 - wrapBool(stream.writeUInt8((uint8_t)data.vis)) - wrapBool(stream.writeUInt8(data.dir)) + wrapBool(priv::writeBGRColor(*stream, color)) // range 0.255 + wrapBool(stream->writeUInt8(128)) // 128 as we save slab6 + wrapBool(stream->writeUInt8(data.z)) + wrapBool(stream->writeUInt8(0)) // 0 as we save slab6 + wrapBool(stream->writeUInt8((uint8_t)data.vis)) + wrapBool(stream->writeUInt8(data.dir)) Log::debug("voxel z-low: %u, vis: %i. dir: %u, pal: %u", data.z, (uint8_t)data.vis, data.dir, data.col); } for (int x = 0u; x < xsiz_w; ++x) { - wrapBool(stream.writeInt32(xoffsets[x])) + wrapBool(stream->writeInt32(xoffsets[x])) Log::debug("xoffsets[%u]: %i", x, xoffsets[x]); } for (int x = 0; x < xsiz_w; ++x) { for (int y = 0; y < ysiz_d; ++y) { - wrapBool(stream.writeUInt16(xyoffsets[x][y])) + wrapBool(stream->writeUInt16(xyoffsets[x][y])) Log::debug("xyoffsets[%u][%u]: %u", x, y, xyoffsets[x][y]); } } const uint32_t palMagic = FourCC('S', 'P', 'a', 'l'); - wrapBool(stream.writeUInt32(palMagic)) + wrapBool(stream->writeUInt32(palMagic)) for (int i = 0; i < node->palette().colorCount(); ++i) { const core::RGBA color = node->palette().color(i); - wrapBool(priv::writeRGBScaledColor(stream, color)) // range 0..63 + wrapBool(priv::writeRGBScaledColor(*stream, color)) // range 0..63 } for (int i = node->palette().colorCount(); i < palette::PaletteMaxColors; ++i) { core::RGBA color(0); - wrapBool(priv::writeRGBScaledColor(stream, color)) + wrapBool(priv::writeRGBScaledColor(*stream, color)) } return true; diff --git a/src/modules/voxelformat/private/slab6/KV6Format.h b/src/modules/voxelformat/private/slab6/KV6Format.h index 0429db4979..5bc77519c9 100644 --- a/src/modules/voxelformat/private/slab6/KV6Format.h +++ b/src/modules/voxelformat/private/slab6/KV6Format.h @@ -18,11 +18,11 @@ namespace voxelformat { */ class KV6Format : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; /** * @brief KWALK kv6 sprite animations @@ -32,11 +32,11 @@ class KV6Format : public PaletteFormat { * https://github.com/Ericson2314/Voxlap/blob/no-asm/share/documentation/kwalkhlp.txt * Example files at https://github.com/Ericson2314/Voxlap/tree/no-asm/share (anasplit.kfa) */ - bool loadKFA(const core::String &filename, const voxel::RawVolume *volume, scenegraph::SceneGraph &sceneGraph, - const palette::Palette &palette); + bool loadKFA(const core::String &filename, const io::ArchivePtr &archive, const voxel::RawVolume *volume, + scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette); public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/slab6/KVXFormat.cpp b/src/modules/voxelformat/private/slab6/KVXFormat.cpp index 514f4fb32c..db6cc687bf 100644 --- a/src/modules/voxelformat/private/slab6/KVXFormat.cpp +++ b/src/modules/voxelformat/private/slab6/KVXFormat.cpp @@ -5,6 +5,7 @@ #include "KVXFormat.h" #include "SLABShared.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/collection/Vector.h" #include "io/Stream.h" #include "scenegraph/SceneGraph.h" @@ -63,18 +64,23 @@ struct VoxtypeKVX { return false; \ } -bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool KVXFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } // Total # of bytes (not including numbytes) in each mip-map level // but there is only 1 mip-map level (or 5 in unstripped kvx files) uint32_t numbytes; - wrap(stream.readUInt32(numbytes)) + wrap(stream->readUInt32(numbytes)) // Dimensions of voxel. (our depth is kvx height) uint32_t xsiz_w, ysiz_d, zsiz_h; - wrap(stream.readUInt32(xsiz_w)) - wrap(stream.readUInt32(ysiz_d)) - wrap(stream.readUInt32(zsiz_h)) + wrap(stream->readUInt32(xsiz_w)) + wrap(stream->readUInt32(ysiz_d)) + wrap(stream->readUInt32(zsiz_h)) Log::debug("Dimensions: %i:%i:%i", xsiz_w, ysiz_d, zsiz_h); @@ -94,9 +100,9 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead */ scenegraph::SceneGraphTransform transform; int32_t pivx_w, pivy_d, pivz_h; - wrap(stream.readInt32(pivx_w)) - wrap(stream.readInt32(pivy_d)) - wrap(stream.readInt32(pivz_h)) + wrap(stream->readInt32(pivx_w)) + wrap(stream->readInt32(pivy_d)) + wrap(stream->readInt32(pivz_h)) // For extra precision, this location has been shifted up by 8 bits. pivx_w >>= 8; @@ -120,12 +126,12 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead uint16_t xyoffsets[256][257]; uint32_t xoffsets[257]; for (uint32_t x = 0u; x <= xsiz_w; ++x) { - wrap(stream.readUInt32(xoffsets[x])) + wrap(stream->readUInt32(xoffsets[x])) } for (uint32_t x = 0u; x < xsiz_w; ++x) { for (uint32_t y = 0u; y <= ysiz_d; ++y) { - wrap(stream.readUInt16(xyoffsets[x][y])) + wrap(stream->readUInt16(xyoffsets[x][y])) } } @@ -135,12 +141,12 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead return false; } // Read the color palette from the end of the file and convert to our palette - const int64_t currentPos = stream.pos(); - if (stream.seek(-3l * palette::PaletteMaxColors, SEEK_END) == -1) { + const int64_t currentPos = stream->pos(); + if (stream->seek(-3l * palette::PaletteMaxColors, SEEK_END) == -1) { Log::error("Can't seek to palette data"); return false; } - if (stream.pos() < currentPos) { + if (stream->pos() < currentPos) { Log::error("Seek to palette data yields invalid stream position"); return false; } @@ -153,10 +159,10 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead */ for (int i = 0; i < palette.colorCount(); ++i) { core::RGBA color; - wrapBool(priv::readRGBScaledColor(stream, color)) + wrapBool(priv::readRGBScaledColor(*stream, color)) palette.setColor(i, color); } - stream.seek(currentPos); + stream->seek(currentPos); voxel::RawVolume *volume = new voxel::RawVolume(region); scenegraph::SceneGraphNode node; @@ -176,12 +182,12 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead while (n > 0) { priv::VoxtypeKVX header; - wrap(stream.readUInt8(header.ztop)) - wrap(stream.readUInt8(header.zlength)) - wrap(stream.readUInt8((uint8_t &)header.vis)) + wrap(stream->readUInt8(header.ztop)) + wrap(stream->readUInt8(header.zlength)) + wrap(stream->readUInt8((uint8_t &)header.vis)) for (uint8_t i = 0u; i < header.zlength; ++i) { uint8_t col; - wrap(stream.readUInt8(col)) + wrap(stream->readUInt8(col)) voxel::Voxel voxel = voxel::createVoxel(palette, col); const int nx = (int)x; const int ny = region.getUpperY() - (int)header.ztop - (int)i; @@ -206,7 +212,12 @@ bool KVXFormat::loadGroupsPalette(const core::String &filename, io::SeekableRead } bool KVXFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -220,27 +231,27 @@ bool KVXFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const voxel::RawVolume &volume = *node->volume(); - const int64_t numBytesPos = stream.pos(); + const int64_t numBytesPos = stream->pos(); core_assert(numBytesPos == 0); - wrapBool(stream.writeUInt32(0)) // numbytes + wrapBool(stream->writeUInt32(0)) // numbytes // flip y and z here - wrapBool(stream.writeUInt32(dim.x)) - wrapBool(stream.writeUInt32(dim.z)) - wrapBool(stream.writeUInt32(dim.y)) + wrapBool(stream->writeUInt32(dim.x)) + wrapBool(stream->writeUInt32(dim.z)) + wrapBool(stream->writeUInt32(dim.y)) Log::debug("Dimensions: %i:%i:%i", dim.x, dim.z, dim.y); // TODO: support the pivot glm::ivec3 pivot(0); // normalized pivot - wrapBool(stream.writeInt32(-pivot.x)) - wrapBool(stream.writeInt32(pivot.z)) - wrapBool(stream.writeInt32(-pivot.y)) + wrapBool(stream->writeInt32(-pivot.x)) + wrapBool(stream->writeInt32(pivot.z)) + wrapBool(stream->writeInt32(-pivot.y)) - const int64_t offsetPos = stream.pos(); + const int64_t offsetPos = stream->pos(); const size_t xoffsetSize = (dim.x + 1) * sizeof(uint32_t); const size_t xyoffsetSize = dim.x * (dim.z + 1) * sizeof(uint16_t); // skip offset tables for now - filled later - if (stream.seek(xoffsetSize + xyoffsetSize, SEEK_CUR) == -1) { + if (stream->seek(xoffsetSize + xyoffsetSize, SEEK_CUR) == -1) { Log::error("Can't seek past offset tables"); return false; } @@ -265,11 +276,11 @@ bool KVXFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: if (!bytes || z > voxdat.ztop + voxdat.zlength) { if (bytes) { - stream.writeUInt8(voxdat.ztop); - stream.writeUInt8(voxdat.zlength); - stream.writeUInt8((uint8_t)voxdat.vis); + stream->writeUInt8(voxdat.ztop); + stream->writeUInt8(voxdat.zlength); + stream->writeUInt8((uint8_t)voxdat.vis); for (uint8_t k = 0u; k < voxdat.zlength; ++k) { - stream.writeUInt8(voxdat.colors[k]); + stream->writeUInt8(voxdat.colors[k]); } xoffset += bytes; @@ -288,11 +299,11 @@ bool KVXFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: bytes++; } if (bytes) { - stream.writeUInt8(voxdat.ztop); - stream.writeUInt8(voxdat.zlength); - stream.writeUInt8((uint8_t)voxdat.vis); + stream->writeUInt8(voxdat.ztop); + stream->writeUInt8(voxdat.zlength); + stream->writeUInt8((uint8_t)voxdat.vis); for (uint8_t k = 0u; k < voxdat.zlength; ++k) { - stream.writeUInt8(voxdat.colors[k]); + stream->writeUInt8(voxdat.colors[k]); } xoffset += bytes; } @@ -305,32 +316,32 @@ bool KVXFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const palette::Palette &palette = node->palette(); for (int i = 0; i < palette.colorCount(); ++i) { const core::RGBA color = palette.color(i); - wrapBool(priv::writeRGBScaledColor(stream, color)) + wrapBool(priv::writeRGBScaledColor(*stream, color)) } for (int i = palette.colorCount(); i < palette::PaletteMaxColors; ++i) { core::RGBA color(0); - wrapBool(priv::writeRGBScaledColor(stream, color)) + wrapBool(priv::writeRGBScaledColor(*stream, color)) } - if (stream.seek(offsetPos) == -1) { + if (stream->seek(offsetPos) == -1) { Log::error("Can't seek to offset tables"); return false; } for (int x = 0u; x <= dim.x; ++x) { - wrapBool(stream.writeInt32(xoffsets[x])) + wrapBool(stream->writeInt32(xoffsets[x])) } for (int x = 0u; x < dim.x; ++x) { for (int y = 0; y <= dim.z; ++y) { - wrapBool(stream.writeUInt16(xyoffsets[x][y])) + wrapBool(stream->writeUInt16(xyoffsets[x][y])) } } - if (stream.seek(numBytesPos) == -1) { + if (stream->seek(numBytesPos) == -1) { Log::error("Can't seek to numbytes"); return false; } - wrapBool(stream.writeUInt32(xoffsets[dim.x] + 24)); // header size + wrapBool(stream->writeUInt32(xoffsets[dim.x] + 24)); // header size - if (stream.seek(0, SEEK_END) == -1) { + if (stream->seek(0, SEEK_END) == -1) { Log::error("Can't seek to end of file"); return false; } diff --git a/src/modules/voxelformat/private/slab6/KVXFormat.h b/src/modules/voxelformat/private/slab6/KVXFormat.h index 7bc71a9485..b7e4dfbe12 100644 --- a/src/modules/voxelformat/private/slab6/KVXFormat.h +++ b/src/modules/voxelformat/private/slab6/KVXFormat.h @@ -49,11 +49,11 @@ namespace voxelformat { */ class KVXFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: bool singleVolume() const override { return true; diff --git a/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.cpp b/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.cpp index 27177e6d1a..55da53c3e5 100644 --- a/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.cpp +++ b/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.cpp @@ -4,6 +4,7 @@ #include "SLAB6VoxFormat.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "scenegraph/SceneGraph.h" #include "palette/Palette.h" #include "SLABShared.h" @@ -22,35 +23,45 @@ namespace voxelformat { -size_t SLAB6VoxFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, +size_t SLAB6VoxFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t depth, height, width; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); return false; } - stream.skip((int64_t)width * height * depth); + stream->skip((int64_t)width * height * depth); palette.setSize(palette::PaletteMaxColors); for (int i = 0; i < palette.colorCount(); ++i) { core::RGBA color; - wrapBool(priv::readRGBScaledColor(stream, color)) + wrapBool(priv::readRGBScaledColor(*stream, color)) palette.setColor(i, color); } return palette.colorCount(); } -bool SLAB6VoxFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool SLAB6VoxFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t depth, height, width; - wrap(stream.readUInt32(width)) - wrap(stream.readUInt32(depth)) - wrap(stream.readUInt32(height)) + wrap(stream->readUInt32(width)) + wrap(stream->readUInt32(depth)) + wrap(stream->readUInt32(height)) if (width > 2048 || height > 2048 || depth > 2048) { Log::error("Volume exceeds the max allowed size: %i:%i:%i", width, height, depth); @@ -63,12 +74,12 @@ bool SLAB6VoxFormat::loadGroupsPalette(const core::String &filename, io::Seekabl return false; } - const int64_t voxelPos = stream.pos(); - stream.skip((int64_t)width * height * depth); + const int64_t voxelPos = stream->pos(); + stream->skip((int64_t)width * height * depth); palette.setSize(palette::PaletteMaxColors); for (int i = 0; i < palette.colorCount(); ++i) { core::RGBA color; - wrapBool(priv::readRGBScaledColor(stream, color)) + wrapBool(priv::readRGBScaledColor(*stream, color)) palette.setColor(i, color); } @@ -76,13 +87,13 @@ bool SLAB6VoxFormat::loadGroupsPalette(const core::String &filename, io::Seekabl scenegraph::SceneGraphNode node; node.setVolume(volume, true); - stream.seek(voxelPos); + stream->seek(voxelPos); const uint8_t emptyColorIndex = (uint8_t)emptyPaletteIndex(); for (uint32_t w = 0u; w < width; ++w) { for (uint32_t d = 0u; d < depth; ++d) { for (uint32_t h = 0u; h < height; ++h) { uint8_t palIdx; - wrap(stream.readUInt8(palIdx)) + wrap(stream->readUInt8(palIdx)) if (palIdx == emptyColorIndex) { continue; } @@ -100,7 +111,12 @@ bool SLAB6VoxFormat::loadGroupsPalette(const core::String &filename, io::Seekabl } bool SLAB6VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -109,9 +125,9 @@ bool SLAB6VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const const uint8_t emptyColorIndex = (uint8_t)emptyPaletteIndex(); const glm::ivec3 &dim = region.getDimensionsInVoxels(); - wrapBool(stream.writeUInt32(dim.x)) - wrapBool(stream.writeUInt32(dim.z)) - wrapBool(stream.writeUInt32(dim.y)) + wrapBool(stream->writeUInt32(dim.x)) + wrapBool(stream->writeUInt32(dim.z)) + wrapBool(stream->writeUInt32(dim.y)) // we have to flip depth with height for our own coordinate system for (int w = region.getLowerX(); w <= region.getUpperX(); ++w) { @@ -119,10 +135,10 @@ bool SLAB6VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const for (int h = region.getUpperY(); h >= region.getLowerY(); --h) { const voxel::Voxel &voxel = node->volume()->voxel(w, h, d); if (voxel::isAir(voxel.getMaterial())) { - wrapBool(stream.writeUInt8(emptyColorIndex)) + wrapBool(stream->writeUInt8(emptyColorIndex)) } else { core_assert(voxel.getColor() != emptyColorIndex); - wrapBool(stream.writeUInt8(voxel.getColor())) + wrapBool(stream->writeUInt8(voxel.getColor())) } } } @@ -130,11 +146,11 @@ bool SLAB6VoxFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const for (int i = 0; i < palette.colorCount(); ++i) { const core::RGBA &color = palette.color(i); - wrapBool(priv::writeRGBScaledColor(stream, color)) + wrapBool(priv::writeRGBScaledColor(*stream, color)) } for (int i = palette.colorCount(); i < palette::PaletteMaxColors; ++i) { core::RGBA color(0); - wrapBool(priv::writeRGBScaledColor(stream, color)) + wrapBool(priv::writeRGBScaledColor(*stream, color)) } return true; diff --git a/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.h b/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.h index 54f38119a8..9b05514b1c 100644 --- a/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.h +++ b/src/modules/voxelformat/private/slab6/SLAB6VoxFormat.h @@ -15,12 +15,12 @@ namespace voxelformat { */ class SLAB6VoxFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + const io::ArchivePtr &archive, const SaveContext &ctx) override; + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; int emptyPaletteIndex() const override { return 255; diff --git a/src/modules/voxelformat/private/sproxel/SproxelFormat.cpp b/src/modules/voxelformat/private/sproxel/SproxelFormat.cpp index e17ea08c69..c47f083eb9 100644 --- a/src/modules/voxelformat/private/sproxel/SproxelFormat.cpp +++ b/src/modules/voxelformat/private/sproxel/SproxelFormat.cpp @@ -4,8 +4,10 @@ #include "SproxelFormat.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/Tokenizer.h" +#include "io/Stream.h" #include "scenegraph/SceneGraph.h" #include "voxel/MaterialColor.h" #include "palette/PaletteLookup.h" @@ -58,10 +60,15 @@ static bool skipComma(io::SeekableReadStream &stream) { return true; } -size_t SproxelFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t SproxelFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } char buf[512]; - if (!stream.readLine(sizeof(buf), buf)) { + if (!stream->readLine(sizeof(buf), buf)) { Log::error("Could not load sproxel csv file"); return 0u; } @@ -81,7 +88,7 @@ size_t SproxelFormat::loadPalette(const core::String &filename, io::SeekableRead for (int z = 0; z < size.z; z++) { for (int x = 0; x < size.x; x++) { char hex[10]; - if ((stream.read(hex, 9)) == -1) { + if ((stream->read(hex, 9)) == -1) { Log::error("Could not load sproxel csv color line"); return 0u; } @@ -97,28 +104,33 @@ size_t SproxelFormat::loadPalette(const core::String &filename, io::SeekableRead palette.tryAdd(color, false); } if (x != size.x - 1) { - if (!skipComma(stream)) { + if (!skipComma(*stream)) { Log::error("Failed to skip 1 byte"); return false; } } } - if (!skipNewline(stream)) { + if (!skipNewline(*stream)) { return false; } } - if (!skipNewline(stream)) { + if (!skipNewline(*stream)) { return false; } } return palette.colorCount(); } -bool SproxelFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool SproxelFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } char buf[512]; - wrapBool(stream.readLine(sizeof(buf), buf)) + wrapBool(stream->readLine(sizeof(buf), buf)) core::Tokenizer tok(buf, ","); if (tok.size() != 3u) { @@ -145,7 +157,7 @@ bool SproxelFormat::loadGroupsRGBA(const core::String &filename, io::SeekableRea for (int z = 0; z < sizez; z++) { for (int x = 0; x < sizex; x++) { char hex[10]; - if (stream.read(hex, 9) == -1) { + if (stream->read(hex, 9) == -1) { Log::error("Could not load sproxel csv color line"); return false; } @@ -163,17 +175,17 @@ bool SproxelFormat::loadGroupsRGBA(const core::String &filename, io::SeekableRea volume->setVoxel(x, y, z, voxel); } if (x != sizex - 1) { - if (!skipComma(stream)) { + if (!skipComma(*stream)) { Log::error("Failed to skip 1 byte"); return false; } } } - if (!skipNewline(stream)) { + if (!skipNewline(*stream)) { return false; } } - if (!skipNewline(stream)) { + if (!skipNewline(*stream)) { return false; } } @@ -183,7 +195,12 @@ bool SproxelFormat::loadGroupsRGBA(const core::String &filename, io::SeekableRea } bool SproxelFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -195,7 +212,7 @@ bool SproxelFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const c const int width = region.getWidthInVoxels(); const int height = region.getHeightInVoxels(); const int depth = region.getDepthInVoxels(); - if (!stream.writeStringFormat(false, "%i,%i,%i\n", width, height, depth)) { + if (!stream->writeStringFormat(false, "%i,%i,%i\n", width, height, depth)) { Log::error("Could not save sproxel csv file"); return false; } @@ -206,18 +223,18 @@ bool SproxelFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const c core_assert_always(sampler.setPosition(lower.x + x, lower.y + y, lower.z + z)); const voxel::Voxel &voxel = sampler.voxel(); if (voxel.getMaterial() == voxel::VoxelType::Air) { - stream.writeString("#00000000", false); + stream->writeString("#00000000", false); } else { const core::RGBA rgba = palette.color(voxel.getColor()); - stream.writeStringFormat(false, "#%02X%02X%02X%02X", rgba.r, rgba.g, rgba.b, rgba.a); + stream->writeStringFormat(false, "#%02X%02X%02X%02X", rgba.r, rgba.g, rgba.b, rgba.a); } if (x != width - 1) { - stream.writeString(",", false); + stream->writeString(",", false); } } - stream.writeString("\n", false); + stream->writeString("\n", false); } - stream.writeString("\n", false); + stream->writeString("\n", false); } return true; } diff --git a/src/modules/voxelformat/private/sproxel/SproxelFormat.h b/src/modules/voxelformat/private/sproxel/SproxelFormat.h index 1e47f03cd3..62713b5250 100644 --- a/src/modules/voxelformat/private/sproxel/SproxelFormat.h +++ b/src/modules/voxelformat/private/sproxel/SproxelFormat.h @@ -17,13 +17,13 @@ namespace voxelformat { */ class SproxelFormat : public RGBASinglePaletteFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/starmade/SMFormat.cpp b/src/modules/voxelformat/private/starmade/SMFormat.cpp index ffc148ec97..2f2f3c7504 100644 --- a/src/modules/voxelformat/private/starmade/SMFormat.cpp +++ b/src/modules/voxelformat/private/starmade/SMFormat.cpp @@ -62,7 +62,7 @@ static bool readIvec3(io::SeekableReadStream &stream, glm::ivec3 &v) { return true; } -bool SMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool SMFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { core::Map blockPal; @@ -71,16 +71,21 @@ bool SMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStre } const core::String &extension = core::string::extractExtension(filename); if (extension == "smd3") { - return readSmd3(stream, sceneGraph, blockPal, {0, 0, 0}, palette); + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } + return readSmd3(*stream, sceneGraph, blockPal, {0, 0, 0}, palette); } else if (extension == "smd2") { - return readSmd2(stream, sceneGraph, blockPal, {0, 0, 0}, palette); - } else if (extension == "sment") { - io::ZipArchive archive; - if (!archive.init(filename, &stream)) { - Log::error("Failed to load zip archive from %s", filename.c_str()); - return false; + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); } - const io::ArchiveFiles &files = archive.files(); + return readSmd2(*stream, sceneGraph, blockPal, {0, 0, 0}, palette); + } else if (extension == "sment") { + const io::ArchiveFiles &files = archive->files(); for (const io::FilesystemEntry &e : files) { const core::String &extension = core::string::extractExtension(e.name); const bool isSmd3 = extension == "smd3"; @@ -96,7 +101,7 @@ bool SMFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStre position[i] = core::string::toInt(e.name.substr(dot + 1)) * priv::segments; } } - core::ScopedPtr modelStream(archive.readStream(e.fullPath)); + core::ScopedPtr modelStream(archive->readStream(e.fullPath)); if (!modelStream) { Log::warn("Failed to load zip archive entry %s", e.fullPath.c_str()); continue; @@ -184,7 +189,7 @@ static glm::ivec3 posByIndex(uint32_t blockIndex) { return glm::ivec3(x, y, z); } -size_t SMFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t SMFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { for (int i = 0; i < lengthof(BLOCKCOLOR); ++i) { uint8_t index = 0; diff --git a/src/modules/voxelformat/private/starmade/SMFormat.h b/src/modules/voxelformat/private/starmade/SMFormat.h index 0141582204..032c1786c8 100644 --- a/src/modules/voxelformat/private/starmade/SMFormat.h +++ b/src/modules/voxelformat/private/starmade/SMFormat.h @@ -29,16 +29,16 @@ class SMFormat : public RGBAFormat { bool readSegment(io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, const core::Map &blockPal, int headerVersion, int fileVersion, const palette::Palette &palette); - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override { + const io::ArchivePtr &archive, const SaveContext &ctx) override { return false; } public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/starmade/SMTPLFormat.cpp b/src/modules/voxelformat/private/starmade/SMTPLFormat.cpp index 14e6dfa062..d10aa3fecb 100644 --- a/src/modules/voxelformat/private/starmade/SMTPLFormat.cpp +++ b/src/modules/voxelformat/private/starmade/SMTPLFormat.cpp @@ -6,6 +6,7 @@ #include "SMPalette.h" #include "core/Color.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "palette/Palette.h" #include "scenegraph/SceneGraph.h" #include "voxel/Voxel.h" @@ -27,25 +28,30 @@ namespace voxelformat { return false; \ } -bool SMTPLFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool SMTPLFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint8_t version; - wrap(stream.readUInt8(version)) + wrap(stream->readUInt8(version)) glm::ivec3 mins; - wrap(stream.readInt32BE(mins.x)) - wrap(stream.readInt32BE(mins.y)) - wrap(stream.readInt32BE(mins.z)) + wrap(stream->readInt32BE(mins.x)) + wrap(stream->readInt32BE(mins.y)) + wrap(stream->readInt32BE(mins.z)) glm::ivec3 maxs; - wrap(stream.readInt32BE(maxs.x)) - wrap(stream.readInt32BE(maxs.y)) - wrap(stream.readInt32BE(maxs.z)) + wrap(stream->readInt32BE(maxs.x)) + wrap(stream->readInt32BE(maxs.y)) + wrap(stream->readInt32BE(maxs.z)) Log::debug("Region: %i:%i:%i - %i:%i:%i", mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z); uint32_t numBlocks; - wrap(stream.readUInt32BE(numBlocks)) + wrap(stream->readUInt32BE(numBlocks)) Log::debug("Number of blocks: %i", numBlocks); @@ -71,20 +77,20 @@ bool SMTPLFormat::loadGroupsPalette(const core::String &filename, io::SeekableRe node.setVolume(volume, true); for (uint32_t i = 0; i < numBlocks; ++i) { uint32_t x, y, z; - wrap(stream.readUInt32BE(x)) - wrap(stream.readUInt32BE(y)) - wrap(stream.readUInt32BE(z)) + wrap(stream->readUInt32BE(x)) + wrap(stream->readUInt32BE(y)) + wrap(stream->readUInt32BE(z)) // TODO the following bytes are handled differently since version > 3 uint8_t type; - wrap(stream.readUInt8(type)) + wrap(stream->readUInt8(type)) #if 0 uint8_t orientation; - wrap(stream.readUInt8(orientation)) + wrap(stream->readUInt8(orientation)) uint8_t active; - wrap(stream.readUInt8(active)) + wrap(stream->readUInt8(active)) #else uint16_t block; - wrap(stream.readUInt16BE(block)) + wrap(stream->readUInt16BE(block)) #endif int color = 0; blockPal.get(block, color); @@ -92,77 +98,77 @@ bool SMTPLFormat::loadGroupsPalette(const core::String &filename, io::SeekableRe } #if 0 uint32_t connections; - wrap(stream.readUInt32BE(connections)) + wrap(stream->readUInt32BE(connections)) Log::debug("Connections: %i", connections); for (uint32_t i = 0; i < connections; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) int32_t size; - wrap(stream.readInt32BE(size)) + wrap(stream->readInt32BE(size)) for (int32_t j = 0; j < size; ++j) { int64_t value; - wrap(stream.readInt64BE(value)) + wrap(stream->readInt64BE(value)) } } if (version >= 2) { uint32_t texts; - wrap(stream.readUInt32BE(texts)) + wrap(stream->readUInt32BE(texts)) Log::debug("Texts: %i", texts); for (uint32_t i = 0; i < texts; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) core::String text; - wrap(stream.readPascalStringUInt16BE(text)) + wrap(stream->readPascalStringUInt16BE(text)) } if (version >= 3) { uint32_t filters; - wrap(stream.readUInt32BE(filters)) + wrap(stream->readUInt32BE(filters)) Log::debug("Filters: %i", filters); for (uint32_t i = 0; i < filters; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) int32_t size; - wrap(stream.readInt32BE(size)) + wrap(stream->readInt32BE(size)) for (int32_t j = 0; j < size; ++j) { int16_t key; - wrap(stream.readInt16BE(key)) + wrap(stream->readInt16BE(key)) int32_t value; - wrap(stream.readInt32BE(value)) + wrap(stream->readInt32BE(value)) } } uint32_t productions; - wrap(stream.readUInt32BE(productions)) + wrap(stream->readUInt32BE(productions)) Log::debug("Productions: %i", productions); for (uint32_t i = 0; i < productions; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) int16_t text; - wrap(stream.readInt16BE(text)) + wrap(stream->readInt16BE(text)) } if (version >= 5) { int32_t productionLimits; - wrap(stream.readInt32BE(productionLimits)) + wrap(stream->readInt32BE(productionLimits)) Log::debug("Production limits: %i", productionLimits); for (int32_t i = 0; i < productionLimits; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) int32_t text; - wrap(stream.readInt32BE(text)) + wrap(stream->readInt32BE(text)) } int32_t fillUpFilters; - wrap(stream.readInt32BE(fillUpFilters)) + wrap(stream->readInt32BE(fillUpFilters)) Log::debug("Fill up filters: %i", fillUpFilters); for (int32_t i = 0; i < fillUpFilters; ++i) { int64_t key; - wrap(stream.readInt64BE(key)) + wrap(stream->readInt64BE(key)) int32_t size; - wrap(stream.readInt32BE(size)) + wrap(stream->readInt32BE(size)) for (int32_t j = 0; j < size; ++j) { int16_t key; - wrap(stream.readInt16BE(key)) + wrap(stream->readInt16BE(key)) int32_t value; - wrap(stream.readInt32BE(value)) + wrap(stream->readInt32BE(value)) } } } @@ -194,7 +200,12 @@ static uint16_t resolveBlockId(const palette::Palette &starMadePal, const palett } bool SMTPLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -207,22 +218,22 @@ bool SMTPLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor const uint32_t depth = region.getDepthInVoxels(); uint8_t version = 3; - wrapBool(stream.writeUInt8(version)) + wrapBool(stream->writeUInt8(version)) glm::ivec3 mins{0}; - wrapBool(stream.writeInt32BE(mins.x)) - wrapBool(stream.writeInt32BE(mins.y)) - wrapBool(stream.writeInt32BE(mins.z)) + wrapBool(stream->writeInt32BE(mins.x)) + wrapBool(stream->writeInt32BE(mins.y)) + wrapBool(stream->writeInt32BE(mins.z)) - wrapBool(stream.writeUInt32BE(width)) - wrapBool(stream.writeUInt32BE(height)) - wrapBool(stream.writeUInt32BE(depth)) + wrapBool(stream->writeUInt32BE(width)) + wrapBool(stream->writeUInt32BE(height)) + wrapBool(stream->writeUInt32BE(depth)) palette::Palette starMadePal; loadPalette(starMadePal); const int32_t numBlocks = voxelutil::visitVolume( *node->volume(), [](int, int, int, const voxel::Voxel &) {}, voxelutil::SkipEmpty()); - wrapBool(stream.writeUInt32BE(numBlocks)) + wrapBool(stream->writeUInt32BE(numBlocks)) Log::debug("Number of blocks: %i", numBlocks); int32_t blocks = 0; @@ -234,12 +245,12 @@ bool SMTPLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor if (voxel::isAir(voxel.getMaterial())) { continue; } - wrapBool(stream.writeUInt32BE(x)) - wrapBool(stream.writeUInt32BE(y)) - wrapBool(stream.writeUInt32BE(z)) - wrapBool(stream.writeUInt8(0)) // type + wrapBool(stream->writeUInt32BE(x)) + wrapBool(stream->writeUInt32BE(y)) + wrapBool(stream->writeUInt32BE(z)) + wrapBool(stream->writeUInt8(0)) // type uint16_t blockId = resolveBlockId(starMadePal, node->palette(), voxel.getColor()); - wrapBool(stream.writeUInt16BE(blockId)) + wrapBool(stream->writeUInt16BE(blockId)) ++blocks; } } @@ -248,10 +259,10 @@ bool SMTPLFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor Log::error("Number of blocks written does not match the expected number: %i != %i", blocks, numBlocks); return false; } - wrapBool(stream.writeUInt32BE(0)) // no connections - wrapBool(stream.writeUInt32BE(0)) // no texts - wrapBool(stream.writeUInt32BE(0)) // no inventory filters - wrapBool(stream.writeUInt32BE(0)) // no productions + wrapBool(stream->writeUInt32BE(0)) // no connections + wrapBool(stream->writeUInt32BE(0)) // no texts + wrapBool(stream->writeUInt32BE(0)) // no inventory filters + wrapBool(stream->writeUInt32BE(0)) // no productions return true; } @@ -272,7 +283,7 @@ void SMTPLFormat::loadPalette(palette::Palette &palette) { } } -size_t SMTPLFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t SMTPLFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { loadPalette(palette); return palette.size(); diff --git a/src/modules/voxelformat/private/starmade/SMTPLFormat.h b/src/modules/voxelformat/private/starmade/SMTPLFormat.h index 06a27d33c4..805baa90d6 100644 --- a/src/modules/voxelformat/private/starmade/SMTPLFormat.h +++ b/src/modules/voxelformat/private/starmade/SMTPLFormat.h @@ -15,18 +15,18 @@ namespace voxelformat { */ class SMTPLFormat : public PaletteFormat { protected: - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: static void loadPalette(palette::Palette &palette); bool singleVolume() const override { return true; } - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/vengi/VENGIFormat.cpp b/src/modules/voxelformat/private/vengi/VENGIFormat.cpp index 9eccac14d3..6d79442f5a 100644 --- a/src/modules/voxelformat/private/vengi/VENGIFormat.cpp +++ b/src/modules/voxelformat/private/vengi/VENGIFormat.cpp @@ -6,6 +6,7 @@ #include "core/ArrayLength.h" #include "core/FourCC.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "io/ZipReadStream.h" #include "io/ZipWriteStream.h" #include "scenegraph/SceneGraph.h" @@ -429,10 +430,15 @@ bool VENGIFormat::loadNode(scenegraph::SceneGraph &sceneGraph, int parent, uint3 } bool VENGIFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } Log::debug("Save scenegraph as vengi"); - wrapBool(stream.writeUInt32(FourCC('V', 'E', 'N', 'G'))) - io::ZipWriteStream zipStream(stream); + wrapBool(stream->writeUInt32(FourCC('V', 'E', 'N', 'G'))) + io::ZipWriteStream zipStream(*stream, stream->size()); wrapBool(zipStream.writeUInt32(3)) if (!saveNode(sceneGraph, zipStream, sceneGraph.root())) { return false; @@ -440,15 +446,20 @@ bool VENGIFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const cor return true; } -bool VENGIFormat::loadGroups(const core::String &filename, io::SeekableReadStream &stream, +bool VENGIFormat::loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } uint32_t magic; - wrap(stream.readUInt32(magic)) + wrap(stream->readUInt32(magic)) if (magic != FourCC('V', 'E', 'N', 'G')) { Log::error("Invalid magic"); return false; } - io::ZipReadStream zipStream(stream); + io::ZipReadStream zipStream(*stream, stream->size()); uint32_t version; wrap(zipStream.readUInt32(version)) if (version > 3) { diff --git a/src/modules/voxelformat/private/vengi/VENGIFormat.h b/src/modules/voxelformat/private/vengi/VENGIFormat.h index 7f8e724e51..f7ac1860b4 100644 --- a/src/modules/voxelformat/private/vengi/VENGIFormat.h +++ b/src/modules/voxelformat/private/vengi/VENGIFormat.h @@ -52,8 +52,8 @@ class VENGIFormat : public Format { protected: bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; - bool loadGroups(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph, + const io::ArchivePtr &archive, const SaveContext &ctx) override; + bool loadGroups(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/private/voxel3d/V3AFormat.cpp b/src/modules/voxelformat/private/voxel3d/V3AFormat.cpp index e207e462b5..dbc4bfa8ad 100644 --- a/src/modules/voxelformat/private/voxel3d/V3AFormat.cpp +++ b/src/modules/voxelformat/private/voxel3d/V3AFormat.cpp @@ -4,6 +4,7 @@ #include "V3AFormat.h" #include "core/Log.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "scenegraph/SceneGraph.h" #include "palette/Palette.h" @@ -26,14 +27,19 @@ namespace voxelformat { return false; \ } -bool V3AFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool V3AFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } core::String line; int width, depth, height; width = depth = height = 0; - while (!stream.eos()) { - wrapBool(stream.readLine(line)); + while (!stream->eos()) { + wrapBool(stream->readLine(line)); if (core::string::startsWith(line, "VERSION")) { line = line.substr(8); if (line != "1.0") { @@ -89,7 +95,7 @@ bool V3AFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr int y = 0; do { if (line.empty()) { - if (!stream.readLine(line)) { + if (!stream->readLine(line)) { break; } y = 0; @@ -125,7 +131,7 @@ bool V3AFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr volume->setVoxel(x, y, (int)i / 4, voxel); } ++y; - } while (stream.readLine(line)); + } while (stream->readLine(line)); node.setName(filename); node.setPalette(palLookup.palette()); sceneGraph.emplace(core::move(node)); @@ -142,7 +148,12 @@ bool V3AFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr } bool V3AFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) { + const io::ArchivePtr &archive, const SaveContext &ctx) { + core::ScopedPtr stream(archive->writeStream(filename)); + if (!stream) { + Log::error("Could not open file %s", filename.c_str()); + return image::ImagePtr(); + } const scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); core_assert(node); @@ -151,17 +162,17 @@ bool V3AFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: const uint32_t height = region.getHeightInVoxels(); const uint32_t depth = region.getDepthInVoxels(); - wrapBool(stream.writeString("VERSION 1.0\r\n", false)) - wrapBool(stream.writeString("TYPE VoxelCubic\r\n", false)) - if (!stream.writeStringFormat(false, "DIMENSION %d.0 %d.0 %d.0\r\n", width, height, depth)) { + wrapBool(stream->writeString("VERSION 1.0\r\n", false)) + wrapBool(stream->writeString("TYPE VoxelCubic\r\n", false)) + if (!stream->writeStringFormat(false, "DIMENSION %d.0 %d.0 %d.0\r\n", width, height, depth)) { Log::error("Failed to write DIMENSION line"); return false; } - if (!stream.writeStringFormat(false, "SIZE %d %d %d\r\n", width, height, depth)) { + if (!stream->writeStringFormat(false, "SIZE %d %d %d\r\n", width, height, depth)) { Log::error("Failed to write SIZE line"); return false; } - wrapBool(stream.writeString("DATA ", false)) + wrapBool(stream->writeString("DATA ", false)) const voxel::RawVolume &volume = *node->volume(); const palette::Palette &palette = node->palette(); for (int32_t x = region.getLowerX(); x <= region.getUpperX(); x++) { @@ -169,18 +180,18 @@ bool V3AFormat::saveGroups(const scenegraph::SceneGraph &sceneGraph, const core: for (int32_t z = region.getLowerZ(); z <= region.getUpperZ(); z++) { const voxel::Voxel &voxel = volume.voxel(x, y, z); if (voxel::isAir(voxel.getMaterial())) { - wrapBool(stream.writeString("-1 -1 -1 -1 ", false)) + wrapBool(stream->writeString("-1 -1 -1 -1 ", false)) } else { const core::RGBA color = palette.color(voxel.getColor()); - if (!stream.writeStringFormat(false, "%d %d %d %d ", color.r, color.g, color.b, color.a)) { + if (!stream->writeStringFormat(false, "%d %d %d %d ", color.r, color.g, color.b, color.a)) { Log::error("Failed to write voxel data"); return false; } } } - wrapBool(stream.writeString("\r\n", false)) + wrapBool(stream->writeString("\r\n", false)) } - wrapBool(stream.writeString("\r\n", false)) + wrapBool(stream->writeString("\r\n", false)) } return true; diff --git a/src/modules/voxelformat/private/voxel3d/V3AFormat.h b/src/modules/voxelformat/private/voxel3d/V3AFormat.h index a81ef1a7a5..4e4ece0ca0 100644 --- a/src/modules/voxelformat/private/voxel3d/V3AFormat.h +++ b/src/modules/voxelformat/private/voxel3d/V3AFormat.h @@ -15,11 +15,11 @@ namespace voxelformat { */ class V3AFormat : public RGBAFormat { protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override; + const io::ArchivePtr &archive, const SaveContext &ctx) override; public: bool singleVolume() const override { diff --git a/src/modules/voxelformat/private/voxelbuilder/VBXFormat.cpp b/src/modules/voxelformat/private/voxelbuilder/VBXFormat.cpp index 0d235cadd4..e9f3e0e136 100644 --- a/src/modules/voxelformat/private/voxelbuilder/VBXFormat.cpp +++ b/src/modules/voxelformat/private/voxelbuilder/VBXFormat.cpp @@ -6,6 +6,7 @@ #include "core/Color.h" #include "core/Log.h" #include "core/RGBA.h" +#include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "core/collection/StringMap.h" #include "io/BufferedReadWriteStream.h" @@ -32,9 +33,9 @@ bool VBXFormat::loadGLB(const core::String &data, scenegraph::SceneGraph &sceneG } stream.seek(0); GLTFFormat format; - if (!format.load("file.glb", stream, sceneGraph, ctx)) { - Log::error("Failed to load embedded glb file: %s", data.c_str()); - } + // if (!format.load("file.glb", stream, sceneGraph, ctx)) { + // Log::error("Failed to load embedded glb file: %s", data.c_str()); + // } return true; } @@ -68,11 +69,16 @@ static bool loadVoxels(const core::String &voxels, FUNC func) { return true; } -bool VBXFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, +bool VBXFormat::loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } core::StringMap> ini; - if (!util::parseIni(stream, ini)) { + if (!util::parseIni(*stream, ini)) { Log::error("Failed to parse ini file: %s", filename.c_str()); return false; } @@ -137,10 +143,15 @@ bool VBXFormat::loadGroupsRGBA(const core::String &filename, io::SeekableReadStr return true; } -size_t VBXFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t VBXFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { + core::ScopedPtr stream(archive->readStream(filename)); + if (!stream) { + Log::error("Could not load file %s", filename.c_str()); + return image::ImagePtr(); + } util::IniMap ini; - if (!util::parseIni(stream, ini)) { + if (!util::parseIni(*stream, ini)) { Log::error("Failed to parse ini file: %s", filename.c_str()); return 0; } diff --git a/src/modules/voxelformat/private/voxelbuilder/VBXFormat.h b/src/modules/voxelformat/private/voxelbuilder/VBXFormat.h index b70ced889e..f2c20b068a 100644 --- a/src/modules/voxelformat/private/voxelbuilder/VBXFormat.h +++ b/src/modules/voxelformat/private/voxelbuilder/VBXFormat.h @@ -20,15 +20,15 @@ class VBXFormat : public RGBASinglePaletteFormat { bool loadGLB(const core::String &voxels, scenegraph::SceneGraph &sceneGraph, const LoadContext &ctx) const; protected: - bool loadGroupsRGBA(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsRGBA(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, const palette::Palette &palette, const LoadContext &ctx) override; public: - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override { + const io::ArchivePtr &archive, const SaveContext &ctx) override { return false; } }; diff --git a/src/modules/voxelformat/private/voxelmax/VMaxFormat.cpp b/src/modules/voxelformat/private/voxelmax/VMaxFormat.cpp index 0618a7fe9c..d2e04f872e 100644 --- a/src/modules/voxelformat/private/voxelmax/VMaxFormat.cpp +++ b/src/modules/voxelformat/private/voxelmax/VMaxFormat.cpp @@ -4,13 +4,11 @@ #include "VMaxFormat.h" #include "BinaryPList.h" -#include "app/App.h" #include "core/Log.h" #include "core/ScopedPtr.h" #include "core/StringUtil.h" #include "image/Image.h" #include "io/Archive.h" -#include "io/BufferedReadWriteStream.h" #include "io/LZFSEReadStream.h" #include "io/MemoryReadStream.h" #include "io/StdStreamBuf.h" @@ -157,15 +155,10 @@ bool VMaxFormat::loadSceneJson(const io::ArchivePtr &archive, VMaxScene &scene) return true; } -bool VMaxFormat::loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, +bool VMaxFormat::loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) { VMaxScene scene; - const io::ArchivePtr &archive = io::openArchive(io::filesystem(), filename, &stream); - if (!archive) { - Log::error("Failed to create archive for %s", filename.c_str()); - return false; - } if (!loadSceneJson(archive, scene)) { return false; } @@ -421,20 +414,16 @@ bool VMaxFormat::loadObjectFromArchive(const core::String &filename, const io::A return sceneGraph.emplace(core::move(node), parent) != InvalidNodeId; } -image::ImagePtr VMaxFormat::loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, +image::ImagePtr VMaxFormat::loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) { - const io::ArchivePtr &archive = io::openArchive(io::filesystem(), filename, &stream); - if (!archive) { - Log::error("Failed to create archive for %s", filename.c_str()); - return image::ImagePtr{}; - } const core::String &thumbnailPath = core::string::path("QuickLook", "Thumbnail.png"); - core::ScopedPtr contentsStream(archive->readStream(thumbnailPath)); - if (!contentsStream) { + core::ScopedPtr stream(archive->readStream(thumbnailPath)); + if (!stream) { Log::error("Failed to load %s from %s", thumbnailPath.c_str(), filename.c_str()); return image::ImagePtr(); } - return image::loadImage(core::string::extractFilenameWithExtension(thumbnailPath), *contentsStream); + + return image::loadImage(core::string::extractFilenameWithExtension(thumbnailPath), *stream); } bool VMaxFormat::loadPaletteFromArchive(const io::ArchivePtr &archive, const core::String &paletteName, @@ -457,14 +446,8 @@ bool VMaxFormat::loadPaletteFromArchive(const io::ArchivePtr &archive, const cor return true; } -size_t VMaxFormat::loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, +size_t VMaxFormat::loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) { - const io::ArchivePtr &archive = io::openArchive(io::filesystem(), filename, &stream); - if (!archive) { - Log::error("Failed to create archive for %s", filename.c_str()); - return 0u; - } - // TODO: there is also a "pal" dict in the vmaxb plist file for some files // pal->dict // colors->data diff --git a/src/modules/voxelformat/private/voxelmax/VMaxFormat.h b/src/modules/voxelformat/private/voxelmax/VMaxFormat.h index 025f052a11..1e426d5f78 100644 --- a/src/modules/voxelformat/private/voxelmax/VMaxFormat.h +++ b/src/modules/voxelformat/private/voxelmax/VMaxFormat.h @@ -241,18 +241,18 @@ class VMaxFormat : public PaletteFormat { const palette::Palette &palette) const; bool loadPaletteFromArchive(const io::ArchivePtr &archive, const core::String &paletteName, palette::Palette &palette, const LoadContext &ctx) const; - bool loadGroupsPalette(const core::String &filename, io::SeekableReadStream &stream, + bool loadGroupsPalette(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, palette::Palette &palette, const LoadContext &ctx) override; public: bool saveGroups(const scenegraph::SceneGraph &sceneGraph, const core::String &filename, - io::SeekableWriteStream &stream, const SaveContext &ctx) override { + const io::ArchivePtr &archive, const SaveContext &ctx) override { return false; } - image::ImagePtr loadScreenshot(const core::String &filename, io::SeekableReadStream &stream, + image::ImagePtr loadScreenshot(const core::String &filename, const io::ArchivePtr &archive, const LoadContext &ctx) override; - size_t loadPalette(const core::String &filename, io::SeekableReadStream &stream, palette::Palette &palette, + size_t loadPalette(const core::String &filename, const io::ArchivePtr &archive, palette::Palette &palette, const LoadContext &ctx) override; }; diff --git a/src/modules/voxelformat/tests/AbstractFormatTest.cpp b/src/modules/voxelformat/tests/AbstractFormatTest.cpp index 6ec43c590a..02a686e92e 100644 --- a/src/modules/voxelformat/tests/AbstractFormatTest.cpp +++ b/src/modules/voxelformat/tests/AbstractFormatTest.cpp @@ -3,13 +3,17 @@ */ #include "AbstractFormatTest.h" +#include "app/App.h" #include "core/GameConfig.h" #include "core/Log.h" #include "core/StringUtil.h" #include "image/Image.h" +#include "io/Archive.h" #include "io/BufferedReadWriteStream.h" #include "io/File.h" #include "io/FileStream.h" +#include "io/MemoryArchive.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "io/Stream.h" #include "palette/Palette.h" @@ -34,6 +38,18 @@ namespace voxelformat { const voxel::Voxel AbstractFormatTest::Empty; +io::ArchivePtr AbstractFormatTest::helper_archive(const core::String &filename) { +#if WRITE_TO_FILE + return io::openFilesystemArchive(_testApp->filesystem(), filename); +#else + return io::openMemoryArchive(); +#endif +} + +io::ArchivePtr AbstractFormatTest::helper_filesystemarchive() { + return io::openFilesystemArchive(_testApp->filesystem()); +} + image::ImagePtr AbstractFormatTest::helper_testThumbnailCreator(const scenegraph::SceneGraph &sceneGraph, const ThumbnailContext &ctx) { return image::ImagePtr(); @@ -46,68 +62,65 @@ void AbstractFormatTest::testFirstAndLastPaletteIndex(const core::String &filena voxel::RawVolume volume(region); EXPECT_TRUE(volume.setVoxel(0, 0, 0, voxel::createVoxel(voxel::VoxelType::Generic, 0))); EXPECT_TRUE(volume.setVoxel(0, 0, 1, voxel::createVoxel(voxel::VoxelType::Generic, 255))); - io::BufferedReadWriteStream stream((int64_t)(10 * 1024 * 1024)); + const io::ArchivePtr &archive = helper_archive(); scenegraph::SceneGraph sceneGraphsave(2); { scenegraph::SceneGraphNode node; node.setVolume(&volume, false); sceneGraphsave.emplace(core::move(node)); } - ASSERT_TRUE(format->save(sceneGraphsave, filename, stream, testSaveCtx)); - stream.seek(0); + ASSERT_TRUE(format->save(sceneGraphsave, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraphLoad; - ASSERT_TRUE(format->load(filename, stream, sceneGraphLoad, testLoadCtx)); + ASSERT_TRUE(format->load(filename, archive, sceneGraphLoad, testLoadCtx)); voxel::sceneGraphComparator(sceneGraphsave, sceneGraphLoad, flags, 0.001f); } -void AbstractFormatTest::testFirstAndLastPaletteIndexConversion(Format &srcFormat, const core::String &destFilename, - Format &destFormat, voxel::ValidateFlags flags) { +void AbstractFormatTest::testFirstAndLastPaletteIndexConversion(Format &srcFormat, const core::String &srcFilename, + Format &destFormat, const core::String &destFilename, + voxel::ValidateFlags flags) { SCOPED_TRACE(destFilename.c_str()); voxel::Region region(glm::ivec3(0), glm::ivec3(1)); voxel::RawVolume original(region); const palette::Palette pal1 = voxel::getPalette(); EXPECT_TRUE(original.setVoxel(0, 0, 0, voxel::createVoxel(voxel::VoxelType::Generic, 0u))); EXPECT_TRUE(original.setVoxel(0, 0, 1, voxel::createVoxel(voxel::VoxelType::Generic, 255u))); - io::BufferedReadWriteStream srcFormatStream((int64_t)(10 * 1024 * 1024)); + const io::ArchivePtr &archive = helper_archive(); scenegraph::SceneGraph sceneGraphsave1(2); { scenegraph::SceneGraphNode node; node.setVolume(&original, false); node.setPalette(pal1); sceneGraphsave1.emplace(core::move(node)); - EXPECT_TRUE(srcFormat.save(sceneGraphsave1, destFilename, srcFormatStream, testSaveCtx)) - << "Could not save " << destFilename; + EXPECT_TRUE(srcFormat.save(sceneGraphsave1, srcFilename, archive, testSaveCtx)) + << "Could not save " << srcFilename; } - srcFormatStream.seek(0); scenegraph::SceneGraph sceneGraphLoad1; - ASSERT_TRUE(srcFormat.load(destFilename, srcFormatStream, sceneGraphLoad1, testLoadCtx)); + ASSERT_TRUE(srcFormat.load(srcFilename, archive, sceneGraphLoad1, testLoadCtx)); voxel::sceneGraphComparator(sceneGraphsave1, sceneGraphLoad1, flags, 0.001f); io::BufferedReadWriteStream stream((int64_t)(10 * 1024 * 1024)); { - EXPECT_TRUE(destFormat.save(sceneGraphLoad1, destFilename, stream, testSaveCtx)) + EXPECT_TRUE(destFormat.save(sceneGraphLoad1, destFilename, archive, testSaveCtx)) << "Could not save " << destFilename; } - stream.seek(0); scenegraph::SceneGraph sceneGraphLoad; - ASSERT_TRUE(destFormat.load(destFilename, stream, sceneGraphLoad, testLoadCtx)); + ASSERT_TRUE(destFormat.load(destFilename, archive, sceneGraphLoad, testLoadCtx)); voxel::sceneGraphComparator(sceneGraphsave1, sceneGraphLoad, flags, 0.001f); } void AbstractFormatTest::testLoad(scenegraph::SceneGraph &sceneGraph, const core::String &filename, size_t expectedVolumes) { - const io::FilePtr &file = open(filename); - if (!file->validHandle()) { + const io::ArchivePtr &archive = helper_filesystemarchive(); + if (!archive->exists(filename)) { GTEST_SKIP() << "Could not open " << filename; } else { SCOPED_TRACE(filename.c_str()); - io::FileStream stream(file); io::FileDescription fileDesc; fileDesc.set(filename); - ASSERT_TRUE(voxelformat::loadFormat(fileDesc, stream, sceneGraph, testLoadCtx)) + ASSERT_TRUE(voxelformat::loadFormat(fileDesc, archive, sceneGraph, testLoadCtx)) << "Could not load " << filename; ASSERT_EQ(expectedVolumes, sceneGraph.size()); } @@ -120,12 +133,12 @@ void AbstractFormatTest::checkColor(core::RGBA c1, const palette::Palette &palet << "], delta[" << delta << "]"; } -void AbstractFormatTest::testRGBSmall(const core::String &filename, io::SeekableReadStream &stream, +void AbstractFormatTest::testRGBSmall(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph) { SCOPED_TRACE(filename.c_str()); io::FileDescription fileDesc; fileDesc.set(filename); - ASSERT_TRUE(voxelformat::loadFormat(fileDesc, stream, sceneGraph, testLoadCtx)); + ASSERT_TRUE(voxelformat::loadFormat(fileDesc, archive, sceneGraph, testLoadCtx)); EXPECT_EQ(1u, sceneGraph.size()); palette::Palette palette; @@ -146,11 +159,9 @@ void AbstractFormatTest::testRGBSmall(const core::String &filename, io::Seekable } void AbstractFormatTest::testRGBSmall(const core::String &filename) { + const io::ArchivePtr &archive = helper_filesystemarchive();; scenegraph::SceneGraph sceneGraph; - const io::FilePtr &file = open(filename); - ASSERT_TRUE(file->validHandle()); - io::FileStream stream(file); - testRGBSmall(filename, stream, sceneGraph); + testRGBSmall(filename, archive, sceneGraph); } void AbstractFormatTest::testRGBSmallSaveLoad(const core::String &filename) { @@ -162,9 +173,7 @@ void AbstractFormatTest::testRGBSmallSaveLoad(const core::String &filename) { void AbstractFormatTest::testLoadScreenshot(const core::String &filename, int width, int height, const core::RGBA expectedColor, int expectedX, int expectedY) { SCOPED_TRACE(filename.c_str()); - io::FileStream loadStream(open(filename)); - ASSERT_TRUE(loadStream.valid()); - const image::ImagePtr &image = voxelformat::loadScreenshot(filename, loadStream, testLoadCtx); + const image::ImagePtr &image = voxelformat::loadScreenshot(filename, helper_filesystemarchive(), testLoadCtx); ASSERT_TRUE(image); EXPECT_EQ(image->width(), width) << image::print(image); EXPECT_EQ(image->height(), height) << image::print(image); @@ -177,37 +186,28 @@ void AbstractFormatTest::testLoadScreenshot(const core::String &filename, int wi void AbstractFormatTest::testRGBSmallSaveLoad(const core::String &filename, const core::String &saveFilename) { SCOPED_TRACE(filename.c_str()); scenegraph::SceneGraph sceneGraph; - { - // load and check that the file contains the expected colors - io::FileStream loadStream(open(filename)); - ASSERT_TRUE(loadStream.valid()); - testRGBSmall(filename, loadStream, sceneGraph); - } + io::ArchivePtr archive = helper_filesystemarchive(); + testRGBSmall(filename, archive, sceneGraph); - io::BufferedReadWriteStream saveStream((int64_t)(10 * 1024 * 1024)); - ASSERT_TRUE(voxelformat::saveFormat(sceneGraph, saveFilename, nullptr, saveStream, testSaveCtx)); - saveStream.seek(0); + ASSERT_TRUE(voxelformat::saveFormat(sceneGraph, saveFilename, nullptr, archive, testSaveCtx)); scenegraph::SceneGraph loadSceneGraph; - testRGBSmall(saveFilename, saveStream, loadSceneGraph); + testRGBSmall(saveFilename, archive, loadSceneGraph); } bool AbstractFormatTest::helper_saveSceneGraph(scenegraph::SceneGraph &sceneGraph, const core::String &filename) { - const io::FilePtr &file = open(filename, io::FileMode::SysWrite); - io::FileStream stream(file); + const io::ArchivePtr &archive = helper_filesystemarchive(); voxelformat::SaveContext saveCtx; - return voxelformat::saveFormat(sceneGraph, filename, nullptr, stream, saveCtx); + return voxelformat::saveFormat(sceneGraph, filename, nullptr, archive, saveCtx); } void AbstractFormatTest::testRGB(const core::String &filename, float maxDelta) { SCOPED_TRACE(filename.c_str()); scenegraph::SceneGraph sceneGraph; - const io::FilePtr &file = open(filename); - ASSERT_TRUE(file->validHandle()) << "Could not open " << filename.c_str(); - io::FileStream stream(file); io::FileDescription fileDesc; fileDesc.set(filename); - ASSERT_TRUE(voxelformat::loadFormat(fileDesc, stream, sceneGraph, testLoadCtx)) + const io::ArchivePtr &archive = helper_filesystemarchive(); + ASSERT_TRUE(voxelformat::loadFormat(fileDesc, archive, sceneGraph, testLoadCtx)) << "Failed to load " << filename.c_str(); EXPECT_EQ(1u, sceneGraph.size()) << "Unexpected scene graph size for " << filename.c_str(); @@ -257,38 +257,17 @@ void AbstractFormatTest::testConvert(const core::String &srcFilename, Format &sr const core::String &destFilename, Format &destFormat, voxel::ValidateFlags flags, float maxDelta) { SCOPED_TRACE("src: " + srcFilename); + io::ArchivePtr archive = helper_filesystemarchive(); scenegraph::SceneGraph sceneGraph; - ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename, srcFormat, sceneGraph)) << "Failed to load " << srcFilename; - - io::SeekableReadStream *readStream; - io::SeekableWriteStream *writeStream; + ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename, archive, srcFormat, sceneGraph)) << "Failed to load " << srcFilename; -#if WRITE_TO_FILE - io::FilePtr sfile = open(destFilename, io::FileMode::SysWrite); - SCOPED_TRACE("target: " + sfile->name()); - io::FileStream fileWriteStream(sfile); - writeStream = &fileWriteStream; -#else - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); - writeStream = &bufferedStream; -#endif + SCOPED_TRACE("target: " + destFilename); - ASSERT_TRUE(destFormat.save(sceneGraph, destFilename, *writeStream, testSaveCtx)) + ASSERT_TRUE(destFormat.save(sceneGraph, destFilename, archive, testSaveCtx)) << "Could not save " << destFilename; -#if WRITE_TO_FILE - sfile->close(); - io::FilePtr readfile = open(destFilename); - io::FileStream fileReadStream(readfile); - readStream = &fileReadStream; -#else - readStream = &bufferedStream; -#endif - - readStream->seek(0); - scenegraph::SceneGraph sceneGraphLoad; - ASSERT_TRUE(destFormat.load(destFilename, *readStream, sceneGraphLoad, testLoadCtx)); + ASSERT_TRUE(destFormat.load(destFilename, archive, sceneGraphLoad, testLoadCtx)); voxel::sceneGraphComparator(sceneGraph, sceneGraphLoad, flags, maxDelta); } @@ -297,10 +276,11 @@ void AbstractFormatTest::testConvertSceneGraph(const core::String &srcFilename1, voxel::ValidateFlags flags, float maxDelta) { SCOPED_TRACE("src1: " + srcFilename1); scenegraph::SceneGraph srcSceneGraph1; - ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename1, srcFormat1, srcSceneGraph1)) << "Failed to load " << srcFilename1; + io::ArchivePtr archive = helper_filesystemarchive(); + ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename1, archive, srcFormat1, srcSceneGraph1)) << "Failed to load " << srcFilename1; SCOPED_TRACE("src2: " + srcFilename2); scenegraph::SceneGraph srcSceneGraph2; - ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename2, srcFormat2, srcSceneGraph2)) << "Failed to load " << srcFilename2; + ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename2, archive, srcFormat2, srcSceneGraph2)) << "Failed to load " << srcFilename2; voxel::sceneGraphComparator(srcSceneGraph1, srcSceneGraph2, flags, maxDelta); } @@ -309,30 +289,13 @@ void AbstractFormatTest::testLoadSaveAndLoadSceneGraph(const core::String &srcFi voxel::ValidateFlags flags, float maxDelta) { SCOPED_TRACE("src: " + srcFilename); SCOPED_TRACE("target: " + destFilename); + const io::ArchivePtr &archive = helper_filesystemarchive(); scenegraph::SceneGraph srcSceneGraph; - ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename, srcFormat, srcSceneGraph)) << "Failed to load " << srcFilename; -#if WRITE_TO_FILE - { - io::FileStream stream(open(destFilename, io::FileMode::SysWrite)); - ASSERT_TRUE(destFormat.save(srcSceneGraph, destFilename, stream, testSaveCtx)) - << "Could not save " << destFilename; - } -#else - io::BufferedReadWriteStream stream((int64_t)(10 * 1024 * 1024)); - ASSERT_TRUE(destFormat.save(srcSceneGraph, destFilename, stream, testSaveCtx)) << "Could not save " << destFilename; - stream.seek(0); -#endif + ASSERT_TRUE(helper_loadIntoSceneGraph(srcFilename, archive, srcFormat, srcSceneGraph)) << "Failed to load " << srcFilename; + ASSERT_TRUE(destFormat.save(srcSceneGraph, destFilename, archive, testSaveCtx)) << "Could not save " << destFilename; scenegraph::SceneGraph destSceneGraph; -#if WRITE_TO_FILE - { - io::FileStream stream(open(destFilename)); - ASSERT_TRUE(destFormat.load(destFilename, stream, destSceneGraph, testLoadCtx)) - << "Failed to load the target format"; - } -#else - ASSERT_TRUE(destFormat.load(destFilename, stream, destSceneGraph, testLoadCtx)) + ASSERT_TRUE(destFormat.load(destFilename, archive, destSceneGraph, testLoadCtx)) << "Failed to load the target format"; -#endif voxel::sceneGraphComparator(srcSceneGraph, destSceneGraph, flags, maxDelta); } @@ -341,7 +304,6 @@ void AbstractFormatTest::testSaveSingleVoxel(const core::String &filename, Forma voxel::Region region(glm::ivec3(0), glm::ivec3(0)); voxel::RawVolume original(region); original.setVoxel(0, 0, 0, voxel::createVoxel(voxel::VoxelType::Generic, 0)); - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); scenegraph::SceneGraph sceneGraphsave(2); { palette::Palette pal; @@ -351,11 +313,11 @@ void AbstractFormatTest::testSaveSingleVoxel(const core::String &filename, Forma node.setPalette(pal); sceneGraphsave.emplace(core::move(node)); } - ASSERT_TRUE(format->save(sceneGraphsave, filename, bufferedStream, testSaveCtx)); - bufferedStream.seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(format->save(sceneGraphsave, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraph; - ASSERT_TRUE(format->load(filename, bufferedStream, sceneGraph, testLoadCtx)); + ASSERT_TRUE(format->load(filename, archive, sceneGraph, testLoadCtx)); voxel::sceneGraphComparator(sceneGraph, sceneGraphsave, flags); } @@ -370,7 +332,6 @@ void AbstractFormatTest::testSaveSmallVolume(const core::String &filename, Forma ASSERT_TRUE(original.setVoxel(0, 0, 1, voxel::createVoxel(pal, 200))); ASSERT_TRUE(original.setVoxel(0, 1, 1, voxel::createVoxel(pal, 201))); ASSERT_TRUE(original.setVoxel(0, 0, 0, voxel::createVoxel(pal, pal.colorCount() - 1))); - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); scenegraph::SceneGraph sceneGraphsave(3); int modelNodeId; { @@ -389,11 +350,11 @@ void AbstractFormatTest::testSaveSmallVolume(const core::String &filename, Forma node.setTransform(keyFrameIdx, transform); ASSERT_NE(InvalidNodeId, sceneGraphsave.emplace(core::move(node))); } - ASSERT_TRUE(format->save(sceneGraphsave, filename, bufferedStream, testSaveCtx)); - bufferedStream.seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(format->save(sceneGraphsave, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraph; - ASSERT_TRUE(format->load(filename, bufferedStream, sceneGraph, testLoadCtx)); + ASSERT_TRUE(format->load(filename, archive, sceneGraph, testLoadCtx)); voxel::sceneGraphComparator(sceneGraph, sceneGraphsave, flags); } @@ -431,12 +392,11 @@ void AbstractFormatTest::testSaveMultipleModels(const core::String &filename, Fo sceneGraph.emplace(core::move(node3)); sceneGraph.emplace(core::move(node4)); sceneGraph.emplace(core::move(node5)); - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); - ASSERT_TRUE(format->save(sceneGraph, filename, bufferedStream, testSaveCtx)); - bufferedStream.seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(format->save(sceneGraph, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraphLoad; - EXPECT_TRUE(format->load(filename, bufferedStream, sceneGraphLoad, testLoadCtx)); + EXPECT_TRUE(format->load(filename, archive, sceneGraphLoad, testLoadCtx)); voxel::sceneGraphComparator(sceneGraph, sceneGraphLoad, flags); } @@ -451,12 +411,11 @@ void AbstractFormatTest::testSave(const core::String &filename, Format *format, node1.setPalette(palette); node1.setVolume(&model1, false); sceneGraph.emplace(core::move(node1)); - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); - ASSERT_TRUE(format->save(sceneGraph, filename, bufferedStream, testSaveCtx)); - bufferedStream.seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(format->save(sceneGraph, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraphLoad; - EXPECT_TRUE(format->load(filename, bufferedStream, sceneGraphLoad, testLoadCtx)); + EXPECT_TRUE(format->load(filename, archive, sceneGraphLoad, testLoadCtx)); voxel::sceneGraphComparator(sceneGraph, sceneGraphLoad, flags); } @@ -558,33 +517,11 @@ void AbstractFormatTest::testSaveLoadVolumes(const core::String &filename, const sceneGraph.emplace(core::move(node)); } } - io::SeekableReadStream *readStream; - io::SeekableWriteStream *writeStream; - -#if WRITE_TO_FILE - io::FilePtr sfile = open(filename, io::FileMode::SysWrite); - io::FileStream fileWriteStream(sfile); - writeStream = &fileWriteStream; -#else - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); - writeStream = &bufferedStream; -#endif - - ASSERT_TRUE(format->save(sceneGraph, filename, *writeStream, testSaveCtx)) << "Could not save the scene graph"; - -#if WRITE_TO_FILE - sfile->close(); - io::FilePtr readfile = open(filename); - io::FileStream fileReadStream(readfile); - readStream = &fileReadStream; -#else - readStream = &bufferedStream; -#endif - - readStream->seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(format->save(sceneGraph, filename, archive, testSaveCtx)) << "Could not save the scene graph"; scenegraph::SceneGraph sceneGraphLoad; - ASSERT_TRUE(format->load(filename, *readStream, sceneGraphLoad, testLoadCtx)); + ASSERT_TRUE(format->load(filename, archive, sceneGraphLoad, testLoadCtx)) << "Failed to load the scene grpah"; voxel::sceneGraphComparator(sceneGraph, sceneGraphLoad, flags, maxDelta); } @@ -595,25 +532,13 @@ io::FilePtr AbstractFormatTest::open(const core::String &filename, io::FileMode return file; } -bool AbstractFormatTest::helper_loadIntoSceneGraph(const core::String &filename, Format &format, +bool AbstractFormatTest::helper_loadIntoSceneGraph(const core::String &filename, const io::ArchivePtr &archive, Format &format, scenegraph::SceneGraph &sceneGraph) { - const io::FilePtr &file = open(filename); - if (!file->validHandle()) { - Log::error("Could not open %s for reading", filename.c_str()); - return false; - } - io::FileStream stream(file); - return format.load(filename, stream, sceneGraph, testLoadCtx); + return format.load(filename, archive, sceneGraph, testLoadCtx); } -int AbstractFormatTest::helper_loadPalette(const core::String &filename, Format &format, palette::Palette &palette) { - const io::FilePtr &file = open(filename); - if (!file->validHandle()) { - Log::error("Could not open %s for reading the palette", filename.c_str()); - return -1; - } - io::FileStream stream(file); - return (int)format.loadPalette(filename, stream, palette, testLoadCtx); +int AbstractFormatTest::helper_loadPalette(const core::String &filename, const io::ArchivePtr &archive, Format &format, palette::Palette &palette) { + return (int)format.loadPalette(filename, archive, palette, testLoadCtx); } bool AbstractFormatTest::onInitApp() { diff --git a/src/modules/voxelformat/tests/AbstractFormatTest.h b/src/modules/voxelformat/tests/AbstractFormatTest.h index d67df8c071..6c73dbfbc1 100644 --- a/src/modules/voxelformat/tests/AbstractFormatTest.h +++ b/src/modules/voxelformat/tests/AbstractFormatTest.h @@ -21,11 +21,11 @@ class AbstractFormatTest : public voxel::AbstractVoxelTest { /** * Helper method to load a scenegraph */ - bool helper_loadIntoSceneGraph(const core::String &filename, Format &format, scenegraph::SceneGraph &sceneGraph); + bool helper_loadIntoSceneGraph(const core::String &filename, const io::ArchivePtr &archive, Format &format, scenegraph::SceneGraph &sceneGraph); void testSaveLoadVolumes(const core::String &filename, const voxel::RawVolume &v, Format *format, voxel::ValidateFlags flags = voxel::ValidateFlags::All, float maxDelta = 0.001f); - void testRGBSmall(const core::String &filename, io::SeekableReadStream &stream, scenegraph::SceneGraph &sceneGraph); + void testRGBSmall(const core::String &filename, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph); protected: static const voxel::Voxel Empty; @@ -36,8 +36,12 @@ class AbstractFormatTest : public voxel::AbstractVoxelTest { testSaveCtx.thumbnailCreator = helper_testThumbnailCreator; } + io::ArchivePtr helper_archive(const core::String &filename = ""); + io::ArchivePtr helper_filesystemarchive(); + void testFirstAndLastPaletteIndex(const core::String &filename, Format *format, voxel::ValidateFlags flags); - void testFirstAndLastPaletteIndexConversion(Format &srcFormat, const core::String &destFilename, Format &destFormat, + void testFirstAndLastPaletteIndexConversion(Format &srcFormat, const core::String &srcFilename, Format &destFormat, + const core::String &destFilename, voxel::ValidateFlags flags = voxel::ValidateFlags::All); void testLoad(scenegraph::SceneGraph &sceneGraph, const core::String &filename, size_t expectedVolumes = 1); @@ -81,7 +85,7 @@ class AbstractFormatTest : public voxel::AbstractVoxelTest { /** * @brief Not a test, but a helper method to load a palette from a given format */ - int helper_loadPalette(const core::String &filename, Format &format, palette::Palette &palette); + int helper_loadPalette(const core::String &filename, const io::ArchivePtr &archive, Format &format, palette::Palette &palette); /** * @brief Not a test, but a helper method to store a scenegraph for manual inspection diff --git a/src/modules/voxelformat/tests/AoSVXLFormatTest.cpp b/src/modules/voxelformat/tests/AoSVXLFormatTest.cpp index 2a993a3b35..3189b8c8c7 100644 --- a/src/modules/voxelformat/tests/AoSVXLFormatTest.cpp +++ b/src/modules/voxelformat/tests/AoSVXLFormatTest.cpp @@ -4,7 +4,7 @@ #include "voxelformat/private/aceofspades/AoSVXLFormat.h" #include "AbstractFormatTest.h" -#include "io/BufferedReadWriteStream.h" +#include "io/Archive.h" #include "voxelformat/tests/TestHelper.h" namespace voxelformat { @@ -28,7 +28,7 @@ TEST_F(AoSVXLFormatTest, testLoad) { TEST_F(AoSVXLFormatTest, testLoadPalette) { AoSVXLFormat f; palette::Palette pal; - EXPECT_GT(helper_loadPalette("aceofspades.vxl", f, pal), 200); + EXPECT_GT(helper_loadPalette("aceofspades.vxl", helper_filesystemarchive(), f, pal), 200); } TEST_F(AoSVXLFormatTest, testLoadSaveAndLoadSceneGraph) { @@ -54,12 +54,11 @@ TEST_F(AoSVXLFormatTest, testSave) { scenegraph::SceneGraphNode node1; node1.setVolume(&model1, false); sceneGraph.emplace(core::move(node1)); - io::BufferedReadWriteStream bufferedStream((int64_t)(10 * 1024 * 1024)); - ASSERT_TRUE(f.save(sceneGraph, filename, bufferedStream, testSaveCtx)); - bufferedStream.seek(0); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(f.save(sceneGraph, filename, archive, testSaveCtx)); scenegraph::SceneGraph sceneGraphLoad; - EXPECT_TRUE(f.load(filename, bufferedStream, sceneGraphLoad, testLoadCtx)); + EXPECT_TRUE(f.load(filename, archive, sceneGraphLoad, testLoadCtx)); EXPECT_EQ(sceneGraphLoad.size(), 1u); } diff --git a/src/modules/voxelformat/tests/ConvertTest.cpp b/src/modules/voxelformat/tests/ConvertTest.cpp index 1dadb86760..945cba9f7e 100644 --- a/src/modules/voxelformat/tests/ConvertTest.cpp +++ b/src/modules/voxelformat/tests/ConvertTest.cpp @@ -31,14 +31,14 @@ TEST_F(ConvertTest, testVoxToVXMPalette) { VoxFormat src; VXMFormat target; const voxel::ValidateFlags flags = voxel::ValidateFlags::All & ~(voxel::ValidateFlags::Palette); - testFirstAndLastPaletteIndexConversion(src, "palette-check.vxm", target, flags); + testFirstAndLastPaletteIndexConversion(src, "palette-in.vox", target, "palette-check.vxm", flags); } TEST_F(ConvertTest, testVoxToSLAB6VoxPalette) { VoxFormat src; SLAB6VoxFormat target; const voxel::ValidateFlags flags = voxel::ValidateFlags::All & ~(voxel::ValidateFlags::Palette); - testFirstAndLastPaletteIndexConversion(src, "palette-check.vox", target, flags); + testFirstAndLastPaletteIndexConversion(src, "palette-in.vox", target, "palette-check.vox", flags); } TEST_F(ConvertTest, testVoxToVXM) { diff --git a/src/modules/voxelformat/tests/CubFormatTest.cpp b/src/modules/voxelformat/tests/CubFormatTest.cpp index 9f1411af21..ce976b36d9 100644 --- a/src/modules/voxelformat/tests/CubFormatTest.cpp +++ b/src/modules/voxelformat/tests/CubFormatTest.cpp @@ -18,7 +18,7 @@ TEST_F(CubFormatTest, testLoad) { TEST_F(CubFormatTest, testLoadPalette) { CubFormat f; palette::Palette pal; - EXPECT_EQ(5, helper_loadPalette("rgb.cub", f, pal)); + EXPECT_EQ(5, helper_loadPalette("rgb.cub", helper_filesystemarchive(), f, pal)); } TEST_F(CubFormatTest, testLoadRGB) { diff --git a/src/modules/voxelformat/tests/CubzhFormatTest.cpp b/src/modules/voxelformat/tests/CubzhFormatTest.cpp index 5fb1d7eafc..6bed91951b 100644 --- a/src/modules/voxelformat/tests/CubzhFormatTest.cpp +++ b/src/modules/voxelformat/tests/CubzhFormatTest.cpp @@ -28,7 +28,7 @@ TEST_F(CubzhFormatTest, testLoad3ZH) { TEST_F(CubzhFormatTest, testLoadPalette) { PCubesFormat f; palette::Palette pal; - EXPECT_EQ(96, helper_loadPalette("particubes.pcubes", f, pal)); + EXPECT_EQ(96, helper_loadPalette("particubes.pcubes", helper_filesystemarchive(), f, pal)); } TEST_F(CubzhFormatTest, testSaveSmallVoxel3ZH) { diff --git a/src/modules/voxelformat/tests/FormatPaletteTest.cpp b/src/modules/voxelformat/tests/FormatPaletteTest.cpp index a2917b9a57..c49457791f 100644 --- a/src/modules/voxelformat/tests/FormatPaletteTest.cpp +++ b/src/modules/voxelformat/tests/FormatPaletteTest.cpp @@ -33,25 +33,21 @@ class FormatPaletteTest : public AbstractFormatTest { SCOPED_TRACE("rgb file: " + rgbFile); SCOPED_TRACE("pal file: " + palFile); - io::FileStream rgbStream(_testApp->filesystem()->open(rgbFile)); + io::ArchivePtr archive = helper_filesystemarchive(); palette::Palette rgbPalette; - EXPECT_EQ(rgbExpectedColors, rgbFormat.loadPalette(rgbFile, rgbStream, rgbPalette, testLoadCtx)) + EXPECT_EQ(rgbExpectedColors, rgbFormat.loadPalette(rgbFile, archive, rgbPalette, testLoadCtx)) << "Found expected amount of colors in the rgb format"; ASSERT_TRUE(checkNoAlpha(rgbPalette)) << "Found alpha in the rgb palette"; - rgbStream.seek(0); - scenegraph::SceneGraph rgbSceneGraph; - ASSERT_TRUE(rgbFormat.load(rgbFile, rgbStream, rgbSceneGraph, testLoadCtx)) + ASSERT_TRUE(rgbFormat.load(rgbFile, archive, rgbSceneGraph, testLoadCtx)) << "Failed to load rgb model " << rgbFile; - io::BufferedReadWriteStream palWriteStream; - ASSERT_TRUE(paletteFormat.save(rgbSceneGraph, palFile, palWriteStream, testSaveCtx)) + ASSERT_TRUE(paletteFormat.save(rgbSceneGraph, palFile, archive, testSaveCtx)) << "Failed to write pal model " << palFile; - palWriteStream.seek(0); palette::Palette palPalette; - ASSERT_GT(paletteFormat.loadPalette(palFile, palWriteStream, palPalette, testLoadCtx), 0u) + ASSERT_GT(paletteFormat.loadPalette(palFile, archive, palPalette, testLoadCtx), 0u) << "Found expected amount of colors in the palette format"; // ASSERT_TRUE(checkNoAlpha(palPalette)); @@ -83,24 +79,21 @@ class FormatPaletteTest : public AbstractFormatTest { SCOPED_TRACE("pal file: " + palFile); SCOPED_TRACE("rgb file: " + rgbFile); - io::FileStream palStream(_testApp->filesystem()->open(palFile)); + io::ArchivePtr archive = helper_filesystemarchive(); + palette::Palette palPalette; - ASSERT_EQ(palExpectedColors, palFormat.loadPalette(palFile, palStream, palPalette, testLoadCtx)); + ASSERT_EQ(palExpectedColors, palFormat.loadPalette(palFile, archive, palPalette, testLoadCtx)); // ASSERT_TRUE(checkNoAlpha(palPalette)); - palStream.seek(0); - scenegraph::SceneGraph palSceneGraph; - ASSERT_TRUE(palFormat.load(palFile, palStream, palSceneGraph, testLoadCtx)) + ASSERT_TRUE(palFormat.load(palFile, archive, palSceneGraph, testLoadCtx)) << "Failed to load pal model " << palFile; - io::BufferedReadWriteStream rgbWriteStream; - ASSERT_TRUE(rgbFormat.save(palSceneGraph, rgbFile, rgbWriteStream, testSaveCtx)) + ASSERT_TRUE(rgbFormat.save(palSceneGraph, rgbFile, archive, testSaveCtx)) << "Failed to write rgb model " << rgbFile; - rgbWriteStream.seek(0); palette::Palette rgbPalette; - ASSERT_EQ(rgbFormat.loadPalette(rgbFile, rgbWriteStream, rgbPalette, testLoadCtx), rgbExpectedColors); + ASSERT_EQ(rgbFormat.loadPalette(rgbFile, archive, rgbPalette, testLoadCtx), rgbExpectedColors); ASSERT_TRUE(checkNoAlpha(rgbPalette)); for (size_t i = 0; i < rgbExpectedColors; ++i) { @@ -115,24 +108,21 @@ class FormatPaletteTest : public AbstractFormatTest { SCOPED_TRACE("1. rgb file: " + rgbFile1); SCOPED_TRACE("2. rgb file: " + rgbFile2); - io::FileStream palStream(_testApp->filesystem()->open(rgbFile1)); + io::ArchivePtr archive = helper_filesystemarchive(); + palette::Palette rgbPalette1; - ASSERT_EQ(expectedColors, rgbFormat1.loadPalette(rgbFile1, palStream, rgbPalette1, testLoadCtx)); + ASSERT_EQ(expectedColors, rgbFormat1.loadPalette(rgbFile1, archive, rgbPalette1, testLoadCtx)); ASSERT_TRUE(checkNoAlpha(rgbPalette1)); - palStream.seek(0); - scenegraph::SceneGraph palSceneGraph; - ASSERT_TRUE(rgbFormat1.load(rgbFile1, palStream, palSceneGraph, testLoadCtx)) + ASSERT_TRUE(rgbFormat1.load(rgbFile1, archive, palSceneGraph, testLoadCtx)) << "Failed to load rgb model " << rgbFile1; - io::BufferedReadWriteStream rgbWriteStream; - ASSERT_TRUE(rgbFormat2.save(palSceneGraph, rgbFile2, rgbWriteStream, testSaveCtx)) + ASSERT_TRUE(rgbFormat2.save(palSceneGraph, rgbFile2, archive, testSaveCtx)) << "Failed to write rgb model " << rgbFile2; - rgbWriteStream.seek(0); palette::Palette rgbPalette2; - ASSERT_EQ(rgbFormat2.loadPalette(rgbFile2, rgbWriteStream, rgbPalette2, testLoadCtx), expectedColors); + ASSERT_EQ(rgbFormat2.loadPalette(rgbFile2, archive, rgbPalette2, testLoadCtx), expectedColors); ASSERT_TRUE(checkNoAlpha(rgbPalette2)); // the colors might have a different ordering here it depends on the order we read the volume for the rgb format @@ -149,24 +139,21 @@ class FormatPaletteTest : public AbstractFormatTest { SCOPED_TRACE("1. pal file: " + palFile1); SCOPED_TRACE("2. pal file: " + palFile2); - io::FileStream palStream(_testApp->filesystem()->open(palFile1)); + io::ArchivePtr archive = helper_filesystemarchive(); + palette::Palette palPalette1; - ASSERT_EQ(expectedColors, palFormat1.loadPalette(palFile1, palStream, palPalette1, testLoadCtx)); + ASSERT_EQ(expectedColors, palFormat1.loadPalette(palFile1, archive, palPalette1, testLoadCtx)); // ASSERT_TRUE(checkNoAlpha(palPalette1)); - palStream.seek(0); - scenegraph::SceneGraph palSceneGraph; - ASSERT_TRUE(palFormat1.load(palFile1, palStream, palSceneGraph, testLoadCtx)) + ASSERT_TRUE(palFormat1.load(palFile1, archive, palSceneGraph, testLoadCtx)) << "Failed to load pal model " << palFile1; - io::BufferedReadWriteStream rgbWriteStream; - ASSERT_TRUE(palFormat2.save(palSceneGraph, palFile2, rgbWriteStream, testSaveCtx)) + ASSERT_TRUE(palFormat2.save(palSceneGraph, palFile2, archive, testSaveCtx)) << "Failed to write pal model " << palFile2; - rgbWriteStream.seek(0); palette::Palette palPalette2; - ASSERT_EQ(palFormat2.loadPalette(palFile2, rgbWriteStream, palPalette2, testLoadCtx), expectedColors); + ASSERT_EQ(palFormat2.loadPalette(palFile2, archive, palPalette2, testLoadCtx), expectedColors); // ASSERT_TRUE(checkNoAlpha(palPalette2)); for (size_t i = 0; i < expectedColors; ++i) { diff --git a/src/modules/voxelformat/tests/MeshFormatTest.cpp b/src/modules/voxelformat/tests/MeshFormatTest.cpp index a443c2ef17..40c1493d8e 100644 --- a/src/modules/voxelformat/tests/MeshFormatTest.cpp +++ b/src/modules/voxelformat/tests/MeshFormatTest.cpp @@ -5,6 +5,7 @@ #include "voxelformat/private/mesh/MeshFormat.h" #include "core/Color.h" #include "core/tests/TestColorHelper.h" +#include "io/Archive.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" #include "video/ShapeBuilder.h" @@ -118,7 +119,7 @@ TEST_F(MeshFormatTest, testVoxelizeColor) { class TestMesh : public MeshFormat { public: bool saveMeshes(const core::Map &, const scenegraph::SceneGraph &, const Meshes &, - const core::String &, io::SeekableWriteStream &, const glm::vec3 &, bool, bool, bool) override { + const core::String &, const io::ArchivePtr &, const glm::vec3 &, bool, bool, bool) override { return false; } void voxelize(scenegraph::SceneGraph &sceneGraph, const MeshFormat::TriCollection &tris) { diff --git a/src/modules/voxelformat/tests/VolumeFormatTest.cpp b/src/modules/voxelformat/tests/VolumeFormatTest.cpp index 3069c751a4..c684050584 100644 --- a/src/modules/voxelformat/tests/VolumeFormatTest.cpp +++ b/src/modules/voxelformat/tests/VolumeFormatTest.cpp @@ -4,8 +4,7 @@ #include "voxelformat/VolumeFormat.h" #include "AbstractFormatTest.h" -#include "io/File.h" -#include "io/FileStream.h" +#include "io/FilesystemArchive.h" namespace voxelformat { @@ -21,14 +20,12 @@ TEST_F(VolumeFormatTest, testImportPalette) { TEST_F(VolumeFormatTest, testLoadFormat) { const char *files[] = {"rgb.csv", "rgb.cub", "rgb.gox", "rgb.qb", "rgb.qbcl", "rgb.qef", "rgb.vox", "rgb.vxl", "rgb.vxm"}; + const io::ArchivePtr &archive = io::openFilesystemArchive(_testApp->filesystem()); for (int i = 0; i < lengthof(files); ++i) { - io::FilePtr file = _testApp->filesystem()->open(files[i]); - ASSERT_TRUE(file->validHandle()); - io::FileStream stream(file); io::FileDescription fileDesc; - fileDesc.set(file->name()); + fileDesc.set(files[i]); scenegraph::SceneGraph newSceneGraph; - EXPECT_TRUE(loadFormat(fileDesc, stream, newSceneGraph, testLoadCtx)) << "Failed to load " << files[i]; + EXPECT_TRUE(loadFormat(fileDesc, archive, newSceneGraph, testLoadCtx)) << "Failed to load " << files[i]; EXPECT_GT(newSceneGraph.size(), 0u) << "Empty scene graph for " << files[i]; } } diff --git a/src/modules/voxelformat/tests/VoxFormatTest.cpp b/src/modules/voxelformat/tests/VoxFormatTest.cpp index 8c47545a1a..26f38586c4 100644 --- a/src/modules/voxelformat/tests/VoxFormatTest.cpp +++ b/src/modules/voxelformat/tests/VoxFormatTest.cpp @@ -4,7 +4,6 @@ #include "voxelformat/private/magicavoxel/VoxFormat.h" #include "AbstractFormatTest.h" -#include "io/BufferedReadWriteStream.h" #include "scenegraph/SceneGraphNode.h" #include "voxel/MaterialColor.h" #include "palette/Palette.h" @@ -15,8 +14,6 @@ #include "vox_glasses.h" #include "8ontop.h" -#define VOX_TEST_SAVE_TO_FILE 0 - namespace voxelformat { class VoxFormatTest : public AbstractFormatTest {}; @@ -45,16 +42,9 @@ TEST_F(VoxFormatTest, testLoadMaterials) { scenegraph::SceneGraph sceneGraph; -#if VOX_TEST_SAVE_TO_FILE - saveSceneGraph(mvSceneGraph, name); - canLoad(sceneGraph, name, 12u); -#else - io::BufferedReadWriteStream stream(10 * 1024 * 1024); - ASSERT_TRUE(f.save(mvSceneGraph, name, stream, testSaveCtx)); - stream.seek(0); - f.load(name, stream, sceneGraph, testLoadCtx); - ASSERT_EQ(12u, sceneGraph.size()); -#endif + io::ArchivePtr archive = helper_filesystemarchive(); + helper_saveSceneGraph(mvSceneGraph, name); + testLoad(sceneGraph, name, 12u); scenegraph::SceneGraphNode *node = sceneGraph.firstModelNode(); ASSERT_TRUE(node != nullptr); @@ -174,16 +164,10 @@ TEST_F(VoxFormatTest, testSaveBigVolume) { sceneGraphsave.emplace(core::move(node)); } -#if VOX_TEST_SAVE_TO_FILE - saveSceneGraph(sceneGraphsave, name); - canLoad(sceneGraph, name, 3u); -#else - io::BufferedReadWriteStream stream(10 * 1024 * 1024); - ASSERT_TRUE(f.save(sceneGraphsave, name, stream, testSaveCtx)); - stream.seek(0); - f.load(name, stream, sceneGraph, testLoadCtx); + io::ArchivePtr archive = helper_archive(); + ASSERT_TRUE(f.save(sceneGraphsave, name, archive, testSaveCtx)); + f.load(name, archive, sceneGraph, testLoadCtx); EXPECT_EQ(3, (int)sceneGraph.size()); -#endif } TEST_F(VoxFormatTest, testSave) { diff --git a/src/modules/voxelgenerator/LUAApi.cpp b/src/modules/voxelgenerator/LUAApi.cpp index e7bf61e6b2..9cf252d13f 100644 --- a/src/modules/voxelgenerator/LUAApi.cpp +++ b/src/modules/voxelgenerator/LUAApi.cpp @@ -12,6 +12,7 @@ #include "image/Image.h" #include "io/Stream.h" #include "io/BufferedReadWriteStream.h" +#include "io/StreamArchive.h" #include "lua.h" #include "math/Axis.h" #include "noise/Simplex.h" @@ -532,12 +533,13 @@ static int luaVoxel_shape_bezier(lua_State* s) { static int luaVoxel_load_palette(lua_State *s) { const char *filename = luaL_checkstring(s, 1); - io::BufferedReadWriteStream *readStream = clua_tostream(s, 2); + io::SeekableReadStream *readStream = clua_tostream(s, 2); io::FileDescription fileDesc; fileDesc.set(filename); voxelformat::LoadContext ctx; + auto archive = core::make_shared(readStream); palette::Palette *palette = new palette::Palette(); - const bool ret = voxelformat::loadPalette(filename, *readStream, *palette, ctx); + const bool ret = voxelformat::loadPalette(filename, archive, *palette, ctx); if (!ret) { delete palette; return clua_error(s, "Could not load palette %s from string", filename); @@ -578,12 +580,13 @@ static int luaVoxel_import_imageasplane(lua_State *s) { static int luaVoxel_import_scene(lua_State *s) { const char *filename = luaL_checkstring(s, 1); - io::BufferedReadWriteStream *readStream = clua_tostream(s, 2); + io::SeekableReadStream *readStream = clua_tostream(s, 2); io::FileDescription fileDesc; fileDesc.set(filename); voxelformat::LoadContext ctx; scenegraph::SceneGraph newSceneGraph; - const bool ret = voxelformat::loadFormat(fileDesc, *readStream, newSceneGraph, ctx); + auto archive = core::make_shared(readStream); + const bool ret = voxelformat::loadFormat(fileDesc, archive, newSceneGraph, ctx); if (!ret) { newSceneGraph.clear(); return clua_error(s, "Could not load file %s", filename); diff --git a/src/modules/voxelgenerator/tests/ShapeGeneratorTest.cpp b/src/modules/voxelgenerator/tests/ShapeGeneratorTest.cpp index 1678153b8e..a3516bcb39 100644 --- a/src/modules/voxelgenerator/tests/ShapeGeneratorTest.cpp +++ b/src/modules/voxelgenerator/tests/ShapeGeneratorTest.cpp @@ -5,9 +5,7 @@ #include "app/App.h" #include "core/Color.h" #include "core/ScopedPtr.h" -#include "io/File.h" -#include "io/FileStream.h" -#include "io/Filesystem.h" +#include "io/FilesystemArchive.h" #include "app/tests/AbstractTest.h" #include "math/Axis.h" #include "voxel/MaterialColor.h" @@ -89,13 +87,11 @@ class ShapeGeneratorTest: public app::AbstractTest { } void verify(const char* filename) { - voxelformat::QBFormat format; - const io::FilePtr& file = io::filesystem()->open(filename); - ASSERT_TRUE(file) << "Can't open " << filename; - io::FileStream stream(file); + const io::ArchivePtr &archive = io::openFilesystemArchive(_testApp->filesystem()); scenegraph::SceneGraph sceneGraph; voxelformat::LoadContext loadCtx; - ASSERT_TRUE(format.load(file->fileName(), stream, sceneGraph, loadCtx)); + voxelformat::QBFormat format; + ASSERT_TRUE(format.load(filename, archive, sceneGraph, loadCtx)); scenegraph::SceneGraph::MergedVolumePalette merged = sceneGraph.merge(); core::ScopedPtr v( merged.first); ASSERT_NE(nullptr, v) << "Can't load " << filename; diff --git a/src/modules/voxelpathtracer/tests/PathTracerTest.cpp b/src/modules/voxelpathtracer/tests/PathTracerTest.cpp index 19b36b31da..fb61137ff8 100644 --- a/src/modules/voxelpathtracer/tests/PathTracerTest.cpp +++ b/src/modules/voxelpathtracer/tests/PathTracerTest.cpp @@ -6,7 +6,7 @@ #include "app/App.h" #include "app/tests/AbstractTest.h" #include "image/Image.h" -#include "io/FileStream.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "scenegraph/SceneGraph.h" #include "voxel/MaterialColor.h" @@ -32,16 +32,13 @@ class PathTracerTest : public app::AbstractTest { }; TEST_F(PathTracerTest, testHMec) { - const io::FilePtr &file = io::filesystem()->open("hmec.vxl"); - ASSERT_TRUE(file->validHandle()); - io::FileStream stream(file); + const io::ArchivePtr &archive = io::openFilesystemArchive(_testApp->filesystem()); io::FileDescription fileDesc; - fileDesc.set(file->name()); + fileDesc.set("hmec.vxl"); scenegraph::SceneGraph sceneGraph; voxelformat::LoadContext testLoadCtx; - - ASSERT_TRUE(voxelformat::loadFormat(fileDesc, stream, sceneGraph, testLoadCtx)) - << "Could not load " << file->name(); + ASSERT_TRUE(voxelformat::loadFormat(fileDesc, archive, sceneGraph, testLoadCtx)) + << "Could not load " << fileDesc.name.c_str(); voxelpathtracer::PathTracer pathTracer; pathTracer.state().params.resolution = 512; @@ -55,6 +52,6 @@ TEST_F(PathTracerTest, testHMec) { ASSERT_TRUE(img->isLoaded()); ASSERT_EQ(512, img->width()); // ASSERT_EQ(dimensions, img->height()); - image::writeImage(img, file->name() + ".png"); + image::writeImage(img, "hmec.vxl.png"); ASSERT_TRUE(pathTracer.stop()); } diff --git a/src/tools/thumbnailer/Thumbnailer.cpp b/src/tools/thumbnailer/Thumbnailer.cpp index 733b76b80f..5ce87250b6 100644 --- a/src/tools/thumbnailer/Thumbnailer.cpp +++ b/src/tools/thumbnailer/Thumbnailer.cpp @@ -6,10 +6,11 @@ #include "core/Log.h" #include "core/StringUtil.h" #include "core/TimeProvider.h" -#include "core/Var.h" #include "image/Image.h" +#include "io/Archive.h" #include "io/FileStream.h" #include "io/Filesystem.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "voxelformat/FormatConfig.h" #include "voxelformat/VolumeFormat.h" @@ -94,19 +95,18 @@ app::AppState Thumbnailer::onInit() { return state; } -static image::ImagePtr volumeThumbnail(const core::String &fileName, io::SeekableReadStream &stream, +static image::ImagePtr volumeThumbnail(const core::String &fileName, const io::ArchivePtr &archive, voxelformat::ThumbnailContext &ctx) { voxelformat::LoadContext loadctx; - image::ImagePtr image = voxelformat::loadScreenshot(fileName, stream, loadctx); + image::ImagePtr image = voxelformat::loadScreenshot(fileName, archive, loadctx); if (image && image->isLoaded()) { return image; } scenegraph::SceneGraph sceneGraph; - stream.seek(0); io::FileDescription fileDesc; fileDesc.set(fileName); - if (!voxelformat::loadFormat(fileDesc, stream, sceneGraph, loadctx)) { + if (!voxelformat::loadFormat(fileDesc, archive, sceneGraph, loadctx)) { Log::error("Failed to load given input file: %s", fileName.c_str()); return image::ImagePtr(); } @@ -117,12 +117,11 @@ static image::ImagePtr volumeThumbnail(const core::String &fileName, io::Seekabl static bool volumeTurntable(const core::String &fileName, const core::String &imageFile, voxelformat::ThumbnailContext ctx, int loops) { scenegraph::SceneGraph sceneGraph; - io::FileStream stream(io::filesystem()->open(fileName, io::FileMode::SysRead)); - stream.seek(0); + const io::ArchivePtr &archive = io::openFilesystemArchive(io::filesystem()); voxelformat::LoadContext loadctx; io::FileDescription fileDesc; fileDesc.set(fileName); - if (!voxelformat::loadFormat(fileDesc, stream, sceneGraph, loadctx)) { + if (!voxelformat::loadFormat(fileDesc, archive, sceneGraph, loadctx)) { Log::error("Failed to load given input file: %s", fileName.c_str()); return false; } @@ -185,12 +184,12 @@ app::AppState Thumbnailer::onRunning() { if (renderTurntable) { volumeTurntable(_infile->name(), _outfile, ctx, 16); } else { - io::FileStream stream(_infile); - if (!stream.valid()) { + const io::ArchivePtr &archive = io::openFilesystemArchive(_filesystem); + if (!archive) { Log::error("Failed to open %s for reading", _infile->name().c_str()); return app::AppState::Cleanup; } - const image::ImagePtr &image = volumeThumbnail(_infile->name(), stream, ctx); + const image::ImagePtr &image = volumeThumbnail(_infile->name(), archive, ctx); saveImage(image); } diff --git a/src/tools/voxbrowser/modules/voxbrowser-ui/MainWindow.cpp b/src/tools/voxbrowser/modules/voxbrowser-ui/MainWindow.cpp index 97aef8af98..6e5d8d628c 100644 --- a/src/tools/voxbrowser/modules/voxbrowser-ui/MainWindow.cpp +++ b/src/tools/voxbrowser/modules/voxbrowser-ui/MainWindow.cpp @@ -10,6 +10,7 @@ #include "io/File.h" #include "io/FileStream.h" #include "io/Filesystem.h" +#include "io/FilesystemArchive.h" #include "ui/IMGUIApp.h" #include "ui/IMGUIEx.h" #include "ui/PopupAbout.h" @@ -139,10 +140,9 @@ void MainWindow::createThumbnail(const voxelcollection::VoxelFile &voxelFile) { io::FileDescription fileDesc; fileDesc.set(fullPath); voxelformat::LoadContext loadctx; - const io::FilePtr &file = _app->filesystem()->open(fullPath, io::FileMode::SysRead); - io::FileStream stream(file); - if (!voxelformat::loadFormat(fileDesc, stream, sceneGraph, loadctx)) { - Log::error("Failed to load given input file: %s", file->name().c_str()); + const io::ArchivePtr &archive = io::openFilesystemArchive(_app->filesystem()); + if (!voxelformat::loadFormat(fileDesc, archive, sceneGraph, loadctx)) { + Log::error("Failed to load given input file: %s", fileDesc.name.c_str()); return; } diff --git a/src/tools/voxconvert/VoxConvert.cpp b/src/tools/voxconvert/VoxConvert.cpp index 642b4afa03..2cfbe0ac76 100644 --- a/src/tools/voxconvert/VoxConvert.cpp +++ b/src/tools/voxconvert/VoxConvert.cpp @@ -19,8 +19,10 @@ #include "io/Archive.h" #include "io/FileStream.h" #include "io/Filesystem.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "io/Stream.h" +#include "io/ZipArchive.h" #include "palette/Palette.h" #include "palette/PaletteLookup.h" #include "scenegraph/SceneGraph.h" @@ -51,8 +53,8 @@ #define MaxHeightmapWidth 4096 #define MaxHeightmapHeight 4096 -VoxConvert::VoxConvert(const io::FilesystemPtr& filesystem, const core::TimeProviderPtr& timeProvider) : - Super(filesystem, timeProvider, core::cpus()) { +VoxConvert::VoxConvert(const io::FilesystemPtr &filesystem, const core::TimeProviderPtr &timeProvider) + : Super(filesystem, timeProvider, core::cpus()) { init(ORGANISATION, "voxconvert"); _wantCrashLogs = true; } @@ -69,25 +71,44 @@ app::AppState VoxConvert::onConstruct() { registerArg("--force").setShort("-f").setDescription("Overwrite existing files"); registerArg("--image-as-plane").setDescription("Import given input images as planes"); registerArg("--image-as-volume").setDescription("Import given input image as volume"); - registerArg("--image-as-volume-max-depth").setDefaultValue("8").setDescription("Importing image as volume max depth"); - registerArg("--image-as-volume-both-sides").setDefaultValue("false").setDescription("Importing image as volume for both sides"); + registerArg("--image-as-volume-max-depth") + .setDefaultValue("8") + .setDescription("Importing image as volume max depth"); + registerArg("--image-as-volume-both-sides") + .setDefaultValue("false") + .setDescription("Importing image as volume for both sides"); registerArg("--image-as-heightmap").setDescription("Import given input images as heightmaps"); - registerArg("--colored-heightmap").setDescription("Use the alpha channel of the heightmap as height and the rgb data as surface color"); + registerArg("--colored-heightmap") + .setDescription("Use the alpha channel of the heightmap as height and the rgb data as surface color"); registerArg("--input").setShort("-i").setDescription("Allow to specify input files").addFlag(ARGUMENT_FLAG_FILE); - registerArg("--wildcard").setShort("-w").setDescription("Allow to specify input file filter if --input is a directory"); + registerArg("--wildcard") + .setShort("-w") + .setDescription("Allow to specify input file filter if --input is a directory"); registerArg("--merge").setShort("-m").setDescription("Merge models into one volume"); registerArg("--mirror").setDescription("Mirror by the given axis (x, y or z)"); - registerArg("--output").setShort("-o").setDescription("Allow to specify the output file").addFlag(ARGUMENT_FLAG_FILE); - registerArg("--rotate").setDescription("Rotate by 90 degree at the given axis (x, y or z), specify e.g. x:180 to rotate around x by 180 degree."); + registerArg("--output") + .setShort("-o") + .setDescription("Allow to specify the output file") + .addFlag(ARGUMENT_FLAG_FILE); + registerArg("--rotate") + .setDescription( + "Rotate by 90 degree at the given axis (x, y or z), specify e.g. x:180 to rotate around x by 180 degree."); registerArg("--resize").setDescription("Resize the volume by the given x (right), y (up) and z (back) values"); registerArg("--scale").setShort("-s").setDescription("Scale model to 50% of its original size"); - registerArg("--script").setDefaultValue("script.lua").setDescription("Apply the given lua script to the output volume"); - registerArg("--scriptcolor").setDefaultValue("1").setDescription("Set the palette index that is given to the script parameters"); + registerArg("--script") + .setDefaultValue("script.lua") + .setDescription("Apply the given lua script to the output volume"); + registerArg("--scriptcolor") + .setDefaultValue("1") + .setDescription("Set the palette index that is given to the script parameters"); registerArg("--split").setDescription("Slices the models into pieces of the given size "); - registerArg("--surface-only").setDescription("Remove any non surface voxel. If you are meshing with this, you get also faces on the inner side of your mesh."); + registerArg("--surface-only") + .setDescription("Remove any non surface voxel. If you are meshing with this, you get also faces on the inner " + "side of your mesh."); registerArg("--translate").setShort("-t").setDescription("Translate the models by x (right), y (up), z (back)"); registerArg("--print-formats").setDescription("Print supported formats as json for easier parsing in other tools"); - registerArg("--slice").setDescription("Allows to save the volume data as png slices if the output file is a png file"); + registerArg("--slice").setDescription( + "Allows to save the volume data as png slices if the output file is a png file"); voxelformat::FormatConfig::init(); @@ -151,7 +172,8 @@ void VoxConvert::usage() const { Log::info(" * Discord: https://vengi-voxel.de/discord"); if (core::Var::getSafe(cfg::MetricFlavor)->strVal().empty()) { - Log::info("Please enable anonymous usage statistics. You can do this by setting the metric_flavor cvar to 'json'"); + Log::info( + "Please enable anonymous usage statistics. You can do this by setting the metric_flavor cvar to 'json'"); Log::info("Example: '%s -set metric_flavor json --input xxx --output yyy'", fullAppname().c_str()); } } @@ -181,7 +203,7 @@ static void printFormatDetails(const io::FormatDescription *desc, const core::St } } -bool VoxConvert::slice(const scenegraph::SceneGraph& sceneGraph, const core::String &outfile) { +bool VoxConvert::slice(const scenegraph::SceneGraph &sceneGraph, const core::String &outfile) { const core::String &ext = core::string::extractExtension(outfile); const core::String &basePath = core::string::stripExtension(outfile); for (const auto &e : sceneGraph.nodes()) { @@ -209,7 +231,7 @@ bool VoxConvert::slice(const scenegraph::SceneGraph& sceneGraph, const core::Str rgba[idx] = color; } } - if (!image.loadRGBA((const uint8_t*)rgba.data(), region.getWidthInVoxels(), region.getHeightInVoxels())) { + if (!image.loadRGBA((const uint8_t *)rgba.data(), region.getWidthInVoxels(), region.getHeightInVoxels())) { Log::error("Failed to load slice image %s", filename.c_str()); return false; } @@ -300,19 +322,19 @@ app::AppState VoxConvert::onInit() { } } - _mergeModels = hasArg("--merge"); - _scaleModels = hasArg("--scale"); - _mirrorModels = hasArg("--mirror"); - _rotateModels = hasArg("--rotate"); - _translateModels = hasArg("--translate"); - _exportPalette = hasArg("--export-palette"); - _exportModels = hasArg("--export-models"); - _cropModels = hasArg("--crop"); - _surfaceOnly = hasArg("--surface-only"); - _splitModels = hasArg("--split"); - _dumpSceneGraph = hasArg("--dump"); - _dumpMeshDetails = hasArg("--dump-mesh"); - _resizeModels = hasArg("--resize"); + _mergeModels = hasArg("--merge"); + _scaleModels = hasArg("--scale"); + _mirrorModels = hasArg("--mirror"); + _rotateModels = hasArg("--rotate"); + _translateModels = hasArg("--translate"); + _exportPalette = hasArg("--export-palette"); + _exportModels = hasArg("--export-models"); + _cropModels = hasArg("--crop"); + _surfaceOnly = hasArg("--surface-only"); + _splitModels = hasArg("--split"); + _dumpSceneGraph = hasArg("--dump"); + _dumpMeshDetails = hasArg("--dump-mesh"); + _resizeModels = hasArg("--resize"); Log::info("Options"); if (inputIsMesh || outputIsMesh) { @@ -343,22 +365,23 @@ app::AppState VoxConvert::onInit() { } Log::info("* script: - %s", scriptParameters.c_str()); } - Log::info("* dump scene graph: - %s", (_dumpSceneGraph ? "true" : "false")); - Log::info("* dump mesh details: - %s", (_dumpMeshDetails ? "true" : "false")); - Log::info("* merge models: - %s", (_mergeModels ? "true" : "false")); - Log::info("* scale models: - %s", (_scaleModels ? "true" : "false")); - Log::info("* crop models: - %s", (_cropModels ? "true" : "false")); - Log::info("* surface only: - %s", (_surfaceOnly ? "true" : "false")); - Log::info("* split models: - %s", (_splitModels ? "true" : "false")); - Log::info("* mirror models: - %s", (_mirrorModels ? "true" : "false")); - Log::info("* translate models: - %s", (_translateModels ? "true" : "false")); - Log::info("* rotate models: - %s", (_rotateModels ? "true" : "false")); - Log::info("* export palette: - %s", (_exportPalette ? "true" : "false")); - Log::info("* export models: - %s", (_exportModels ? "true" : "false")); - Log::info("* resize models: - %s", (_resizeModels ? "true" : "false")); + Log::info("* dump scene graph: - %s", (_dumpSceneGraph ? "true" : "false")); + Log::info("* dump mesh details: - %s", (_dumpMeshDetails ? "true" : "false")); + Log::info("* merge models: - %s", (_mergeModels ? "true" : "false")); + Log::info("* scale models: - %s", (_scaleModels ? "true" : "false")); + Log::info("* crop models: - %s", (_cropModels ? "true" : "false")); + Log::info("* surface only: - %s", (_surfaceOnly ? "true" : "false")); + Log::info("* split models: - %s", (_splitModels ? "true" : "false")); + Log::info("* mirror models: - %s", (_mirrorModels ? "true" : "false")); + Log::info("* translate models: - %s", (_translateModels ? "true" : "false")); + Log::info("* rotate models: - %s", (_rotateModels ? "true" : "false")); + Log::info("* export palette: - %s", (_exportPalette ? "true" : "false")); + Log::info("* export models: - %s", (_exportModels ? "true" : "false")); + Log::info("* resize models: - %s", (_resizeModels ? "true" : "false")); if (core::Var::getSafe(cfg::MetricFlavor)->strVal().empty()) { - Log::info("Please enable anonymous usage statistics. You can do this by setting the metric_flavor cvar to 'json'"); + Log::info( + "Please enable anonymous usage statistics. You can do this by setting the metric_flavor cvar to 'json'"); Log::info("Example: '%s -set metric_flavor json --input xxx --output yyy'", fullAppname().c_str()); } @@ -393,8 +416,9 @@ app::AppState VoxConvert::onInit() { return app::AppState::InitFailure; } + const io::ArchivePtr &fsArchive = io::openFilesystemArchive(filesystem()); scenegraph::SceneGraph sceneGraph; - for (const core::String& infile : infiles) { + for (const core::String &infile : infiles) { if (shouldQuit()) { break; } @@ -411,7 +435,7 @@ app::AppState VoxConvert::onInit() { continue; } const core::String fullpath = core::string::path(infile, entry.name); - if (handleInputFile(fullpath, sceneGraph, entities.size() > 1)) { + if (handleInputFile(fullpath, fsArchive, sceneGraph, entities.size() > 1)) { ++success; } } @@ -421,35 +445,12 @@ app::AppState VoxConvert::onInit() { } } else if (io::isSupportedArchive(infile)) { io::FileStream archiveStream(filesystem()->open(infile, io::FileMode::SysRead)); - io::ArchivePtr archive = io::openArchive(filesystem(), infile, &archiveStream); + io::ArchivePtr archive = io::openZipArchive(&archiveStream); if (!archive) { Log::error("Failed to open archive %s", infile.c_str()); return app::AppState::InitFailure; } - // save the files to our home, then process it - for (const auto &entry : archive->files()) { - if (shouldQuit()) { - break; - } - if (!entry.isFile()) { - continue; - } - const core::String &fullPath = entry.fullPath; - const io::FilePtr &targetFile = filesystem()->open(fullPath, io::FileMode::Write); - io::FileStream targetStream(targetFile); - core::ScopedPtr readStream(archive->readStream(fullPath)); - if (!readStream) { - Log::error("Failed to extract %s", fullPath.c_str()); - return app::AppState::InitFailure; - } - io::ReadStream &s = *readStream; - if (!targetStream.writeStream(s)) { - Log::error("Failed to write %s", fullPath.c_str()); - return app::AppState::InitFailure; - } - } - const core::String filter = getArgVal("--wildcard", ""); for (const auto &entry : archive->files()) { if (shouldQuit()) { @@ -468,12 +469,12 @@ app::AppState VoxConvert::onInit() { } } const core::String &fullPath = filesystem()->writePath(entry.fullPath); - if (!handleInputFile(fullPath, sceneGraph, archive->files().size() > 1)) { + if (!handleInputFile(fullPath, archive, sceneGraph, archive->files().size() > 1)) { Log::error("Failed to handle input file %s", fullPath.c_str()); } } } else { - if (!handleInputFile(infile, sceneGraph, infiles.size() > 1)) { + if (!handleInputFile(infile, fsArchive, sceneGraph, infiles.size() > 1)) { return app::AppState::InitFailure; } } @@ -630,17 +631,18 @@ static void printProgress(const char *name, int cur, int max) { // Log::info("%s: %i/%i", name, cur, max); } -bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGraph &sceneGraph, bool multipleInputs) { +bool VoxConvert::handleInputFile(const core::String &infile, const io::ArchivePtr &archive, + scenegraph::SceneGraph &sceneGraph, bool multipleInputs) { Log::info("-- current input file: %s", infile.c_str()); - const io::FilePtr inputFile = filesystem()->open(infile, io::FileMode::SysRead); - if (!inputFile->exists()) { + core::ScopedPtr stream(archive->readStream(infile)); + if (!stream) { Log::error("Given input file '%s' does not exist", infile.c_str()); _exitCode = 127; return false; } - const bool inputIsImage = inputFile->isAnyOf(io::format::images()); + const bool inputIsImage = io::isA(infile, io::format::images()); if (inputIsImage) { - const image::ImagePtr& image = image::loadImage(inputFile); + const image::ImagePtr &image = image::loadImage(infile, *stream); if (!image || !image->isLoaded()) { Log::error("Couldn't load image %s", infile.c_str()); return false; @@ -662,9 +664,10 @@ bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGr if (maxHeight == 1) { Log::warn("There is no height value in the image - it is imported as flat plane"); } - Log::info("Generate from heightmap (%i:%i) with max height of %i", image->width(), image->height(), maxHeight); + Log::info("Generate from heightmap (%i:%i) with max height of %i", image->width(), image->height(), + maxHeight); voxel::Region region(0, 0, 0, image->width() - 1, maxHeight - 1, image->height() - 1); - voxel::RawVolume* volume = new voxel::RawVolume(region); + voxel::RawVolume *volume = new voxel::RawVolume(region); voxel::RawVolumeWrapper wrapper(volume); scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); const voxel::Voxel dirtVoxel = voxel::createVoxel(voxel::VoxelType::Generic, 1); @@ -677,7 +680,7 @@ bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGr voxelutil::importHeightmap(wrapper, image, dirtVoxel, grassVoxel); } node.setVolume(volume, true); - node.setName(inputFile->fileName()); + node.setName(core::string::extractFilename(infile)); sceneGraph.emplace(core::move(node)); } if (importAsVolume) { @@ -690,7 +693,7 @@ bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGr } scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); node.setVolume(v, true); - node.setName(inputFile->fileName()); + node.setName(core::string::extractFilename(infile)); sceneGraph.emplace(core::move(node)); } if (importAsPlane) { @@ -701,7 +704,7 @@ bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGr } scenegraph::SceneGraphNode node(scenegraph::SceneGraphNodeType::Model); node.setVolume(v, true); - node.setName(inputFile->fileName()); + node.setName(core::string::extractFilename(infile)); sceneGraph.emplace(core::move(node)); } if (_exportPalette) { @@ -713,13 +716,12 @@ bool VoxConvert::handleInputFile(const core::String &infile, scenegraph::SceneGr Log::info("Wrote palette %s", filename.c_str()); } } else { - io::FileStream inputFileStream(inputFile); scenegraph::SceneGraph newSceneGraph; voxelformat::LoadContext loadCtx; loadCtx.monitor = printProgress; io::FileDescription fileDesc; - fileDesc.set(inputFile->name()); - if (!voxelformat::loadFormat(fileDesc, inputFileStream, newSceneGraph, loadCtx)) { + fileDesc.set(infile); + if (!voxelformat::loadFormat(fileDesc, archive, newSceneGraph, loadCtx)) { return false; } @@ -760,7 +762,8 @@ static bool hasUniqueModelNames(const scenegraph::SceneGraph &sceneGraph) { return true; } -void VoxConvert::exportModelsIntoSingleObjects(scenegraph::SceneGraph& sceneGraph, const core::String &inputfile, const core::String &ext) { +void VoxConvert::exportModelsIntoSingleObjects(scenegraph::SceneGraph &sceneGraph, const core::String &inputfile, + const core::String &ext) { Log::info("Export models into single objects"); int id = 0; voxelformat::SaveContext saveCtx; @@ -775,8 +778,9 @@ void VoxConvert::exportModelsIntoSingleObjects(scenegraph::SceneGraph& sceneGrap scenegraph::SceneGraphNode newNode; scenegraph::copyNode(node, newNode, false); newSceneGraph.emplace(core::move(newNode)); - const core::String& filename = getFilenameForModelName(inputfile, node.name(), ext, id, uniqueNames); - if (voxelformat::saveFormat(filesystem()->open(filename, io::FileMode::SysWrite), nullptr, newSceneGraph, saveCtx)) { + const core::String &filename = getFilenameForModelName(inputfile, node.name(), ext, id, uniqueNames); + if (voxelformat::saveFormat(filesystem()->open(filename, io::FileMode::SysWrite), nullptr, newSceneGraph, + saveCtx)) { Log::info(" .. %s", filename.c_str()); } else { Log::error(" .. %s", filename.c_str()); @@ -792,7 +796,7 @@ glm::ivec3 VoxConvert::getArgIvec3(const core::String &name) { return t; } -void VoxConvert::split(const glm::ivec3 &size, scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::split(const glm::ivec3 &size, scenegraph::SceneGraph &sceneGraph) { Log::info("split volumes at %i:%i:%i", size.x, size.y, size.z); const scenegraph::SceneGraph::MergedVolumePalette &merged = sceneGraph.merge(); sceneGraph.clear(); @@ -807,8 +811,9 @@ void VoxConvert::split(const glm::ivec3 &size, scenegraph::SceneGraph& sceneGrap } } -VoxConvert::NodeStats VoxConvert::dumpNode_r(const scenegraph::SceneGraph& sceneGraph, int nodeId, int indent, bool meshDetails) { - const scenegraph::SceneGraphNode& node = sceneGraph.node(nodeId); +VoxConvert::NodeStats VoxConvert::dumpNode_r(const scenegraph::SceneGraph &sceneGraph, int nodeId, int indent, + bool meshDetails) { + const scenegraph::SceneGraphNode &node = sceneGraph.node(nodeId); const scenegraph::SceneGraphNodeType type = node.type(); @@ -832,16 +837,17 @@ VoxConvert::NodeStats VoxConvert::dumpNode_r(const scenegraph::SceneGraph& scene Log::info("%*s |- farplane: %f", indent, " ", cameraNode.farPlane()); Log::info("%*s |- mode: %s", indent, " ", cameraNode.isOrthographic() ? "ortho" : "perspective"); } - for (const auto & entry : node.properties()) { + for (const auto &entry : node.properties()) { Log::info("%*s |- %s: %s", indent, " ", entry->key.c_str(), entry->value.c_str()); } for (const scenegraph::SceneGraphKeyFrame &kf : node.keyFrames()) { Log::info("%*s |- keyframe: %i", indent, " ", kf.frameIdx); Log::info("%*s |- long rotation: %s", indent, " ", kf.longRotation ? "true" : "false"); - Log::info("%*s |- interpolation: %s", indent, " ", scenegraph::InterpolationTypeStr[core::enumVal(kf.interpolation)]); + Log::info("%*s |- interpolation: %s", indent, " ", + scenegraph::InterpolationTypeStr[core::enumVal(kf.interpolation)]); Log::info("%*s |- transform", indent, " "); const scenegraph::SceneGraphTransform &transform = kf.transform(); - const glm::vec3 &tr = transform.worldTranslation(); + const glm::vec3 &tr = transform.worldTranslation(); Log::info("%*s |- translation %f:%f:%f", indent, " ", tr.x, tr.y, tr.z); const glm::vec3 <r = transform.localTranslation(); Log::info("%*s |- local translation %f:%f:%f", indent, " ", ltr.x, ltr.y, ltr.z); @@ -862,7 +868,8 @@ VoxConvert::NodeStats VoxConvert::dumpNode_r(const scenegraph::SceneGraph& scene const bool mergeQuads = core::Var::getSafe(cfg::VoxformatMergequads)->boolVal(); const bool reuseVertices = core::Var::getSafe(cfg::VoxformatReusevertices)->boolVal(); const bool ambientOcclusion = core::Var::getSafe(cfg::VoxformatAmbientocclusion)->boolVal(); - const voxel::SurfaceExtractionType type = (voxel::SurfaceExtractionType)core::Var::getSafe(cfg::VoxelMeshMode)->intVal(); + const voxel::SurfaceExtractionType type = + (voxel::SurfaceExtractionType)core::Var::getSafe(cfg::VoxelMeshMode)->intVal(); voxel::ChunkMesh mesh; voxel::SurfaceExtractionContext ctx = voxel::createContext(type, node.volume(), node.region(), node.palette(), mesh, {0, 0, 0}, mergeQuads, @@ -884,19 +891,19 @@ VoxConvert::NodeStats VoxConvert::dumpNode_r(const scenegraph::SceneGraph& scene return stats; } -void VoxConvert::dumpMeshDetails(const scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::dumpMeshDetails(const scenegraph::SceneGraph &sceneGraph) { NodeStats stats = dumpNode_r(sceneGraph, sceneGraph.root().id(), 0, true); Log::info("Voxel count: %i", stats.voxels); Log::info("Vertex count: %i", stats.vertices); Log::info("Index count: %i", stats.indices); } -void VoxConvert::dump(const scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::dump(const scenegraph::SceneGraph &sceneGraph) { NodeStats stats = dumpNode_r(sceneGraph, sceneGraph.root().id(), 0, false); Log::info("Voxel count: %i", stats.voxels); } -void VoxConvert::crop(scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::crop(scenegraph::SceneGraph &sceneGraph) { Log::info("Crop volumes"); for (auto iter = sceneGraph.beginModel(); iter != sceneGraph.end(); ++iter) { scenegraph::SceneGraphNode &node = *iter; @@ -904,7 +911,7 @@ void VoxConvert::crop(scenegraph::SceneGraph& sceneGraph) { } } -void VoxConvert::removeNonSurfaceVoxels(scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::removeNonSurfaceVoxels(scenegraph::SceneGraph &sceneGraph) { Log::info("Remove non-surface voxels"); for (auto iter = sceneGraph.beginModel(); iter != sceneGraph.end(); ++iter) { scenegraph::SceneGraphNode &node = *iter; @@ -918,7 +925,7 @@ void VoxConvert::removeNonSurfaceVoxels(scenegraph::SceneGraph& sceneGraph) { } } -void VoxConvert::script(const core::String &scriptParameters, scenegraph::SceneGraph& sceneGraph, uint8_t color) { +void VoxConvert::script(const core::String &scriptParameters, scenegraph::SceneGraph &sceneGraph, uint8_t color) { voxelgenerator::LUAApi script(_filesystem); if (!script.init()) { Log::warn("Failed to initialize the script bindings"); @@ -957,22 +964,22 @@ void VoxConvert::script(const core::String &scriptParameters, scenegraph::SceneG script.shutdown(); } -void VoxConvert::scale(scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::scale(scenegraph::SceneGraph &sceneGraph) { Log::info("Scale models"); for (auto iter = sceneGraph.beginModel(); iter != sceneGraph.end(); ++iter) { scenegraph::SceneGraphNode &node = *iter; const voxel::Region srcRegion = node.region(); - const glm::ivec3& targetDimensionsHalf = (srcRegion.getDimensionsInVoxels() / 2) - 1; + const glm::ivec3 &targetDimensionsHalf = (srcRegion.getDimensionsInVoxels() / 2) - 1; const voxel::Region destRegion(srcRegion.getLowerCorner(), srcRegion.getLowerCorner() + targetDimensionsHalf); if (destRegion.isValid()) { - voxel::RawVolume* destVolume = new voxel::RawVolume(destRegion); + voxel::RawVolume *destVolume = new voxel::RawVolume(destRegion); voxelutil::scaleDown(*node.volume(), node.palette(), *destVolume); node.setVolume(destVolume, true); } } } -void VoxConvert::resize(const glm::ivec3 &size, scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::resize(const glm::ivec3 &size, scenegraph::SceneGraph &sceneGraph) { Log::info("Resize models"); for (auto iter = sceneGraph.beginModel(); iter != sceneGraph.end(); ++iter) { scenegraph::SceneGraphNode &node = *iter; @@ -985,7 +992,7 @@ void VoxConvert::resize(const glm::ivec3 &size, scenegraph::SceneGraph& sceneGra } } -void VoxConvert::filterModels(scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::filterModels(scenegraph::SceneGraph &sceneGraph) { const core::String &filter = getArgVal("--filter"); if (filter.empty()) { Log::warn("No filter specified"); @@ -995,7 +1002,7 @@ void VoxConvert::filterModels(scenegraph::SceneGraph& sceneGraph) { core::Set models; core::DynamicArray tokens; core::string::splitString(filter, tokens, ","); - for (const core::String& token : tokens) { + for (const core::String &token : tokens) { if (token.contains("-")) { const int start = token.toInt(); const size_t index = token.find("-"); @@ -1023,7 +1030,8 @@ void VoxConvert::filterModels(scenegraph::SceneGraph& sceneGraph) { Log::info("Filtered models: %i", (int)models.size()); } -void VoxConvert::filterModelsByProperty(scenegraph::SceneGraph& sceneGraph, const core::String &property, const core::String &value) { +void VoxConvert::filterModelsByProperty(scenegraph::SceneGraph &sceneGraph, const core::String &property, + const core::String &value) { if (property.empty()) { Log::warn("No property specified to filter"); return; @@ -1051,7 +1059,7 @@ void VoxConvert::filterModelsByProperty(scenegraph::SceneGraph& sceneGraph, cons Log::info("Filtered models: %i", (int)sceneGraph.size(scenegraph::SceneGraphNodeType::Model)); } -void VoxConvert::mirror(const core::String& axisStr, scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::mirror(const core::String &axisStr, scenegraph::SceneGraph &sceneGraph) { const math::Axis axis = math::toAxis(axisStr); if (axis == math::Axis::None) { return; @@ -1063,7 +1071,7 @@ void VoxConvert::mirror(const core::String& axisStr, scenegraph::SceneGraph& sce } } -void VoxConvert::rotate(const core::String& axisStr, scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::rotate(const core::String &axisStr, scenegraph::SceneGraph &sceneGraph) { const math::Axis axis = math::toAxis(axisStr); if (axis == math::Axis::None) { return; @@ -1085,7 +1093,7 @@ void VoxConvert::rotate(const core::String& axisStr, scenegraph::SceneGraph& sce } } -void VoxConvert::translate(const glm::ivec3& pos, scenegraph::SceneGraph& sceneGraph) { +void VoxConvert::translate(const glm::ivec3 &pos, scenegraph::SceneGraph &sceneGraph) { Log::info("Translate by %i:%i:%i", pos.x, pos.y, pos.z); for (auto iter = sceneGraph.beginModel(); iter != sceneGraph.end(); ++iter) { scenegraph::SceneGraphNode &node = *iter; @@ -1096,8 +1104,8 @@ void VoxConvert::translate(const glm::ivec3& pos, scenegraph::SceneGraph& sceneG } int main(int argc, char *argv[]) { - const io::FilesystemPtr& filesystem = core::make_shared(); - const core::TimeProviderPtr& timeProvider = core::make_shared(); + const io::FilesystemPtr &filesystem = core::make_shared(); + const core::TimeProviderPtr &timeProvider = core::make_shared(); VoxConvert app(filesystem, timeProvider); return app.startMainLoop(argc, argv); } diff --git a/src/tools/voxconvert/VoxConvert.h b/src/tools/voxconvert/VoxConvert.h index 0807afc1ca..5f2e572a03 100644 --- a/src/tools/voxconvert/VoxConvert.h +++ b/src/tools/voxconvert/VoxConvert.h @@ -5,6 +5,7 @@ #pragma once #include "app/CommandlineApp.h" +#include "io/Archive.h" #include "scenegraph/SceneGraph.h" /** @@ -64,7 +65,8 @@ class VoxConvert: public app::CommandlineApp { glm::ivec3 getArgIvec3(const core::String &name); core::String getFilenameForModelName(const core::String &inputfile, const core::String &modelName, const core::String &outExt, int id, bool uniqueNames); - bool handleInputFile(const core::String &infile, scenegraph::SceneGraph &sceneGraph, bool multipleInputs); + bool handleInputFile(const core::String &infile, const io::ArchivePtr &archive, scenegraph::SceneGraph &sceneGraph, + bool multipleInputs); void usage() const override; void printUsageHeader() const override; diff --git a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp index ad4a659ebe..7f0624914e 100644 --- a/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp +++ b/src/tools/voxedit/modules/voxedit-util/SceneManager.cpp @@ -17,11 +17,14 @@ #include "core/TimeProvider.h" #include "core/UTF8.h" #include "core/collection/DynamicArray.h" +#include "io/Archive.h" #include "io/File.h" #include "io/FileStream.h" #include "io/Filesystem.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "io/MemoryReadStream.h" +#include "io/MemoryArchive.h" #include "io/Stream.h" #include "math/Axis.h" #include "math/Random.h" @@ -446,17 +449,12 @@ bool SceneManager::import(const core::String& file) { Log::error("Can't import model: No file given"); return false; } - const io::FilePtr& filePtr = _filesystem->open(file); - if (!filePtr->validHandle()) { - Log::error("Failed to open model file %s", file.c_str()); - return false; - } + const io::ArchivePtr &archive = io::openFilesystemArchive(_filesystem); scenegraph::SceneGraph newSceneGraph; - io::FileStream stream(filePtr); voxelformat::LoadContext loadCtx; io::FileDescription fileDesc; - fileDesc.set(filePtr->name()); - if (!voxelformat::loadFormat(fileDesc, stream, newSceneGraph, loadCtx)) { + fileDesc.set(file); + if (!voxelformat::loadFormat(fileDesc, archive, newSceneGraph, loadCtx)) { Log::error("Failed to load %s", file.c_str()); return false; } @@ -478,8 +476,8 @@ bool SceneManager::importDirectory(const core::String& directory, const io::Form if (directory.empty()) { return false; } - core::DynamicArray entities; - _filesystem->list(directory, entities, format ? format->wildCard() : "", depth); + const io::ArchivePtr &archive = io::openFilesystemArchive(_filesystem, directory); + const core::DynamicArray &entities = archive->files(); if (entities.empty()) { Log::info("Could not find any model in %s", directory.c_str()); return false; @@ -499,7 +497,7 @@ bool SceneManager::importDirectory(const core::String& directory, const io::Form voxelformat::LoadContext loadCtx; io::FileDescription fileDesc; fileDesc.set(filePtr->name(), format); - if (!voxelformat::loadFormat(fileDesc, stream, newSceneGraph, loadCtx)) { + if (!voxelformat::loadFormat(fileDesc, archive, newSceneGraph, loadCtx)) { Log::error("Failed to load %s", e.fullPath.c_str()); } else { mergeIfNeeded(newSceneGraph); @@ -516,21 +514,15 @@ bool SceneManager::load(const io::FileDescription& file) { if (file.empty()) { return false; } - const io::FilePtr& filePtr = _filesystem->open(file.name); - if (!filePtr->validHandle()) { - Log::error("Failed to open model file '%s'", file.c_str()); - return false; - } - if (_loadingFuture.valid()) { Log::error("Failed to load '%s' - still loading another model", file.c_str()); return false; } - _loadingFuture = app::async([filePtr, file] () { + const io::ArchivePtr &archive = io::openFilesystemArchive(_filesystem); + _loadingFuture = app::async([archive, file] () { scenegraph::SceneGraph newSceneGraph; - io::FileStream stream(filePtr); voxelformat::LoadContext loadCtx; - voxelformat::loadFormat(file, stream, newSceneGraph, loadCtx); + voxelformat::loadFormat(file, archive, newSceneGraph, loadCtx); mergeIfNeeded(newSceneGraph); /** * @todo stuff that happens in MeshState::scheduleRegionExtraction() and @@ -538,15 +530,16 @@ bool SceneManager::load(const io::FileDescription& file) { */ return core::move(newSceneGraph); }); - _lastFilename.set(filePtr->name(), &file.desc); + _lastFilename.set(file.name, &file.desc); return true; } bool SceneManager::load(const io::FileDescription& file, const uint8_t *data, size_t size) { scenegraph::SceneGraph newSceneGraph; - io::MemoryReadStream stream(data, size); + io::MemoryArchivePtr archive = io::openMemoryArchive(); + archive->add(file.name, data, size); voxelformat::LoadContext loadCtx; - voxelformat::loadFormat(file, stream, newSceneGraph, loadCtx); + voxelformat::loadFormat(file, archive, newSceneGraph, loadCtx); mergeIfNeeded(newSceneGraph); if (loadSceneGraph(core::move(newSceneGraph))) { _needAutoSave = false; diff --git a/src/tools/voxedit/modules/voxedit-util/modifier/brush/StampBrush.cpp b/src/tools/voxedit/modules/voxedit-util/modifier/brush/StampBrush.cpp index 35a2b1238b..0177126660 100644 --- a/src/tools/voxedit/modules/voxedit-util/modifier/brush/StampBrush.cpp +++ b/src/tools/voxedit/modules/voxedit-util/modifier/brush/StampBrush.cpp @@ -9,6 +9,7 @@ #include "core/Log.h" #include "io/File.h" #include "io/FileStream.h" +#include "io/FilesystemArchive.h" #include "io/FormatDescription.h" #include "scenegraph/SceneGraph.h" #include "scenegraph/SceneGraphNode.h" @@ -182,17 +183,16 @@ void StampBrush::convertToPalette(const palette::Palette &palette) { } bool StampBrush::load(const core::String &filename) { - const io::FilePtr &filePtr = io::filesystem()->open(filename); - if (!filePtr->validHandle()) { + const io::ArchivePtr &archive = io::openFilesystemArchive(io::filesystem()); + if (!archive->exists(filename)) { Log::error("Failed to open model file %s", filename.c_str()); return false; } scenegraph::SceneGraph newSceneGraph; - io::FileStream stream(filePtr); voxelformat::LoadContext loadCtx; io::FileDescription fileDesc; - fileDesc.set(filePtr->name()); - if (!voxelformat::loadFormat(fileDesc, stream, newSceneGraph, loadCtx)) { + fileDesc.set(filename); + if (!voxelformat::loadFormat(fileDesc, archive, newSceneGraph, loadCtx)) { Log::error("Failed to load %s", filename.c_str()); return false; }