/*
(c) 2013 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=1071
*/
#include <vector>
#include <memory>
#include <tuple>
#include <cstdint>
#include <cassert>
/// Texture formats (enum of uint8_t, C++11).
enum class Texture_format8 : uint8_t
{
Rgb,
Rgba
};
/// General IO PODs (32-bit aligned).
namespace io {
struct Blob
{
uint8_t tag[4];
uint32_t size;
};
struct Block
{
uint32_t size;
uint32_t offset;
};}
/// Texture related IO PODs (32-bit aligned).
namespace io_texture {
struct Header
{
uint8_t version;
uint8_t reserved0;
uint8_t reserved1;
Texture_format8 format;
uint32_t width;
uint32_t height;
uint32_t size;
};
struct Texture
{
io::Blob blob;
io::Block header_block;
io::Block data_block;
// [header]
// [data]
};}
/// Texture reference (doesn't own the data, it's just a mapping => cheap to create & copy).
class Texture_ref
{
public:
Texture_ref(const void *blob)
{
// create the mappings:
using namespace io;
using namespace io_texture;
const uint8_t *bytes = static_cast<const uint8_t *>(blob);
const Texture *texture = reinterpret_cast<const Texture *>(bytes);
assert(std::make_tuple(texture->blob.tag[0], texture->blob.tag[1], texture->blob.tag[2], texture->blob.tag[3]) == std::make_tuple('T', 'E', 'X', '!'));
const Header *header = reinterpret_cast<const Header *>(bytes + texture->header_block.offset);
_version = header->version;
_format = header->format;
_width = header->width;
_height = header->height;
_data = bytes + texture->data_block.offset;
_size = header->size;
}
uint8_t version() const { return _version; }
Texture_format8 format() const { return _format; }
uint32_t width() const { return _width; }
uint32_t height() const { return _height; }
const void * data() const { return _data; }
uint32_t size() const {return _size; }
private:
// sorted from bigger to smaller:
const void *_data;
uint32_t _width;
uint32_t _height;
uint32_t _size;
uint8_t _version;
Texture_format8 _format;
};
/// Loads a BLOB and checks its ID (signature).
std::vector<uint8_t> load_blob(const char *fname, std::tuple<uint8_t, uint8_t, uint8_t, uint8_t> id)
{
auto deleter = [](FILE *f) { if(f) fclose(f); };
std::unique_ptr<FILE, decltype(deleter)> file(fopen(fname, "rb"), deleter);
io::Blob blob;
fread(&blob, sizeof(io::Blob), 1, file.get());
assert(std::make_tuple(blob.tag[0], blob.tag[1], blob.tag[2], blob.tag[3]) == id);
std::vector<uint8_t> bytes(blob.size);
const uint8_t *blob_bytes = reinterpret_cast<const uint8_t *>(&blob);
std::copy(blob_bytes, blob_bytes + sizeof(io::Blob), bytes.data());
fread(bytes.data() + sizeof(io::Blob), blob.size - sizeof(io::Blob), 1, file.get());
return bytes; // move it out (C++11)
}
int main()
{
const auto blob = load_blob("texture.blob", std::make_tuple('T', 'E', 'X', '!'));
const Texture_ref tex(blob.data());
printf(
"version=%u, format='%s', width=%u, height=%u\n",
unsigned(tex.version()), tex.format() == Texture_format8::Rgb ? "Rgb" : "Rgba",
unsigned(tex.width()), unsigned(tex.height())
);
printf("data(size=%u): ", unsigned(tex.size()));
for(auto i = 0; i < tex.size(); ++i)
printf("%x ", static_cast<const uint8_t *>(tex.data())[i]);
printf("\n");
return 0;
}
// Compiled under Visual C++ 2012, output:
// version=1, format='Rgba', width=2, height=2
// data(size=16): ff 0 0 ff 0 ff 0 ff 0 0 ff ff 80 80 80 ff