From da5f621a19ed9b752963963e7d1e340528382edf Mon Sep 17 00:00:00 2001 From: Andrew Tribick Date: Sat, 6 Nov 2021 11:45:30 +0100 Subject: [PATCH] Set up common binary IO functions --- CMakeLists.txt | 3 + config.h.in | 1 + src/cel3ds/3dsread.cpp | 116 +++++++++++---------------- src/celengine/stardb.cpp | 75 ++++++++--------- src/celimage/bmp.cpp | 160 ++++++++++++++++--------------------- src/celimage/dds.cpp | 13 ++- src/celutil/CMakeLists.txt | 2 + src/celutil/binaryread.h | 124 ++++++++++++++++++++++++++++ src/celutil/binarywrite.h | 115 ++++++++++++++++++++++++++ 9 files changed, 404 insertions(+), 205 deletions(-) create mode 100644 src/celutil/binaryread.h create mode 100644 src/celutil/binarywrite.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b90703a8..284e17db1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,9 @@ endif() try_compile(HAVE_STRING_VIEW ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/checks/cxxsv.cpp") try_compile(HAVE_EXPERIMENTAL_STRING_VIEW ${CMAKE_BINARY_DIR} "${CMAKE_SOURCE_DIR}/checks/cxxsvexp.cpp") +include(TestBigEndian) +test_big_endian(WORDS_BIGENDIAN) + configure_file("config.h.in" "config.h") set(BASE_DATA_SOURCES diff --git a/config.h.in b/config.h.in index 96d4a2dea..9f2cc0b86 100644 --- a/config.h.in +++ b/config.h.in @@ -4,3 +4,4 @@ #cmakedefine HAVE_STRING_VIEW #cmakedefine HAVE_EXPERIMENTAL_STRING_VIEW #cmakedefine HAVE_WORDEXP +#cmakedefine WORDS_BIGENDIAN diff --git a/src/cel3ds/3dsread.cpp b/src/cel3ds/3dsread.cpp index c60dd542b..a72f681e0 100644 --- a/src/cel3ds/3dsread.cpp +++ b/src/cel3ds/3dsread.cpp @@ -18,11 +18,13 @@ #include #include -#include +#include #include "3dschunk.h" #include "3dsmodel.h" #include "3dsread.h" +namespace celutil = celestia::util; + namespace { constexpr std::int32_t READ_FAILURE = -1; @@ -33,56 +35,6 @@ template using ProcessChunkFunc = std::int32_t (*)(std::istream &, std::uint16_t, std::int32_t, T*); -bool readInt(std::istream& in, std::int32_t& value) -{ - char buffer[sizeof(value)]; - if (!in.read(buffer, sizeof(buffer)).good()) { return false; } - std::memcpy(&value, buffer, sizeof(value)); - LE_TO_CPU_INT32(value, value); - return true; -} - - -bool readShort(std::istream& in, std::int16_t& value) -{ - char buffer[sizeof(value)]; - if (!in.read(buffer, sizeof(buffer)).good()) { return false; } - std::memcpy(&value, buffer, sizeof(value)); - LE_TO_CPU_INT16(value, value); - return true; -} - - -bool readUshort(std::istream& in, std::uint16_t& value) -{ - char buffer[sizeof(value)]; - if (!in.read(buffer, sizeof(buffer)).good()) { return false; } - std::memcpy(&value, buffer, sizeof(value)); - LE_TO_CPU_INT16(value, value); - return true; -} - - -bool readFloat(std::istream& in, float& value) -{ - char buffer[sizeof(value)]; - if (!in.read(buffer, sizeof(buffer)).good()) { return false; } - std::memcpy(&value, buffer, sizeof(value)); - LE_TO_CPU_FLOAT(value, value); - return true; -} - - -bool readUchar(std::istream& in, unsigned char& value) -{ - char c; - in.get(c); - if (!in.good()) { return false; } - value = static_cast(c); - return true; -} - - std::int32_t readString(std::istream& in, std::string& value) { constexpr std::size_t maxLength = 1024; @@ -109,9 +61,9 @@ std::int32_t read3DSChunk(std::istream& in, T* obj) { std::uint16_t chunkType; - if (!readUshort(in, chunkType)) { return READ_FAILURE; } + if (!celutil::readLE(in, chunkType)) { return READ_FAILURE; } std::int32_t chunkSize; - if (!readInt(in, chunkSize) || chunkSize < 6) { return READ_FAILURE; } + if (!celutil::readLE(in, chunkSize) || chunkSize < 6) { return READ_FAILURE; } std::int32_t contentSize = chunkSize - 6; std::int32_t processedSize = chunkFunc(in, chunkType, contentSize, obj); @@ -163,8 +115,13 @@ std::int32_t read3DSChunks(std::istream& in, std::int32_t readColor(std::istream& in, M3DColor& color) { - unsigned char r, g, b; - if (!readUchar(in, r) || !readUchar(in, g) || !readUchar(in, b)) { return READ_FAILURE; } + std::uint8_t r, g, b; + if (!celutil::readLE(in, r) + || !celutil::readLE(in, g) + || !celutil::readLE(in, b)) + { + return READ_FAILURE; + } color = {static_cast(r) / 255.0f, static_cast(g) / 255.0f, @@ -177,7 +134,12 @@ std::int32_t readColor(std::istream& in, M3DColor& color) std::int32_t readFloatColor(std::istream& in, M3DColor& color) { float r, g, b; - if (!readFloat(in, r) || !readFloat(in, g) || !readFloat(in, b)) { return READ_FAILURE; } + if (!celutil::readLE(in, r) + || !celutil::readLE(in, g) + || !celutil::readLE(in, b)) + { + return READ_FAILURE; + } color = { r, g, b }; return static_cast(3 * sizeof(float)); @@ -189,7 +151,7 @@ std::int32_t readMeshMatrix(std::istream& in, Eigen::Matrix4f& m) float elements[12]; for (std::size_t i = 0; i < 12; ++i) { - if (!readFloat(in, elements[i])) { return READ_FAILURE; } + if (!celutil::readLE(in, elements[i])) { return READ_FAILURE; } } m << elements[0], elements[1], elements[2], 0, @@ -204,13 +166,18 @@ std::int32_t readMeshMatrix(std::istream& in, Eigen::Matrix4f& m) std::int32_t readPointArray(std::istream& in, M3DTriangleMesh* triMesh) { std::uint16_t nPoints; - if (!readUshort(in, nPoints)) { return READ_FAILURE; } + if (!celutil::readLE(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; } + if (!celutil::readLE(in, x) + || !celutil::readLE(in, y) + || !celutil::readLE(in, z)) + { + return READ_FAILURE; + } bytesRead += static_cast(3 * sizeof(float)); triMesh->addVertex(Eigen::Vector3f(x, y, z)); } @@ -224,13 +191,16 @@ std::int32_t readTextureCoordArray(std::istream& in, M3DTriangleMesh* triMesh) std::int32_t bytesRead = 0; std::uint16_t nPoints; - if (!readUshort(in, nPoints)) { return READ_FAILURE; } + if (!celutil::readLE(in, nPoints)) { return READ_FAILURE; } bytesRead += static_cast(sizeof(nPoints)); for (int i = 0; i < static_cast(nPoints); i++) { float u, v; - if (!readFloat(in, u) || !readFloat(in, v)) { return READ_FAILURE; } + if (!celutil::readLE(in, u) || !celutil::readLE(in, v)) + { + return READ_FAILURE; + } bytesRead += static_cast(2 * sizeof(float)); triMesh->addTexCoord(Eigen::Vector2f(u, -v)); } @@ -254,13 +224,16 @@ std::int32_t processFaceArrayChunk(std::istream& in, matGroup = std::make_unique(); bytesRead = readString(in, matGroup->materialName); - if (bytesRead == READ_FAILURE || !readUshort(in, nFaces)) { return READ_FAILURE; } + if (bytesRead == READ_FAILURE || !celutil::readLE(in, nFaces)) + { + return READ_FAILURE; + } bytesRead += static_cast(sizeof(nFaces)); for (std::uint16_t i = 0; i < nFaces; i++) { std::uint16_t faceIndex; - if (!readUshort(in, faceIndex)) { return READ_FAILURE; } + if (!celutil::readLE(in, faceIndex)) { return READ_FAILURE; } bytesRead += static_cast(sizeof(faceIndex)); matGroup->faces.push_back(faceIndex); } @@ -275,7 +248,7 @@ std::int32_t processFaceArrayChunk(std::istream& in, for (std::uint16_t i = 0; i < nFaces; i++) { std::int32_t groups; - if (!readInt(in, groups) || groups < 0) { return READ_FAILURE; } + if (!celutil::readLE(in, groups) || groups < 0){ return READ_FAILURE; } bytesRead += static_cast(sizeof(groups)); triMesh->addSmoothingGroups(static_cast(groups)); } @@ -290,13 +263,16 @@ std::int32_t processFaceArrayChunk(std::istream& in, std::int32_t readFaceArray(std::istream& in, M3DTriangleMesh* triMesh, std::int32_t contentSize) { std::uint16_t nFaces; - if (!readUshort(in, nFaces)) { return READ_FAILURE; } + if (!celutil::readLE(in, nFaces)) { return READ_FAILURE; } std::int32_t bytesRead = static_cast(sizeof(nFaces)); for (int i = 0; i < static_cast(nFaces); i++) { std::uint16_t v0, v1, v2, flags; - if (!readUshort(in, v0) || !readUshort(in, v1) || !readUshort(in, v2) || !readUshort(in, flags)) + if (!celutil::readLE(in, v0) + || !celutil::readLE(in, v1) + || !celutil::readLE(in, v2) + || !celutil::readLE(in, flags)) { return READ_FAILURE; } @@ -391,12 +367,12 @@ std::int32_t processPercentageChunk(std::istream& in, case M3DCHUNK_INT_PERCENTAGE: { std::int16_t value; - if (!readShort(in, value)) { return READ_FAILURE; } + if (!celutil::readLE(in, value)) { return READ_FAILURE; } *percent = static_cast(value); return sizeof(value); } case M3DCHUNK_FLOAT_PERCENTAGE: - return readFloat(in, *percent) ? sizeof(float) : READ_FAILURE; + return celutil::readLE(in, *percent) ? sizeof(float) : READ_FAILURE; default: return UNKNOWN_CHUNK; } @@ -537,14 +513,14 @@ std::int32_t processTopLevelChunk(std::istream& in, std::unique_ptr Read3DSFile(std::istream& in) { std::uint16_t chunkType; - if (!readUshort(in, chunkType) || chunkType != M3DCHUNK_MAGIC) + if (!celutil::readLE(in, chunkType) || chunkType != M3DCHUNK_MAGIC) { fmt::print(std::clog, "Read3DSFile: Wrong magic number in header\n"); return nullptr; } std::int32_t chunkSize; - if (!readInt(in, chunkSize) || chunkSize < 6) + if (!celutil::readLE(in, chunkSize) || chunkSize < 6) { fmt::print(std::clog, "Read3DSFile: Error reading 3DS file top level chunk size\n"); return nullptr; diff --git a/src/celengine/stardb.cpp b/src/celengine/stardb.cpp index 83421f72c..7568d900c 100644 --- a/src/celengine/stardb.cpp +++ b/src/celengine/stardb.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include "stardb.h" @@ -31,6 +31,8 @@ using namespace Eigen; using namespace std; using namespace celmath; +namespace celutil = celestia::util; + constexpr const char HDCatalogPrefix[] = "HD "; constexpr const char HIPPARCOSCatalogPrefix[] = "HIP "; @@ -564,8 +566,8 @@ bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in) { int headerLength = strlen(CROSSINDEX_FILE_HEADER); char* header = new char[headerLength]; - in.read(header, headerLength); - if (strncmp(header, CROSSINDEX_FILE_HEADER, headerLength)) + if (!in.read(header, headerLength).good() + || strncmp(header, CROSSINDEX_FILE_HEADER, headerLength)) { cerr << _("Bad header for cross index\n"); delete[] header; @@ -576,12 +578,10 @@ bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in) // Verify the version { - uint16_t version; - in.read((char*) &version, sizeof version); - LE_TO_CPU_INT16(version, version); - if (version != 0x0100) + std::uint16_t version; + if (!celutil::readLE(in, version) || version != 0x0100) { - cerr << _("Bad version for cross index\n"); + std::cerr << _("Bad version for cross index\n"); return false; } } @@ -592,16 +592,17 @@ bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in) for (;;) { CrossIndexEntry ent; - in.read((char *) &ent.catalogNumber, sizeof ent.catalogNumber); - LE_TO_CPU_INT32(ent.catalogNumber, ent.catalogNumber); - if (in.eof()) - break; - - in.read((char *) &ent.celCatalogNumber, sizeof ent.celCatalogNumber); - LE_TO_CPU_INT32(ent.celCatalogNumber, ent.celCatalogNumber); - if (in.fail()) + if (!celutil::readLE(in, ent.catalogNumber)) { - cerr << fmt::sprintf(_("Loading cross index failed at record %u\n"), record); + if (in.eof()) { break; } + std::cerr << _("Loading cross index failed\n"); + delete xindex; + return false; + } + + if (!celutil::readLE(in, ent.celCatalogNumber)) + { + std::cerr << fmt::sprintf(_("Loading cross index failed at record %u\n"), record); delete xindex; return false; } @@ -627,8 +628,7 @@ bool StarDatabase::loadBinary(istream& in) { int headerLength = strlen(FILE_HEADER); char* header = new char[headerLength]; - in.read(header, headerLength); - if (strncmp(header, FILE_HEADER, headerLength)) { + if (!in.read(header, headerLength).good() || strncmp(header, FILE_HEADER, headerLength)) { delete[] header; return false; } @@ -637,18 +637,18 @@ bool StarDatabase::loadBinary(istream& in) // Verify the version { - uint16_t version; - in.read((char*) &version, sizeof version); - LE_TO_CPU_INT16(version, version); - if (version != 0x0100) + std::uint16_t version; + if (!celutil::readLE(in, version) || version != 0x0100) + { return false; + } } // Read the star count - in.read((char *) &nStarsInFile, sizeof nStarsInFile); - LE_TO_CPU_INT32(nStarsInFile, nStarsInFile); - if (!in.good()) + if (!celutil::readLE(in, nStarsInFile)) + { return false; + } unsigned int totalStars = nStars + nStarsInFile; @@ -659,20 +659,15 @@ bool StarDatabase::loadBinary(istream& in) int16_t absMag; uint16_t spectralType; - in.read((char *) &catNo, sizeof catNo); - LE_TO_CPU_INT32(catNo, catNo); - in.read((char *) &x, sizeof x); - LE_TO_CPU_FLOAT(x, x); - in.read((char *) &y, sizeof y); - LE_TO_CPU_FLOAT(y, y); - in.read((char *) &z, sizeof z); - LE_TO_CPU_FLOAT(z, z); - in.read((char *) &absMag, sizeof absMag); - LE_TO_CPU_INT16(absMag, absMag); - in.read((char *) &spectralType, sizeof spectralType); - LE_TO_CPU_INT16(spectralType, spectralType); - if (in.bad()) - break; + if (!celutil::readLE(in, catNo) + || !celutil::readLE(in, x) + || !celutil::readLE(in, y) + || !celutil::readLE(in, z) + || !celutil::readLE(in, absMag) + || !celutil::readLE(in, spectralType)) + { + return false; + } Star star; star.setPosition(x, y, z); diff --git a/src/celimage/bmp.cpp b/src/celimage/bmp.cpp index 0a60f0f11..d8300409d 100644 --- a/src/celimage/bmp.cpp +++ b/src/celimage/bmp.cpp @@ -11,13 +11,14 @@ #include #include // ifstream #include // ios +#include +#include + #include -#include +#include #include -using std::ifstream; -using std::ios; -using celestia::PixelFormat; +namespace celutil = celestia::util; namespace { @@ -26,120 +27,100 @@ namespace typedef struct { unsigned char magic[2]; - uint32_t size; - uint32_t reserved; - uint32_t offset; + std::uint32_t size; + std::uint32_t reserved; + std::uint32_t offset; } BMPFileHeader; typedef struct { - uint32_t size; - int32_t width; - int32_t height; - uint16_t planes; - uint16_t bpp; - uint32_t compression; - uint32_t imageSize; - int32_t widthPPM; - int32_t heightPPM; - uint32_t colorsUsed; - uint32_t colorsImportant; + std::uint32_t size; + std::int32_t width; + std::int32_t height; + std::uint16_t planes; + std::uint16_t bpp; + std::uint32_t compression; + std::uint32_t imageSize; + std::int32_t widthPPM; + std::int32_t heightPPM; + std::uint32_t colorsUsed; + std::uint32_t colorsImportant; } BMPImageHeader; -int32_t readInt32(ifstream& in) -{ - uint8_t b[4]; - in.read(reinterpret_cast(b), 4); - int32_t val = ((int32_t) b[3] << 24) + - ((int32_t) b[2] << 16) + - ((int32_t) b[1] << 8) + - ((int32_t) b[0]); - LE_TO_CPU_INT32(val, val); - return val; -} - -int16_t readInt16(ifstream& in) -{ - uint8_t b[2]; - in.read(reinterpret_cast(b), 2); - int16_t val = ((int16_t) b[1] << 8) + (int16_t) b[0]; - LE_TO_CPU_INT16(val, val); - return val; -} - - -Image* LoadBMPImage(ifstream& in) +Image* LoadBMPImage(std::istream& in) { BMPFileHeader fileHeader; BMPImageHeader imageHeader; - uint8_t* pixels; - in >> fileHeader.magic[0]; - in >> fileHeader.magic[1]; - fileHeader.size = readInt32(in); - fileHeader.reserved = readInt32(in); - fileHeader.offset = readInt32(in); - - if (fileHeader.magic[0] != 'B' || fileHeader.magic[1] != 'M') + if (!celutil::readLE(in, fileHeader.magic[0]) + || fileHeader.magic[0] != 'B' + || !celutil::readLE(in, fileHeader.magic[1]) + || fileHeader.magic[1] != 'M' + || !celutil::readLE(in, fileHeader.size) + || !celutil::readLE(in, fileHeader.reserved) + || !celutil::readLE(in, fileHeader.offset) + || !celutil::readLE(in, imageHeader.size) + || !celutil::readLE(in, imageHeader.width) + || imageHeader.width <= 0 + || !celutil::readLE(in, imageHeader.height) + || imageHeader.height <= 0 + || !celutil::readLE(in, imageHeader.planes) + || !celutil::readLE(in, imageHeader.bpp) + // We don't handle 1-, 2-, or 4-bpp images + || (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32) + || !celutil::readLE(in, imageHeader.compression) + // We currently don't support compressed BMPs + || imageHeader.compression != 0 + || !celutil::readLE(in, imageHeader.imageSize) + || !celutil::readLE(in, imageHeader.widthPPM) + || !celutil::readLE(in, imageHeader.heightPPM) + || !celutil::readLE(in, imageHeader.colorsUsed) + || !celutil::readLE(in, imageHeader.colorsImportant)) + { return nullptr; + } - imageHeader.size = readInt32(in); - imageHeader.width = readInt32(in); - imageHeader.height = readInt32(in); - imageHeader.planes = readInt16(in); - imageHeader.bpp = readInt16(in); - imageHeader.compression = readInt32(in); - imageHeader.imageSize = readInt32(in); - imageHeader.widthPPM = readInt32(in); - imageHeader.heightPPM = readInt32(in); - imageHeader.colorsUsed = readInt32(in); - imageHeader.colorsImportant = readInt32(in); - - if (imageHeader.width <= 0 || imageHeader.height <= 0) - return nullptr; - - // We currently don't support compressed BMPs - if (imageHeader.compression != 0) - return nullptr; - // We don't handle 1-, 2-, or 4-bpp images - if (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32) - return nullptr; - - uint8_t* palette = nullptr; + std::vector palette; if (imageHeader.bpp == 8) { DPRINTF(LOG_LEVEL_DEBUG, "Reading %u color palette\n", imageHeader.colorsUsed); - palette = new uint8_t[imageHeader.colorsUsed * 4]; - in.read(reinterpret_cast(palette), imageHeader.colorsUsed * 4); + palette.resize(imageHeader.colorsUsed * 4); + if (!in.read(reinterpret_cast(palette.data()), imageHeader.colorsUsed * 4).good()) + { + return nullptr; + } } - in.seekg(fileHeader.offset, ios::beg); + if (!in.seekg(fileHeader.offset, std::ios::beg)) { return nullptr; } - uint32_t bytesPerRow = + std::size_t bytesPerRow = (imageHeader.width * imageHeader.bpp / 8 + 1) & ~1; - uint32_t imageBytes = bytesPerRow * imageHeader.height; + std::size_t imageBytes = bytesPerRow * imageHeader.height; // slurp the image data - pixels = new uint8_t[imageBytes]; - in.read(reinterpret_cast(pixels), imageBytes); + std::vector pixels(imageBytes); + if (!in.read(reinterpret_cast(pixels.data()), imageBytes).good()) + { + return nullptr; + } // check for truncated file - auto* img = new Image(PixelFormat::RGB, imageHeader.width, imageHeader.height); + auto img = std::make_unique(celestia::PixelFormat::RGB, imageHeader.width, imageHeader.height); // Copy the image and perform any necessary conversions - for (int32_t y = 0; y < imageHeader.height; y++) + for (std::int32_t y = 0; y < imageHeader.height; y++) { - uint8_t* src = &pixels[y * bytesPerRow]; + const std::uint8_t* src = pixels.data() + y * bytesPerRow; uint8_t* dst = img->getPixelRow(y); switch (imageHeader.bpp) { case 8: - for (int32_t x = 0; x < imageHeader.width; x++) + for (std::int32_t x = 0; x < imageHeader.width; x++) { - uint8_t* color = palette + (*src << 2); + const uint8_t* color = palette.data() + (*src << 2); dst[0] = color[2]; dst[1] = color[1]; dst[2] = color[0]; @@ -148,7 +129,7 @@ Image* LoadBMPImage(ifstream& in) } break; case 24: - for (int32_t x = 0; x < imageHeader.width; x++) + for (std::int32_t x = 0; x < imageHeader.width; x++) { dst[0] = src[2]; dst[1] = src[1]; @@ -158,7 +139,7 @@ Image* LoadBMPImage(ifstream& in) } break; case 32: - for (int32_t x = 0; x < imageHeader.width; x++) + for (std::int32_t x = 0; x < imageHeader.width; x++) { dst[0] = src[2]; dst[1] = src[1]; @@ -170,16 +151,13 @@ Image* LoadBMPImage(ifstream& in) } } - delete[] pixels; - delete[] palette; - - return img; + return img.release(); } } // anonymous namespace Image* LoadBMPImage(const fs::path& filename) { - ifstream bmpFile(filename.string(), ios::in | ios::binary); + std::ifstream bmpFile(filename.string(), std::ios::in | std::ios::binary); if (bmpFile.good()) { diff --git a/src/celimage/dds.cpp b/src/celimage/dds.cpp index 474baa110..3cc2632aa 100644 --- a/src/celimage/dds.cpp +++ b/src/celimage/dds.cpp @@ -145,15 +145,20 @@ Image* LoadDDSImage(const fs::path& filename) char header[4]; in.read(header, sizeof header); - if (header[0] != 'D' || header[1] != 'D' || - header[2] != 'S' || header[3] != ' ') + if (!in.read(header, sizeof(header)).good() + || header[0] != 'D' || header[1] != 'D' + || header[2] != 'S' || header[3] != ' ') { DPRINTF(LOG_LEVEL_ERROR, "DDS texture file %s has bad header.\n", filename); return nullptr; } DDSurfaceDesc ddsd; - in.read(reinterpret_cast(&ddsd), sizeof ddsd); + if (!in.read(reinterpret_cast(&ddsd), sizeof ddsd).good()) + { + DPRINTF(LOG_LEVEL_ERROR, "DDS file %s has bad surface desc.\n", filename); + return nullptr; + } LE_TO_CPU_INT32(ddsd.size, ddsd.size); LE_TO_CPU_INT32(ddsd.pitch, ddsd.pitch); LE_TO_CPU_INT32(ddsd.width, ddsd.width); @@ -296,7 +301,7 @@ Image* LoadDDSImage(const fs::path& filename) (int) ddsd.height, max(ddsd.mipMapLevels, 1u)); in.read(reinterpret_cast(img->getPixels()), img->getSize()); - if (!in.eof() && !in.good()) + if (!in.good()) { DPRINTF(LOG_LEVEL_ERROR, "Failed reading data from DDS texture file %s.\n", filename); delete img; diff --git a/src/celutil/CMakeLists.txt b/src/celutil/CMakeLists.txt index 7e86dc0cc..f9f31fd21 100644 --- a/src/celutil/CMakeLists.txt +++ b/src/celutil/CMakeLists.txt @@ -1,6 +1,8 @@ set(CELUTIL_SOURCES bigfix.cpp bigfix.h + binaryread.h + binarywrite.h blockarray.h bytes.h color.cpp diff --git a/src/celutil/binaryread.h b/src/celutil/binaryread.h new file mode 100644 index 000000000..6b6aa9d76 --- /dev/null +++ b/src/celutil/binaryread.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include + +#include + + +namespace celestia +{ +namespace util +{ +/*! Read a value stored in machine-native byte order from an input stream. + */ +template::value>> +inline bool readNative(std::istream& in, T& value) +{ + char data[sizeof(T)]; + if (!in.read(data, sizeof(T)).good()) { return false; } + std::memcpy(&value, data, sizeof(T)); + return true; +} + +template<> +inline bool readNative(std::istream& in, char& value) +{ + return in.get(value).good(); +} + +template<> +inline bool readNative(std::istream& in, signed char& value) +{ + char c; + if (!in.get(c).good()) { return false; } + value = static_cast(c); + return true; +} + +template<> +inline bool readNative(std::istream& in, unsigned char& value) +{ + char c; + if (!in.get(c).good()) { return false; } + value = static_cast(c); + return true; +} + + +/*! Read a value stored opposite to machine-native byte order from an input stream + */ +template::value>> +inline bool readReversed(std::istream& in, T& value) +{ + char data[sizeof(T)]; + if (!in.read(data, sizeof(T)).good()) { return false; } + for (std::size_t i = 0; i < sizeof(T) / 2; ++i) + { + std::swap(data[i], data[sizeof(T) - i - 1]); + } + + std::memcpy(&value, data, sizeof(T)); + return true; +} + +template<> +inline bool readReversed(std::istream& in, char& value) +{ + return readNative(in, value); +} + +template<> +inline bool readReversed(std::istream& in, signed char& value) +{ + return readNative(in, value); +} + +template<> +inline bool readReversed(std::istream& in, unsigned char& value) +{ + return readNative(in, value); +} + +#ifdef WORDS_BIGENDIAN + +/*! Read a value stored in little-endian byte order from an input stream. + */ +template::value>> +inline bool readLE(std::istream& in, T& value) +{ + return readReversed(in, value); +} + +/*! Read a value stored in big-endian byte order from an input stream. + */ +template::value>> +inline bool readBE(std::istream& in, T& value) +{ + return readNative(in, value); +} + +#else + +/*! Read a value stored in little-endian byte order from an input stream. + */ +template::value>> +inline bool readLE(std::istream& in, T& value) +{ + return readNative(in, value); +} + +/*! Read a value stored in big-endian byte order from an input stream. + */ +template::value>> +inline bool readBE(std::istream& in, T& value) +{ + return readReversed(in, value); +} + +#endif + +} +} diff --git a/src/celutil/binarywrite.h b/src/celutil/binarywrite.h new file mode 100644 index 000000000..2c92b1abc --- /dev/null +++ b/src/celutil/binarywrite.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include +#include +#include + +#include + + +namespace celestia +{ +namespace util +{ +/*! Write a value to an output stream in machine-native byte order. + */ +template::value>> +inline bool writeNative(std::ostream& out, T value) +{ + char data[sizeof(T)]; + std::memcpy(data, &value, sizeof(T)); + return out.write(data, sizeof(T)).good(); +} + +template<> +inline bool writeNative(std::ostream& out, char value) +{ + return out.put(value).good(); +} + +template<> +inline bool writeNative(std::ostream& out, signed char value) +{ + return out.put(static_cast(value)).good(); +} + +template<> +inline bool writeNative(std::ostream& out, unsigned char value) +{ + return out.put(static_cast(value)).good(); +} + + +/*! Write a value to an output stream with the opposite of machine-native byte order. + */ +template::value>> +inline bool writeReversed(std::ostream& out, T value) +{ + char data[sizeof(T)]; + std::memcpy(data, &value, sizeof(T)); + for (std::size_t i = 0; i < sizeof(T) / 2; ++i) + { + std::swap(data[i], data[sizeof(T) - i - 1]); + } + return out.write(data, sizeof(T)).good(); +} + +template<> +inline bool writeReversed(std::ostream& out, char value) +{ + return writeNative(out, value); +} + +template<> +inline bool writeReversed(std::ostream& out, signed char value) +{ + return writeNative(out, value); +} + +template<> +inline bool writeReversed(std::ostream& out, unsigned char value) +{ + return writeNative(out, value); +} + +#ifdef WORDS_BIGENDIAN + +/*! Write a value to an output stream in little-endian byte order + */ +template::value>> +inline bool writeLE(std::ostream& out, T value) +{ + return writeReversed(out, value); +} + +/*! Write a value to an output stream in big-endian byte order + */ +template::value>> +inline bool writeBE(std::ostream& out, T value) +{ + return writeNative(out, value); +} + +#else + +/*! Write a value to an output stream in little-endian byte order + */ +template::value>> +inline bool writeLE(std::ostream& out, T value) +{ + return writeNative(out, value); +} + +/*! Write a value to an output stream in big-endian byte order + */ +template::value>> +inline bool writeBE(std::ostream& out, T value) +{ + return writeReversed(out, value); +} + +#endif + +} +}