// http://forums.qhimm.com/index.php?topic=5146.0
// http://forums.qhimm.com/index.php?topic=5423.msg70264#msg70264
// http://forums.qhimm.com/index.php?topic=5923.msg76045#msg76045
// OpenGL and GLUT
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
// c runtime
#include <cmath>
#include <wchar.h>
#include <locale.h>
// c++ stl
#include <map>
// utility
#include "file.h"
#include "vector.h"
#include "endian.h"
#include "lzsload.h"
#include "textMap.h"
using namespace std;
// ---------------------------------------------------------------------------
class XenoModel {
private:
struct TexInfo {
u16 status;
u16 clut;
u8 alpha;
bool abe;
bool operator<(const TexInfo & rhs) const {
if (status != rhs.status) {
return status < rhs.status;
}
if (clut != rhs.clut) {
return clut < rhs.clut;
}
if (alpha != rhs.alpha) {
return alpha < rhs.alpha;
}
return abe < rhs.abe;
}
};
map<TexInfo, int> texInfoMap;
u8 * m_model;
u8 * m_walkmesh;
u8 * m_vram;
u8 * m_archive;
public:
XenoModel(void * archive, void * model, void * walkmesh);
~XenoModel();
void draw();
void drawModel();
void drawWalkmesh();
void uploadTextures(void *data, int size);
void dumpVRAM();
int getTexture(u16 status, u16 clut, bool adb, int alpha);
};
XenoModel::XenoModel(void * archive, void * model, void * walkmesh): m_archive((u8*)archive), m_model((u8*)model) , m_walkmesh((u8*)walkmesh)
{
// generate virtual vram to make texture page calculations easier
m_vram = new u8[2048 * 512];
memset(m_vram, 0xAA, 2048 * 512);
}
XenoModel::~XenoModel()
{
delete m_vram;
}
int
XenoModel::getTexture(u16 status, u16 clut, bool abe, int alpha)
{
alpha = 128;
TexInfo texInfo;
texInfo.status = status;
texInfo.clut = clut;
texInfo.abe = abe;
texInfo.alpha = alpha;
if (texInfoMap.find(texInfo) != texInfoMap.end()) {
return texInfoMap[texInfo];
}
u8 *texture = new u8[256*256*4];
int tx = (status & 0xF) * 64 * 2;
int ty = ((status >> 4) & 1) * 256;
int tp = (status >> 7) & 3;
int px = clut & 63;
int py = clut >> 6;
switch (tp) {
case 0: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u8 val = m_vram[(y + ty) * 2048 + (x/2) + tx];
int idx = (x & 1) ? val >> 4 : val & 0xF;
u16 col = get_u16le(m_vram + (py) * 2048 + idx * 2 + px * 2);
bool stp = (col & 0x8000) != 0;
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 = /*(idx == 0) || */(col == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
case 1: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
int idx = m_vram[(y + ty) * 2048 + x + tx];
u16 col = get_u16le(m_vram + (py) * 2048 + idx * 2 + px * 2);
bool stp = (col & 0x8000) != 0;
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 = /* (idx == 0) ||*/ (col == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
case 2: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u16 col = get_u16le(m_vram + (y + ty) * 2048 + x * 2 + tx);
bool stp = (col & 0x8000) != 0;
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 == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
}
GLuint 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);
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, 256, 256, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
delete texture;
texInfoMap[texInfo] = textureId;
return textureId;
}
void XenoModel::uploadTextures(void *data, int size)
{
// expand TIM into "vram"
int offset = 0;
while (offset < size) {
u8 * texture = ((u8*)data) + offset;
u32 type = get_u16le(texture + 0x00);
u16 pos_x = get_u16le(texture + 0x04);
u16 pos_y = get_u16le(texture + 0x06);
u16 move_x = get_u16le(texture + 0x08);
u16 move_y = get_u16le(texture + 0x0a);
u16 width = get_u16le(texture + 0x0c);
u16 height = get_u16le(texture + 0x0e);
u16 chunks = get_u16le(texture + 0x12);
if (width > 2048 || height > 512 || width == 0 || height == 0) {
return;
}
int blockSize = 0x1C + chunks * 2;
offset += (blockSize + 2047) & ~2047;
for (int i=0; i<chunks; i++) {
height = get_u16le(texture + 0x1C + i * 2);
for (int j=0; j < height; j++) {
memcpy(m_vram + (pos_y + move_y + j) * 2048 + (pos_x + move_x) * 2, ((u8*)data) + offset + j * width * 2, width * 2);
}
pos_y += height;
blockSize = width * height * 2;
offset += (blockSize + 2047) & ~2047;
}
}
}
void XenoModel::dumpVRAM()
{
save("vram.bin", m_vram, 2048 * 512);
}
void XenoModel::draw()
{
drawModel();
drawWalkmesh();
}
#if 0
bool first = true;
#endif
void XenoModel::drawWalkmesh()
{
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glMatrixMode(GL_MODELVIEW);
// glLoadIdentity();
//glDisable(GL_DEPTH_TEST);
u32 block_count = get_u32le(m_walkmesh + 0);
//printf("drawWalkmesh, block_count = %i\n", block_count);
for (int i = 0; i < block_count; ++i) {
u32 block_size = get_u32le(m_walkmesh + 0x04 + i * 0x04);
u32 block_start = get_u32le(m_walkmesh + 0x18 + i * 0x08);
u32 block_vertex = get_u32le(m_walkmesh + 0x1c + i * 0x08);
// Walkmesh
glBegin(GL_TRIANGLES);
glColor3f((i + 1) & 1 ? 1 : 0, (i + 1) & 2 ? 1 : 0, (i + 1) & 4 ? 1 : 0);
for (int j = 0; j < block_size; j += 0x0E) {
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + j + k * 2) * 0x08;
int x = (s16)get_u16le(m_walkmesh + 0x00 + offset);
int y = (s16)get_u16le(m_walkmesh + 0x02 + offset);
int z = (s16)get_u16le(m_walkmesh + 0x04 + offset);
glVertex3i(x, y, z);
#if 0
if (first) {
printf("%i: %x\n", j, get_u16le(m_walkmesh + 0x06 + offset));
}
#endif
}
}
glEnd();
// Connectivity
glBegin(GL_LINES);
glColor3f((i + 1) & 1 ? 0 : 1, (i + 1) & 2 ? 0 : 1, (i + 1) & 4 ? 0 : 1);
for (int j = 0; j < (block_size / 0x0E); j ++) {
#if 0
if (first) {
printf("%i: %x\n", j, get_u16le(m_walkmesh + block_start + 0x0C));
}
#endif
int sx = 0;
int sy = 0;
int sz = 0;
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + j * 0x0E + k * 2) * 0x08;
sx += (s16)get_u16le(m_walkmesh + 0x00 + offset);
sy += (s16)get_u16le(m_walkmesh + 0x02 + offset);
sz += (s16)get_u16le(m_walkmesh + 0x04 + offset);
}
sx /= 3;
sy /= 3;
sz /= 3;
for (int l=0; l<3; l++) {
int to = get_u16le(m_walkmesh + block_start + j * 0x0E + 0x06 + l * 2);
if (to != 0xFFFF) {
int tx = 0;
int ty = 0;
int tz = 0;
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + to * 0x0E + k * 2) * 0x08;
tx += (s16)get_u16le(m_walkmesh + 0x00 + offset);
ty += (s16)get_u16le(m_walkmesh + 0x02 + offset);
tz += (s16)get_u16le(m_walkmesh + 0x04 + offset);
}
tx /= 3;
ty /= 3;
tz /= 3;
glVertex3i(sx, sy, sz);
glVertex3i(tx, ty, tz);
}
}
}
glEnd();
}
//glEnable(GL_DEPTH_TEST);
//exit(0);
#if 0
first = false;
#endif
}
void XenoModel::drawModel()
{
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
#if 0
printf("Unknown:\n");
u16 unknown1 = get_u16le(((u8*)archiveData) + 0x0184);
u16 unknown2 = get_u16le(((u8*)archiveData) + 0x0186);
u32 unknown3 = get_u32le(((u8*)archiveData) + 0x0188);
u32 count = get_u32le(((u8*)archiveData) + 0x018C);
printf("??:%i ??:%i ??:%i count:%i\n", unknown1, unknown2, unknown3, count);
for (u32 i=0; i<count; i++) {
void * base = ((u8*)archiveData) + 0x0190 + i * 16;
for(u32 j=0; j<8; j++) {
int val = (s16)get_u16le(((u8*)base) + j * 2);
printf("%6i ", val);
}
printf("\n");
}
#endif
u32 item_count = get_u32le(((u8*)m_archive) + 0x018C);
for(u32 i=0; i<item_count; i++) {
void * item_base = ((u8*)m_archive) + 0x0190 + i * 16;
u32 model_count = get_u32le(m_model);
//printf("drawModel, model_count = %i\n", model_count);
//for (int m=0; m<model_count; m++) {
int item_flags = get_u16le(((u8*)item_base) + 0 * 2);
int item_rotx = get_u16le(((u8*)item_base) + 1 * 2);
int item_roty = get_u16le(((u8*)item_base) + 2 * 2);
int item_rotz = get_u16le(((u8*)item_base) + 3 * 2);
int item_posx = (s16)get_u16le(((u8*)item_base) + 4 * 2);
int item_posy = (s16)get_u16le(((u8*)item_base) + 5 * 2);
int item_posz = (s16)get_u16le(((u8*)item_base) + 6 * 2);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(item_posx, item_posy, item_posz);
glRotatef(item_rotx * 90.0 / 1024.0, 1, 0, 0);
glRotatef(item_roty * 90.0 / 1024.0, 0, 1, 0);
glRotatef(item_rotz * 90.0 / 1024.0, 0, 0, 1);
if(item_flags != 480) {
int m = get_u16le(((u8*)item_base) + 7 * 2);
u32 offset_model = get_u32le(m_model + 4 + m * 4);
u8 * model = m_model + offset_model;
u32 number_blocks = get_u32le(model);
//printf("drawModel, number_blocks = %i\n", number_blocks);
for (int i=0; i<number_blocks; i++) {
u8 * block = model + 16 + i * 0x38;
u16 number_vertices = get_u16le(block + 2);
u16 number_mesh = get_u16le(block + 4);
u16 number_mesh_block = get_u16le(block + 6);
u32 offset_vertices = get_u32le(block + 8);
u32 offset_normals = get_u32le(block + 12);
u32 offset_mesh_blocks = get_u32le(block + 16);
u32 offset_displaylist = get_u32le(block + 20);
#if 0
if (first) {
printf("%i/%i\n", m, i);
for (int i=0; i<16; i++) {
s16 val = get_u16le(block + 24 + i*2);
printf("%5i ", val);
}
printf("\n");
}
#endif
u8 * vertices = model + offset_vertices;
u8 * mesh_blocks = model + offset_mesh_blocks;
u8 * normals = model + offset_normals;
u8 * displaylist = model + offset_displaylist;
int poly_available = 0;
glColor3f(1,1,1);
bool quad_block = false;
u16 status = 0;
u16 clut = 0;
while (poly_available > 0 || number_mesh_block > 0) {
// init the mesh block
if (poly_available == 0) {
quad_block = (mesh_blocks[0] & 16) != 0;
poly_available = get_u16le(mesh_blocks + 2);
mesh_blocks+=4;
number_mesh_block--;
}
// decode command
u32 cmd = get_u32le(displaylist);
bool hp = ((cmd >> 24) & 16) != 0; // geraud shading
bool quad = ((cmd >> 24) & 8) != 0; // quad or tri
bool tme = ((cmd >> 24) & 4) != 0; // texture mapping
bool abe = ((cmd >> 24) & 2) != 0; // semi transparency
bool fullbright = ((cmd >> 24) & 1) != 0; // bypass lighting
int srcAlpha = 255;
int dstAlpha = 255;
if (abe) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
switch ((status >> 3) & 7) {
case 0:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
break;
case 1:
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
break;
case 2:
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_SUBTRACT);
break;
case 3:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
break;
}
} else {
glDisable(GL_BLEND);
}
// printf("%8.8x\n", cmd);
displaylist += 4;
switch ((cmd >> 24) & ~(16|2|1)) { // (careful not to mask 0xc4 or 0xc8!)
case 0xC4: // texture page
status = cmd & 0xFFFF;
switch ((status >> 3) & 7) {
case 0:
srcAlpha = dstAlpha = 128;
break;
case 1:
srcAlpha = dstAlpha = 255;
break;
case 2:
srcAlpha = dstAlpha = 255;
break;
case 3:
srcAlpha = 64;
dstAlpha = 255;
break;
}
break;
case 0xC8: // clut
clut = cmd & 0xFFFF;
break;
case 0x24: { // triangle with texture
Vector::CVector2f uv[3];
uv[0][0] = displaylist[0];
uv[0][1] = displaylist[1];
uv[1][0] = displaylist[2];
uv[1][1] = displaylist[3];
uv[2][0] = cmd & 255;
uv[2][1] = (cmd >> 8) & 255;
displaylist += 4;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, getTexture(status, clut, abe, srcAlpha));
glBegin(GL_TRIANGLES);
for (int j=0; j<3; j++) {
int vtx = get_u16le(mesh_blocks + j * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
if (hp) {
float nx = (s16)get_u16le(normals + vtx * 8 + 0) / 4096.0f;
float ny = (s16)get_u16le(normals + vtx * 8 + 2) / 4096.0f;
float nz = (s16)get_u16le(normals + vtx * 8 + 4) / 4096.0f;
glNormal3f(nx, ny, nz);
} else {
// no normal
glNormal3f(0.0f, 0.0f, 0.0f);
}
glTexCoord2i(uv[j][0],uv[j][1]);
glVertex3i(x, y, z);
}
glEnd();
glDisable(GL_TEXTURE_2D);
mesh_blocks += 8;
poly_available--;
break;
}
case 0x2c: { // quad with texture
Vector::CVector2f uv[4];
uv[0][0] = displaylist[0];
uv[0][1] = displaylist[1];
uv[1][0] = displaylist[2];
uv[1][1] = displaylist[3];
uv[2][0] = displaylist[4];
uv[2][1] = displaylist[5];
uv[3][0] = displaylist[6];
uv[3][1] = displaylist[7];
displaylist += 8;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, getTexture(status, clut, abe, srcAlpha));
glBegin(GL_QUADS);
const int quad_idx[] = { 0, 1, 3, 2 };
for (int j=0; j<4; j++) {
int vtx = get_u16le(mesh_blocks + quad_idx[j] * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glTexCoord2i(uv[quad_idx[j]][0],uv[quad_idx[j]][1]);
glVertex3i(x, y, z);
}
glEnd();
glDisable(GL_TEXTURE_2D);
mesh_blocks += 8;
poly_available--;
break;
}
case 0x20: { // monochrome triangle
glBegin(GL_TRIANGLES);
glColor4ub((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, srcAlpha);
for (int j=0; j<3; j++) {
int vtx = get_u16le(mesh_blocks + j * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glVertex3i(x, y, z);
}
glEnd();
mesh_blocks += 8;
poly_available--;
break;
}
case 0x28: { // monochrome quad
glBegin(GL_QUADS);
glColor4ub((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, srcAlpha);
const int quad_idx[] = { 0, 1, 3, 2 };
for (int j=0; j<4; j++) {
int vtx = get_u16le(mesh_blocks + quad_idx[j] * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glVertex3i(x, y, z);
}
glEnd();
mesh_blocks += 8;
poly_available--;
break;
}
default:
printf("unknown cmd: %8.8x\n", cmd);
break;
}
number_mesh--;
}
}
glPopMatrix();
//}
}
}
// first = false;
}
// ---------------------------------------------------------------------------
XenoModel * xenoModel;
int rot_x, rot_y;
Vector::CVector3f position;
void ReshapeWindowFunc(int width, int height)
{
const double kFOVy = 0.57735f;
const double kZNear = 0.1f;
const double kZFar = 1000.0f;
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, kZFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void MainRenderLoop(void)
{
// render
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
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);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glDisable(GL_LIGHTING);
//glDisable(GL_BLEND);
//glEnable(GL_TEXTURE_2D);
//glEnable(GL_TEXTURE_RECTANGLE_EXT);
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(position[0], position[1], position[2]);
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);
xenoModel->draw();
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 'w': {
float sx = sin((float)rot_x * M_PI * 2.0f / 256.0f);
float cx = -cos((float)rot_x * M_PI * 2.0f / 256.0f);
float sy = sin((float)rot_y * M_PI * 2.0f / 256.0f);
float cy = -cos((float)rot_y * M_PI * 2.0f / 256.0f);
position[0] += sy * cx;
position[1] += sx;
position[2] += cy * cx;
break;
}
case 'x': {
float sx = sin((float)rot_x * M_PI * 2.0f / 256.0f);
float cx = -cos((float)rot_x * M_PI * 2.0f / 256.0f);
float sy = sin((float)rot_y * M_PI * 2.0f / 256.0f);
float cy = -cos((float)rot_y * M_PI * 2.0f / 256.0f);
position[0] -= sy * cx;
position[1] -= sx;
position[2] -= cy * cx;
break;
}
case 27:
exit(0);
break;
}
}
u32 getRawData(void * archiveData, int archiveSize, void ** data, int index)
{
u32 offset = get_u32le(((u8*)archiveData) + 0x0130 + index * 4);
u32 compLen = (index < 8 ? get_u32le(((u8*)archiveData) + 0x0134 + index * 4) : archiveSize) - offset;
*data = ((u8*)archiveData) + offset;
return compLen;
}
u32 getData(void * archiveData, int archiveSize, void ** data, int index)
{
u32 offset = get_u32le(((u8*)archiveData) + 0x0130 + index * 4);
u32 compLen = (index < 8 ? get_u32le(((u8*)archiveData) + 0x0134 + index * 4) : archiveSize) - offset;
u32 uncompLen = get_u32le(((u8*)archiveData) + 0x010c + index * 4);
u32 uncompLenHeader = get_u32le(((u8*)archiveData) + offset);
// printf("h:%4.4x b:%4.4x\n", uncompLen, uncompLenHeader);
*data = malloc(uncompLenHeader);
*data = decompress_lzs(((u8*)archiveData) + offset + 4, compLen, *data, uncompLenHeader);
return uncompLen;
}
void dumpText(void * textData, unsigned int textSize)
{
u32 textCount = get_u32le(((u8*)textData)) + 1;
for (int i=0; i<textCount; i++) {
u32 textOffset = get_u16le(((u8*)textData) + i * 2 + 4);
if (textOffset > 0) {
if (textOffset > textSize) {
printf("--- overflow ---\n");
return;
}
u8 textPosX = ((u8*)textData)[4 + textCount * 2 + i * 2];
u8 textPosY = ((u8*)textData)[4 + textCount * 2 + i * 2 + 1];
printf("%4.4x %i/%i\n", textOffset, textPosX, textPosY);
u8 * text = ((u8*)textData) + textOffset;
while (*text != 0) {
switch (*text) {
case 0x01: printf("\n"); break;
case 0x02: printf("\n[new dialog]\n"); break;
case 0x03: printf("[input]"); break;
case 0x0F: {
text++;
u8 opcode = *(text++);
u8 value = *text;
switch (opcode) {
case 0x00: printf("[delay %i]", value); break;
case 0x01: printf("[accelerator %i]", value); break;
case 0x02: printf("[delayed close %i]", value); break;
case 0x04: printf("[item %i]", value); break;
case 0x05: {
switch (value) {
case 7:
printf("[Chu Chu]");
break;
default:
printf("[char name %i]", value);
break;
}
break;
}
default: printf("[unknown %2.2x %i]", opcode, value); break;
}
break;
}
default:
if (textMap[*text] != 0) {
wprintf(L"%lc", textMap[*text]);
} else {
printf("[%2.2x]", *text);
}
break;
}
text++;
}
printf("\n----\n");
}
}
}
struct OpCode {
u8 code;
const char * name;
const char * args;
};
const OpCode opCodeList[] = {
{ 0x00, "Return", "" },
{ 0x01, "Jump", "w" },
{ 0x02, "ConditionalJumpTo", "bbwbw" },
{ 0x0b, "SpriteSet", "bb" },
{ 0x19, "SpriteSetPosition", "wwb" },
{ 0x20, "SpriteSetSolid", "bb" },
{ 0x26, "Wait", "bb" },
{ 0x2C, "SpritePlayAnimation", "b" },
{ 0x36, "VariableSetTrue", "bb" },
{ 0x37, "VariableSetFalse", "bb" },
{ 0x4A, "SpriteGoToPosition", "wwb" },
{ 0x69, "SpriteSetDirection", "bb" },
{ 0x6b, "SpriteRotateClockwise", "bb" },
{ 0x6C, "SpriteRotateAntiClockwise", "bb" },
{ 0x6F, "SpriteRotateToEntity", "b" },
{ 0x98, "MapLoad", "wbb" },
{ 0xA8, "VariableRandom", "bbbb" },
{ 0xB3, "FadeOut", "bb" },
{ 0xB4, "FadeIn", "bb" },
{ 0xB5, "CameraSetDirection", "bb" },
{ 0xC7, "CameraRotate", "bb" },
{ 0xC8, "CameraRotate2", "bb" },
{ 0xD2, "DialogShow", "bw" },
{ 0x1e, "Unknown", "b" },
{ 0x28, "Unknown", "b" },
{ 0xC4, "Unknown", "b" },
{ 0xC5, "Unknown", "b" },
{ 0x3c, "Unknown", "b" },
{ 0x5d, "Unknown", "bb" },
{ 0xf4, "Unknown", "bb" },
{ 0xfe, "Unknown", "bbb" },
{ 0xA4, "Unknown", "bbbb" },
{ 0x35, "Unknown", "w" }
};
const unsigned opCodeCount = sizeof(opCodeList) / sizeof(opCodeList[0]);
void dumpScript(void * scriptData, unsigned int scriptSize)
{
u16 entityCount = get_u32le((u8*)scriptData + 0x80);
u8 * code = (u8*)scriptData + entityCount * 0x40 + 0x84;
int codeLen = scriptSize - 0x84 - entityCount * 0x40;
int codePtr = 0;
while (codePtr < codeLen) {
for (int i=0; i<entityCount; i++) {
u8 * entityStart = (u8*)scriptData + i * 0x40 + 0x84;
for (int j=0; j<32; j++) {
u16 addr = get_u16le(entityStart + j * 2);
if (addr != 0 && addr == codePtr) {
printf("e%is%i:\n", i, j);
}
}
}
printf("[%4.4x] ", codePtr);
u8 cmd = code[codePtr++];
printf("%2.2x", cmd);
int opIndex = -1;
for(unsigned int i = 0; i < opCodeCount && opIndex < 0; i++) {
if( opCodeList[i].code == cmd ) {
opIndex = i;
}
}
if(opIndex < 0) {
printf(" Unknown\n");
} else {
int cmdSize = 0;
const char * arg = opCodeList[opIndex].args;
while(*arg) {
switch(*arg) {
case 'b': cmdSize += 1; break;
case 'w': cmdSize += 2; break;
}
arg++;
}
for (int i = 0; i<cmdSize; i++) {
printf(" %2.2x", code[codePtr+i]);
}
for (int i = cmdSize; i<10; i++) {
printf(" ");
}
printf("%s(", opCodeList[opIndex].name);
int argPos = 0;
arg = opCodeList[opIndex].args;
while(*arg) {
if(argPos > 0) {
printf(", ");
}
switch(*arg) {
case 'b':
printf("0x%2.2x", code[codePtr+argPos]);
argPos += 1;
break;
case 'w':
printf("0x%4.4x", get_u16le(code + codePtr + argPos));
argPos += 2;
break;
}
arg++;
}
printf(")\n");
codePtr += cmdSize;
}
}
}
const char * section[] =
{
"unknown",
"walkmesh",
"models",
"unknown",
"unknown",
"scripts",
"unknown",
"text",
"unknown"
};
void dumpDisplaylist(void * archiveData)
{
u16 unknown1 = get_u16le(((u8*)archiveData) + 0x0184);
u16 unknown2 = get_u16le(((u8*)archiveData) + 0x0186);
u32 unknown3 = get_u32le(((u8*)archiveData) + 0x0188);
u32 count = get_u32le(((u8*)archiveData) + 0x018C);
printf("??:%i ??:%i ??:%i count:%i\n", unknown1, unknown2, unknown3, count);
for (u32 i=0; i<count; i++) {
void * base = ((u8*)archiveData) + 0x0190 + i * 16;
for(u32 j=0; j<8; j++) {
int val = (s16)get_u16le(((u8*)base) + j * 2);
printf("%6i ", val);
}
printf("\n");
}
}
int main(int argc, char ** argv)
{
if (argc != 2) {
printf("view_level <index>\n");
} else {
#if 1
// 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);
#endif
setlocale(LC_CTYPE, "en_US");
#if 0
for (int i=0; i<255; i++) {
wprintf(L"%lc", textCypher[i]);
if ((i & 15) == 15) { printf("\n"); }
}
#endif
// access files by index
//for (int i=0; i<730; i++) {
// printf("Room:%i\n", i);
int index = atoi(argv[1]);
int disk = 1;
char path[256];
// 730 rooms
// load data
void * archiveData = NULL;
sprintf(path, "disk%i/dir%i/file%i.bin", disk, 11, index * 2);
int archiveSize = load(path, &archiveData);
if (strncmp((char*)archiveData, "It's", 4) == 0) {
printf("%s\n", (char*)archiveData);
return 0;
}
void * textureData = NULL;
sprintf(path, "disk%i/dir%i/file%i.bin", disk, 11, index * 2 + 1);
int textureSize = load(path, &textureData);
// dumpDisplaylist(archiveData);
void * walkmeshData = NULL;
int walkmeshSize = getData(archiveData, archiveSize, &walkmeshData, 1);
void * modelData = NULL;
int modelSize = getData(archiveData, archiveSize, &modelData, 2);
void * scriptData = NULL;
int scriptSize = getData(archiveData, archiveSize, &scriptData, 5);
void * textData = NULL;
int textSize = getData(archiveData, archiveSize, &textData, 7);
#if 0
for (int i=0; i<9; i++) {
void * sectionData = NULL;
int sectionSize = getData(archiveData, archiveSize, §ionData, i);
char name [256];
sprintf(name, "file%is%i-%s.bin", index, i, section[i]);
save(name, sectionData, sectionSize);
}
#endif
//dumpText(textData, textSize);
//dumpScript(scriptData, scriptSize);
// init camera
position[0] = 0;
position[1] = -1;
position[2] = 0;
rot_y = rot_x = 0;
#if 1
// setup viewing code
xenoModel = new XenoModel(archiveData, modelData, walkmeshData);
xenoModel->uploadTextures(textureData, textureSize);
//xenoModel->dumpVRAM();
// main (this will never return)
glutMainLoop();
#endif
//}
}
return 0;
}
I was asked for the code, so here it is... very hacky...Mostly based on Akari's information.