// 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 functionsu16 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 storagestruct 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;};intadd_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;}intadd_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;}voidload_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 stuffBackground 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;}