[Xenogears] Level viewer in Python

  • Thread starter Thread starter Micky
  • Start date Start date
Status
Not open for further replies.
M

Micky

Guest
I dug out my old Xenogears field viewer again:
Xenogears.png

As you can see, some background parts and the walkmesh are rotated relative to each other. Has anyone seen a transform set, animations or a skeleton in the files? Or is that set from the script?
 
Xenogears brings back memories. It's too bad Namco messed up Xenosaga 2 so badly that only one game release was left (3). 
12 year old games :D  (xenogears)

Interesting view, it looks familiar.

Cyb
 
Xenogears brings back memories. It's too bad Namco messed up Xenosaga 2 so badly that only one game release was left (3). 
12 year old games :D  (xenogears)
Who knows? Didn't many people resign during or after Xenosaga 2? That normally points to internal trouble.
The only thing I know is that Xenosaga 3 was a lot better, especially the battle systems. And it has a lot of Xenogears cameos and references. I was still hoping that they load the Zohar onto the Eldridge at the end, but it was just another cliffhanger. Sigh.
Interesting view, it looks familiar.
That's inside Aveh castle. It's an extreme case where one half of the meshes match the walk mesh, and the other half is rotated relative to the walkmesh. In most other levels all meshes have the same rotation relative to the walkmesh, and there are only a few where it matches.
 
Sorry for the thread necromancy, but if anybody cares:
Xenogears2.jpg

There is a displaylist between the model/data/text/script offset data and the beginning of the model data:
0x0184: u16: unknown
0x0186: u16: unknown
0x0188: u32: unknown
0x018c: u32: number of instances
0x0190: number * 16 byte instance data
instance:
u16 flags : mostly unknown, though items with value 480 may not be visible
u16 rot_x : 1024 units = 90 degree
u16 rot_y
u16 rot_z
s16 trans_x
s16 trans_y
s16 trans_z
u16 mesh_index : which mesh to draw from the model data
 
Last edited:
You fixed it?
That is what I was trying to say. I can display all the field maps with correct placement of individual meshes, though I'll have to work out the flags some time. So if anyone has a chance to update the Wiki (uploading Akari's X-Gears first)...
 
Hmmm, interesting. Is animations for field meshes also stores there? Or just idle?
 
What do the colored control lines mean green = walkmesh, yellow = camera?
 
Hmmm, interesting. Is animations for field meshes also stores there? Or just idle?
I haven't found that, yet. Maybe they just place a field model into the scene and animate that if they want an animation. Or the animation is controlled by the script, like doors. The table only contains the base transform for each instance.
What do the colored control lines mean green = walkmesh, yellow = camera?
The data is a list of meshes with connectivity, and I draw each one with a new colour. I assume the decision if they're a camera constraint or a walkmesh is done based on flags or from the script, but I haven't found that, yet. For areas where the character can walk on several levels there are actually several overlapping walkmeshes. It's possible they set the current walkmesh based on trigger zones.
 
Code: [Select]
Code:
// https://www.ff7catalog.com/threads/3003/// https://www.ff7catalog.com/posts/47174/// https://www.ff7catalog.com/posts/52325/// 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 0bool first = true;#endifvoid 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, &sectionData, 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.
 
It's always appreciated when people share Micky LOL

I haven't really done anything with FF7/8/9 in 2 years scarey huh?
However I may have some time to tinker.
I have WAY too many games... SIGH. :D

Cyb
 
