Created
October 19, 2016 04:22
-
-
Save lioncash/628bd6ab0e65bfc89e652fd2e296e784 to your computer and use it in GitHub Desktop.
Doom ByteMap font reader | License: Don't care; WTFPL or public domain, etc, etc.
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 "BMF.h" | |
#include <algorithm> | |
#include <array> | |
#include <fstream> | |
#include <iterator> | |
namespace | |
{ | |
constexpr std::array<unsigned char, 4> magic_identifier{0xE1, 0xE6, 0xD5, 0x1A}; | |
bool is_valid_magic(std::istream& in) | |
{ | |
std::array<unsigned char, 4> magic; | |
in.read(reinterpret_cast<char*>(magic.data()), magic.size()); | |
return std::equal(magic.begin(), magic.end(), | |
magic_identifier.begin(), magic_identifier.end()); | |
} | |
std::vector<uint8_t> read_palette_data(uint32_t num_rgb_entries, std::istream& in) | |
{ | |
const uint32_t palette_data_size = num_rgb_entries * 3; | |
std::vector<uint8_t> palette_data(palette_data_size); | |
in.read(reinterpret_cast<char*>(palette_data.data()), palette_data.size()); | |
return palette_data; | |
} | |
std::string read_info_string(std::istream& in) | |
{ | |
uint8_t info_string_size; | |
in.read(reinterpret_cast<char*>(&info_string_size), sizeof(info_string_size)); | |
if (!in) | |
return {}; | |
std::string info_string(info_string_size, '\0'); | |
in.read(&info_string[0], info_string.size()); | |
return info_string; | |
} | |
BMF::bitmap_vector read_character_data(std::istream& in) | |
{ | |
uint16_t num_characters_in_font; | |
in.read(reinterpret_cast<char*>(&num_characters_in_font), sizeof(num_characters_in_font)); | |
if (!in) | |
return {}; | |
BMF::bitmap_vector character_data(num_characters_in_font); | |
std::copy_n(std::istream_iterator<BMF::BitmapCharacter>(in), | |
num_characters_in_font, | |
character_data.begin()); | |
return character_data; | |
} | |
} // Anonymous namespace | |
BMF::BMF(const std::string& file_path) | |
{ | |
read_file(file_path); | |
} | |
bool BMF::is_valid() const noexcept | |
{ | |
return m_valid; | |
} | |
void BMF::read_file(const std::string& file_path) | |
{ | |
std::ifstream file(file_path, std::ifstream::binary); | |
if (!file) | |
return; | |
file >> *this; | |
if (file.good()) | |
m_valid = true; | |
} | |
uint8_t BMF::version() const noexcept | |
{ | |
return m_version; | |
} | |
uint8_t BMF::line_height() const noexcept | |
{ | |
return m_line_height; | |
} | |
int8_t BMF::size_over_base_line() const noexcept | |
{ | |
return m_size_over_base_line; | |
} | |
int8_t BMF::size_under_base_line() const noexcept | |
{ | |
return m_size_under_base_line; | |
} | |
int8_t BMF::add_space_after_each_char() const noexcept | |
{ | |
return m_add_space_after_each_char; | |
} | |
int8_t BMF::size_inner() const noexcept | |
{ | |
return m_size_inner; | |
} | |
uint8_t BMF::num_colors_used() const noexcept | |
{ | |
return m_num_colors_used; | |
} | |
uint8_t BMF::highest_used_color_attribute() const noexcept | |
{ | |
return m_highest_used_color_attribute; | |
} | |
uint8_t BMF::num_rgb_entries() const noexcept | |
{ | |
return m_num_rgb_entries; | |
} | |
const std::vector<uint8_t>& BMF::font_palette() const noexcept | |
{ | |
return m_font_palette_rgb; | |
} | |
const std::string& BMF::info_string() const noexcept | |
{ | |
return m_info_string; | |
} | |
const BMF::bitmap_vector& BMF::character_data() const noexcept | |
{ | |
return m_character_data; | |
} | |
std::ostream& operator<<(std::ostream& out, const BMF& bmf) | |
{ | |
// Damn | |
out.write(reinterpret_cast<const char*>(magic_identifier.data()), magic_identifier.size()); | |
out.write(reinterpret_cast<const char*>(&bmf.m_version), sizeof(bmf.m_version)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_line_height), sizeof(bmf.m_line_height)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_size_over_base_line), sizeof(bmf.m_size_over_base_line)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_size_under_base_line), sizeof(bmf.m_size_under_base_line)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_add_space_after_each_char), sizeof(bmf.m_add_space_after_each_char)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_size_inner), sizeof(bmf.m_size_inner)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_num_colors_used), sizeof(bmf.m_num_colors_used)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_highest_used_color_attribute), sizeof(bmf.m_highest_used_color_attribute)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_reserved), sizeof(bmf.m_reserved)); | |
out.write(reinterpret_cast<const char*>(&bmf.m_num_rgb_entries), sizeof(bmf.m_num_rgb_entries)); | |
out.write(reinterpret_cast<const char*>(bmf.m_font_palette_rgb.data()), bmf.m_font_palette_rgb.size()); | |
const auto info_size = static_cast<uint8_t>(bmf.m_info_string.size()); | |
out.write(reinterpret_cast<const char*>(&info_size), sizeof(info_size)); | |
out.write(bmf.m_info_string.c_str(), bmf.m_info_string.size()); | |
const auto data_size = static_cast<uint16_t>(bmf.m_character_data.size()); | |
out.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size)); | |
std::copy(bmf.m_character_data.begin(), | |
bmf.m_character_data.end(), | |
std::ostream_iterator<BMF::BitmapCharacter>(out)); | |
return out; | |
} | |
std::istream& operator>>(std::istream& in, BMF& bmf) | |
{ | |
bmf.m_valid = is_valid_magic(in); | |
if (!bmf.m_valid) | |
{ | |
in.setstate(std::ios::failbit); | |
return in; | |
} | |
// >Try to wake up | |
// >Can't wake up | |
in.read(reinterpret_cast<char*>(&bmf.m_version), sizeof(bmf.m_version)); | |
in.read(reinterpret_cast<char*>(&bmf.m_line_height), sizeof(bmf.m_line_height)); | |
in.read(reinterpret_cast<char*>(&bmf.m_size_over_base_line), sizeof(bmf.m_size_over_base_line)); | |
in.read(reinterpret_cast<char*>(&bmf.m_size_under_base_line), sizeof(bmf.m_size_under_base_line)); | |
in.read(reinterpret_cast<char*>(&bmf.m_add_space_after_each_char), sizeof(bmf.m_add_space_after_each_char)); | |
in.read(reinterpret_cast<char*>(&bmf.m_size_inner), sizeof(bmf.m_size_inner)); | |
in.read(reinterpret_cast<char*>(&bmf.m_num_colors_used), sizeof(bmf.m_num_colors_used)); | |
in.read(reinterpret_cast<char*>(&bmf.m_highest_used_color_attribute), sizeof(bmf.m_highest_used_color_attribute)); | |
in.read(reinterpret_cast<char*>(&bmf.m_reserved), sizeof(bmf.m_reserved)); | |
in.read(reinterpret_cast<char*>(&bmf.m_num_rgb_entries), sizeof(bmf.m_num_rgb_entries)); | |
if (!in) | |
return in; | |
bmf.m_font_palette_rgb = read_palette_data(bmf.m_num_rgb_entries, in); | |
if (!in) | |
return in; | |
bmf.m_info_string = read_info_string(in); | |
if (!in) | |
return in; | |
bmf.m_character_data = read_character_data(in); | |
return in; | |
} | |
std::ostream& operator<<(std::ostream& out, const BMF::BitmapCharacter& character) | |
{ | |
// Death, take me now; thanks in advance. | |
out.write(&character.character, sizeof(character.character)); | |
out.write(reinterpret_cast<const char*>(&character.width), sizeof(character.width)); | |
out.write(reinterpret_cast<const char*>(&character.height), sizeof(character.height)); | |
out.write(reinterpret_cast<const char*>(&character.horizontal_offset), sizeof(character.horizontal_offset)); | |
out.write(reinterpret_cast<const char*>(&character.vertical_offset), sizeof(character.vertical_offset)); | |
out.write(reinterpret_cast<const char*>(&character.horizontal_cursor_shift), sizeof(character.horizontal_cursor_shift)); | |
out.write(reinterpret_cast<const char*>(character.data.data()), character.data.size()); | |
return out; | |
} | |
std::istream& operator>>(std::istream& in, BMF::BitmapCharacter& character) | |
{ | |
// Wew lad | |
in.read(&character.character, sizeof(character.character)); | |
in.read(reinterpret_cast<char*>(&character.width), sizeof(character.width)); | |
in.read(reinterpret_cast<char*>(&character.height), sizeof(character.height)); | |
in.read(reinterpret_cast<char*>(&character.horizontal_offset), sizeof(character.horizontal_offset)); | |
in.read(reinterpret_cast<char*>(&character.vertical_offset), sizeof(character.vertical_offset)); | |
in.read(reinterpret_cast<char*>(&character.horizontal_cursor_shift), sizeof(character.horizontal_cursor_shift)); | |
if (!in) | |
return in; | |
character.data.resize(character.width * character.height); | |
in.read(reinterpret_cast<char*>(character.data.data()), character.data.size()); | |
return in; | |
} |
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
#pragma once | |
#include <cstdint> | |
#include <iosfwd> | |
#include <string> | |
#include <vector> | |
/// Represents a ByteMap v1.1 font | |
class BMF final | |
{ | |
public: | |
struct BitmapCharacter final | |
{ | |
char character = '\0'; | |
uint8_t width = 0; | |
uint8_t height = 0; | |
int8_t horizontal_offset = 0; | |
int8_t vertical_offset = 0; | |
uint8_t horizontal_cursor_shift = 0; | |
std::vector<uint8_t> data; | |
}; | |
using bitmap_vector = std::vector<BitmapCharacter>; | |
using iterator = bitmap_vector::iterator; | |
using const_iterator = bitmap_vector::const_iterator; | |
using reverse_iterator = bitmap_vector::reverse_iterator; | |
using const_reverse_iterator = bitmap_vector::const_reverse_iterator; | |
/** | |
* Default constructor | |
* | |
* @note read_file must be used to populate this instance with data. | |
*/ | |
BMF() = default; | |
/** | |
* Constructor | |
* | |
* @param file_path Path to the BMF file to open. | |
*/ | |
explicit BMF(const std::string& file_path); | |
/** | |
* Whether or not this BMF instance is valid. | |
* | |
* A BMF instance is invalid if any errors occurred | |
* while reading the BMF file. | |
*/ | |
bool is_valid() const noexcept; | |
/** | |
* Reads the contents of a BMF file into this instance. | |
* This allows for two-step construction. | |
* | |
* @param file_path Path the the BMF file to read. | |
*/ | |
void read_file(const std::string& file_path); | |
iterator begin() noexcept { return m_character_data.begin(); } | |
const_iterator begin() const noexcept { return m_character_data.begin(); } | |
const_iterator cbegin() const noexcept { return m_character_data.cbegin(); } | |
iterator end() noexcept { return m_character_data.end(); } | |
const_iterator end() const noexcept { return m_character_data.end(); } | |
const_iterator cend() const noexcept { return m_character_data.cend(); } | |
reverse_iterator rbegin() noexcept { return m_character_data.rbegin(); } | |
const_reverse_iterator rbegin() const noexcept { return m_character_data.rbegin(); } | |
const_reverse_iterator crbegin() const noexcept { return m_character_data.crbegin(); } | |
reverse_iterator rend() noexcept { return m_character_data.rend(); } | |
const_reverse_iterator rend() const noexcept { return m_character_data.rend(); } | |
const_reverse_iterator crend() const noexcept { return m_character_data.crend(); } | |
uint8_t version() const noexcept; | |
uint8_t line_height() const noexcept; | |
int8_t size_over_base_line() const noexcept; | |
int8_t size_under_base_line() const noexcept; | |
int8_t add_space_after_each_char() const noexcept; | |
int8_t size_inner() const noexcept; | |
uint8_t num_colors_used() const noexcept; | |
uint8_t highest_used_color_attribute() const noexcept; | |
uint8_t num_rgb_entries() const noexcept; | |
const std::vector<uint8_t>& font_palette() const noexcept; | |
const std::string& info_string() const noexcept; | |
const bitmap_vector& character_data() const noexcept; | |
private: | |
friend std::ostream& operator<<(std::ostream&, const BMF&); | |
friend std::istream& operator>>(std::istream&, BMF&); | |
bool m_valid = false; | |
uint8_t m_version = 0; | |
uint8_t m_line_height = 0; | |
int8_t m_size_over_base_line = 0; | |
int8_t m_size_under_base_line = 0; | |
int8_t m_add_space_after_each_char = 0; | |
int8_t m_size_inner = 0; | |
uint8_t m_num_colors_used = 0; | |
uint8_t m_highest_used_color_attribute = 0; | |
uint32_t m_reserved = 0; | |
uint8_t m_num_rgb_entries = 0; | |
std::vector<uint8_t> m_font_palette_rgb; | |
std::string m_info_string; | |
bitmap_vector m_character_data; | |
}; | |
std::ostream& operator<<(std::ostream&, const BMF&); | |
std::istream& operator>>(std::istream&, BMF&); | |
std::ostream& operator<<(std::ostream&, const BMF::BitmapCharacter&); | |
std::istream& operator>>(std::istream&, BMF::BitmapCharacter&); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment