Set up common binary IO functions

pull/1157/head
Andrew Tribick 2021-11-06 11:45:30 +01:00 committed by ajtribick
parent 368d94c0ad
commit da5f621a19
9 changed files with 404 additions and 205 deletions

View File

@ -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

View File

@ -4,3 +4,4 @@
#cmakedefine HAVE_STRING_VIEW
#cmakedefine HAVE_EXPERIMENTAL_STRING_VIEW
#cmakedefine HAVE_WORDEXP
#cmakedefine WORDS_BIGENDIAN

View File

@ -18,11 +18,13 @@
#include <Eigen/Core>
#include <fmt/ostream.h>
#include <celutil/bytes.h>
#include <celutil/binaryread.h>
#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<typename T>
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<unsigned char>(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<std::uint16_t>(in, chunkType)) { return READ_FAILURE; }
std::int32_t chunkSize;
if (!readInt(in, chunkSize) || chunkSize < 6) { return READ_FAILURE; }
if (!celutil::readLE<std::int32_t>(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<std::uint8_t>(in, r)
|| !celutil::readLE<std::uint8_t>(in, g)
|| !celutil::readLE<std::uint8_t>(in, b))
{
return READ_FAILURE;
}
color = {static_cast<float>(r) / 255.0f,
static_cast<float>(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<float>(in, r)
|| !celutil::readLE<float>(in, g)
|| !celutil::readLE<float>(in, b))
{
return READ_FAILURE;
}
color = { r, g, b };
return static_cast<std::int32_t>(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<float>(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<std::uint16_t>(in, nPoints)) { return READ_FAILURE; }
std::int32_t bytesRead = static_cast<std::int32_t>(sizeof(nPoints));
for (int i = 0; i < static_cast<int>(nPoints); i++)
{
float x, y, z;
if (!readFloat(in, x) || !readFloat(in, y) || !readFloat(in, z)) { return READ_FAILURE; }
if (!celutil::readLE<float>(in, x)
|| !celutil::readLE<float>(in, y)
|| !celutil::readLE<float>(in, z))
{
return READ_FAILURE;
}
bytesRead += static_cast<std::int32_t>(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<std::uint16_t>(in, nPoints)) { return READ_FAILURE; }
bytesRead += static_cast<std::int32_t>(sizeof(nPoints));
for (int i = 0; i < static_cast<int>(nPoints); i++)
{
float u, v;
if (!readFloat(in, u) || !readFloat(in, v)) { return READ_FAILURE; }
if (!celutil::readLE<float>(in, u) || !celutil::readLE<float>(in, v))
{
return READ_FAILURE;
}
bytesRead += static_cast<std::int32_t>(2 * sizeof(float));
triMesh->addTexCoord(Eigen::Vector2f(u, -v));
}
@ -254,13 +224,16 @@ std::int32_t processFaceArrayChunk(std::istream& in,
matGroup = std::make_unique<M3DMeshMaterialGroup>();
bytesRead = readString(in, matGroup->materialName);
if (bytesRead == READ_FAILURE || !readUshort(in, nFaces)) { return READ_FAILURE; }
if (bytesRead == READ_FAILURE || !celutil::readLE<std::uint16_t>(in, nFaces))
{
return READ_FAILURE;
}
bytesRead += static_cast<std::int32_t>(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<std::uint16_t>(in, faceIndex)) { return READ_FAILURE; }
bytesRead += static_cast<std::int32_t>(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<std::int32_t>(in, groups) || groups < 0){ return READ_FAILURE; }
bytesRead += static_cast<std::int32_t>(sizeof(groups));
triMesh->addSmoothingGroups(static_cast<std::uint32_t>(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<std::uint16_t>(in, nFaces)) { return READ_FAILURE; }
std::int32_t bytesRead = static_cast<std::int32_t>(sizeof(nFaces));
for (int i = 0; i < static_cast<int>(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<std::uint16_t>(in, v0)
|| !celutil::readLE<std::uint16_t>(in, v1)
|| !celutil::readLE<std::uint16_t>(in, v2)
|| !celutil::readLE<std::uint16_t>(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<std::int16_t>(in, value)) { return READ_FAILURE; }
*percent = static_cast<float>(value);
return sizeof(value);
}
case M3DCHUNK_FLOAT_PERCENTAGE:
return readFloat(in, *percent) ? sizeof(float) : READ_FAILURE;
return celutil::readLE<float>(in, *percent) ? sizeof(float) : READ_FAILURE;
default:
return UNKNOWN_CHUNK;
}
@ -537,14 +513,14 @@ std::int32_t processTopLevelChunk(std::istream& in,
std::unique_ptr<M3DScene> Read3DSFile(std::istream& in)
{
std::uint16_t chunkType;
if (!readUshort(in, chunkType) || chunkType != M3DCHUNK_MAGIC)
if (!celutil::readLE<std::uint16_t>(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<std::int32_t>(in, chunkSize) || chunkSize < 6)
{
fmt::print(std::clog, "Read3DSFile: Error reading 3DS file top level chunk size\n");
return nullptr;

View File

@ -15,7 +15,7 @@
#include <cassert>
#include <algorithm>
#include <celmath/mathlib.h>
#include <celutil/bytes.h>
#include <celutil/binaryread.h>
#include <celutil/debug.h>
#include <celutil/gettext.h>
#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<std::uint16_t>(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<AstroCatalog::IndexNumber>(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<AstroCatalog::IndexNumber>(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<std::uint16_t>(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<std::uint32_t>(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<AstroCatalog::IndexNumber>(in, catNo)
|| !celutil::readLE<float>(in, x)
|| !celutil::readLE<float>(in, y)
|| !celutil::readLE<float>(in, z)
|| !celutil::readLE<std::int16_t>(in, absMag)
|| !celutil::readLE<std::uint16_t>(in, spectralType))
{
return false;
}
Star star;
star.setPosition(x, y, z);

View File

@ -11,13 +11,14 @@
#include <cstdint>
#include <fstream> // ifstream
#include <iostream> // ios
#include <memory>
#include <vector>
#include <celengine/image.h>
#include <celutil/bytes.h>
#include <celutil/binaryread.h>
#include <celutil/debug.h>
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<char*>(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<char*>(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<unsigned char>(in, fileHeader.magic[0])
|| fileHeader.magic[0] != 'B'
|| !celutil::readLE<unsigned char>(in, fileHeader.magic[1])
|| fileHeader.magic[1] != 'M'
|| !celutil::readLE<std::uint32_t>(in, fileHeader.size)
|| !celutil::readLE<std::uint32_t>(in, fileHeader.reserved)
|| !celutil::readLE<std::uint32_t>(in, fileHeader.offset)
|| !celutil::readLE<std::uint32_t>(in, imageHeader.size)
|| !celutil::readLE<std::int32_t>(in, imageHeader.width)
|| imageHeader.width <= 0
|| !celutil::readLE<std::int32_t>(in, imageHeader.height)
|| imageHeader.height <= 0
|| !celutil::readLE<std::uint16_t>(in, imageHeader.planes)
|| !celutil::readLE<std::uint16_t>(in, imageHeader.bpp)
// We don't handle 1-, 2-, or 4-bpp images
|| (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32)
|| !celutil::readLE<std::uint32_t>(in, imageHeader.compression)
// We currently don't support compressed BMPs
|| imageHeader.compression != 0
|| !celutil::readLE<std::uint32_t>(in, imageHeader.imageSize)
|| !celutil::readLE<std::int32_t>(in, imageHeader.widthPPM)
|| !celutil::readLE<std::int32_t>(in, imageHeader.heightPPM)
|| !celutil::readLE<std::uint32_t>(in, imageHeader.colorsUsed)
|| !celutil::readLE<std::uint32_t>(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<uint8_t> 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<char*>(palette), imageHeader.colorsUsed * 4);
palette.resize(imageHeader.colorsUsed * 4);
if (!in.read(reinterpret_cast<char*>(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<char*>(pixels), imageBytes);
std::vector<std::uint8_t> pixels(imageBytes);
if (!in.read(reinterpret_cast<char*>(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<Image>(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())
{

View File

@ -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<char*>(&ddsd), sizeof ddsd);
if (!in.read(reinterpret_cast<char*>(&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<char*>(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;

View File

@ -1,6 +1,8 @@
set(CELUTIL_SOURCES
bigfix.cpp
bigfix.h
binaryread.h
binarywrite.h
blockarray.h
bytes.h
color.cpp

View File

@ -0,0 +1,124 @@
#pragma once
#include <cstring>
#include <istream>
#include <type_traits>
#include <utility>
#include <config.h>
namespace celestia
{
namespace util
{
/*! Read a value stored in machine-native byte order from an input stream.
*/
template<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<char>(std::istream& in, char& value)
{
return in.get(value).good();
}
template<>
inline bool readNative<signed char>(std::istream& in, signed char& value)
{
char c;
if (!in.get(c).good()) { return false; }
value = static_cast<signed char>(c);
return true;
}
template<>
inline bool readNative<unsigned char>(std::istream& in, unsigned char& value)
{
char c;
if (!in.get(c).good()) { return false; }
value = static_cast<unsigned char>(c);
return true;
}
/*! Read a value stored opposite to machine-native byte order from an input stream
*/
template<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<char>(std::istream& in, char& value)
{
return readNative(in, value);
}
template<>
inline bool readReversed<signed char>(std::istream& in, signed char& value)
{
return readNative(in, value);
}
template<>
inline bool readReversed<unsigned char>(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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
inline bool readBE(std::istream& in, T& value)
{
return readReversed(in, value);
}
#endif
}
}

View File

@ -0,0 +1,115 @@
#pragma once
#include <cstring>
#include <ostream>
#include <type_traits>
#include <utility>
#include <config.h>
namespace celestia
{
namespace util
{
/*! Write a value to an output stream in machine-native byte order.
*/
template<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<char>(std::ostream& out, char value)
{
return out.put(value).good();
}
template<>
inline bool writeNative<signed char>(std::ostream& out, signed char value)
{
return out.put(static_cast<char>(value)).good();
}
template<>
inline bool writeNative<unsigned char>(std::ostream& out, unsigned char value)
{
return out.put(static_cast<unsigned char>(value)).good();
}
/*! Write a value to an output stream with the opposite of machine-native byte order.
*/
template<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<char>(std::ostream& out, char value)
{
return writeNative(out, value);
}
template<>
inline bool writeReversed<signed char>(std::ostream& out, signed char value)
{
return writeNative(out, value);
}
template<>
inline bool writeReversed<unsigned char>(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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::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<typename T, typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
inline bool writeBE(std::ostream& out, T value)
{
return writeReversed(out, value);
}
#endif
}
}