To avoid thread necromancy:
Code: (Python) [Select]
Code:
import sys, struct, array, math, osfrom OpenGL.GL import *from OpenGL.GLU import *import pygamefrom pygame.locals import *def decompressLzs(data, size):    """Decompress Xenogears-style compressed data. The data must exclude the initial size word"""    ibuf = array.array("B", data)    obuf = array.array("B")        iofs = 0    cmd = 0    bit = 0    while iofs < len(ibuf) and len(obuf) < size: if bit == 0:     cmd = ibuf[iofs]     bit = 8     iofs += 1 if cmd & 1:     a = ibuf[iofs]     iofs += 1     b = ibuf[iofs]     iofs += 1          o = a | ((b & 0x0F) << 8)     l = ((b & 0xF0) >> 4) + 3     rofs = len(obuf) - o     for j in xrange(l):  if rofs < 0:      obuf.append(0)  else:      obuf.append(obuf[rofs])  rofs += 1 else:     obuf.append(ibuf[iofs])     iofs += 1 cmd >>= 1 bit -= 1    return obuf.tostring()def loadLzs(name):    """Read a compressed file from disc"""    f = open(name, "rb")    buf = f.read()    f.close()        (size,) = struct.unpack_from("<I", buf, 0)    return decompressLzs(buf[4:], size)def getData(archiveData, index):    """Get the compressed data block out of the field map archive"""    (offset,) = struct.unpack_from("<I", archiveData, 0x0130 + index * 4)    dataEnd = len(archiveData)    if index < 8: (dataEnd,) = struct.unpack_from("<I", archiveData, 0x0134 + index * 4)    (size,) = struct.unpack_from("<I", archiveData, 0x010c + index * 4)    dataStart = offset + 4    return decompressLzs(archiveData[dataStart:dataEnd], size) class ShaderBuilder:    def __init__(self): self.shaders = {}     def getShader(self, data): try:     return self.shaders[data] except KeyError:     i = len(self.shaders)     self.shaders[data] = i     return i         def getList(self): items = [(val, key) for (key, val) in self.shaders.items()] items.sort() return [x for _,x in items]abe_alpha = [128, 0, 0, 64]def loadModel(data):    shaderBuilder = ShaderBuilder()    object = {}    object["parts"] = []    (partCount,) = struct.unpack_from("<I", data, 0)    for partIndex in xrange(partCount): (partOffset,) = struct.unpack_from("<I", data, 4 + partIndex * 4) part = {} part["blocks"] = [] (blockCount,) = struct.unpack_from("<I", data, partOffset) for blockIndex in xrange(blockCount):     blockOffset = partOffset + 16 + blockIndex * 0x38     (vertexCount, meshCount, meshBlockCount, vertexOffset, normalOffset, meshBlockOffset, displayListOffset) = struct.unpack_from("<xxHHHIIII", data, blockOffset)     tri_tex = []     quad_tex = []     tri_mono = []     quad_mono = []     status = 0     clut = 0     for meshBlockIndex in xrange(meshBlockCount):  # init the mesh block  quad_block, polyCount = struct.unpack_from("<BxH", data, partOffset + meshBlockOffset)  meshBlockOffset += 4    while polyCount > 0:       # decode command      (cmd,) = struct.unpack_from("<I", data, partOffset + displayListOffset)            hp = ((cmd >> 24) & 16) != 0     # geraud shading      quad = ((cmd >> 24) & 8) != 0     # quad or tri      tme = ((cmd >> 24) & 4) != 0     # texture mapping      abe = ((cmd >> 24) & 2) != 0     # semi transparency      fullbright = ((cmd >> 24) & 1) != 0     # bypass lighting      op = (cmd >> 24) & 255      # operator      pop = op & ~(16|2|1)      # operator, with shading and lighting mask      displayListOffset += 4      if op == 0xC4: # texture page   status = cmd & 0xFFFF      elif op == 0xC8: # clut   clut = cmd & 0xFFFF      elif pop == 0x24: # triangle with texture    (ua, va, ub, vb) = struct.unpack_from("<BBBB", data, partOffset + displayListOffset)   uc = cmd & 255   vc = (cmd >> 8) & 255   displayListOffset += 4      shader = shaderBuilder.getShader((status, clut, abe, True))      vertex = []   for j in xrange(3):       (vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)       (x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)       if hp:    (nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)       else:    (nx, ny, nz) = (0, 0, 0)       vertex.append(((x, y, z), (nx, ny, nz)))          tri_tex.append((shader, ((vertex[0][0], vertex[0][1], (ua, va)), (vertex[2][0], vertex[2][1], (uc, vc)), (vertex[1][0], vertex[1][1], (ub, vb)))))      meshBlockOffset += 8   polyCount -= 1      elif pop == 0x2C: # quad with texture        (ua, va, ub, vb, uc, vc, ud, vd) = struct.unpack_from("<BBBBBBBB", data, partOffset + displayListOffset)   displayListOffset += 8   shader = shaderBuilder.getShader((status, clut, abe, True))      vertex = []   for j in xrange(4):       (vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)       (x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)       if hp:    (nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)       else:    (nx, ny, nz) = (0, 0, 0)       vertex.append(((x, y, z), (nx, ny, nz)))      quad_tex.append((shader, ((vertex[1][0], vertex[1][1], (ub, vb)), (vertex[0][0], vertex[0][1], (ua, va)), (vertex[2][0], vertex[2][1], (uc, vc)), (vertex[3][0], vertex[3][1], (ud, vd)))))      meshBlockOffset += 8   polyCount -= 1      elif pop == 0x20: # monochrome triangle   if abe:       abr = (status >> 5) & 3       alpha = abe_alpha[abr]   else:       alpha = 255         col = ((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, alpha)   shader = shaderBuilder.getShader((status, clut, abe, False))   vertex = []   for j in xrange(3):       (vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)       (x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)       if hp:    (nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)       else:    (nx, ny, nz) = (0, 0, 0)       vertex.append(((x, y, z), (nx, ny, nz)))   tri_mono.append((shader, ((vertex[0][0], vertex[0][1], col), (vertex[2][0], vertex[2][1], col), (vertex[1][0], vertex[1][1], col))))      meshBlockOffset += 8   polyCount -= 1      elif pop == 0x28: # monochrome quad   if abe:       abr = (status >> 5) & 3       alpha = abe_alpha[abr]   else:       alpha = 255   col = ((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, alpha)   shader = shaderBuilder.getShader((status, clut, abe, False))   vertex = []   for j in xrange(4):       (vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)       (x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)       if hp:    (nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)       else:    (nx, ny, nz) = (0, 0, 0)       vertex.append(((x, y, z), (nx, ny, nz)))   quad_mono.append((shader, ((vertex[1][0], vertex[1][1], col), (vertex[0][0], vertex[0][1], col), (vertex[2][0], vertex[2][1], col), (vertex[3][0], vertex[3][1], col))))      meshBlockOffset += 8   polyCount -= 1      else:   print("unknown cmd: %8.8x\n" % cmd)     block = {}     block["tri_tex"] = tri_tex     block["quad_tex"] = quad_tex     block["tri_mono"] = tri_mono     block["quad_mono"] = quad_mono     part["blocks"].append(block) object["parts"].append(part)    object["shaders"] = shaderBuilder.getList()        return objectdef getColour(col, abe, alpha):    stp = (col & 0x8000) != 0    r = (((col     ) & 31) * 255 + 15) / 31    g = (((col >>  5) & 31) * 255 + 15) / 31    b = (((col >> 10) & 31) * 255 + 15) / 31    if (col & 0x7FFF) == 0: if stp:     a = 255 else:     a = 0    elif stp and abe: a = alpha    else: a = 255    return (r<<24)|(g<<16)|(b<<8)|a    def loadTextures(textureData, shaderList):    vram = array.array("B", [0] * (2048 * 1024))    # unpack MIM data into "VRAM"    offset = 0    while offset < len(textureData): header = offset (type, pos_x, pos_y, move_x, move_y, width, height, chunks) = struct.unpack_from("<IHHHHHHxxH", textureData, header) # print (type, pos_x, pos_y, move_x, move_y, width, height, chunks) blockSize = 0x1C + chunks * 2 offset += (blockSize + 2047) & ~2047 for i in xrange(chunks):     (height,) = struct.unpack_from("<H", textureData, header + 0x1C)     for j in xrange(height):  vramAddr = (pos_y + move_y + j) * 2048 + (pos_x + move_x) * 2  texAddr = offset + j * width * 2  for k in xrange(width * 2):      vram[vramAddr] = ord(textureData[texAddr])      vramAddr += 1      texAddr += 1     pos_y += height     blockSize = width * height * 2     offset += (blockSize + 2047) & ~2047    if False: f = open("vram.bin", "wb") vram.tofile(f) f.close()        # convert textures with their palette    textures = []    for shader in shaderList: (status, clut, abe, tme) = shader if tme:     tx = (status & 0xF) * 64 * 2     ty = ((status >> 4) & 1) * 256     abr = (status >> 5) & 3     tp = (status >> 7) & 3     px = (clut & 63) * 16     py = clut >> 6          if abe:  alpha = abe_alpha[abr]     else:  alpha = 0          image = array.array("I")     if tp == 0: # 4-bit  pal = array.array("I")  for idx in xrange(16):      vaddr = py * 2048 + idx * 2 + px * 2      col = vram[vaddr] + vram[vaddr+1] * 256      pal.append(getColour(col, abe, alpha))  for y in xrange(256):      for x in xrange(256):   val = vram[(y + ty) * 2048 + (x/2) + tx]   if x & 1:       idx = val >> 4   else:       idx = val & 0xF   image.append(pal[idx])  del pal     elif tp == 1:  pal = array.array("I")  for idx in xrange(256):      vaddr = py * 2048 + idx * 2 + px * 2      col = vram[vaddr] + vram[vaddr+1] * 256      pal.append(getColour(col, abe, alpha))  for y in xrange(256):      for x in xrange(256):   idx = vram[(y + ty) * 2048 + x + tx];   image.append(pal[idx])  del pal     elif tp == 2:  for y in xrange(256):      for x in xrange(256):   vaddr = (y + ty) * 2048 + x * 2 + tx   col = vram[vaddr] + vram[vaddr+1] * 256   image.append(getColour(col, abe, alpha))           textures.append(image.tostring())     del image else:     textures.append(None)    del vram    return texturesclass OpenGLObject:    def __init__(self, model): self.model = model self.drawNormals = False self.list = None self.abe = None self.abr = None self.texture = None self.textureList = [] for t in model["textures"]:     if t is not None:  texIndex = glGenTextures(1)  glBindTexture(GL_TEXTURE_2D, texIndex)  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, t)  if True:      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)  else:      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE)  self.textureList.append(texIndex)     else:  self.textureList.append(None)     def setBlend(self, status, abe): if abe:     if self.abe != abe:  self.abe = abe  glEnable(GL_BLEND)  glDisable(GL_ALPHA_TEST)     abr = (status >> 3) & 3     if self.abr != abr:  self.abr = abr  if abr == 0:       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);      glBlendEquation(GL_FUNC_ADD);  elif abr == 1:      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);      glBlendEquation(GL_FUNC_ADD);  elif abr == 2:      glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);      glBlendEquation(GL_FUNC_SUBTRACT);  elif abr == 3:      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);      glBlendEquation(GL_FUNC_ADD); else:     if self.abe != abe:  self.abe = abe  glDisable(GL_BLEND)  glEnable(GL_ALPHA_TEST)  glAlphaFunc(GL_GREATER, 0.0)        def setTexture(self, texture): if self.texture != texture:     self.texture = texture     glBindTexture(GL_TEXTURE_2D, texture)        def drawPart(self, part, trans): for block in part["blocks"]:     glDisable(GL_TEXTURE_2D);     # glColor4ub(255,255,255,255)     if len(block["tri_mono"]) > 0:  for tri_mono in block["tri_mono"]:      (status, _, abe, tme) = self.model["shaders"][tri_mono[0]]      if (abe and trans) or (not abe and not trans):   self.setBlend(status, abe)   glBegin(GL_TRIANGLES)   for j in xrange(3):       pos, normal, colour = tri_mono[1][j]       glColor4ub(colour[0], colour[1], colour[2], colour[3])       glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)        glVertex3i(pos[0], pos[1], pos[2])   glEnd()     if len(block["quad_mono"]) > 0:  for quad_mono in block["quad_mono"]:      (status, _, abe, tme) = self.model["shaders"][quad_mono[0]]      if (abe and trans) or (not abe and not trans):   self.setBlend(status, abe)   glBegin(GL_QUADS)   for j in xrange(4):       pos, normal, colour = quad_mono[1][j]       glColor4ub(colour[0], colour[1], colour[2], colour[3])       glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)        glVertex3i(pos[0], pos[1], pos[2])   glEnd()     # glColor4ub(255,255,255,255)     if True:  glEnable(GL_TEXTURE_2D)  if len(block["tri_tex"]) > 0:      for tri_tex in block["tri_tex"]:   (status, _, abe, tme) = self.model["shaders"][tri_tex[0]]   if (abe and trans) or (not abe and not trans):       self.setBlend(status, abe)       self.setTexture(self.textureList[tri_tex[0]])       glBegin(GL_TRIANGLES)       for j in xrange(3):    pos, normal, uv = tri_tex[1][j]    glTexCoord2i(uv[0], uv[1])    glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)     glVertex3i(pos[0], pos[1], pos[2])       glEnd()  if len(block["quad_tex"]) > 0:      for quad_tex in block["quad_tex"]:   (status, _, abe, tme) = self.model["shaders"][quad_tex[0]]   if (abe and trans) or (not abe and not trans):       self.setBlend(status, abe)       self.setTexture(self.textureList[quad_tex[0]])       glBegin(GL_QUADS)       for j in xrange(4):    pos, normal, uv = quad_tex[1][j]    glTexCoord2i(uv[0], uv[1])    glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)     glVertex3i(pos[0], pos[1], pos[2])       glEnd()        def draw(self): glMatrixMode(GL_TEXTURE) glLoadIdentity() glScalef(1.0/256.0, 1.0/256.0, 0) glTranslatef(0.5, 0.5, 0.0)     glMatrixMode(GL_MODELVIEW) glRotatef(180, 1, 0, 0)  if self.list is None:     self.list = glGenLists(1)     glNewList(self.list, GL_COMPILE_AND_EXECUTE)     # render opaque objects     glDepthMask(GL_TRUE)     for item in self.model["nodes"]:  (flags, index, pos, rot) = item  if flags != 480:      glPushMatrix()      glTranslatef(pos[0], pos[1], pos[2])      glRotatef(rot[0] * 90.0 / 1024.0, 1, 0, 0)      glRotatef(rot[1] * 90.0 / 1024.0, 0, 1, 0)      glRotatef(rot[2] * 90.0 / 1024.0, 0, 0, 1)      self.drawPart(self.model["parts"][index], False)      glPopMatrix()          # render transparent objects     glDepthMask(GL_FALSE)     for item in self.model["nodes"]:  (flags, index, pos, rot) = item  if flags != 480:      glPushMatrix()      glTranslatef(pos[0], pos[1], pos[2])      glRotatef(rot[0] * 90.0 / 1024.0, 1, 0, 0)      glRotatef(rot[1] * 90.0 / 1024.0, 0, 1, 0)      glRotatef(rot[2] * 90.0 / 1024.0, 0, 0, 1)      self.drawPart(self.model["parts"][index], True)      glPopMatrix()     glDepthMask(GL_TRUE)     glEndList() else:     glCallList(self.list)         def clearList(self): glDeleteLists(self.list, 1) self.list = Nonedef getNodes(archiveData):    nodes = []    (itemCount,) = struct.unpack_from("<I", archiveData, 0x018C)    for itemIndex in xrange(itemCount): (flags, rot_x, rot_y, rot_z, pos_x, pos_y, pos_z, index) = struct.unpack_from("<HHHHhhhH", archiveData, 0x0190 + itemIndex * 16) nodes.append((flags, index, (pos_x, pos_y, pos_z), (rot_x, rot_y, rot_z)))    return nodesdef writeData(f, node):    f.write("<%s" % node[0])    items = node[1].items()    items.sort()    for key, value in items: f.write(" %s=\"%s\"" % (key, value))    if len(node[2]) > 0: f.write(">") x = [] for item in node[2]:     if type(item) == tuple:  if len(x) > 0:      f.write(" ".join(x))      x = []  writeData(f, item)     else:  x.append(str(item)) if len(x) > 0:     f.write(" ".join(x))     x = [] f.write("</%s>" % node[0])    else: f.write("/>\n")def flattenBuffer(buffer):    l = [(value,key) for key,value in buffer.items()]    l.sort()    v = []    for x in [list(key) for _,key in l]: v.extend(x)    return vdef saveModel(name, model):    basename = os.path.splitext(name)[0]    print("saving textures...")    for index,t in enumerate(model["textures"]): if t is not None:     n = "%s_%i.tga" % (basename, index)     image = pygame.image.fromstring(t, (256, 256), "RGBA", True)     pygame.image.save(image, n)     del image    print "saving model..."    collada = ("COLLADA", {"xmlns":"http://www.collada.org/2005/11/COLLADASchema", "version":"1.4.0"}, [])    library_visual_scenes = ("library_visual_scenes", {}, [])    visual_scene = ("visual_scene", {"id":"scene", "name":"level"}, [])    for i,n in enumerate(model["nodes"]): (flags, partIndex, pos, rot) = n nodeName = "node%i_%4.4x_%i" % (i, flags, partIndex) node = ("node", {"name":nodeName, "id":nodeName}, []) if pos[0] != 0 or pos[1] != 0 or pos[2] != 0:     translate = ("translate", {}, [pos[0], pos[1], pos[2]])     node[2].append(translate) if rot[0] != 0:     rotate = ("rotate", {}, [1,0,0,rot[0] * 90.0 / 1024.0])     node[2].append(rotate) if rot[1] != 0:     rotate = ("rotate", {}, [0,1,0,rot[1] * 90.0 / 1024.0])     node[2].append(rotate) if rot[2] != 0:     rotate = ("rotate", {}, [0,0,1,rot[2] * 90.0 / 1024.0])     node[2].append(rotate) if flags != 480:     for blockIndex, block in enumerate(model["parts"][partIndex]["blocks"]):  shaders = set([shader for shader,_ in block["tri_tex"]]+[shader for shader,_ in block["quad_tex"]]+[shader for shader,_ in block["tri_mono"]]+[shader for shader,_ in block["quad_mono"]])  for s in shaders:      meshName = "#mesh%i_%i_%i" % (partIndex, blockIndex,s)      instance_geometry = ("instance_geometry", {"url":meshName}, [])      bind_material = ("bind_material", {}, [])      technique_common = ("technique_common", {}, [])      shader = model["shaders"][s]      instance_material = ("instance_material", {"symbol":"slot%i" % s, "target":"#material%i" % s}, [])      if shader[3]:   bind_vertex_input = ("bind_vertex_input", {"semantic":"UVSET0", "input_semantic":"TEXCOORD", "input_set":0}, [])   instance_material[2].append(bind_vertex_input)      else:   bind_vertex_input = ("bind_vertex_input", {"semantic":"DIFFUSE", "input_semantic":"COLOUR", "input_set":0}, [])   instance_material[2].append(bind_vertex_input)      technique_common[2].append(instance_material)      bind_material[2].append(technique_common)      instance_geometry[2].append(bind_material)      node[2].append(instance_geometry) visual_scene[2].append(node)    library_visual_scenes[2].append(visual_scene)    collada[2].append(library_visual_scenes)    library_images = ("library_images", {}, [])    for index,t in enumerate(model["textures"]): if t is not None:     n = "%s_%i.tga" % (basename, index)     image = ("image", {"id":"image%i" % index}, [  ("init_from", {}, [n])     ])     library_images[2].append(image)    collada[2].append(library_images)    library_geometries = ("library_geometries", {}, [])    for partIndex, part in enumerate(model["parts"]): for blockIndex, block in enumerate(part["blocks"]):         # create separate mesh for each shader and for textured and untextured geometry     meshlist = {}        for tri_tex in block["tri_tex"]:  (shader, prim) = tri_tex  try:      d = meshlist[shader]  except KeyError:      d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}      meshlist[shader] = d  poly = []  for vtx in prim:      try:   p = d["position"][vtx[0]]      except KeyError:   p = len(d["position"])   d["position"][vtx[0]] = p      try:   n = d["normal"][vtx[1]]      except KeyError:   n = len(d["normal"])   d["normal"][vtx[1]] = n      try:   t = d["uv"][vtx[2]]      except KeyError:   t = len(d["uv"])   d["uv"][vtx[2]] = t      poly.append((p,n,t))  d["poly"].append(poly)     for quad_tex in block["quad_tex"]:  (shader, prim) = quad_tex  try:      d = meshlist[shader]  except KeyError:      d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}      meshlist[shader] = d  poly = []  for vtx in prim:      try:   p = d["position"][vtx[0]]      except KeyError:   p = len(d["position"])   d["position"][vtx[0]] = p      try:   n = d["normal"][vtx[1]]      except KeyError:   n = len(d["normal"])   d["normal"][vtx[1]] = n      try:   t = d["uv"][vtx[2]]      except KeyError:   t = len(d["uv"])   d["uv"][vtx[2]] = t      poly.append((p,n,t))  d["poly"].append(poly)          for tri_mono in block["tri_mono"]:  (shader, prim) = tri_mono  try:      d = meshlist[shader]  except KeyError:      d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}      meshlist[shader] = d  poly = []  for vtx in prim:      try:   p = d["position"][vtx[0]]      except KeyError:   p = len(d["position"])   d["position"][vtx[0]] = p      try:   n = d["normal"][vtx[1]]      except KeyError:   n = len(d["normal"])   d["normal"][vtx[1]] = n      try:   c = d["colour"][vtx[2]]      except KeyError:   c = len(d["colour"])   d["colour"][vtx[2]] = c      poly.append((p,n,c))  d["poly"].append(poly)          for quad_mono in block["quad_mono"]:  (shader, prim) = quad_mono  try:      d = meshlist[shader]  except KeyError:      d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}      meshlist[shader] = d  poly = []  for vtx in prim:      try:   p = d["position"][vtx[0]]      except KeyError:   p = len(d["position"])   d["position"][vtx[0]] = p      try:   n = d["normal"][vtx[1]]      except KeyError:   n = len(d["normal"])   d["normal"][vtx[1]] = n      try:   c = d["colour"][vtx[2]]      except KeyError:   c = len(d["colour"])   d["colour"][vtx[2]] = c      poly.append((p,n,c))  d["poly"].append(poly)     for shader, meshdata in meshlist.items():  meshName = "mesh%i_%i_%i" % (partIndex, blockIndex, shader)  geometry = ("geometry", {"id":meshName}, [])  mesh = ("mesh", {}, [])    position = meshdata["position"]  if len(position) > 0:      source = ("source", {"id":"%s-position" % meshName}, [])      float_array = ("float_array", {"id":"%s-position-array" % meshName, "count":(len(position) * 3)}, [float(x) for x in flattenBuffer(position)])       source[2].append(float_array)      technique_common = ("technique_common", {}, [   ("accessor", {"count":len(position), "source":"%s-position-array" % meshName, "stride":3}, [       ("param", {"name":"X", "type":"float"}, []),       ("param", {"name":"Y", "type":"float"}, []),       ("param", {"name":"Z", "type":"float"}, [])   ])      ])      source[2].append(technique_common)      mesh[2].append(source)  normal = meshdata["normal"]  if len(normal) > 0:      source = ("source", {"id":"%s-normal" % meshName}, [])      float_array = ("float_array", {"id":"%s-normal-array" % meshName, "count":(len(normal) * 3)}, [float(x) / 4096.0 for x in flattenBuffer(normal)])       source[2].append(float_array)      technique_common = ("technique_common", {}, [   ("accessor", {"count":len(normal), "source":"%s-normal-array" % meshName, "stride":3}, [       ("param", {"name":"X", "type":"float"}, []),       ("param", {"name":"Y", "type":"float"}, []),       ("param", {"name":"Z", "type":"float"}, [])   ])      ])      source[2].append(technique_common)      mesh[2].append(source)  uv = meshdata["uv"]  if len(uv) > 0:      source = ("source", {"id":"%s-uv" % meshName}, [])      float_array = ("float_array", {"id":"%s-uv-array" % meshName, "count":(len(uv) * 2)}, [(float(x) + 0.5) / 256.0 for x in flattenBuffer(uv)])       source[2].append(float_array)      technique_common = ("technique_common", {}, [   ("accessor", {"count":len(uv), "source":"%s-uv-array" % meshName, "stride":2}, [       ("param", {"name":"U", "type":"float"}, []),       ("param", {"name":"V", "type":"float"}, [])   ])      ])      source[2].append(technique_common)      mesh[2].append(source)  colour = meshdata["colour"]  if len(colour) > 0:      source = ("source", {"id":"%s-colour" % meshName}, [])      float_array = ("float_array", {"id":"%s-colour-array" % meshName, "count":(len(colour) * 4)}, [float(x) / 255.0 for x in flattenBuffer(colour)])       source[2].append(float_array)      technique_common = ("technique_common", {}, [   ("accessor", {"count":len(colour), "source":"%s-colour-array" % meshName, "stride":4}, [       ("param", {"name":"R", "type":"float"}, []),       ("param", {"name":"G", "type":"float"}, []),       ("param", {"name":"B", "type":"float"}, []),       ("param", {"name":"A", "type":"float"}, [])   ])      ])      source[2].append(technique_common)      mesh[2].append(source)  vertices = ("vertices", {"id":"%s-vertex" % meshName}, [      ("input", {"semantic":"POSITION", "source":"#%s-position" % meshName}, [])  ])  mesh[2].append(vertices)          primitives = meshdata["poly"]  polylist = ("polylist", {"count":len(primitives), "material":"slot%i" % shader}, [])  input = ("input", {"semantic":"VERTEX", "source":"#%s-vertex" % meshName, "offset":0}, [])  polylist[2].append(input)  input = ("input", {"semantic":"NORMAL", "source":"#%s-normal" % meshName, "offset":1}, [])  polylist[2].append(input)  if len(uv) > 0:      input = ("input", {"semantic":"TEXCOORD", "source":"#%s-uv" % meshName, "offset":2}, [])      polylist[2].append(input)  if len(colour) > 0:      input = ("input", {"semantic":"COLOUR", "source":"#%s-colour" % meshName, "offset":2}, [])      polylist[2].append(input)  vcount = ("vcount", {}, [len(poly) for poly in primitives])  polylist[2].append(vcount)  p = ("p", {}, [])  for poly in primitives:      for vtx in poly:   p[2].extend(list(vtx))  polylist[2].append(p)  mesh[2].append(polylist)  geometry[2].append(mesh)  library_geometries[2].append(geometry)    collada[2].append(library_geometries)    library_materials = ("library_materials", {}, [])    for shaderIndex,shader in enumerate(model["shaders"]): matName = "material%i" % shaderIndex effName = "effect%i" % shaderIndex material = ("material", {"id":matName}, [     ("instance_effect", {"url":"#%s" % effName}, []) ]) library_materials[2].append(material)    collada[2].append(library_materials)        library_effects = ("library_effects", {}, [])    for shaderIndex,shader in enumerate(model["shaders"]): matName = "material%i" % shaderIndex effName = "effect%i" % shaderIndex effect = ("effect", {"id":effName}, []) profile_COMMON = ("profile_COMMON", {}, []) if shader[3]:     newparam = ("newparam", {"sid":"surface%i" % shaderIndex}, [  ("surface", {"type":"2D"}, [      ("init_from", {}, ["image%i" % shaderIndex])  ])     ])     profile_COMMON[2].append(newparam)     newparam = ("newparam", {"sid":"sampler%i" % shaderIndex}, [  ("sampler2D", {}, [      ("source", {}, ["surface%i" % shaderIndex])  ])     ])     profile_COMMON[2].append(newparam) else:     newparam = ("newparam", {"sid":"colour"}, [  ("semantic", {}, ["DIFFUSE"]),  ("modifier", {}, ["VARYING"])     ])     profile_COMMON[2].append(newparam) technique = ("technique", {"sid":"COMMON"}, []) lambert = ("lambert", {}, []) if shader[3]:     # textured     diffuse = ("diffuse", {}, [  ("texture", {"texture":"sampler%i" % shaderIndex, "texcoord":"UVSET0"}, [])     ])     lambert[2].append(diffuse) else:     # vertex colour     diffuse = ("diffuse", {}, [  ("param", {"ref":"colour"}, [])     ])     lambert[2].append(diffuse)  technique[2].append(lambert) profile_COMMON[2].append(technique) effect[2].append(profile_COMMON) library_effects[2].append(effect)    collada[2].append(library_effects)        scene = ("scene", {}, [ ("instance_visual_scene", {"url":"#scene"}, [])    ])    collada[2].append(scene)        f = open(name, "wt")    writeData(f, collada)    f.close()        print "done..."def main(*argv):    print "loading archive..."    diskIndex = 1 # there are disk 1 and disk 2    dirIndex = 11 # 0-based index    fileIndex = int(argv[0]) # 0-based index    archivePath = os.path.join("disk%i" % diskIndex, "dir%i" % dirIndex, "file%i.bin" % (fileIndex * 2))       f = open(archivePath, "rb")    archiveData = f.read()    f.close()        if archiveData[:4] == "It's": # file was removed from disk image print "This file was removed from the disk image. Most likely it is a room that is not reachable any more." return 0    modelData = getData(archiveData, 2)    print "loading texture..."    texturePath = os.path.join("disk%i" % diskIndex, "dir%i" % dirIndex, "file%i.bin" % (fileIndex * 2 + 1))        f = open(texturePath, "rb")    textureData = f.read()    f.close()    print "converting meshes..."    model = loadModel(modelData)        print "converting textures..."    model["textures"] = loadTextures(textureData, model["shaders"])        print "getting nodes..."    model["nodes"] = getNodes(archiveData)    print "starting OpenGL..."    pygame.init()        screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL)    glViewport(0, 0, 640, 480)    glMatrixMode(GL_PROJECTION)    glLoadIdentity()    kFOVy = 0.57735    kZNear = 10.0    kZFar = 10000.0    aspect = (640.0 / 480.0) * kZNear * kFOVy    glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar)    glEnable(GL_DEPTH_TEST)    glDepthFunc(GL_LEQUAL)    glDisable(GL_CULL_FACE)    glDisable(GL_LIGHTING)    glDisable(GL_BLEND)    glPolygonMode(GL_FRONT, GL_FILL)    glPolygonMode(GL_BACK, GL_LINE)    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)    wireframe = False        clock = pygame.time.Clock()    key_up = key_down = key_left = key_right = key_front = key_back = False    rot_x = 0.0    rot_y = 0.0    pos_x = 0.0    pos_y = 0    pos_z = -2000    object = OpenGLObject(model)    while True: for event in pygame.event.get():     if event.type == QUIT:  exit()     elif event.type == KEYDOWN:  if event.key == pygame.K_UP:      key_up = True  elif event.key == pygame.K_DOWN:      key_down = True  elif event.key == pygame.K_LEFT:      key_left = True  elif event.key == pygame.K_RIGHT:      key_right = True  elif event.key == pygame.K_w:      key_front = True  elif event.key == pygame.K_s:      key_back = True  elif event.key == pygame.K_ESCAPE:      exit()  elif event.key == pygame.K_d:      wireframe = not wireframe      if wireframe:          glPolygonMode(GL_FRONT, GL_LINE)      else:          glPolygonMode(GL_FRONT, GL_FILL)  elif event.key == pygame.K_c:      saveModel("level%i.dae" % fileIndex, model)     elif event.type == KEYUP:  if event.key == pygame.K_UP:      key_up = False  elif event.key == pygame.K_DOWN:      key_down = False  elif event.key == pygame.K_LEFT:      key_left = False  elif event.key == pygame.K_RIGHT:      key_right = False  elif event.key == pygame.K_w:      key_front = False  elif event.key == pygame.K_s:      key_back = False       if key_up:     rot_x -= 1.0 elif key_down:     rot_x += 1.0      if key_left:     rot_y -= 1.0 elif key_right:     rot_y += 1.0      if key_front:     sx = math.sin(rot_x * math.pi * 2.0 / 256.0)     cx = -math.cos(rot_x * math.pi * 2.0 / 256.0)     sy = math.sin(rot_y * math.pi * 2.0 / 256.0)     cy = -math.cos(rot_y * math.pi * 2.0 / 256.0)     pos_x += sy * cx * 16     pos_y += sx * 16     pos_z += cy * cx * 16 elif key_back:     sx = math.sin(rot_x * math.pi * 2.0 / 256.0)     cx = -math.cos(rot_x * math.pi * 2.0 / 256.0)     sy = math.sin(rot_y * math.pi * 2.0 / 256.0)     cy = -math.cos(rot_y * math.pi * 2.0 / 256.0)     pos_x -= sy * cx * 16     pos_y -= sx * 16     pos_z -= cy * cx * 16 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)  glMatrixMode(GL_PROJECTION) glLoadIdentity() glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar) if False:     glTranslatef(0, 0, -2000)     glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)     glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0) else:     glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0)     glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)     glTranslatef(pos_x, pos_y, pos_z) glMatrixMode(GL_MODELVIEW) glLoadIdentity()      object.draw()     clock.tick(60) pygame.display.flip()if __name__ == "__main__":    main(*sys.argv[1:])
I wrote a Python version of my Xenogears level viewer. It requires pygame and the python opengl bindings.
It takes a single argument with the room number [<=729], and it expects the data relative to the current directory in disk1/dir11/file<index>.bin. If you're unpacking your ISO, the directory index and file index are 0-based.
There is still a lot to decode, to debug and to optimise. But this version should handle transparencies a lot better then the last version. Texture conversion is a bit slow, though.

