Files
wc3re/font2png.cc
Matthias Blankertz 29e9acd734 Initial work on globals.iff
- iffexplore to decocde globals.iff
- font2png parses the font blobs found therein
2015-04-16 21:14:52 +02:00

209 lines
5.2 KiB
C++

#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cstdint>
#include <vector>
#include <array>
#include <algorithm>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <png.h>
#include "common.hh"
struct Image {
unsigned height, width;
std::vector<uint8_t> data;
};
struct Font {
unsigned numChars;
unsigned charHeight;
unsigned maxCharWidth;
std::vector<Image> chars;
};
struct FontHeader { // little endian
uint32_t unknown1; // (always)? "1.\0\0"
uint32_t entries;
uint32_t charWidth;
uint32_t unknown3; // (always)? 0x000000ff
};
Font parseFont(FILE* file, size_t length)
{
// if (fseeko(file, 16, SEEK_SET) != 0) {
// throw POSIXException(errno, "Could not seek");
// }
// uint32_t data, last_data = 0;
// int last_diff = -1, count = 0;
// size_t pos = 16;
// while (pos < length) {
// if (fread(&data, 4, 1, file) != 1) {
// if (feof(file))
// throw Exception{"Unexpected EOF"};
// throw POSIXException{errno, "Could not read"};
// }
// if ((last_diff == -1) ||
// (data-last_data == last_diff)) {
// ++count;
// last_diff = data-last_data;
// // printf("%.8x\n", data);
// } else {
// if ((count > 1) && last_diff )
// printf("%d times diff %d\n", count, last_diff);
// count = 0;
// last_diff = -1;
// }
// last_data = data;
// pos += 4;
// }
FontHeader header;
if (fread(&header, sizeof(FontHeader), 1, file) != 1)
throw POSIXException{errno, "Could not read"};
const unsigned index_start = sizeof(FontHeader);
const unsigned index_end = index_start+4*header.entries;
const unsigned char_width = header.charWidth;
std::vector<uint32_t> indices;
for (unsigned i = index_start;i < index_end;i+=4) {
uint32_t data;
if (fread(&data, 4, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
indices.push_back(data);
}
auto minmax = std::minmax_element(indices.begin(), indices.end());
printf("Index range %u .. %u\n",
*minmax.first, *minmax.second);
Font ret;
ret.numChars = header.entries;
ret.charHeight = char_width;
ret.maxCharWidth = 0;
for (auto const& index : indices) {
uint32_t len;
if (fseeko(file, index, SEEK_SET) != 0)
throw POSIXException(errno, "Could not seek");
if (fread(&len, 4, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
if (!len) {
ret.chars.push_back(Image{});
continue;
}
Image chr;
chr.height = char_width;
chr.width = len;
chr.data.resize(char_width*len);
if (chr.width > ret.maxCharWidth)
ret.maxCharWidth = chr.width;
if (fread(chr.data.data(), len*char_width, 1, file) != 1)
throw POSIXException{errno, "Could not read"};
ret.chars.push_back(chr);
}
return ret;
}
void saveImage(std::string const& name, Image const& img)
{
FILEUPtr file{fopen(name.c_str(), "wb")};
if (!file)
throw POSIXException{errno, "Could not open "s + name};
png_image pngImage;
pngImage.version = PNG_IMAGE_VERSION;
pngImage.width = img.width;
pngImage.height = img.height;
pngImage.format = PNG_FORMAT_GRAY;
pngImage.flags = 0;
pngImage.opaque = nullptr;
pngImage.colormap_entries = 0;
pngImage.warning_or_error = 0;
memset(pngImage.message, '\0', 64);
if (!png_image_write_to_stdio(&pngImage, file.get(), false, img.data.data(),
img.width, nullptr))
throw Exception{"PNG write failed: "s + pngImage.message};
}
Image makeFontImage(Font const& font)
{
const unsigned charsPerLine = 16;
Image ret;
ret.width = font.maxCharWidth*charsPerLine;
unsigned numCharLines = font.numChars/charsPerLine;
if ((font.numChars%charsPerLine) != 0)
++numCharLines;
ret.height = font.charHeight*numCharLines;
ret.data.reserve(ret.width*ret.height);
for (unsigned int c_y = 0;c_y < numCharLines;++c_y) {
for (unsigned int y = 0;y < font.charHeight;++y) {
for (unsigned int c_x = 0;c_x < charsPerLine;++c_x) {
unsigned charNo = c_y*charsPerLine+c_x;
if (charNo > font.numChars) {
for (unsigned x = 0;x < font.maxCharWidth;++x)
ret.data.push_back(0xff);
} else {
for (unsigned x = 0;x < font.chars[charNo].width;++x)
ret.data.push_back(font.chars[charNo].data[y*font.chars[charNo].width+x]);
for (unsigned x = font.chars[charNo].width; x < font.maxCharWidth;++x)
ret.data.push_back(0xff);
}
}
}
}
return ret;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s font-file\n", argv[0]);
return 1;
}
try {
FILEUPtr fontFile{fopen(argv[1], "rb")};
if (!fontFile) {
throw POSIXException{errno, "Could not open "s + argv[1]};
}
struct stat statBuf;
if (fstat(fileno(fontFile.get()), &statBuf) != 0) {
throw POSIXException(errno, "Could not stat");
}
auto font = parseFont(fontFile.get(), statBuf.st_size);
saveImage(argv[1] + ".png"s, makeFontImage(font));
} catch (POSIXException &ex) {
fprintf(stderr, "%s\n", ex.toString().c_str());
return 2;
} catch (Exception &ex) {
fprintf(stderr, "%s\n", ex.toString().c_str());
return 3;
}
return 0;
}