Here is my experimental field viewer. It's a bit sloppy, but then again it is not finished.
// System
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// STL
#include <string>
#include <map>
// C
#include <cassert>
#include <errno.h>
#if 1
// GLUT
#include <GLUT/glut.h>
// OpenGL
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
// GLUT
#include <glut/glut.h>
// OpenGL
#include <gl/gl.h>
#include <gl/glu.h>
#endif
using namespace std;
typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned long int u32;
typedef signed char s8;
typedef signed short int s16;
typedef signed long int s32;
// - utility functions --------------------------------------------------------
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);
}
#ifndef O_BINARY
#define O_BINARY 0
#endif
int load( const string & name, void ** data )
{
size_t size = 0;
*data = NULL;
int fd = open( name.c_str(), O_RDONLY|O_BINARY, 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 );
} else {
printf("%i\n", errno);
}
return size;
}
void save(const std::string & name, const void *data, int size)
{
int fd = open( name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666 );
if (fd >= 0) {
write( fd, data, size );
close( fd );
}
}
int load_lzs(const string & name, void ** data )
{
// read compressed data
u8 *buf;
int tsize = load( name, (void**)&buf );
if (buf == NULL) {
*data = NULL;
return 0;
}
int isize = get_u32le(buf) + 4;
if (isize != tsize) {
free(buf);
*data = NULL;
return 0;
}
// decompress;
int osize = (isize + 255) & ~255;
u8 * obuf = (u8 *)malloc(osize);
int iofs = 4, oofs = 0;
u8 cmd=0, bit=0;
while (iofs < isize) {
if (bit == 0) {
cmd = buf[iofs++];
bit = 8;
}
if (cmd&1) {
obuf[oofs++]=buf[iofs++];
if (oofs==osize) {
osize+=256;
obuf = (u8*)realloc(obuf, osize);
}
} else {
u8 a = buf[iofs++];
u8 b = buf[iofs++];
u16 o = a | ((b&0xF0)<<4);
u8 len = (b&0xF)+3;
int rofs = oofs - ((oofs - 18 - o) & 0xFFF);
for (int j=0; j<len; j++) {
if (rofs < 0) {
obuf[oofs++]=0;
} else {
obuf[oofs++]=obuf[rofs];
}
if (oofs==osize) {
osize+=256;
obuf = (u8*)realloc(obuf, osize);
}
rofs++;
}
}
cmd>>=1;
bit--;
}
free( buf );
*data = obuf;
return oofs;
}
// - field class --------------------------------------------------------------
class CFieldBackground {
private:
struct TexInfo {
int page, pal;
bool hc;
bool operator<(const TexInfo & rhs)
{
return memcmp(this, &rhs, sizeof(TexInfo)) < 0;
}
};
map<TexInfo, int> texInfoMap;
u8 * mimData;
int mimSize;
u8 * datData;
int datSize;
u8 * bsxData;
int bsxSize;
struct Texture {
u8 * rgba; // data
int id; // OpenGL texture ID
};
struct Tile {
int u, v, pal; // info from the request, pal=-1 for RGBA
int s, t; // texture position in OpenGL texture
int texture; // Texture
};
int getTileI8(int u, int v, int pal);
int getTileRGBA(int u, int v);
int getTexture(u8 atr1, u8 atr2, u8 x);
public:
CFieldBackground();
~CFieldBackground();
void load(const std::string & name);
void draw();
};
CFieldBackground::CFieldBackground():
mimData(NULL), mimSize(0),
datData(NULL), datSize(0),
bsxData(NULL), bsxSize(0)
{
}
CFieldBackground::~CFieldBackground()
{
}
void
CFieldBackground::load(const std::string & name)
{
// read compressed data
string baseName = name.substr(0, name.find('.'));
// MIM
string mimName = baseName + ".MIM";
mimData = NULL;
mimSize = load_lzs( mimName, (void **)&mimData );
// DAT
string datName = baseName + ".DAT";
datData = NULL;
datSize = load_lzs( datName, (void **)&datData );
save(datName + ".bin", datData, datSize);
// BSX
string bsxName = baseName + ".BSX";
bsxData = NULL;
bsxSize = load_lzs( bsxName, (void **)&bsxData );
save(bsxName + ".bin", bsxData, bsxSize);
}
int
CFieldBackground::getTexture(u8 atr1, u8 atr2, u8 x)
{
TexInfo texInfo;
texInfo.page = (atr1 >> 4); // * 128
int px = texInfo.page * 64 + x;
texInfo.pal = px < 512 ? px / 256 : ((px - 512) / 128) + 2;
texInfo.hc = false; // atr2 & 1;
if (texInfoMap.find(texInfo) != texInfoMap.end())
{
return texInfoMap[texInfo];
}
printf("%2.2x %2.2x\n", atr1, atr2);
int ofs = get_u32le(mimData);
int step = get_u32le(mimData+ofs);
int npal = get_u16le(mimData+10);
int pal_ofs = 12;
int pic_ofs = ofs+12;
int xsize = get_u16le(mimData+ofs+8)*2;
int ysize = get_u16le(mimData+ofs+10);
u8 *texture = new u8[256*256*4];
if (texInfo.hc) {
int page = texInfo.page * 128;
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u16 col = get_u16le(mimData + (y*xsize+x*2+page) + pic_ofs);
int r = (((col ) & 31) * 255 + 15) / 31;
int g = (((col >> 5) & 31) * 255 + 15) / 31;
int b = (((col >> 10) & 31) * 255 + 15) / 31;
int a = (col & 0x8000) || (col == 0) ? 0 : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
} else {
int page = texInfo.page * 64;
int pal = texInfo.pal * 512;
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u8 idx = mimData[(y*xsize+x+page) + pic_ofs];
u16 col = get_u16le(mimData + idx*2 + pal_ofs + pal);
int r = (((col ) & 31) * 255 + 15) / 31;
int g = (((col >> 5) & 31) * 255 + 15) / 31;
int b = (((col >> 10) & 31) * 255 + 15) / 31;
int a = (col & 0x8000) || (col == 0) ? 0 : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
}
int textureId;
glGenTextures(1, &textureId);
glBindTexture( GL_TEXTURE_2D, textureId);
#if 0
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
#else
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
#endif
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
delete texture;
texInfoMap[texInfo] = textureId;
return textureId;
}
void
CFieldBackground::draw()
{
u32 memaddr[7];
for (int i=0; i<7; i++) {
memaddr[i] = get_u32le(datData+i*4);
}
u32 base = memaddr[0] - 7 * 4;
u32 section[7];
for (int i=0; i<7; i++) {
section[i] = memaddr[i] - base;
}
u8 * tilemap = datData + section[2] + get_u32le(datData + section[2]);
int count = (get_u32le(datData + section[2] + 4) - get_u32le(datData + section[2])) / 8;
#if 1
printf("---- %4.4x\n", count);
for (int i=0; i<get_u32le(datData + section[2]); i++) {
printf("%2.2x ", (u8*)(datData + section[2])[i]);
}
printf("\n----\n");
for (int i=0; i<get_u32le(datData + section[2] + 8)-get_u32le(datData + section[2] + 4); i++) {
printf("%2.2x ", (u8*)(datData + section[2] + get_u32le(datData + section[2] + 4))[i]);
}
printf("\n----\n");
#endif
for (int i=0; i<count; i++) {
s16 tx = (s16)get_u16le(tilemap + 8 * i + 0) + 256;
s16 ty = (s16)get_u16le(tilemap + 8 * i + 2) + 256;
u8 sx = tilemap[8 * i + 4];
u8 sy = tilemap[8 * i + 5];
u8 atr1 = tilemap[8 * i + 6];
u8 atr2 = tilemap[8 * i + 7];
// printf("%2.2x %2.2x|", atr1, atr2);
glBindTexture(GL_TEXTURE_2D, getTexture(atr1, atr2, sx));
glBegin(GL_QUADS);
glTexCoord2i(sx, sy); glVertex2i(tx, ty);
glTexCoord2i(sx+15, sy); glVertex2i(tx+16, ty);
glTexCoord2i(sx+15, sy+15); glVertex2i(tx+16, ty+16);
glTexCoord2i(sx, sy+15); glVertex2i(tx, ty+16);
glEnd();
}
//printf("\n");
#if 0
tilemap = datData + section[2] + get_u32le(datData + section[2] + 4);
count = get_u16le(tilemap);
for (int i=0; i<count; i++) {
s16 tx = (s16)get_u16le(tilemap + 14 * i + 0 + 2) + 512;
s16 ty = (s16)get_u16le(tilemap + 14 * i + 2 + 2) + 256;
u8 sx = tilemap[14 * i + 4 + 2];
u8 sy = tilemap[14 * i + 5 + 2];
u8 atr1 = tilemap[14 * i + 6 + 2];
u8 atr2 = tilemap[14 * i + 7 + 2];
// printf("%2.2x %2.2x\n", atr1, atr2);
glBindTexture(GL_TEXTURE_2D, getTexture(atr1, atr2, sx));
glBegin(GL_QUADS);
glTexCoord2i(sx, sy); glVertex2i(tx, ty);
glTexCoord2i(sx+15, sy); glVertex2i(tx+16, ty);
glTexCoord2i(sx+15, sy+15); glVertex2i(tx+16, ty+16);
glTexCoord2i(sx, sy+15); glVertex2i(tx, ty+16);
glEnd();
}
#endif
}
CFieldBackground g_FieldBackground;
// - OpenGL stuff --------------------------------------------------------------
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)
{
// render
glClearColor(0, 0, 0, 1.00);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
g_FieldBackground.draw();
glFlush();
}
void SpecialHandler(int key, int x, int y)
{
#if 0
switch (key) {
case GLUT_KEY_UP:
rot_x -= 4;
if (rot_x < -64) {
rot_x = -64;
}
break;
case GLUT_KEY_DOWN:
rot_x += 4;
if (rot_x > 64) {
rot_x = 64;
}
break;
case GLUT_KEY_LEFT:
rot_y -= 5;
break;
case GLUT_KEY_RIGHT:
rot_y += 5;
break;
}
glutPostRedisplay();
#endif
}
void KeyboardHandler(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "fieldview <field.dat>\n");
} else {
// get current directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
// init opengl and glut
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);
// Setup OpenGL
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glDisable(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 /*1.0f/255.0f*/);
glDisable(GL_LINE_SMOOTH);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f/256.0f, 1.0f/256.0f, 1);
glTranslatef(0.5f, 0.5f, 0.0f);
// Setup projection matrices
glViewport(0, 0, 640, 480);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, 640, 480, 0.0, 0.0, 1.0);
// if the filename is not absolute try to add the current work directory.
string path;
if (argv[1][0] != 0 && argv[1][0] != '/' && argv[1][1] != 0 && argv[1][1] != ':' && argv[1][2] != 0 && argv[1][2] != '\\') {
path = string(cwd) + "/";
}
// load the background data
g_FieldBackground.load(path + argv[1]);
// main (this will never return)
glutMainLoop();
}
return 0;
}