Program Listing for File fonts.cpp

Return to documentation for file (blam\components\content\fonts\fonts.cpp)

#include "fonts.h"

#include <fstream>

#include "components/rendering/rendering.h"
#include "components/rendering/directx11/direct2d/drawing.h"
#include "components/rendering/directx11/dw_font_loader/FontLoader.h"
#include "components/3rdparty/rapidxml/rapidxml.hpp"
#include "components/core/utils/converters/converters.h"
#include "components/core/utils/string/string.h"
#include "components/core/utils/io/io.h"
#include "components/core/logger/logger.h"
#include "components/core/error/error.h"
#include "components/core/cache/cache.h"
#include "components/core/config/config.h"
#include "res/mc/errors.h"

using namespace Blam::Content::Fonts;
using namespace rapidxml;

std::map<std::string, Font> font_list;

bool Blam::Content::Fonts::FontExists(std::string id)
{
    std::map<std::string, Font>::iterator it;

    for (it = font_list.begin(); it != font_list.end(); it++)
    {
        if (it->first == id)
        {
            return true;
        }
    }

    return false;
}


HRESULT LoadGlyph(char character, int width, int height, std::string bitmap_path, std::string format, Font* font);
bool resolve_glyph_character(std::string xml_glyph, char* character);

HRESULT Blam::Content::Fonts::LoadFont(std::string path)
{
    HRESULT hr = S_OK;

    Font new_font;

    new_font.path = path;

    std::string xml_path = path + "/" + FONTINFO_FILENAME;
    std::string bin_path = path + FONT_PACKAGE_EXTENSION;

    if (Blam::Utils::IO::file_exists(xml_path))
    {
        new_font.is_font_package = false;

        new_font.file_path = xml_path;

        //load as XML unpackaged font
        xml_document<> font_xml_file;

        //read file into vector buffer
        std::ifstream font_file(new_font.file_path);
        std::vector<char> buffer((std::istreambuf_iterator<char>(font_file)), std::istreambuf_iterator<char>());
        buffer.push_back('\0');

        //load document
        font_xml_file.parse<0>(&buffer[0]);

        //get root node
        xml_node<>* root_node = font_xml_file.first_node("fontinfo");

        Blam::Logger::LogEvent("### begin dumping font XML file text to console ###");
        Blam::Logger::LogEvent(std::string(buffer.begin(), buffer.end()));
        Blam::Logger::LogEvent("### end dumping font XML file text to console ###");

        bool valid_font = true;

        xml_node<>* properties_node = root_node->first_node("properties");

        //xml attributes
        xml_node<>* id_element = properties_node->first_node("id");
        xml_node<>* size_element = properties_node->first_node("size");
        xml_node<>* charspacing_element = properties_node->first_node("charspacing");
        xml_node<>* monospace_element = properties_node->first_node("monospace");
        xml_node<>* monospace_width = properties_node->first_node("monowidth");
        xml_node<>* space_width = properties_node->first_node("spacewidth");
        xml_node<>* truetype = properties_node->first_node("truetype");
        xml_node<>* ttf_path = properties_node->first_node("ttf_path");
        xml_node<>* ttf_name = properties_node->first_node("ttf_name");

        //id
        if (id_element)
        {
            if (!FontExists(id_element->value()))
            {
                new_font.id = id_element->value();
            }
            else
            {
                valid_font = false;
                Blam::Logger::LogEvent("### WARNING tried to load a font with duplicate id " + std::string(id_element->value()) + ". font will NOT be loaded!", true, WSV_ERROR);
            }
        }
        else
        {
            valid_font = false;
            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' fontinfo is missing id element! font will NOT be loaded!", true, WSV_ERROR);
        }

        // truetype element
        if (truetype)
        {
            new_font.is_truetype = Blam::Converters::StringToBool(truetype->value());
        }
        else
        {
            new_font.is_truetype = false;
        }

        // if truetype is enabled, ensure ttf file exists
        if (new_font.is_truetype)
        {
            if (ttf_path)
            {
                new_font.ttf_path = path + "/" + ttf_path->value();

                if (!Blam::Utils::IO::file_exists(new_font.ttf_path))
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' ttf_path element referenced an invalid file. font will NOT be loaded!", WSV_WARNING);
                    valid_font = false;
                }
            }
            else
            {
                Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' ttf_path element was not found, but truetype was set to true. font will NOT be loaded!", WSV_WARNING);
                valid_font = false;
            }

            if (ttf_name)
            {
                new_font.ttf_name = ttf_name->value();
            }
            else
            {
                Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' ttf_name element was not found, but truetype was set to true. substituting id for font name, this may cause the font to fail to display correctly", WSV_WARNING);
            }
        }
        else
        {
            //get size value
            if (size_element)
            {
                try
                {
                    new_font.size = std::stoi(size_element->value());
                }
                catch (std::invalid_argument ex)
                {
                    valid_font = false;
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' size element (" + size_element->value() + ") was in a bad format (std::invalid_argument thrown). font will NOT be loaded!", true, WSV_ERROR);
                }
                catch (std::out_of_range ex)
                {
                    valid_font = false;
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' size element (" + size_element->value() + ") caused an overflow (std::out_of_range thrown). font will NOT be loaded!", true, WSV_ERROR);
                }
            }
            else
            {
                valid_font = false;
                Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' fontinfo is missing size element! font will NOT be loaded!", true, WSV_ERROR);
            }

            //get charspacing value
            if (charspacing_element)
            {
                try
                {
                    new_font.charspacing = std::stoi(charspacing_element->value());
                }
                catch (std::invalid_argument ex)
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' charspacing element (" + charspacing_element->value() + ") was in a bad format (std::invalid_argument thrown). defaulting charspacing to 0, this may cause unexpected results.", true, WSV_WARNING);
                    new_font.charspacing = 0;
                }
                catch (std::out_of_range ex)
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' charspacing element (" + charspacing_element->value() + ") caused an overflow (std::out_of_range thrown). defaulting charspacing to 0, this may cause unexpected results.", true, WSV_WARNING);
                    new_font.charspacing = 0;
                }
            }
            else
            {
                Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' fontinfo is missing charspacing element! defaulting to 0, this may cause unexpected results.", true, WSV_WARNING);
                new_font.charspacing = 0;
            }

            //get monospaced value
            if (monospace_element)
            {
                std::string monospace_str = Blam::Utils::String::to_lower(monospace_element->value());

                if (monospace_str == "false" || monospace_str == "0" || monospace_str == "no")
                {
                    new_font.monospaced = false;
                }
                else if (monospace_str == "true" || monospace_str == "1" || monospace_str == "yes")
                {
                    new_font.monospaced = true;
                }
                else
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' fontinfo has invalid monospace element! defaulting to FALSE, this may cause unexpected results.", true, WSV_WARNING);
                    new_font.monospaced = false;
                }
            }
            else
            {
                Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' fontinfo is missing monospace element! defaulting to FALSE, this may cause unexpected results.", true, WSV_WARNING);
                new_font.monospaced = false;
            }

            bool use_spacewidth_element = true;

            //get monowidth value if monospaced
            if (monospace_width)
            {
                try
                {
                    new_font.mono_width = std::stoi(monospace_width->value());
                    new_font.space_width = new_font.mono_width;
                    use_spacewidth_element = false;
                }
                catch (std::invalid_argument ex)
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' monowidth element (" + monospace_width->value() + ") was in a bad format (std::invalid_argument thrown). setting monospace to FALSE, this may cause unexpected results.", true, WSV_WARNING);
                    new_font.mono_width = 0;
                    new_font.monospaced = false;
                }
                catch (std::out_of_range ex)
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' monowidth element (" + monospace_width->value() + ") caused an overflow (std::out_of_range thrown). setting monospace to FALSE, this may cause unexpected results.", true, WSV_WARNING);
                    new_font.mono_width = 0;
                    new_font.monospaced = false;
                }
            }
            else
            {
                if (new_font.monospaced)
                {
                    Blam::Logger::LogEvent("### ERROR unpackaged font at '" + path + "' fontinfo is missing monowidth element, but has monospace set to true. setting monospace to FALSE, this may cause unexpected results.", true, WSV_ERROR);
                }

                new_font.monospaced = false;
                new_font.mono_width = 0;
            }

            if (use_spacewidth_element)
            {
                if (space_width)
                {
                    try
                    {
                        new_font.space_width = std::stoi(space_width->value());
                    }
                    catch (std::invalid_argument ex)
                    {
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' spacewidth element (" + space_width->value() + ") was in a bad format (std::invalid_argument thrown). setting space_width to 6, this may cause unexpected results.", true, WSV_WARNING);
                        new_font.space_width = 6;
                    }
                    catch (std::out_of_range ex)
                    {
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' spacewidth element (" + space_width->value() + ") caused an overflow (std::out_of_range thrown). setting space_width to 6, this may cause unexpected results.", true, WSV_WARNING);
                        new_font.space_width = 6;
                    }
                }
                else
                {
                    Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' spacewidth element was not found. setting space_width to 6, this may cause unexpected results.", true, WSV_ERROR);
                    new_font.space_width = 6;
                }
            }
            else
            {
                new_font.space_width = 0;
            }
        }

        if (valid_font)
        {
            if (new_font.is_truetype)
            {
                using namespace BlamRendering::DirectX::D2D::MF;

                HRESULT hr = BlamRendering::DirectX::D2D::LoadFontFromFile(new_font.id, new_font.ttf_path);
            }
            else
            {
                xml_node<>* glyphs_node = root_node->first_node("glyphs");

                for (xml_node<>* glyph_node = glyphs_node->first_node("glyph"); glyph_node; glyph_node = glyph_node->next_sibling())
                {
                    //<glyph char="a" width="16" height="16" bitmap="./glyphs/a.png" format="PNG"></glyph>

                    xml_attribute<char>* character_attr = glyph_node->first_attribute("char");
                    xml_attribute<char>* width_attr = glyph_node->first_attribute("width");
                    xml_attribute<char>* height_attr = glyph_node->first_attribute("height");
                    xml_attribute<char>* bitmap_attr = glyph_node->first_attribute("bitmap");
                    xml_attribute<char>* format_attr = glyph_node->first_attribute("format");

                    char character = NULL;
                    int width = 0;
                    int height = 0;
                    std::string bitmap_path = "";
                    std::string format = "";

                    bool valid_glyph = true;

                    //get character attribute
                    if (character_attr)
                    {
                        char* e = character_attr->value();

                        bool resolve_result = resolve_glyph_character(e, &character);

                        if (!resolve_result)
                        {
                            valid_glyph = false;
                            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph an invalid character attribute (" + character_attr->value() + ")! glyph will NOT be loaded!", true, WSV_ERROR);
                        }
                    }
                    else
                    {
                        valid_glyph = false;
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph with no character attribute! glyph will NOT be loaded!", true, WSV_ERROR);
                    }

                    //get glyph width attribute
                    if (width_attr)
                    {
                        try
                        {
                            width = std::stoi(width_attr->value());
                        }
                        catch (std::invalid_argument ex)
                        {
                            valid_glyph = false;
                            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' glyph width (" + width_attr->value() + ") attribute was in a bad format (std::invalid_argument thrown). glyph will NOT be loaded!", true, WSV_ERROR);
                        }
                        catch (std::out_of_range ex)
                        {
                            valid_glyph = false;
                            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' glyph width (" + width_attr->value() + ") attribute caused an overflow (std::out_of_range thrown). glyph will NOT be loaded!", true, WSV_ERROR);
                        }
                    }
                    else
                    {
                        valid_glyph = false;
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph with no width attribute! glyph will NOT be loaded!", true, WSV_ERROR);
                    }

                    //get glyph height attribute
                    if (height_attr)
                    {
                        try
                        {
                            height = std::stoi(height_attr->value());
                        }
                        catch (std::invalid_argument ex)
                        {
                            valid_glyph = false;
                            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' glyph height attribute (" + height_attr->value() + ") was in a bad format (std::invalid_argument thrown). glyph will NOT be loaded!", true, WSV_ERROR);
                        }
                        catch (std::out_of_range ex)
                        {
                            valid_glyph = false;
                            Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' glyph height attribute (" + height_attr->value() + ") caused an overflow (std::out_of_range thrown). glyph will NOT be loaded!", true, WSV_ERROR);
                        }
                    }
                    else
                    {
                        valid_glyph = false;
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph with no height attribute! glyph will NOT be loaded!", true, WSV_ERROR);
                    }

                    //get bitmap path attribute
                    if (bitmap_attr)
                    {
                        const char* what_the_fuck = bitmap_attr->value();

                        bitmap_path = std::string(what_the_fuck);
                    }
                    else
                    {
                        valid_glyph = false;
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph with no bitmap attribute! glyph will NOT be loaded!", true, WSV_ERROR);
                    }

                    //get file format attribute
                    if (format_attr)
                    {
                        format = *format_attr->value();
                    }
                    else
                    {
                        valid_glyph = false;
                        Blam::Logger::LogEvent("### WARNING unpackaged font at '" + path + "' contains a glyph with no format attribute! glyph will NOT be loaded!", true, WSV_ERROR);
                    }

                    //if the glyph is valid, try and load as bitmap
                    if (valid_glyph)
                    {
                        HRESULT glyph_hr = LoadGlyph(character, width, height, bitmap_path, format, &new_font);

                        if (SUCCEEDED(glyph_hr))
                        {
                            int e = 0;
                        }
                        else
                        {
                            int e = 0;
                        }
                    }
                    else
                    {
                        hr = BL_S_FONT_LOADED_OK_BAD_GLYPHS;
                        Blam::Logger::LogEvent("### ERROR one or more required glyph properties were invalid. check the <glpyhs> section of your fontinfo.xml at: '" + path + "'", true, WSV_ERROR);
                    }
                }
            }
        }
        else
        {
            hr = BL_E_FONT_INVALID_DATA;
            Blam::Logger::LogEvent("### ERROR one or more required fontinfo properties were invalid. check your fontinfo.xml at: '" + path + "'", true, WSV_ERROR);
        }

        Blam::Logger::LogEvent("loaded uncompressed font '" + new_font.id + "' (" + path + "), contains " + std::to_string(new_font.glyph_list.size()) + " glyphs" , true, WSV_NONE);
        font_list.insert(std::pair<std::string, Font>(new_font.id, new_font));
    }
    else if (Blam::Utils::IO::file_exists(bin_path))
    {
        new_font.is_font_package = true;

        // Load as font package
        std::ifstream font_package;
        font_package.open(bin_path, std::ios::in | std::ios::binary);

        char header[0x400];

        font_package.read(header, 0x400);

        // Ensure we're actually reading a blamite font package, and someone isnt trying to load a blam! package
        if (memcmp(&header[0x0], "BFNT", 0x4) == 0)
        {
            // continue assuming the file is of a valid type
            Blam::Logger::LogEvent("font package header seems valid - continuing");

            // Header properties
            char package_engine_version[0x20];

            int ttf_file_length = 0;
            int ttf_file_offset = 0;

            memcpy(&new_font.package_version, &header[0x26], 0x2);
            memcpy(&package_engine_version, &header[0x11C], 0x1F);
            package_engine_version[0x1F] = NULL; //ensure string is null-terminated

            if (new_font.package_version > FONT_PACKAGE_VERSION)
            {
                Blam::Logger::LogEvent("### WARNING font package at " + path + " has version newer than what the engine supports, this may cause unexpected results", WSV_WARNING);
            }

            // Load font properties after ensuring that package version is ok
            {
                memcpy(&ttf_file_length, &header[0x170], 0x4);
                memcpy(&ttf_file_offset, &header[0x180], 0x4);

                char font_id[0x20];
                memcpy(&font_id, &header[0x17F], 0x1F);
                font_id[0x1F] = NULL;
                new_font.id = std::string(font_id);

                memcpy(&new_font.is_truetype, &header[0x1A0], 0x1);
                memcpy(&new_font.monospaced, &header[0x1A1], 0x1);

                memcpy(&new_font.size, &header[0x1A2], 0x2);
                memcpy(&new_font.mono_width, &header[0x1A4], 0x2);
                memcpy(&new_font.charspacing, &header[0x1A6], 0x2);
                memcpy(&new_font.space_width, &header[0x1A8], 0x2);

                char ttf_display_name[0x20];
                memcpy(&ttf_display_name, &header[0x1AA], 0x1F);
                ttf_display_name[0x1F] = NULL;
                new_font.ttf_name = std::string(ttf_display_name);
                new_font.package_engine_version = std::string(package_engine_version);
            }

            if (new_font.is_truetype)
            {
                memcpy(&new_font.ttf_length, &header[0x170], 0x4);
                memcpy(&new_font.ttf_offset, &header[0x174], 0x4);

                void* font_data = malloc(new_font.ttf_length);

                font_package.seekg(new_font.ttf_offset);

                font_package.read((char*)font_data, new_font.ttf_length);

                std::string cached_file_path = Blam::Cache::WriteFileToCache(font_data, new_font.ttf_length, new_font.id);

                free(font_data);

                BlamRendering::DirectX::D2D::LoadFontFromFile(new_font.id, cached_file_path);
            }
            else
            {
                std::vector<char> char_list;

                // Read character list
                {
                    char character_list[0x40000];

                    font_package.read(character_list, 0x40000);

                    // this is how wchar_t is stored in memory
                    // 65 00

                    for (int i = 0; i < 0x40000; i = i + 0x4)
                    {
                        char glyph[0x4];

                        memcpy(&glyph, &character_list[i], 0x4);

                        if (glyph[1] != 0x0 || glyph[2] == 0x0 || glyph[3] != 0x0)
                        {
                            Blam::Logger::LogEvent("### WARNING unicode and wide characters are not supported at this time", WSV_WARNING);
                        }

                        if (glyph[0] == 0x0 && i != 0x0)
                        {
                            //if we reach null and we aren't at the start of the list, we can assume that character list has ended
                            break;
                        }

                        char_list.push_back(glyph[0x0]);
                    }
                }

                // Read index table
                {
                    const int index_size = 0x10;

                    for (int i = 0; i < char_list.size(); i++)
                    {
                        char index_entry[index_size];
                        byte format;

                        int pos = font_package.tellg();

                        font_package.read(index_entry, index_size);

                        FontGlyph glyph;

                        glyph.character = char_list.at(i);

                        memcpy(&glyph.width, &index_entry[0x0], 0x2);
                        memcpy(&glyph.height, &index_entry[0x2], 0x2);
                        memcpy(&glyph.data_length, &index_entry[0x4], 0x4);
                        memcpy(&format, &index_entry[0x9], 0x1);

                        if (format == 0x0)
                        {
                            glyph.format = FontGlyphFormat::PNG;
                        }
                        else
                        {
                            Blam::Logger::LogEvent("### ERROR unreciognized glyph format 0x" + std::to_string(format), WSV_ERROR);
                        }

                        memcpy(&glyph.data_offset, &index_entry[0xC], 0x4);

                        new_font.glyph_list.insert(std::pair<char, FontGlyph>(glyph.character, glyph));
                    }
                }

                // Read bitmap data
                {
                    std::map<char, FontGlyph>::iterator it;

                    for (it = new_font.glyph_list.begin(); it != new_font.glyph_list.end(); it++)
                    {
                        FontGlyph glyph = it->second;

                        glyph.bitmap_data = malloc(glyph.data_length);

                        font_package.seekg(glyph.data_offset);

                        font_package.read((char*)glyph.bitmap_data, glyph.data_length);

                        BlamRendering::DirectX::WIC::CreateWICBitmapFromMemory(glyph.bitmap_data, glyph.data_length, &glyph.bitmap);

                        it->second = glyph;
                    }
                }
            }

            Blam::Logger::LogEvent("loaded font package '" + new_font.id + "' (" + path + "), contains " + std::to_string(new_font.glyph_list.size()) + " glyphs", true, WSV_NONE);
            font_list.insert(std::pair<std::string, Font>(new_font.id, new_font));
        }
        else
        {
            Blam::Logger::LogEvent("### ERROR font package header appears to be invalid - aborting font load", WSV_ERROR);
            hr = E_FAIL;
        }

        font_package.close();
    }
    else
    {
        Blam::Logger::LogEvent("### ERROR tried to load font at '" + path + "', could not find file", true, WSV_ERROR);
    }

    return hr;
}

