Skip to content

Instantly share code, notes, and snippets.

@tehsausage
Created May 7, 2016 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tehsausage/7102104b977c3c6317963ceeb04e29ea to your computer and use it in GitHub Desktop.
Save tehsausage/7102104b977c3c6317963ceeb04e29ea to your computer and use it in GitHub Desktop.
#include "dib_reader.hpp"
#include <cmath>
#include <cstddef>
#include <cstring>
#define NUM_CONVERT_TABLES 8
static bool convert_table_init = false;
static int convert_table[(2 << (NUM_CONVERT_TABLES + 2)) - 2];
static int* get_convert_table(int bit)
{
return &convert_table[(2 << bit) - 2];
}
static void decode_bitfield(uint32_t m, int& shift_out, uint32_t& mask_out)
{
int shift = 0;
if (m == 0)
{
shift_out = 0;
mask_out = 0;
return;
}
#ifdef __GNUC__
shift = __builtin_ctz(m);
m >>= shift;
#else
while ((m & 1) == 0)
{
m >>= 1;
++shift;
}
#endif
shift_out = shift;
mask_out = m;
}
static void generate_scale_table(int* table, int entries)
{
int i;
for (i = 0; i < entries; ++i)
table[i] = i * 255 / (entries - 1);
}
const char* dib_reader::check_format() const
{
if (width() < 0)
return "Image width less than zero";
if (width() > 0x40000000 || height() < -0x40000000 || height() > 0x40000000)
return "Image dimensions out of bounds";
if (depth() != 16 && depth() != 24 && depth() != 32)
return "Unsupported bit depth";
if (compression() != RGB && compression() != BitFields)
return "Unsupported compression";
constexpr int maxmask = (1 << NUM_CONVERT_TABLES) - 1;
if (rm > maxmask || gm > maxmask || bm > maxmask || am > maxmask)
return "Bit mask too long";
return nullptr;
}
void dib_reader::start()
{
if (compression() == BitFields)
{
decode_bitfield(red_mask(), rs, rm);
decode_bitfield(green_mask(), gs, gm);
decode_bitfield(blue_mask(), bs, bm);
decode_bitfield(alpha_mask(), as, am);
}
else
{
as = am = 0;
switch (depth())
{
case 16:
decode_bitfield(0x00007C00U, rs, rm);
decode_bitfield(0x000003E0U, gs, gm);
decode_bitfield(0x0000001FU, bs, bm);
break;
case 24:
case 32:
decode_bitfield(0x00FF0000U, rs, rm);
decode_bitfield(0x0000FF00U, gs, gm);
decode_bitfield(0x000000FFU, bs, bm);
break;
}
}
for (int i = 0; i < NUM_CONVERT_TABLES; ++i)
{
uint32_t mask = ~(0xFFFFFFFFU << (i+1)) & 0xFFFFFFFFU;
int* table = get_convert_table(i);
int entries = (1 << (i+1));
if (!convert_table_init)
generate_scale_table(table, entries);
if (rm == mask) rtable = table;
if (gm == mask) gtable = table;
if (bm == mask) btable = table;
if (am == mask) atable = table;
}
convert_table_init = true;
}
void dib_reader::read_line(char* outbuf, int row)
{
int line = ((height() < 0) ? row : height() - 1 - row);
const char *linebuf = data() + stride() * line;
for (int i = 0; i < width(); i++)
{
std::size_t pixel_offset = linebuf - data_ptr;
uint32_t pixel;
if (pixel_offset < data_size)
pixel = read_u32_le(pixel_offset);
else
pixel = 0;
char r = static_cast<char>(rtable[((pixel >> rs) & rm)]);
char g = static_cast<char>(gtable[((pixel >> gs) & gm)]);
char b = static_cast<char>(btable[((pixel >> bs) & bm)]);
char a = static_cast<char>((pixel != 0) * 0xFF);
outbuf[0] = r;
outbuf[1] = g;
outbuf[2] = b;
outbuf[3] = a;
linebuf += bpp();
outbuf += 4;
}
}
#ifndef DIB_READER_HPP
#define DIB_READER_HPP
#include "cio/cio.hpp"
#include <cstdint>
#include <utility>
#include "int_pack.hpp"
class dib_reader
{
private:
const char* data_ptr;
std::size_t data_size;
int rs, gs, bs, as;
uint32_t rm, gm, bm, am;
int* rtable = nullptr;
int* gtable = nullptr;
int* btable = nullptr;
int* atable = nullptr;
std::uint16_t read_u16_le(std::size_t offset) const noexcept
{
char a = data_ptr[offset];
char b = data_ptr[offset + 1];
return int_pack_16(a, b);
}
std::uint32_t read_u32_le(std::size_t offset) const noexcept
{
char a = data_ptr[offset];
char b = data_ptr[offset + 1];
char c = data_ptr[offset + 2];
char d = data_ptr[offset + 3];
return int_pack_32(a, b, c, d);
}
public:
enum Compression
{
RGB = 0,
RLE8 = 1,
RLE4 = 2,
BitFields = 3,
JPEG = 4,
PNG = 5
};
// The buffer pointed to by data_ptr must be at least 40 bytes
dib_reader(const char* data_ptr, std::size_t data_size)
: data_ptr(reinterpret_cast<const char*>(data_ptr))
, data_size(data_size)
{ }
bool v2_format() const noexcept { return header_size() >= 52; }
bool v3_format() const noexcept { return header_size() >= 56; }
std::int32_t header_size() const noexcept { return read_u32_le(0); }
std::int32_t width() const noexcept { return read_u32_le(4); }
std::int32_t height() const noexcept { return read_u32_le(8); }
std::int16_t color_planes() const noexcept { return 1; }
std::int16_t depth() const noexcept { return read_u16_le(14); }
Compression compression() const noexcept { return Compression(read_u32_le(16)); }
std::uint32_t image_size() const noexcept { return read_u32_le(20); }
std::size_t palette_size() const noexcept { return (!v2_format() && compression() == BitFields) * 12; }
const char* data() const noexcept { return reinterpret_cast<const char*>(data_ptr + header_size() + palette_size()); }
const char* palette() const noexcept { return reinterpret_cast<const char*>(data_ptr + header_size()); }
const char* raw_data() const noexcept { return reinterpret_cast<const char*>(data_ptr); }
std::int16_t bpp() const noexcept { return static_cast<std::int16_t>(depth() >> 3); }
std::int32_t stride() const noexcept { return width() * bpp() + ((4U - (width() * bpp())) & 3); }
std::uint32_t red_mask() const noexcept {
return v2_format()
? read_u32_le(40)
: read_u32_le(header_size());
}
std::uint32_t green_mask() const noexcept {
return v2_format()
? read_u32_le(44)
: read_u32_le(header_size() + 4);
}
std::uint32_t blue_mask() const noexcept {
return v2_format()
? read_u32_le(48)
: read_u32_le(header_size() + 8);
}
std::uint32_t alpha_mask() const noexcept {
return v3_format()
? read_u32_le(52)
: 0;
}
// Returns a pointer to a human readable string describing what's wrong with the file
// Returns nullptr if the format is acceptable
const char* check_format() const;
void start();
// outbuf must be at least line_size() bytes
void read_line(char* outbuf, int row);
};
#endif // DIB_READER_HPP
#include "pe_reader.hpp"
#include "int_pack.hpp"
#include <cstring>
#include <EASTL/vector.h>
std::uint16_t pe_reader::read_u16_le()
{
char buf[2];
if (file.read(buf, 2) == 2)
{
return int_pack_16(buf[0], buf[1]);
}
return 0;
}
std::uint32_t pe_reader::read_u32_le()
{
char buf[4];
if (file.read(buf, 4) == 4)
{
return int_pack_32(buf[0], buf[1], buf[2], buf[3]);
}
return 0;
}
pe_reader::ResourceDirectory pe_reader::read_ResourceDirectory()
{
return pe_reader::ResourceDirectory{
read_u32_le(),
read_u32_le(),
read_u16_le(),
read_u16_le(),
read_u16_le(),
read_u16_le()
};
}
pe_reader::ResourceDirectoryEntry pe_reader::read_ResourceDirectoryEntry()
{
return pe_reader::ResourceDirectoryEntry{
pe_reader::ResourceType(read_u32_le()),
read_u32_le()
};
}
pe_reader::ResourceDataEntry pe_reader::read_ResourceDataEntry()
{
return pe_reader::ResourceDataEntry{
read_u32_le(),
read_u32_le(),
read_u32_le(),
read_u32_le()
};
}
bool pe_reader::read_header()
{
char buf[8];
file.seek(0x3C);
std::uint16_t pe_header_address = read_u16_le();
file.skip(pe_header_address - 0x3C - 0x02);
file.read(buf, 4);
if (std::memcmp(buf, "PE\0", 4) != 0)
return false;
file.skip(0x02);
std::uint16_t sections = read_u16_le();
file.skip(0x78 - 0x04 + 0x0C);
virtual_address = read_u32_le();
file.skip(0x6C + 0x08 + 0x04);
for (unsigned int i = 0; i < sections; ++i)
{
std::uint32_t check_virtual_address = read_u32_le();
if (check_virtual_address == virtual_address)
{
file.skip(0x04);
root_address = read_u32_le();
break;
}
file.skip(0x24);
}
if (!root_address)
return false;
file.seek(root_address);
ResourceDirectory root_directory = read_ResourceDirectory();
unsigned int directory_entries = root_directory.NumberOfNamedEntries + root_directory.NumberOfIdEntries;
ResourceDirectoryEntry entry;
for (unsigned int i = 0; i < directory_entries; ++i)
{
entry = read_ResourceDirectoryEntry();
if (std::uint32_t(entry.ResourceType_) < 0x80000000 && entry.ResourceType_ == ResourceType::Bitmap)
{
if (entry.SubDirectoryOffset < 0x80000000)
return false;
entry.SubDirectoryOffset -= 0x80000000;
bitmap_directory_entry = entry;
file.skip(8 * (directory_entries - i - 1));
break;
}
}
return true;
}
eastl::map<int, std::pair<std::size_t, std::size_t>> pe_reader::read_bitmap_table()
{
char buf[16];
eastl::map<int, std::pair<std::size_t, std::size_t>> bitmap_pointers;
if (bitmap_directory_entry.ResourceType_ != ResourceType::Bitmap)
return bitmap_pointers;
file.seek(root_address + bitmap_directory_entry.SubDirectoryOffset);
ResourceDirectory bitmap_directory;
file.read(buf, 16);
bitmap_directory.NumberOfNamedEntries = std::uint8_t(buf[12]) + std::uint8_t(buf[13]) * 0x100U;
bitmap_directory.NumberOfIdEntries = std::uint8_t(buf[14]) + std::uint8_t(buf[15]) * 0x100U;
unsigned int directory_entries = bitmap_directory.NumberOfNamedEntries + bitmap_directory.NumberOfIdEntries;
eastl::vector<ResourceDirectoryEntry> bitmap_entries;
bitmap_entries.reserve(directory_entries);
ResourceDirectoryEntry entry;
for (unsigned int i = 0; i < directory_entries; ++i)
{
file.read(buf, 8);
entry.ResourceType_ = ResourceType(std::uint8_t(buf[0]) + std::uint8_t(buf[1]) * 0x100U + std::uint8_t(buf[2]) * 0x10000U + std::uint8_t(buf[3]) * 0x1000000U);
entry.SubDirectoryOffset = std::uint8_t(buf[4]) + std::uint8_t(buf[5]) * 0x100U + std::uint8_t(buf[6]) * 0x10000U + std::uint8_t(buf[7]) * 0x1000000U;
if (entry.SubDirectoryOffset > 0x80000000)
{
entry.SubDirectoryOffset -= 0x80000000;
bitmap_entries.push_back(entry);
}
}
ResourceDataEntry data_entry;
for (auto it = bitmap_entries.begin(); it != bitmap_entries.end(); ++it)
{
file.seek(root_address + it->SubDirectoryOffset + 16);
file.read(buf, 8);
entry.SubDirectoryOffset = std::uint8_t(buf[4]) + std::uint8_t(buf[5]) * 0x100U + std::uint8_t(buf[6]) * 0x10000U + std::uint8_t(buf[7]) * 0x1000000U;
file.seek(root_address + entry.SubDirectoryOffset);
file.read(buf, 16);
data_entry.OffsetToData = std::uint8_t(buf[0]) + std::uint8_t(buf[1]) * 0x100U + std::uint8_t(buf[2]) * 0x10000U + std::uint8_t(buf[3]) * 0x1000000U;
data_entry.Size = std::uint8_t(buf[4]) + std::uint8_t(buf[5]) * 0x100U + std::uint8_t(buf[6]) * 0x10000U + std::uint8_t(buf[7]) * 0x1000000U;
auto entry = std::make_pair(data_entry.OffsetToData - virtual_address + root_address, data_entry.Size);
bitmap_pointers.insert({std::uint32_t(it->ResourceType_), entry});
}
return bitmap_pointers;
}
bool pe_reader::read_resource(char* buf, std::pair<std::size_t, std::size_t> offsets)
{
if (!file.seek(offsets.first))
return false;
if (file.read(buf, offsets.second) != offsets.second)
return false;
return true;
}
#ifndef PE_READER_HPP
#define PE_READER_HPP
#include "cio/cio.hpp"
#include <algorithm>
#include <cstdint>
#include <EASTL/map.h>
#include <utility>
class pe_reader
{
private:
enum class ResourceType : std::uint32_t
{
Cursor = 1,
Bitmap = 2,
Icon = 3,
Menu = 4,
Dialog = 5,
StringTable = 6,
FontDirectory = 7,
Font = 8,
Accelerator = 9,
Unformatted = 10,
MessageTable = 11,
GroupCursor = 12,
GroupIcon = 14,
VersionInformation = 16
};
struct ResourceDirectory
{
std::uint32_t Characteristics;
std::uint32_t TimeDateStamp;
std::uint16_t MajorVersion;
std::uint16_t MinorVersion;
std::uint16_t NumberOfNamedEntries;
std::uint16_t NumberOfIdEntries;
};
struct ResourceDirectoryEntry
{
ResourceType ResourceType_;
std::uint32_t SubDirectoryOffset;
};
struct ResourceDataEntry
{
std::uint32_t OffsetToData;
std::uint32_t Size;
std::uint32_t CodePage;
std::uint32_t unused;
};
cio::stream file;
std::uint32_t root_address = 0;
std::uint32_t virtual_address = 0;
ResourceDirectoryEntry bitmap_directory_entry = {ResourceType{}, 0};
std::uint16_t read_u16_le();
std::uint32_t read_u32_le();
ResourceDirectory read_ResourceDirectory();
ResourceDirectoryEntry read_ResourceDirectoryEntry();
ResourceDataEntry read_ResourceDataEntry();
public:
pe_reader(cio::stream&& file)
: file(std::move(file))
{ }
bool read_header();
eastl::map<int, std::pair<std::size_t, std::size_t>> read_bitmap_table();
bool read_resource(char* buf, std::pair<std::size_t, std::size_t> offsets);
cio::stream& get_file()
{
return file;
}
cio::stream&& finish()
{
return std::move(file);
}
};
#endif // PE_READER_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment