Created
May 7, 2016 10:10
-
-
Save tehsausage/7102104b977c3c6317963ceeb04e29ea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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