Set up common binary IO functions
parent
368d94c0ad
commit
da5f621a19
|
@ -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
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
#cmakedefine HAVE_STRING_VIEW
|
||||
#cmakedefine HAVE_EXPERIMENTAL_STRING_VIEW
|
||||
#cmakedefine HAVE_WORDEXP
|
||||
#cmakedefine WORDS_BIGENDIAN
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
set(CELUTIL_SOURCES
|
||||
bigfix.cpp
|
||||
bigfix.h
|
||||
binaryread.h
|
||||
binarywrite.h
|
||||
blockarray.h
|
||||
bytes.h
|
||||
color.cpp
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue