1. 4FIPS
  2. PHOTOS
  3. VIDEOS
  4. APPS
  5. CODE
  6. FORUMS
  7. ABOUT
/*
(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