// GLUT
#include <GLUT/glut.h>
// OpenGL
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
// System
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// STL
#include <string>
// ZLib
#include <zlib.h>
using namespace std;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
const char * ff7Path = "<path to your ff7 directory>";
// This is a table that contains 16-bit unicode codes for all chars in the FF7 font. (US/NTSC Version)
const u16 charMatrix[] = {
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049,
0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e,
0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073,
0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c1, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0,
0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x0069, 0x00ec, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9,
0x00fb, 0x00fc, 0x0000, 0x00b0, 0x00a2, 0x00a3, 0x0000, 0x0000, 0x0000, 0x00df, 0x0000, 0x0000, 0x0000, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264,
0x2265, 0x00a5, 0x00b5, 0x2202, 0x03a3, 0x03a0, 0x03c0, 0x2321, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x0394, 0x00ab,
0x00bb, 0x2026, 0x0000, 0x0041, 0x0041, 0x004f, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x2662, 0x00ff, 0x0178, 0x2044, 0x0000, 0x2039,
0x203a, 0x1278, 0x1279, 0x275a, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c5, 0x00ca, 0x0041, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, 0x004f, 0x004f, 0x0000, 0x004f,
0x0055, 0x0055, 0x0055, 0x007c, 0x02c6, 0x02dc, 0x02c9, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7
};
u8 * ucodeToChar;
void * kernelBinData;
void * windowBinData;
GLuint fontTextures[8];
u16
get_u16le(void const * const buf)
{
return ((u8*)buf)[0] | (((u8*)buf)[1]<<8);
}
u32
get_u32le(void const * const buf)
{
return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16) | (((u8*)buf)[3]<<24);
}
int
load(const string & name, void ** data)
{
size_t size = 0;
*data = NULL;
int fd = open(name.c_str(), O_RDONLY, 0);
if (fd >= 0) {
struct stat sb;
if (fstat(fd, &sb) >= 0) {
assert(sb.st_size > 0);
void *tmp = malloc(sb.st_size);
if (tmp!=NULL) {
if (read(fd, tmp, sb.st_size) == sb.st_size) {
*data = tmp;
size = sb.st_size;
} else {
free(tmp);
}
}
}
close(fd);
}
return size;
}
int
load_bingzip(const string & fileName, void ** buf)
{
// load library
u8 * buffer = NULL;
int size = load(fileName, (void**)&buffer);
// calc overall decompressed size and number of blocks
int offset = 0;
int output_size = 0;
int output_count = 0;
while (offset < size) {
int comp_size = get_u16le(buffer + offset + 0);
int uncomp_size = get_u16le(buffer + offset + 2);
offset += 6;
offset += comp_size;
output_size += uncomp_size;
output_count ++;
}
// allocate enough memory for output buffer
int bufsize = output_size + output_count * sizeof(u32);
u32 * table = (u32*)malloc(bufsize);
// decompress all blocks with zlib
z_stream s;
memset(&s, 0, sizeof(z_stream));
s.next_out = (Bytef *)(table + output_count);
s.avail_out = output_size;
s.total_out = 0;
offset = 0;
int count = 0;
int out_offset = output_count * sizeof(u32);
while (offset < size) {
table[count++] = out_offset;
int comp_size = get_u16le(buffer + offset + 0);
int uncomp_size = get_u16le(buffer + offset + 2);
int unknown = get_u16le(buffer + offset + 4);
offset += 6;
s.next_in = buffer + offset;
s.avail_in = comp_size;
s.total_in = 0;
offset += comp_size;
out_offset += uncomp_size;
// compressed/uncompressed?
inflateInit2(&s, 16 | 15);
inflate(&s, Z_FINISH);
inflateEnd(&s);
}
free(buffer);
*buf = table;
return bufsize;
}
void
build_font_textures()
{
u32 * table = static_cast<u32*>(windowBinData);
u8 * data = static_cast<u8*>(windowBinData);
glGenTextures(8, fontTextures);
u8 * fontData = data + table[1];
u32 id = get_u32le(fontData + 0);
u32 type = get_u32le(fontData + 4);
u32 clut_size = get_u16le(fontData + 16);
u32 clut_count = get_u16le(fontData + 18);
u32 pic_ofs = clut_size * clut_count * 2 + 20;
u32 width = get_u16le(fontData + pic_ofs + 8) * 4;
u32 height = get_u16le(fontData + pic_ofs + 10);
u8 * bitmap = new u8[256 * 256 * 4];
for (unsigned int i=0; i<clut_count; i++) {
u8 clut[256 * 4];
for (unsigned int j = 0; j<clut_size; j++) {
u16 col = get_u16le(fontData + 20 + (i * clut_size + j) * 2);
clut[j * 4 + 0] = (col & 31) * 255 / 31;
clut[j * 4 + 1] = ((col >> 5) & 31) * 255 / 31;
clut[j * 4 + 2] = ((col >> 10) & 31) * 255 / 31;
clut[j * 4 + 3] = (col >> 15) * 255;
}
for (unsigned int y=0; y<height; y++) {
for (unsigned int x=0; x<width; x++) {
u8 pixel = fontData[pic_ofs + 12 + y * (width / 2) + x / 2];
int index = x & 1 ? pixel >> 4 : pixel & 15;
bitmap[(y * 256 + x) * 4 + 0] = clut[index * 4 + 0];
bitmap[(y * 256 + x) * 4 + 1] = clut[index * 4 + 1];
bitmap[(y * 256 + x) * 4 + 2] = clut[index * 4 + 2];
bitmap[(y * 256 + x) * 4 + 3] = clut[index * 4 + 3];
}
}
glBindTexture(GL_TEXTURE_2D, fontTextures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, bitmap);
}
delete[] bitmap;
}
unsigned int
getKernelStringCount(unsigned int i)
{
u32 * table = static_cast<u32*>(kernelBinData);
u8 * data = static_cast<u8*>(kernelBinData);
u8 * base = data + table[i + 9];
return get_u16le(base) / 2;
}
void
getKernelString(unsigned int i, unsigned int j, wstring & s)
{
u32 * table = static_cast<u32*>(kernelBinData);
u8 * data = static_cast<u8*>(kernelBinData);
u8 * base = data + table[i + 9];
u8 * ptr = base + get_u16le(base + j * 2);
if (*ptr != 0xFF) {
while (*ptr != 0xFF) {
if (*ptr == 0xf9) {
// simple string compression, reference an earlier substring
ptr++;
int dist = ((*ptr) & 0x3F)+2;
int count = ((*ptr) >> 6)*2+4;
for (int k=0; (k<count) && (*(ptr-dist+k)!=0xFF); k++) {
s += charMatrix[*(ptr-dist+k)];
}
ptr++;
} else if (*ptr == 0xf8) {
// unknown escape code..
ptr++;
ptr++;
} else {
s += charMatrix[*ptr];
ptr++;
}
}
}
}
// build inverse lookup table from unicode to FF7 font index
void
build_ucode_to_char_table()
{
ucodeToChar = new u8[65536];
memset(ucodeToChar, 0, 65536);
for (unsigned int i=0; i<sizeof(charMatrix)/sizeof(charMatrix[0]); i++) {
ucodeToChar[charMatrix[i]] = i;
}
}
void
drawString(int colour, int x, int y, const wstring & text)
{
u32 * table = static_cast<u32*>(windowBinData);
u8 * data = static_cast<u8*>(windowBinData);
u8 * widthTable = data + table[2];
glBindTexture(GL_TEXTURE_2D, fontTextures[colour]);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBegin(GL_QUADS);
for (wstring::const_iterator i = text.begin(); i != text.end(); i++) {
u8 glyph = ucodeToChar[*i];
int gx = (glyph % 21) * 12;
int gy = (glyph / 21) * 12;
glTexCoord2i(gx, gy);
glVertex2i(x, y);
glTexCoord2i(gx, gy + 12);
glVertex2i(x, y + 12);
glTexCoord2i(gx + 12, gy + 12);
glVertex2i(x + 12, y + 12);
glTexCoord2i(gx + 12, gy);
glVertex2i(x + 12, y);
x += widthTable[glyph] & 0x0F;
}
glEnd();
}
void
ReshapeWindowFunc(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, width, height, 0.0, 0.0, 1.0);
}
void
MainRenderLoop(void)
{
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawString(0, 8, 8, L"Kernel Strings:");
for (unsigned int i=0; i<getKernelStringCount(10); i++) {
wstring text;
getKernelString(10, i, text);
drawString(i&7, 16, i*12+20, text.c_str());
}
glFlush();
}
void
SpecialHandler(int key, int x, int y)
{
}
void
KeyboardHandler(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
}
}
int
main(int argc, char * argv[])
{
// glut stuff
glutInit(&argc, (char **) argv);
glutInitWindowSize(640, 480);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
glutCreateWindow(argv[0]);
glutDisplayFunc(MainRenderLoop);
glutIdleFunc(MainRenderLoop);
glutReshapeFunc(ReshapeWindowFunc);
glutKeyboardFunc(KeyboardHandler);
glutSpecialFunc(SpecialHandler);
// opengl stuff
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
glViewport(0, 0, 640, 480);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f/256.0f, 1.0f/256.0f, 1.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 640, 480, 0.0, 0.0, 1.0);
load_bingzip(string(ff7Path) + "/INIT/KERNEL.BIN", &kernelBinData);
load_bingzip(string(ff7Path) + "/INIT/WINDOW.BIN", &windowBinData);
build_font_textures();
build_ucode_to_char_table();
glutMainLoop();
delete[] ucodeToChar;
return 0;
}
Features:
- loads kernel.bin and window.bin
- converts font into textures
- converts ff7 strings into unicode
- draws unicode strings with ff7 font
- expands kernel strings
TODO:
- control codes for field text
- layout of text into a window
- decide on C++ or C and convert
- split into files
- you'll have to change the path to your ff7 directory (I'm using the PSX version)