FontGlyphFormat GetGlyphFormatFromString(std::string format)
{
    if (format == "PNG")
    {
        return FontGlyphFormat::PNG;
    }
    else
    {
        return FontGlyphFormat::PNG;
    }
}

HRESULT LoadGlyph(char character, int width, int height, std::string bitmap_path, std::string format, Font* font)
{
    ID2D1DeviceContext* target = BlamRendering::DirectX::D2D::GetD2DRenderTarget();
    ID2D1Factory* factory = BlamRendering::DirectX::D2D::GetD2DFactory();

    //create a new glyph
    FontGlyph glyph;

    glyph.width = width;
    glyph.height = height;
    //BlamRendering::DirectX::WIC::CreateD2DBitmapFromResource(IDB_THEME_DEFAULT_ICON, "PNG", width, height, &glyph.bitmap);
    glyph.character = character;
    glyph.format = GetGlyphFormatFromString(format);
    glyph.path = bitmap_path;
    glyph.file_path = font->path + "/" + bitmap_path;

    bool file_exists = Blam::Utils::IO::file_exists(glyph.file_path);

    if (file_exists)
    {
        HRESULT hr = BlamRendering::DirectX::WIC::CreateWICBitmapFromFile(Blam::Converters::ConvertStringToWstring(glyph.file_path), &glyph.bitmap);

        if (SUCCEEDED(hr))
        {
            font->glyph_list.insert(std::pair<char, FontGlyph>(character, glyph));

            return S_OK;
        }
        else
        {
            Blam::Logger::LogEvent("### ERROR unpackaged font at '" + font->path + "' glyph " + glyph.character + "' encountered an error while trying to load glyph image as ID2D1Bitmap:", true, WSV_ERROR);
            Blam::Logger::LogEvent("" + std::string(Blam::Error::GetStringFromHResult(hr)), true, WSV_ERROR);
            return hr;
        }
    }
    else
    {
        Blam::Logger::LogEvent("### ERROR unpackaged font at '" + font->path + "' glyph " + glyph.character + "' points to a file image that does not exist! glyph will NOT be loaded!", true, WSV_ERROR);
        return BL_W_GLYPH_FILE_NOT_FOUND;
    }
}

