From f3e54440039f979c4f19ea1172003c58441e99c1 Mon Sep 17 00:00:00 2001 From: Andrew Tribick Date: Fri, 29 Oct 2021 22:48:09 +0200 Subject: [PATCH] Add error checking to 3DS loader --- src/cel3ds/3dsmodel.h | 8 +- src/cel3ds/3dsread.cpp | 564 +++++++++++++++++++++++------------------ 2 files changed, 315 insertions(+), 257 deletions(-) diff --git a/src/cel3ds/3dsmodel.h b/src/cel3ds/3dsmodel.h index c6cfb1d82..e78634269 100644 --- a/src/cel3ds/3dsmodel.h +++ b/src/cel3ds/3dsmodel.h @@ -130,12 +130,12 @@ class M3DScene M3DScene() = default; ~M3DScene(); - M3DModel* getModel(uint32_t) const; - uint32_t getModelCount() const; + M3DModel* getModel(std::uint32_t) const; + std::uint32_t getModelCount() const; void addModel(M3DModel*); - M3DMaterial* getMaterial(uint32_t) const; - uint32_t getMaterialCount() const; + M3DMaterial* getMaterial(std::uint32_t) const; + std::uint32_t getMaterialCount() const; void addMaterial(M3DMaterial*); M3DColor getBackgroundColor() const; diff --git a/src/cel3ds/3dsread.cpp b/src/cel3ds/3dsread.cpp index a224cbc63..3617dc214 100644 --- a/src/cel3ds/3dsread.cpp +++ b/src/cel3ds/3dsread.cpp @@ -7,90 +7,94 @@ // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. +#include #include #include #include +#include #include #include +#include #include "celutil/bytes.h" -#include "celutil/debug.h" #include "3dschunk.h" #include "3dsmodel.h" #include "3dsread.h" namespace { +constexpr std::int32_t READ_FAILURE = -1; +constexpr std::int32_t UNKNOWN_CHUNK = -2; + + template -using ProcessChunkFunc = bool (*)(std::istream &, std::uint16_t, std::int32_t, T*); +using ProcessChunkFunc = std::int32_t (*)(std::istream &, std::uint16_t, std::int32_t, T*); -std::int32_t readInt(std::istream& in) +bool readInt(std::istream& in, std::int32_t& value) { - std::int32_t ret; - in.read((char *) &ret, sizeof(std::int32_t)); - LE_TO_CPU_INT32(ret, ret); - return ret; + in.read(reinterpret_cast(&value), sizeof(std::int32_t)); + if (!in.good()) { return false; } + LE_TO_CPU_INT32(value, value); + return true; } -std::int16_t readShort(std::istream& in) +bool readShort(std::istream& in, std::int16_t& value) { - std::int16_t ret; - in.read((char *) &ret, sizeof(std::int16_t)); - LE_TO_CPU_INT16(ret, ret); - return ret; + in.read(reinterpret_cast(&value), sizeof(std::int16_t)); + if (!in.good()) { return false; } + LE_TO_CPU_INT16(value, value); + return true; } -std::uint16_t readUshort(std::istream& in) +bool readUshort(std::istream& in, std::uint16_t& value) { - std::uint16_t ret; - in.read((char *) &ret, sizeof(std::uint16_t)); - LE_TO_CPU_INT16(ret, ret); - return ret; + in.read(reinterpret_cast(&value), sizeof(std::uint16_t)); + if (!in.good()) { return false; } + LE_TO_CPU_INT16(value, value); + return true; } -float readFloat(std::istream& in) +bool readFloat(std::istream& in, float& value) { - float f; - in.read((char*) &f, sizeof(float)); - LE_TO_CPU_FLOAT(f, f); - return f; + in.read(reinterpret_cast(&value), sizeof(float)); + if (!in.good()) { return false; } + LE_TO_CPU_FLOAT(value, value); + return true; } -char readChar(std::istream& in) +bool readUchar(std::istream& in, unsigned char& value) { char c; - in.read(&c, 1); - return c; + in.get(c); + if (!in.good()) { return false; } + value = static_cast(c); + return true; } -std::string readString(std::istream& in) +std::int32_t readString(std::istream& in, std::string& value) { - char s[1024]; - int maxLength = sizeof(s); + constexpr std::size_t maxLength = 1024; + char s[maxLength]; - for (int count = 0; count < maxLength; count++) + for (std::size_t count = 0; count < maxLength; count++) { in.read(s + count, 1); + if (!in.good()) { return READ_FAILURE; } if (s[count] == '\0') - break; + { + value = s; + return count + 1; + } } - return std::string(s); -} - - -void skipBytes(std::istream& in, int count) -{ - char c; - while (count-- > 0) - in.get(c); + return READ_FAILURE; } @@ -99,285 +103,325 @@ std::int32_t read3DSChunk(std::istream& in, ProcessChunkFunc chunkFunc, T* obj) { - std::uint16_t chunkType = readUshort(in); - std::int32_t chunkSize = readInt(in); + std::uint16_t chunkType; + if (!readUshort(in, chunkType)) { return READ_FAILURE; } + std::int32_t chunkSize; + if (!readInt(in, chunkSize) || chunkSize < 6) { return READ_FAILURE; } + std::int32_t contentSize = chunkSize - 6; - - bool chunkWasRead = chunkFunc(in, chunkType, contentSize, obj); - - if (!chunkWasRead) + std::int32_t processedSize = chunkFunc(in, chunkType, contentSize, obj); + switch (processedSize) { - skipBytes(in, contentSize); + case READ_FAILURE: + return READ_FAILURE; + case UNKNOWN_CHUNK: + in.ignore(contentSize); + return in.good() ? chunkSize : READ_FAILURE; + default: + if (processedSize != contentSize) + { + fmt::print(std::clog, "Chunk type {:04x}, expected {} bytes but read {}\n", chunkType, contentSize, processedSize); + return READ_FAILURE; + } + return chunkSize; } - - return chunkSize; } template -int read3DSChunks(std::istream& in, - int nBytes, - ProcessChunkFunc chunkFunc, - T* obj) +std::int32_t read3DSChunks(std::istream& in, + std::int32_t nBytes, + ProcessChunkFunc chunkFunc, + T* obj) { - int bytesRead = 0; + std::int32_t bytesRead = 0; while (bytesRead < nBytes) - bytesRead += read3DSChunk(in, chunkFunc, obj); + { + std::int32_t chunkSize = read3DSChunk(in, chunkFunc, obj); + if (chunkSize < 0) { + fmt::print(std::clog, "Failed to read 3DS chunk\n"); + return READ_FAILURE; + } + bytesRead += chunkSize; + } if (bytesRead != nBytes) - std::cout << "Expected " << nBytes << " bytes but read " << bytesRead << '\n'; + { + fmt::print(std::clog, "Multiple chunks, expected {} bytes but read {}\n", nBytes, bytesRead); + return READ_FAILURE; + } + return bytesRead; } -M3DColor readColor(std::istream& in/*, int nBytes*/) +std::int32_t readColor(std::istream& in, M3DColor& color) { - auto r = (unsigned char) readChar(in); - auto g = (unsigned char) readChar(in); - auto b = (unsigned char) readChar(in); + unsigned char r, g, b; + if (!readUchar(in, r) || !readUchar(in, g) || !readUchar(in, b)) { return READ_FAILURE; } - return {(float) r / 255.0f, - (float) g / 255.0f, - (float) b / 255.0f}; + color = {static_cast(r) / 255.0f, + static_cast(g) / 255.0f, + static_cast(b) / 255.0f}; + + return 3; } -M3DColor readFloatColor(std::istream& in/*, int nBytes*/) +std::int32_t readFloatColor(std::istream& in, M3DColor& color) { - float r = readFloat(in); - float g = readFloat(in); - float b = readFloat(in); + float r, g, b; + if (!readFloat(in, r) || !readFloat(in, g) || !readFloat(in, b)) { return READ_FAILURE; } - return {(float) r / 255.0f, - (float) g / 255.0f, - (float) b / 255.0f}; + color = { r, g, b }; + return static_cast(3 * sizeof(float)); } -Eigen::Matrix4f readMeshMatrix(std::istream& in/*, int nBytes*/) +std::int32_t readMeshMatrix(std::istream& in, Eigen::Matrix4f& m) { - float m00 = readFloat(in); - float m01 = readFloat(in); - float m02 = readFloat(in); - float m10 = readFloat(in); - float m11 = readFloat(in); - float m12 = readFloat(in); - float m20 = readFloat(in); - float m21 = readFloat(in); - float m22 = readFloat(in); - float m30 = readFloat(in); - float m31 = readFloat(in); - float m32 = readFloat(in); - -#if 0 - cout << m00 << " " << m01 << " " << m02 << '\n'; - cout << m10 << " " << m11 << " " << m12 << '\n'; - cout << m20 << " " << m21 << " " << m22 << '\n'; - cout << m30 << " " << m31 << " " << m32 << '\n'; -#endif - - Eigen::Matrix4f m; - m << m00, m01, m02, 0, - m10, m11, m12, 0, - m20, m21, m22, 0, - m30, m31, m32, 1; - - return m; -} - - -void readPointArray(std::istream& in, M3DTriangleMesh* triMesh) -{ - std::uint16_t nPoints = readUshort(in); - - for (int i = 0; i < (int) nPoints; i++) + float elements[12]; + for (std::size_t i = 0; i < 12; ++i) { - float x = readFloat(in); - float y = readFloat(in); - float z = readFloat(in); + if (!readFloat(in, elements[i])) { return READ_FAILURE; } + } + + m << elements[0], elements[1], elements[2], 0, + elements[3], elements[4], elements[5], 0, + elements[6], elements[7], elements[8], 0, + elements[9], elements[10], elements[11], 1; + + return static_cast(12 * sizeof(float)); +} + + +std::int32_t readPointArray(std::istream& in, M3DTriangleMesh* triMesh) +{ + std::uint16_t nPoints; + if (!readUshort(in, nPoints)) { return READ_FAILURE; } + std::int32_t bytesRead = static_cast(sizeof(nPoints)); + + for (int i = 0; i < static_cast(nPoints); i++) + { + float x, y, z; + if (!readFloat(in, x) || !readFloat(in, y) || !readFloat(in, z)) { return READ_FAILURE; } + bytesRead += static_cast(3 * sizeof(float)); triMesh->addVertex(Eigen::Vector3f(x, y, z)); } + + return bytesRead; } -void readTextureCoordArray(std::istream& in, M3DTriangleMesh* triMesh) +std::int32_t readTextureCoordArray(std::istream& in, M3DTriangleMesh* triMesh) { - std::uint16_t nPoints = readUshort(in); + std::int32_t bytesRead = 0; - for (int i = 0; i < (int) nPoints; i++) + std::uint16_t nPoints; + if (!readUshort(in, nPoints)) { return READ_FAILURE; } + bytesRead += static_cast(sizeof(nPoints)); + + for (int i = 0; i < static_cast(nPoints); i++) { - float u = readFloat(in); - float v = readFloat(in); + float u, v; + if (!readFloat(in, u) || !readFloat(in, v)) { return READ_FAILURE; } + bytesRead += static_cast(2 * sizeof(float)); triMesh->addTexCoord(Eigen::Vector2f(u, -v)); } + + return bytesRead; } -bool processFaceArrayChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t /*contentSize*/, - M3DTriangleMesh* triMesh) +std::int32_t processFaceArrayChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t /*contentSize*/, + M3DTriangleMesh* triMesh) { + std::int32_t bytesRead = 0; std::uint16_t nFaces; - M3DMeshMaterialGroup* matGroup; + std::unique_ptr matGroup; switch (chunkType) { case M3DCHUNK_MESH_MATERIAL_GROUP: - matGroup = new M3DMeshMaterialGroup(); + matGroup = std::make_unique(); - matGroup->materialName = readString(in); - nFaces = readUshort(in); + bytesRead = readString(in, matGroup->materialName); + if (bytesRead == READ_FAILURE || !readUshort(in, nFaces)) { return READ_FAILURE; } + bytesRead += static_cast(sizeof(nFaces)); for (std::uint16_t i = 0; i < nFaces; i++) { - std::uint16_t faceIndex = readUshort(in); + std::uint16_t faceIndex; + if (!readUshort(in, faceIndex)) { return READ_FAILURE; } + bytesRead += static_cast(sizeof(faceIndex)); matGroup->faces.push_back(faceIndex); } - triMesh->addMeshMaterialGroup(matGroup); + triMesh->addMeshMaterialGroup(matGroup.release()); + + return bytesRead; - return true; case M3DCHUNK_MESH_SMOOTH_GROUP: nFaces = triMesh->getFaceCount(); for (std::uint16_t i = 0; i < nFaces; i++) { - auto groups = (std::uint32_t) readInt(in); - triMesh->addSmoothingGroups(groups); + std::int32_t groups; + if (!readInt(in, groups) || groups < 0) { return READ_FAILURE; } + bytesRead += static_cast(sizeof(groups)); + triMesh->addSmoothingGroups(static_cast(groups)); } - return true; + return bytesRead; + + default: + return UNKNOWN_CHUNK; } - return false; } -void readFaceArray(std::istream& in, M3DTriangleMesh* triMesh, std::int32_t contentSize) +std::int32_t readFaceArray(std::istream& in, M3DTriangleMesh* triMesh, std::int32_t contentSize) { - std::uint16_t nFaces = readUshort(in); + std::uint16_t nFaces; + if (!readUshort(in, nFaces)) { return READ_FAILURE; } + std::int32_t bytesRead = static_cast(sizeof(nFaces)); - for (int i = 0; i < (int) nFaces; i++) + for (int i = 0; i < static_cast(nFaces); i++) { - std::uint16_t v0 = readUshort(in); - std::uint16_t v1 = readUshort(in); - std::uint16_t v2 = readUshort(in); - /*uint16_t flags = */ readUshort(in); + std::uint16_t v0, v1, v2, flags; + if (!readUshort(in, v0) || !readUshort(in, v1) || !readUshort(in, v2) || !readUshort(in, flags)) + { + return READ_FAILURE; + } + bytesRead += static_cast(4 * sizeof(std::uint16_t)); triMesh->addFace(v0, v1, v2); } - std::int32_t bytesLeft = contentSize - (8 * nFaces + 2); - if (bytesLeft > 0) + if (bytesRead > contentSize) { return READ_FAILURE; } + + if (bytesRead < contentSize) { - read3DSChunks(in, - bytesLeft, - processFaceArrayChunk, - triMesh); + std::int32_t trailingSize = read3DSChunks(in, + contentSize - bytesRead, + processFaceArrayChunk, + triMesh); + bytesRead += trailingSize; } + + return bytesRead; } -bool processTriMeshChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t contentSize, - M3DTriangleMesh* triMesh) +std::int32_t processTriMeshChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t contentSize, + M3DTriangleMesh* triMesh) { switch (chunkType) { case M3DCHUNK_POINT_ARRAY: - readPointArray(in, triMesh); - return true; + return readPointArray(in, triMesh); case M3DCHUNK_MESH_TEXTURE_COORDS: - readTextureCoordArray(in, triMesh); - return true; + return readTextureCoordArray(in, triMesh); case M3DCHUNK_FACE_ARRAY: - readFaceArray(in, triMesh, contentSize); - return true; + return readFaceArray(in, triMesh, contentSize); case M3DCHUNK_MESH_MATRIX: - triMesh->setMatrix(readMeshMatrix(in/*, contentSize*/)); - return true; + { + Eigen::Matrix4f matrix; + std::int32_t bytesRead = readMeshMatrix(in, matrix); + if (bytesRead < 0) { return READ_FAILURE; } + triMesh->setMatrix(matrix); + return bytesRead; + } + default: + return UNKNOWN_CHUNK; } - - return false; } -bool processModelChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t contentSize, - M3DModel* model) +std::int32_t processModelChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t contentSize, + M3DModel* model) { if (chunkType == M3DCHUNK_TRIANGLE_MESH) { - auto* triMesh = new M3DTriangleMesh(); - read3DSChunks(in, contentSize, processTriMeshChunk, triMesh); - model->addTriMesh(triMesh); - return true; + auto triMesh = std::make_unique(); + std::int32_t bytesRead = read3DSChunks(in, contentSize, processTriMeshChunk, triMesh.get()); + if (bytesRead == READ_FAILURE) { return READ_FAILURE; } + model->addTriMesh(triMesh.release()); + return bytesRead; } - return false; + return UNKNOWN_CHUNK; } -bool processColorChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t /*contentSize*/, - M3DColor* color) +std::int32_t processColorChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t /*contentSize*/, + M3DColor* color) { switch (chunkType) { case M3DCHUNK_COLOR_24: - *color = readColor(in/*, contentSize*/); - return true; + return readColor(in, *color); case M3DCHUNK_COLOR_FLOAT: - *color = readFloatColor(in/*, contentSize*/); - return true; + return readFloatColor(in, *color); + default: + return UNKNOWN_CHUNK; } - - return false; } -static bool processPercentageChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t /*contentSize*/, - float* percent) +std::int32_t processPercentageChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t /*contentSize*/, + float* percent) { switch (chunkType) { case M3DCHUNK_INT_PERCENTAGE: - *percent = readShort(in); - return true; + { + std::int16_t value; + if (!readShort(in, value)) { return READ_FAILURE; } + *percent = static_cast(value); + return sizeof(value); + } case M3DCHUNK_FLOAT_PERCENTAGE: - *percent = readFloat(in); - return true; + return readFloat(in, *percent) ? sizeof(float) : READ_FAILURE; + default: + return UNKNOWN_CHUNK; } - - return false; } -static bool processTexmapChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t /*contentSize*/, - M3DMaterial* material) +std::int32_t processTexmapChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t /*contentSize*/, + M3DMaterial* material) { if (chunkType == M3DCHUNK_MATERIAL_MAPNAME) { - std::string name = readString(in); + std::string name; + std::int32_t bytesRead = readString(in, name); + if (bytesRead < 0) { return READ_FAILURE; } material->setTextureMap(name); - return true; + return bytesRead; } - return false; + return UNKNOWN_CHUNK; } -bool processMaterialChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t contentSize, - M3DMaterial* material) +std::int32_t processMaterialChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t contentSize, + M3DMaterial* material) { + std::int32_t bytesRead; std::string name; M3DColor color; float t; @@ -385,92 +429,101 @@ bool processMaterialChunk(std::istream& in, switch (chunkType) { case M3DCHUNK_MATERIAL_NAME: - name = readString(in); + bytesRead = readString(in, name); + if (bytesRead < 0) { return READ_FAILURE; } material->setName(name); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_AMBIENT: - read3DSChunks(in, contentSize, processColorChunk, &color); + bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color); + if (bytesRead < 0) { return READ_FAILURE; } material->setAmbientColor(color); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_DIFFUSE: - read3DSChunks(in, contentSize, processColorChunk, &color); + bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color); + if (bytesRead < 0) { return READ_FAILURE; } material->setDiffuseColor(color); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_SPECULAR: - read3DSChunks(in, contentSize, processColorChunk, &color); + bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color); + if (bytesRead < 0) { return READ_FAILURE; } material->setSpecularColor(color); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_SHININESS: - read3DSChunks(in, contentSize, processPercentageChunk, &t); + bytesRead = read3DSChunks(in, contentSize, processPercentageChunk, &t); + if (bytesRead < 0) { return READ_FAILURE; } material->setShininess(t); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_TRANSPARENCY: - read3DSChunks(in, contentSize, processPercentageChunk, &t); + bytesRead = read3DSChunks(in, contentSize, processPercentageChunk, &t); + if (bytesRead < 0) { return READ_FAILURE; } material->setOpacity(1.0f - t / 100.0f); - return true; + return bytesRead; case M3DCHUNK_MATERIAL_TEXMAP: - read3DSChunks(in, contentSize, processTexmapChunk, material); - return true; + return read3DSChunks(in, contentSize, processTexmapChunk, material); + default: + return UNKNOWN_CHUNK; } - - return false; } -bool processSceneChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t contentSize, - M3DScene* scene) +std::int32_t processSceneChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t contentSize, + M3DScene* scene) { - M3DModel* model; - M3DMaterial* material; + std::int32_t bytesRead, chunksSize; + std::unique_ptr model; + std::unique_ptr material; M3DColor color; std::string name; switch (chunkType) { case M3DCHUNK_NAMED_OBJECT: - name = readString(in); - model = new M3DModel(); + bytesRead = readString(in, name); + if (bytesRead < 0) { return READ_FAILURE; } + model = std::make_unique(); model->setName(name); - read3DSChunks(in, - contentSize - (name.length() + 1), - processModelChunk, - model); - scene->addModel(model); + chunksSize = read3DSChunks(in, + contentSize - bytesRead, + processModelChunk, + model.get()); + if (chunksSize < 0) { return READ_FAILURE; } + scene->addModel(model.release()); - return true; + return bytesRead + chunksSize; case M3DCHUNK_MATERIAL_ENTRY: - material = new M3DMaterial(); - read3DSChunks(in, - contentSize, - processMaterialChunk, - material); - scene->addMaterial(material); + material = std::make_unique(); + bytesRead = read3DSChunks(in, + contentSize, + processMaterialChunk, + material.get()); + if (bytesRead < 0) { return READ_FAILURE; } + scene->addMaterial(material.release()); - return true; + return bytesRead; case M3DCHUNK_BACKGROUND_COLOR: - read3DSChunks(in, contentSize, processColorChunk, &color); + bytesRead = read3DSChunks(in, contentSize, processColorChunk, &color); + if (bytesRead < 0) { return READ_FAILURE; } scene->setBackgroundColor(color); - return true; + return bytesRead; default: - return false; + return UNKNOWN_CHUNK; } } -bool processTopLevelChunk(std::istream& in, - std::uint16_t chunkType, - std::int32_t contentSize, - M3DScene* scene) +std::int32_t processTopLevelChunk(std::istream& in, + std::uint16_t chunkType, + std::int32_t contentSize, + M3DScene* scene) { if (chunkType == M3DCHUNK_MESHDATA) { - read3DSChunks(in, contentSize, processSceneChunk, scene); - return true; + return read3DSChunks(in, contentSize, processSceneChunk, scene); } - return false; + return UNKNOWN_CHUNK; } } // end namespace @@ -478,28 +531,33 @@ bool processTopLevelChunk(std::istream& in, M3DScene* Read3DSFile(std::istream& in) { - std::uint16_t chunkType = readUshort(in); - if (chunkType != M3DCHUNK_MAGIC) + std::uint16_t chunkType; + if (!readUshort(in, chunkType) || chunkType != M3DCHUNK_MAGIC) { - DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Wrong magic number in header\n"); + fmt::print(std::clog, "Read3DSFile: Wrong magic number in header\n"); return nullptr; } - std::int32_t chunkSize = readInt(in); - if (in.bad()) + std::int32_t chunkSize; + if (!readInt(in, chunkSize) || chunkSize < 6) { - DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Error reading 3DS file.\n"); + fmt::print(std::clog, "Read3DSFile: Error reading 3DS file top level chunk size\n"); return nullptr; } - DPRINTF(LOG_LEVEL_INFO, "3DS file, %d bytes\n", chunkSize); + fmt::print(std::clog, "3DS file, {} bytes\n", chunkSize + 6); - auto* scene = new M3DScene(); + auto scene = std::make_unique(); std::int32_t contentSize = chunkSize - 6; - read3DSChunks(in, contentSize, processTopLevelChunk, scene); + std::int32_t bytesRead = read3DSChunks(in, contentSize, processTopLevelChunk, scene.get()); + if (bytesRead < 0) { return nullptr; } + if (bytesRead != contentSize) + { + return nullptr; + } - return scene; + return scene.release(); } @@ -508,7 +566,7 @@ M3DScene* Read3DSFile(const fs::path& filename) std::ifstream in(filename.string(), std::ios::in | std::ios::binary); if (!in.good()) { - DPRINTF(LOG_LEVEL_ERROR, "Read3DSFile: Error opening %s\n", filename); + fmt::print(std::clog, "Read3DSFile: Error opening {}\n", filename); return nullptr; }