Update: Endian fix for Windows
Update: Better endian fix, mipmap fix, (slightly)faster texture conversion
Update: Collada Exporter
 
Last edited:
I don't think anybody would fault you for necro'ing your own thread with something completely on-topic.

I'd check this out if I had Xenogears.
 
Showing off transparency:
christmas.png

I think this is from the Zeboim-era Fei and Elly talking about making Esmeralda.
 
wait, I don't have my copy of Xenogears infront of me.. You have to unpack an archive off the disk to see that, right?
 
wait, I don't have my copy of Xenogears infront of me.. You have to unpack an archive off the disk to see that, right?
Yes. The ISO filesystem only contains the executable and system.cnf file. But LUCKY for you I ported my extraction tool to python as well this morning:

Moved to a separate thread.

You need raw images of your disk (a plain dump of all data, 2352 byte per sector). Just run it with your images as input, and it will create folders in your current directory. And it even automatically detects if your image is disk 1 or disk 2, or not a playstation or xenogears image at all.
I'm using the NTSC U/C version.
 
Last edited:
Showing off transparency:
christmas.png

I think this is from the Zeboim-era Fei and Elly talking about making Esmeralda.
That looks nice!! I wished it was available to do that in ff7 game though. :-\
 
This is perfect!!!!!!. Python!?!?. Damn how many languages are there!...Just kidding, python is the one book I skipped at the library...does it just view the scene or can I dump it somewhere in a 3d format of any kind?.
 
Last edited:
are there currently any programs similar to this for FF7 battle fields?
 
are there currently any programs similar to this for FF7 battle fields?
I'm sure Akari has a better one by now, but you could try this: https://www.ff7catalog.com/threads/1775/
This is perfect!!!!!!. Python!?!?. Damn how many languages are there!...Just kidding, python is the one book I skipped at the library...does it just view the scene or can I dump it somewhere in a 3d format of any kind?.
At the moment it is only a viewer, but I arranged the code in a way that I can easily write out the model and textures.

Update: I tried it on Windows and noticed there is an endian problem. And gluBuild2DMipmaps didn't work. The version in the first message should be fixed now.
screen.jpg
 
Last edited:
Status
Not open for further replies.
Back
Top