bool resolve_glyph_character(std::string xml_glyph, char* character)
{
    std::string glyph_str = "";

    if (Blam::Utils::String::starts_with_ci(xml_glyph, "lower_"))
    {
        //handle as lowercase letter
        std::string letter = Blam::Utils::String::split(xml_glyph, "_").at(1);

        glyph_str = Blam::Utils::String::to_lower(letter);
    }
    else if (Blam::Utils::String::starts_with_ci(xml_glyph, "0x"))
    {
        //handle as special character defined by hex code
        return Blam::Converters::HexStringToChar(xml_glyph, character);
    }
    else
    {
        glyph_str = xml_glyph;
    }

    //read string as a regular character
    if (glyph_str.length() == 1)
    {
        //char c = glyph_str.c_str()[0];

        memcpy(character, &glyph_str.at(0), 1);

        //character = &glyph_str.at(0);

        /*
        #############################################
        # maybe the string gets nuked and as such it fucks itself completely
        # idk man you figure it out
        #############################################
        */


        //memcpy(character, glyph_str.at(0), 1);

        //character = &c;

        return true;
    }
    else
    {
        return false;
    }
}

Font* Blam::Content::Fonts::GetFont(std::string id)
{
    std::map<std::string, Font>::iterator iter = font_list.find(id);
    if (iter != font_list.end())
    {
        return &iter->second;
    }
    else
    {
        return nullptr;
    }
}

