I'm using MinGW, but I can use gdb to provide debugging data (and that'll likely be more useful anyway, since you're using Linux).
First, here's my current makefle:
all: stageview
stageview: stageview.o
gcc $< -o $@ -L/usr/lib -lopengl32 -lglu32 -lglut32 -lstdc++
stageview.o: stageview.cpp
gcc $< -c -o $@ -I/usr/include -g
And here's my current stageview.cpp:
// System
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// STL
#include <string>
#include <cassert>
// GLUT
#include <glut/glut.h>
// OpenGL
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glext.h>
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);
}
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_lzs(const string & name, void ** data )
{
// read compressed data
u8 *buf;
int tsize = load( name, (void**)&buf );
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;
}
// Background loading and storage
struct Vertex {
s16 x, y, z;
s16 u, v;
};
struct Element {
u16 a, b, c, s; // s = shader
};
struct Mesh {
int texture;
int element_start;
int element_count;
};
struct Shader {
int texture;
int palette;
};
struct Background {
int vertex_count;
Vertex *vertex;
int element_count;
Element *element;
int *indexbuffer;
int shader_count;
Shader *shader;
GLuint *textures;
int mesh_count;
Mesh *mesh;
};
int
add_vertex(Background *bg, s16 x, s16 y, s16 z, u8 u, u8 v)
{
for (int i=0; i<bg->vertex_count; i++) {
if (bg->vertex[i].x == x && bg->vertex[i].y == y && bg->vertex[i].z == z && bg->vertex[i].u == u && bg->vertex[i].v == v) {
return i;
}
}
int idx = bg->vertex_count++;
bg->vertex = (Vertex *)realloc(bg->vertex, bg->vertex_count * sizeof(Vertex));
bg->vertex[idx].x = x;
bg->vertex[idx].y = y;
bg->vertex[idx].z = z;
bg->vertex[idx].u = u;
bg->vertex[idx].v = v;
return idx;
}
int
add_triangle(Background *bg, u16 a, u16 b, u16 c, u16 s)
{
int idx = bg->element_count++;
bg->element = (Element*)realloc(bg->element, bg->element_count * sizeof(Element));
bg->element[idx].a = a;
bg->element[idx].b = b;
bg->element[idx].c = c;
bg->element[idx].s = s;
return idx;
}
int
add_shader(Background *bg, int texture, int palette)
{
for (int i=0; i<bg->shader_count; i++) {
if (bg->shader[i].texture == texture && bg->shader[i].palette == palette) {
return i;
}
}
int idx = bg->shader_count++;
bg->shader = (Shader *)realloc(bg->shader, bg->shader_count * sizeof(Shader));
bg->shader[idx].texture = texture;
bg->shader[idx].palette = palette;
return idx;
}
void
load_background(const string & filename, Background *bg)
{
bg->vertex_count = 0;
bg->vertex = NULL;
bg->element_count = 0;
bg->element = NULL;
bg->textures = NULL;
bg->shader_count = 0;
bg->shader = NULL;
bg->mesh_count = 0;
bg->mesh = NULL;
u8 * data;
int size = load_lzs(filename, (void**)&data);
int num_pointer = get_u32le(data);
// background texture
int texdata = get_u32le(data + num_pointer * 4);
int npal = get_u16le(data + texdata + 18);
int paldata_size = get_u32le(data+texdata+8);
int pal_ofs = texdata + 20;
int picdata_size = get_u32le(data+texdata+8+paldata_size);
int pic_ofs = texdata + 20 + npal*512 + 12;
int xsize = get_u16le(data+pic_ofs-4)*2;
int ysize = get_u16le(data+pic_ofs-2);
// convert mesh data
for (int i=1; i<num_pointer-1; i++) {
int base = get_u32le(data + 4 + i * 4);
// triangles
int triangle_offset = base + 4 + get_u32le(data + base);
int num_triangles = get_u16le(data + triangle_offset);
int mesh_tri_flags = get_u16le(data + triangle_offset + 2);
int texture_idx = ((mesh_tri_flags & 0x0E)-6) / 2;
for (int j=0; j<num_triangles; j++) {
int point[3];
int ofs = triangle_offset + 4 + j * 16;
for (int k=0; k<3; k++) {
int p = get_u16le(data + ofs + k * 2) + base + 4;
s16 x = ((s16)get_u16le(data + p + 0*2));
s16 y = -((s16)get_u16le(data + p + 1*2));
s16 z = ((s16)get_u16le(data + p + 2*2));
const int uv_offsets[3] = { 8, 12, 14 };
u8 u = data[ofs + uv_offsets[k] + 0];
u8 v = data[ofs + uv_offsets[k] + 1];
point[k] = add_vertex(bg, x,y,z,u,v);
}
u16 flags1 = get_u16le(data + ofs + 6);
u16 flags2 = get_u16le(data + ofs + 10);
int palette_idx = (flags2 >> 6) & 7;
int shader_idx = add_shader(bg, texture_idx, palette_idx);
add_triangle(bg, point[0], point[1], point[2], shader_idx);
}
// quads
int quad_offset = triangle_offset + 4 + num_triangles * 16;
int num_quads = get_u16le(data + quad_offset);
int mesh_quad_flags = get_u16le(data + quad_offset + 2);
for (int j=0; j<num_quads; j++) {
int point[4];
int ofs = quad_offset + 4 + j * 20;
for (int k=0; k<4; k++) {
int p = get_u16le(data + ofs + k * 2) + base + 4;
s16 x = ((s16)get_u16le(data + p + 0 * 2));
s16 y = -((s16)get_u16le(data + p + 1 * 2));
s16 z = ((s16)get_u16le(data + p + 2 * 2));
const int uv_offsets[4] = { 8, 12, 14, 16 };
u8 u = data[ofs + uv_offsets[k] + 0];
u8 v = data[ofs + uv_offsets[k] + 1];
point[k] = add_vertex(bg, x,y,z,u,v);
}
u16 flags1 = get_u16le(data + ofs + 18);
u16 flags2 = get_u16le(data + ofs + 10);
int palette_idx = (flags2 >> 6) & 7;
int shader_idx = add_shader(bg, texture_idx, palette_idx);
add_triangle(bg, point[0], point[1], point[2], shader_idx);
add_triangle(bg, point[3], point[2], point[1], shader_idx);
}
}
// build meshes
bg->indexbuffer = (int*)malloc(bg->element_count * 3 * sizeof(int));
int cur_mesh = 0;
for (int i=0; i<bg->element_count; i++) {
bg->indexbuffer[i * 3 + 0] = bg->element[i].a;
bg->indexbuffer[i * 3 + 1] = bg->element[i].b;
bg->indexbuffer[i * 3 + 2] = bg->element[i].c;
if (bg->mesh_count > 0 && bg->mesh[cur_mesh].texture == bg->element[i].s) {
bg->mesh[cur_mesh].element_count++;
} else {
cur_mesh = bg->mesh_count++;
bg->mesh = (Mesh*)realloc(bg->mesh, bg->mesh_count * sizeof(Mesh));
bg->mesh[cur_mesh].texture = bg->element[i].s;
bg->mesh[cur_mesh].element_start = i*3;
bg->mesh[cur_mesh].element_count = 1;
}
}
// build required textures
struct RGBA {
u8 r, g, b, a;
};
RGBA *clut = (RGBA*)malloc(sizeof(RGBA) * 256 * (npal));
for (int j = 0; j < npal; j++) {
for (int i=0; i<256; i++) {
u16 col = (data[i*2 + pal_ofs + j*512 + 1]<<8) | data[i*2 + pal_ofs + j * 512];
clut[i+j*256].r = (((col ) & 31) * 255 + 15) / 31;
clut[i+j*256].g = (((col >> 5) & 31) * 255 + 15) / 31;
clut[i+j*256].b = (((col >> 10) & 31) * 255 + 15) / 31;
clut[i+j*256].a = ((col & 0x8000) || (col == 0)) ? 0 : 255;
}
}
bg->textures = (GLuint*)malloc(sizeof(GLuint) * bg->shader_count);
glGenTextures( bg->shader_count, bg->textures);
RGBA *texture = (RGBA*)malloc(sizeof(RGBA) * 256 * 256);
for (int i=0; i<bg->shader_count; i++) {
// build
#if 1
if ( bg->shader[i].palette >= npal ) {
printf("%i %i\n", bg->shader[i].palette, npal);
bg->shader[i].palette = 0;
}
#endif
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
texture[y*256+x] = clut[data[pic_ofs + y * xsize + x + bg->shader[i].texture * 256] + bg->shader[i].palette * 256];
}
}
// copy to GL
glBindTexture( GL_TEXTURE_2D, bg->textures[i]);
#if 0
// blocky
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
#else
// interpolated
gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, 256, 256, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
#endif
}
free(texture);
free(clut);
free(data);
}
// GLUT and OpenGL stuff
Background battle;
int rot_y;
int rot_x;
void ReshapeWindowFunc(int width, int height)
{
const double kFOVy = 0.57735;
const double kZNear = 0.1;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble aspect = ((GLfloat)width / (GLfloat)height) * (kZNear * kFOVy);
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void MainRenderLoop(void)
{
// render
glClearColor(0, 0, 0, 1.00);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LEQUAL );
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 1.0f/255.0f);
glEnable( GL_TEXTURE_2D );
glEnable( GL_CULL_FACE );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
glDisable( GL_LIGHTING );
glDisable( GL_BLEND );
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef( (float)rot_x * 360.0f / 256.0f, 1.0f, 0.0f, 0.0f );
glRotatef( (float)rot_y * 360.0f / 256.0f, 0.0f, 1.0f, 0.0f );
glTranslatef( 0, -16, 0);
glScalef( 1.0f/256.0f, 1.0f/256.0f, 1.0f/256.0f );
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f/256.0f, 1.0f/256.0f, 0);
glTranslatef( 0.5f, 0.5f, 0);
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glVertexPointer( 3, GL_SHORT, sizeof(Vertex), &(battle.vertex[0].x) );
glTexCoordPointer( 2, GL_SHORT, sizeof(Vertex), &(battle.vertex[0].u) );
for (int i = 0; i < battle.mesh_count; i++) {
glBindTexture(GL_TEXTURE_2D, battle.textures[battle.mesh[i].texture]);
glDrawElements(GL_TRIANGLES, battle.mesh[i].element_count * 3, GL_UNSIGNED_INT, battle.indexbuffer + battle.mesh[i].element_start);
}
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );
glFlush();
}
void SpecialHandler(int key, int x, int y)
{
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();
}
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, "stageview <stage.lzs>\n");
} else {
// get current directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));
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) + "/";
}
// 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);
// load data
load_background(path + argv[1], &battle);
rot_y = rot_x = 0;
// main (this will never return)
glutMainLoop();
}
return 0;
}
gdb using STAGE04.LZS (since you mentioned that specific file, though the outcome is the same regardless of which file I use...even if the file doesn't exist or isn't the right format):
[USERNAME OMITTED]@[HOST OMITTED] ~
$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\stageview.exe...done.
(gdb) r C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\STAGE04.LZS
Starting program: C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\STAGE04.LZS
[New Thread 848.0xc08]
Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45 return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb) where
#0 0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
#1 0x00401557 in load_lzs (name=..., data=0x22fa30) at stageview.cpp:77
#2 0x00401ad0 in load_background (filename=..., bg=0x40c020)
at stageview.cpp:228
#3 0x00402e49 in main (argc=2, argv=0x3e26f8) at stageview.cpp:502
(gdb)
So, it seems to dislike the array for get_u32le.