QFN Format (Blood)
Jump to navigation
Jump to search
The QFN format is used by Blood below version 1.21 to store fonts. These font files located inside two game archives in RFF format. Newer versions of the game uses font tiles from TILES016.ART and TILES017.ART instead.
- GUI.RFF (looks like these fonts are unused):
- QFONT1.FNT (8x8, mono, 1BPP)
- QFONT2.FNT (8x9, mono, 1BPP)
- BLOOD.RFF (these fonts are removed from the archive in the newer versions of the game):
- FONTBLOD.QFN (12x14, color, 8BPP)
- FONTSMAL.QFN (5x7, color, 8BPP)
- KFONT6.QFN (16x15, color, 8BPP)
- KFONT7.QFN (6x8, color, 8BPP)
- PFONT2O.QFN (11x11, color, 8BPP)
Like most of the Blood file formats there are a lot of unused data like startChar and endChar - both are usually 0 in mentioned font files and info character table hold records for all 256 characters anyway (actual font image data stored only for existing font characters).
QFONT font header
Data type | Name | Description |
---|---|---|
char[4] | signature | File signature, always "FNT\x1A" |
UINT16LE | version | Format version, always 0x100 |
UINT16LE | type | Font type |
UINT32LE | totalsize | Font character data size |
UINT8 | startChar | ! Unknown First character in font (unused, usually 0) |
UINT8 | endChar | ! Unknown Last character in font (unused, usually 0) |
UINT8 | Blending | ! Unknown Blending (unused, 0) |
UINT8 | baseline | Font baseline |
UINT8 | tcolor | Transparent color for color fonts (usually 0xFF) |
INT8 | charSpace | Horizonal space between characters |
UINT8 | width | Max character width |
UINT8 | height | Max character height |
char[12] | fill | Padding for this structure up to 32 bytes |
CHARINFO[256] | info | Characters info |
Valid type values:
- 0 = FT_MONO - horizontal mono font (1BPP)
- 1 = FT_HCOLOR - horizontal color font (8BPP)
- 2 = FT_VCOLOR - vertical color font (8BPP)
CHARINFO character info
Data type | Name | Description |
---|---|---|
UINT32LE | offset | Character data offset (relative to end of the QFONT header) |
UINT8 | cols | Character width |
UINT8 | rows | Character height |
UINT8 | hspace | Character horizontal size |
INT8 | voffset | Character vertical offset from top |
Source Code
C
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <inttypes.h>
/*
(c) CTPAX-X Team 2017
http://www.CTPAX-X.org/
Helping a tiny bit with the BloodGDX project:
http://m210.duke4.net/index.php/files/viewdownload/9-java/50-bloodgdx
Example source code to show how to work with the font files.
Outputs specified input string with the selected font file to the text screen.
Also for the references see the code of viewDrawChar() and viewDrawText()
in VIEW.CPP file from the leaked Blood Alpha source codes.
*/
#pragma pack(push, 1)
/* 8 bytes each record */
typedef struct {
uint32_t offset; /* image data offset (relative to the end of the header) */
uint8_t cols; /* image data width */
uint8_t rows; /* image data height */
uint8_t hspace; /* horizontal space */
int8_t voffset; /* vertical offset (signed) */
} CHARINFO;
/* 2080 bytes this header */
typedef struct {
char signature[4]; /* always "FNT\x1A" */
uint16_t version; /* always 0x100 */
uint16_t type; /* 0 - monochrome (1 BPP); everything else - color (8BPP) */
uint32_t totalsize; /* total size of font data (size of this header + totalsize = filesize */
uint8_t startChar; /* unused */
uint8_t endChar; /* unused */
uint8_t Blending; /* unused */
uint8_t baseline; /* character baseline */
uint8_t tcolor; /* unused - transparent color - looks like always 0xFF */
int8_t charSpace; /* character space (signed) */
uint8_t width; /* max character width */
uint8_t height; /* max character height */
char fill[12]; /* filler to 32 bytes boundary */
CHARINFO info[256]; /* characters info */
uint8_t data[0]; /* image data */
} QFONT;
#pragma pack(pop)
/* virtual screen size */
#define SCR_W 80
#define SCR_H 50
int main(int argc, char *argv[]) {
FILE *fl;
uint8_t *pFont;
int sz, x, y, i, j;
char *s;
QFONT *qf;
CHARINFO *ci;
/* virtual screen */
char scr[SCR_W*SCR_H];
/* Example: qfn_show FONT1.QFN "Blood Font!" > fontshow.txt */
if (argc != 3) {
printf("Usage: qfn_show <filename.qfn> <string>\n\n");
return(1);
}
fl = fopen(argv[1], "rb");
if (!fl) {
printf("Error opening input file for read!\n\n");
return(2);
}
fseek(fl, 0, SEEK_END);
sz = ftell(fl);
fseek(fl, 0, SEEK_SET);
pFont = (uint8_t *) malloc(sz);
if (!pFont) {
fclose(fl);
printf("Error memory allocation!\n\n");
return(3);
}
fread(pFont, sz, 1, fl);
fclose(fl);
qf = (QFONT *) pFont;
/* check header */
if (
/* signature */
(memcmp(qf->signature, "FNT\x1A", 4)) ||
/* only one existing version */
(qf->version != 0x100) ||
/* whole file size: header + font data */
(sz != (sizeof(qf[0]) + qf->totalsize))
) {
free(pFont);
printf("Error invalid font file!\n\n");
return(4);
}
/* string to output */
s = argv[2];
/* empty virtual screen */
memset(scr, ' ', SCR_W*SCR_H);
/* output text string */
x = 0;
for (; *s; s++) {
/* get current character */
ci = &qf->info[(uint8_t) *s];
/* screen y pos */
y = qf->baseline + ci->voffset;
/* screen overflow? */
if (x + ci->hspace >= SCR_W) { break; }
/* for each pixel */
for (j = 0; j < ci->rows; j++) {
/* screen overflow? */
if (j + y >= SCR_H) { break; }
for (i = 0; i < ci->cols; i++) {
if (qf->type == 0) {
/* mono */
if (qf->data[ci->offset + i + ((j/8)*ci->cols)] & (1 << (j%8))) {
scr[((j + y) * SCR_W) + x + i] = '#';
}
} else {
/* color */
if (qf->data[ci->offset + (i*ci->rows) + j] != 0xFF) {
/* NOTE: this will breaks console output for codes < 32 but this a test tool */
scr[((j + y) * SCR_W) + x + i] = qf->data[ci->offset + (i*ci->rows) + j];
}
}
}
}
/* add char horizonal size + generic space between characters */
x += ci->hspace + qf->charSpace;
}
/* min height for output (reduce empty lines) */
y = (SCR_H < qf->height) ? SCR_H : qf->height;
/* output virtual screen */
for (j = 0; j < y; j++) {
/* find line end */
x = 0;
/* -1 or there will be 2 linebreaks at 80 */
for (i = 0; i < SCR_W - 1; i++) {
if (scr[(j*SCR_W) + i] != ' ') {
x = i + 1;
}
}
i = (j*SCR_W);
/* set line end */
scr[i + x] = 0;
/* output line */
printf("%s\n", &scr[i]);
}
/* show some font info */
printf(
"%s\n%dx%d %s\ncharSpace: %d\nbaseline: %d\n\n",
argv[1], qf->width, qf->height, qf->type ? "color" : "mono", qf->charSpace, qf->baseline
);
free(pFont);
return(0);
}
Credits
If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)