FontGlyph* Blam::Content::Fonts::GetFontGlyph(Font* font, char glyph)
{
    std::map<char, FontGlyph>::iterator iter = font->glyph_list.find(glyph);

    if (iter != font->glyph_list.end())
    {
        return &iter->second;
    }
    else
    {
        return nullptr;
    }
}

std::map<std::string, Font>* Blam::Content::Fonts::GetFontList()
{
    return &font_list;
}

void CleanupFontData(std::string id)
{
    Font* font = Blam::Content::Fonts::GetFont(id);

    if (font)
    {
        std::map<char, FontGlyph>::iterator it;

        if (font->is_font_package)
        {
            for (it = font->glyph_list.begin(); it != font->glyph_list.end(); it++)
            {
                free(it->second.bitmap_data);
            }
        }

        font->glyph_list.clear();
    }
}

HRESULT Blam::Content::Fonts::ReloadFont(std::string id)
{
    Font* font = Blam::Content::Fonts::GetFont(id);

    if (font)
    {
        std::string path = font->path;

        CleanupFontData(id);
        font_list.erase(id);

        return Blam::Content::Fonts::LoadFont(path);
    }
    else
    {
        return 0;
    }
}

void Blam::Content::Fonts::Cleanup()
{
    std::map<std::string, Font>::iterator it;

    for (it = font_list.begin(); it != font_list.end(); it++)
    {
        CleanupFontData(it->first);
    }

    font_list.clear();
}

void Blam::Content::Fonts::LoadAllFonts()
{
    std::string fonts_dir = Blam::Config::GetConfig()->GetString("fonts_dir");

    Blam::Utils::IO::ValidatePath(fonts_dir);

    if (Blam::Config::GetConfig()->GetBoolean("use_font_table"))
    {
        if (Blam::Utils::IO::file_exists(fonts_dir + "/font_table.txt"))
        {
            std::vector<std::string> fonts_to_load = Blam::Utils::IO::GetFileContentsAsLines(fonts_dir + "/font_table.txt");

            for (int i = 0; i < fonts_to_load.size(); i++)
            {
                HRESULT hr = LoadFont(fonts_dir + "/" + fonts_to_load.at(i));

                if (FAILED(hr))
                {
                    Blam::Logger::LogEvent("### WARNING failed to load font in font table: " + fonts_to_load.at(i), WSV_WARNING);
                }
            }
        }
        else
        {
            Blam::Logger::LogEvent("### ERROR font_table.txt could not be found in '" + fonts_dir + "'! this will cause errors!", WSV_ERROR);
        }
    }
    else
    {

    }
}