Author Topic: (PSX) FF7 battle stage viewer?  (Read 16735 times)

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
(PSX) FF7 battle stage viewer?
« on: 2013-03-29 15:41:01 »
Zangan was created for the PC version, but there's no commonly-referred-to app for the PSX version.

I've come across this thread: http://forums.qhimm.com/index.php?topic=3152.0

...which contains micky's app for viewing PSX FF7 battle stages, but after considerable effort to get it to compile (which I finally managed after slapping together a myriad of old libs and include files, and editing the source a bit), it dies with a segmentation fault when fed its expected argument (with no argument, it reminds me to provide a file name for the stage I want to load; with the file name specified, it dies). Reviewing the posts in that thread, I'm not sure anyone actually got it to work in Windows (micky had it working in Linux). In case I'm wrong, does anyone have a compiled, working copy of this app? Or, can anyone else manage to compile it into a working program?

Next I stumbled upon this thread: http://forums.qhimm.com/index.php?topic=4292.0

...which seems to imply that Cyberman had a semi-working program for viewing PSX FF7 battle stage files. So, would anyone (including Cyberman) happen to have a copy of that? Or, any further documentation than I've found in these two threads?

There's only this very brief description on the wiki: http://wiki.qhimm.com/view/FF7/Battle/Battle_Field

Any help or nudge in the right direction is appreciated. I didn't build micky's app on this machine, but I can grab my modified source and post that, along with gdb output for debugging reference, if anyone wants to look at what the hell is crashing this app (seems to be a screwy array, but I don't see what's broken about it).

Thanks in advance.

ajthedj747

  • *
  • Posts: 305
  • Looking Forward to 2017
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #1 on: 2013-03-29 15:47:37 »
Grosse Gott.
I shall help you look for it.


*Update* - http://wiki.qhimm.com/view/FF7/Battle/Battle_scenes
Code: [Select]
There are few programs written that will help you edit scene.bin file: Scene Reader  SceneEdit  Scenester  Proud Clod Spinningcone.com - now that is an interesting name for a company website.


Oh wait, the website is still under construction.
« Last Edit: 2013-03-29 15:54:11 by ajthedj747 »

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #2 on: 2013-03-29 15:54:31 »
Those are for editing battle scenes, not battle stages. I'm aware of those, but they aren't what I'm looking for. Battle stage files contain the actual background image data.

For the PC version, there is Zangan:


ajthedj747

  • *
  • Posts: 305
  • Looking Forward to 2017
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #3 on: 2013-03-29 15:57:06 »
Yup, I knew it. Sorry about that. Um, maybe I should ask other members of this forum?


Have you tried to edit a scene.bin with Zangan for the Final Fantasy VII for the Playstation? I may have read that the scene.bin may be structured in a way that it may be edited. Or I am just completely wrong.
« Last Edit: 2013-03-29 15:59:05 by ajthedj747 »

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #4 on: 2013-03-29 16:01:35 »
FF7 PSX stage files are in the STAGE1\ and STAGE2\ directories, with file names of the format STAGExx.LZS. SCENE.BIN is not related.

And nope, Zangan doesn't open STAGExx.LZS files, whether they're compressed or decompressed. One reason would be TIM texture data rather than TEX, but I suspect some of the other data is rearranged or encoded differently as well.

Even if I sit down and break a few STAGExx.LZS files down and document their contingent parts, it won't get me to a viewer, since I don't have any experience in writing graphic-viewing apps. Plus, it seems like at least a couple of other people have completely or nearly-completely mapped out the format, so I'd just be re-inventing the wheel, so to speak.
« Last Edit: 2013-03-29 16:03:25 by Lazy Bastard »

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #5 on: 2013-03-29 16:56:36 »
I just tried mine again... it should still work. Try running a debug build in visual studio and let me know where it crashes. You can set the argument in the project settings. It needs to be a full path to a stage in  STAGE1 or STAGE2. I guess STAGE04.LZS is the one you've got in the windows viewer.

ajthedj747

  • *
  • Posts: 305
  • Looking Forward to 2017
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #6 on: 2013-03-29 16:58:48 »
Thank Heavens, a solution. Wait, visual studio? Oh, Microsoft Visual Studio 2012. I shall see if I can figure out a way to edit the data with it.
« Last Edit: 2013-03-30 18:26:34 by ajthedj747 »

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #7 on: 2013-03-29 17:23:44 »
I'm using MinGW, but I can use gdb to provide debugging data (and that'll likely be more useful anyway, since you're using Linux).

First, here's my current makefle:

Code: [Select]
all: stageview

stageview: stageview.o
gcc $< -o $@ -L/usr/lib -lopengl32 -lglu32 -lglut32 -lstdc++

stageview.o: stageview.cpp
gcc $< -c -o $@ -I/usr/include -g


And here's my current stageview.cpp:

Code: [Select]
// System
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>



// STL
#include <string>
#include <cassert>



// GLUT
#include <glut/glut.h>

// OpenGL
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glext.h>

using namespace std;

typedef unsigned char u8;
typedef unsigned short int u16;
typedef unsigned long int u32;

typedef signed char s8;
typedef signed short int s16;
typedef signed long int s32;

// utility functions

u16 get_u16le(void const * const buf)
{
return ((u8*)buf)[0] | (((u8*)buf)[1]<<8);
}

u32 get_u32le(void const * const buf)
{
return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16) | (((u8*)buf)[3]<<24);
}

int load( const string & name, void ** data )
{
        size_t size = 0;
        *data = NULL;
        int fd = open( name.c_str(), O_RDONLY, 0 );
        if (fd >= 0) {
                struct stat sb;
                if ( fstat( fd, &sb ) >= 0) {
                        assert( sb.st_size > 0 );
                        void *tmp = malloc( sb.st_size );
                        if ( tmp!=NULL ) {
                                if (read( fd, tmp, sb.st_size ) == sb.st_size) {
                                        *data = tmp;
                                        size = sb.st_size;
                                } else {
                                        free(tmp);
                                }
                        }
                }
                close( fd );
        }
        return size;
}

int load_lzs(const string & name, void ** data )
{
// read compressed data
u8 *buf;
int tsize = load( name, (void**)&buf );
int isize = get_u32le(buf) + 4;

if (isize != tsize) {
free(buf);
*data = NULL;
return 0;
}

// decompress;
int osize = (isize + 255) & ~255;
u8 * obuf = (u8 *)malloc(osize);
int iofs = 4, oofs = 0;
u8 cmd=0, bit=0;
while (iofs < isize) {
if (bit == 0) {
cmd = buf[iofs++];
bit = 8;
}
if (cmd&1) {
obuf[oofs++]=buf[iofs++];
if (oofs==osize) {
osize+=256;
obuf = (u8*)realloc(obuf, osize);
}
} else {
u8 a = buf[iofs++];
u8 b = buf[iofs++];
u16 o = a | ((b&0xF0)<<4);
u8 len = (b&0xF)+3;

int rofs =  oofs - ((oofs - 18 - o) & 0xFFF);
for (int j=0; j<len; j++) {
if (rofs < 0) {
obuf[oofs++]=0;
} else {
obuf[oofs++]=obuf[rofs];
}
if (oofs==osize) {
osize+=256;
obuf = (u8*)realloc(obuf, osize);
}
rofs++;
}
}
cmd>>=1;
bit--;
}
free( buf );

*data = obuf;
return oofs;
}


// Background loading and storage

struct Vertex {
s16 x, y, z;
s16 u, v;
};

struct Element {
u16 a, b, c, s; // s = shader
};

struct Mesh {
int texture;
int element_start;
int element_count;
};

struct Shader {
int texture;
int palette;
};

struct Background {
int vertex_count;
Vertex *vertex;
int element_count;
Element *element;
int *indexbuffer;
int shader_count;
Shader *shader;
GLuint *textures;
int mesh_count;
Mesh *mesh;
};

int
add_vertex(Background *bg, s16 x, s16 y, s16 z, u8 u, u8 v)
{
for (int i=0; i<bg->vertex_count; i++) {
if (bg->vertex[i].x == x && bg->vertex[i].y == y && bg->vertex[i].z == z && bg->vertex[i].u == u && bg->vertex[i].v == v) {
return i;
}
}

int idx = bg->vertex_count++;
bg->vertex = (Vertex *)realloc(bg->vertex, bg->vertex_count * sizeof(Vertex));
bg->vertex[idx].x = x;
bg->vertex[idx].y = y;
bg->vertex[idx].z = z;
bg->vertex[idx].u = u;
bg->vertex[idx].v = v;
return idx;
}

int
add_triangle(Background *bg, u16 a, u16 b, u16 c, u16 s)
{
int idx = bg->element_count++;
bg->element = (Element*)realloc(bg->element, bg->element_count * sizeof(Element));
bg->element[idx].a = a;
bg->element[idx].b = b;
bg->element[idx].c = c;
bg->element[idx].s = s;
return idx;
}

int
add_shader(Background *bg, int texture, int palette)
{
for (int i=0; i<bg->shader_count; i++) {
if (bg->shader[i].texture == texture && bg->shader[i].palette == palette) {
return i;
}
}

int idx = bg->shader_count++;
bg->shader = (Shader *)realloc(bg->shader, bg->shader_count * sizeof(Shader));
bg->shader[idx].texture = texture;
bg->shader[idx].palette = palette;

return idx;
}

void
load_background(const string & filename, Background *bg)
{
bg->vertex_count = 0;
bg->vertex = NULL;
bg->element_count = 0;
bg->element = NULL;
bg->textures = NULL;
bg->shader_count = 0;
bg->shader = NULL;
bg->mesh_count = 0;
bg->mesh = NULL;

u8 * data;
int size = load_lzs(filename, (void**)&data);

int num_pointer = get_u32le(data);

// background texture
int texdata = get_u32le(data + num_pointer * 4);
int npal = get_u16le(data + texdata + 18);
int paldata_size = get_u32le(data+texdata+8);
int pal_ofs = texdata + 20;
int picdata_size = get_u32le(data+texdata+8+paldata_size);
int pic_ofs = texdata + 20 + npal*512 + 12;

int xsize = get_u16le(data+pic_ofs-4)*2;
int ysize = get_u16le(data+pic_ofs-2);

// convert mesh data
for (int i=1; i<num_pointer-1; i++) {
int base = get_u32le(data + 4 + i * 4);

// triangles
int triangle_offset = base + 4 + get_u32le(data + base);
int num_triangles = get_u16le(data + triangle_offset);
int mesh_tri_flags = get_u16le(data + triangle_offset + 2);

int texture_idx = ((mesh_tri_flags & 0x0E)-6) / 2;
for (int j=0; j<num_triangles; j++) {
int point[3];
int ofs = triangle_offset + 4 + j * 16;
for (int k=0; k<3; k++) {
int p = get_u16le(data + ofs + k * 2) + base + 4;
s16 x =  ((s16)get_u16le(data + p + 0*2));
s16 y = -((s16)get_u16le(data + p + 1*2));
s16 z =  ((s16)get_u16le(data + p + 2*2));

const int uv_offsets[3] = { 8, 12, 14 };
u8 u = data[ofs + uv_offsets[k] + 0];
u8 v = data[ofs + uv_offsets[k] + 1];
point[k] = add_vertex(bg, x,y,z,u,v);
}

u16 flags1 = get_u16le(data + ofs + 6);
u16 flags2 = get_u16le(data + ofs + 10);

int palette_idx = (flags2 >> 6) & 7;
int shader_idx = add_shader(bg, texture_idx, palette_idx);

add_triangle(bg, point[0], point[1], point[2], shader_idx);
}

// quads
int quad_offset = triangle_offset + 4 + num_triangles * 16;
int num_quads = get_u16le(data + quad_offset);
int mesh_quad_flags = get_u16le(data + quad_offset + 2);
for (int j=0; j<num_quads; j++) {
int point[4];
int ofs = quad_offset + 4 + j * 20;
for (int k=0; k<4; k++) {
int p = get_u16le(data + ofs + k * 2) + base + 4;
s16 x =  ((s16)get_u16le(data + p + 0 * 2));
s16 y = -((s16)get_u16le(data + p + 1 * 2));
s16 z =  ((s16)get_u16le(data + p + 2 * 2));

const int uv_offsets[4] = { 8, 12, 14, 16 };
u8 u = data[ofs + uv_offsets[k] + 0];
u8 v = data[ofs + uv_offsets[k] + 1];
point[k] = add_vertex(bg, x,y,z,u,v);
}
u16 flags1 = get_u16le(data + ofs + 18);
u16 flags2 = get_u16le(data + ofs + 10);

int palette_idx = (flags2 >> 6) & 7;
int shader_idx = add_shader(bg, texture_idx, palette_idx);

add_triangle(bg, point[0], point[1], point[2], shader_idx);
add_triangle(bg, point[3], point[2], point[1], shader_idx);
}
}

// build meshes
bg->indexbuffer = (int*)malloc(bg->element_count * 3 * sizeof(int));
int cur_mesh = 0;
for (int i=0; i<bg->element_count; i++) {
bg->indexbuffer[i * 3 + 0] = bg->element[i].a;
bg->indexbuffer[i * 3 + 1] = bg->element[i].b;
bg->indexbuffer[i * 3 + 2] = bg->element[i].c;

if (bg->mesh_count > 0 && bg->mesh[cur_mesh].texture == bg->element[i].s) {
bg->mesh[cur_mesh].element_count++;
} else {
cur_mesh = bg->mesh_count++;
bg->mesh = (Mesh*)realloc(bg->mesh, bg->mesh_count * sizeof(Mesh));
bg->mesh[cur_mesh].texture = bg->element[i].s;
bg->mesh[cur_mesh].element_start = i*3;
bg->mesh[cur_mesh].element_count = 1;
}
}

// build required textures
struct RGBA {
u8 r, g, b, a;
};

RGBA *clut = (RGBA*)malloc(sizeof(RGBA) * 256 * (npal));
for (int j = 0; j < npal; j++) {
for (int i=0; i<256; i++) {
u16 col = (data[i*2 + pal_ofs + j*512 + 1]<<8) | data[i*2 + pal_ofs + j * 512];
clut[i+j*256].r = (((col      ) & 31) * 255 + 15) / 31;
clut[i+j*256].g = (((col >>  5) & 31) * 255 + 15) / 31;
clut[i+j*256].b = (((col >> 10) & 31) * 255 + 15) / 31;
clut[i+j*256].a = ((col & 0x8000) || (col == 0)) ? 0 : 255;
}
}

bg->textures = (GLuint*)malloc(sizeof(GLuint) * bg->shader_count);
glGenTextures( bg->shader_count, bg->textures);
RGBA *texture = (RGBA*)malloc(sizeof(RGBA) * 256 * 256);
for (int i=0; i<bg->shader_count; i++) {
// build
#if 1
if ( bg->shader[i].palette >= npal ) {
printf("%i %i\n", bg->shader[i].palette, npal);
bg->shader[i].palette = 0;
}
#endif
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
texture[y*256+x] = clut[data[pic_ofs + y * xsize + x + bg->shader[i].texture * 256] + bg->shader[i].palette * 256];
}
}

// copy to GL
glBindTexture( GL_TEXTURE_2D, bg->textures[i]);
#if 0
// blocky
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
#else
// interpolated
gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGBA, 256, 256, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );
#endif
}
free(texture);
free(clut);
free(data);
}

// GLUT and OpenGL stuff
Background battle;
int rot_y;
int rot_x;

void ReshapeWindowFunc(int width, int height)
{
const double kFOVy = 0.57735;
const double kZNear = 0.1;

glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble aspect = ((GLfloat)width / (GLfloat)height) * (kZNear * kFOVy);
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void MainRenderLoop(void)
{
// render
glClearColor(0, 0, 0, 1.00);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LEQUAL );
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 1.0f/255.0f);
glEnable( GL_TEXTURE_2D );
glEnable( GL_CULL_FACE );
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
        glDisable( GL_LIGHTING );
glDisable( GL_BLEND );

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef( (float)rot_x * 360.0f / 256.0f, 1.0f, 0.0f, 0.0f );
glRotatef( (float)rot_y * 360.0f / 256.0f, 0.0f, 1.0f, 0.0f );
glTranslatef( 0, -16, 0);
glScalef( 1.0f/256.0f, 1.0f/256.0f, 1.0f/256.0f );

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f/256.0f, 1.0f/256.0f, 0);
glTranslatef( 0.5f, 0.5f, 0);

glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );

glVertexPointer( 3, GL_SHORT, sizeof(Vertex), &(battle.vertex[0].x) );
glTexCoordPointer( 2, GL_SHORT, sizeof(Vertex), &(battle.vertex[0].u) );

for (int i = 0; i < battle.mesh_count; i++) {
glBindTexture(GL_TEXTURE_2D, battle.textures[battle.mesh[i].texture]);
glDrawElements(GL_TRIANGLES, battle.mesh[i].element_count * 3, GL_UNSIGNED_INT, battle.indexbuffer + battle.mesh[i].element_start);
}

glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );

glFlush();
}

void SpecialHandler(int key, int x, int y)
{
switch (key) {
case GLUT_KEY_UP:
rot_x -= 4;
if (rot_x < -64) {
rot_x = -64;
}
break;
case GLUT_KEY_DOWN:
rot_x += 4;
if (rot_x > 64) {
rot_x = 64;
}
break;
case GLUT_KEY_LEFT:
rot_y -= 5;
break;
case GLUT_KEY_RIGHT:
rot_y += 5;
break;
}
glutPostRedisplay();
}

void KeyboardHandler(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
}
}

int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "stageview <stage.lzs>\n");
} else {
// get current directory
char cwd[1024];
getcwd(cwd, sizeof(cwd));

              string path;
if (argv[1][0] != 0 && argv[1][0] != '/' && argv[1][1] != 0 && argv[1][1] != ':' && argv[1][2] != 0 && argv[1][2] != '\\') {
path = string(cwd) + "/";
}

// init opengl and glut
glutInit(&argc, (char **) argv);
glutInitWindowSize(640, 480);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
glutCreateWindow(argv[0]);
glutDisplayFunc(MainRenderLoop);
glutIdleFunc(MainRenderLoop);
glutReshapeFunc(ReshapeWindowFunc);
glutKeyboardFunc(KeyboardHandler);
glutSpecialFunc(SpecialHandler);

// load data
load_background(path + argv[1], &battle);

rot_y = rot_x = 0;

// main (this will never return)
glutMainLoop();
}
return 0;
}


gdb using STAGE04.LZS (since you mentioned that specific file, though the outcome is the same regardless of which file I use...even if the file doesn't exist or isn't the right format):

Code: [Select]

[USERNAME OMITTED]@[HOST OMITTED] ~
$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\stageview.exe...done.
(gdb) r C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\STAGE04.LZS
Starting program: C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USERNAME OMITTED]\STAGE04.LZS
[New Thread 848.0xc08]

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb) where
#0  0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
#1  0x00401557 in load_lzs (name=..., data=0x22fa30) at stageview.cpp:77
#2  0x00401ad0 in load_background (filename=..., bg=0x40c020)
    at stageview.cpp:228
#3  0x00402e49 in main (argc=2, argv=0x3e26f8) at stageview.cpp:502
(gdb)


So, it seems to dislike the array for get_u32le.

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #8 on: 2013-03-29 17:39:40 »
It looks like it failed to find/load the stage file. Maybe mingw munges the path somehow? Or there is a conflict in the code that tries to make the path absolute? Can you show the current value of filename in load_background? (I should really port that to python sometime, to make it more portable.)

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #9 on: 2013-03-29 17:57:00 »
Hmm...I don't exactly know how to do that with gdb. Let me know if this is the correct method, or if there's another feature that would be better suited:

Code: [Select]
(gdb) r C:\MinGW\msys\1.0\home\[USER OMITTED]\STAGE04.LZS
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USER OMITTED]\STAGE04.LZS
[New Thread 2904.0x6e4]

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb) p 'load_background'::filename
$15 = (const std::string &) @0x22ff3c: {static npos = <optimized out>,
  _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<
No data fields>}, <No data fields>},
    _M_p = 0x3e2b04 "C:\\MinGW\\msys\\1.0\\home\\[USER OMITTED]\\STAGE04.LZS"}}
(gdb)

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #10 on: 2013-03-29 18:06:12 »
Hmm... that looks correct. At this point I'd step through load_lzs and see where it fails. Though I don't have mingw installed anywhere and it has been ages since I used gdb directly, I'm using xcode on MacOS and Visual Studio 2005 (because I'm poor) on Windows.

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #11 on: 2013-03-29 18:10:42 »
Code: [Select]
$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe...done.
(gdb) b load_lzs
Breakpoint 1 at 0x401537: file stageview.cpp, line 76.
(gdb) r C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
Starting program: C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
[New Thread 2760.0xa88]

Breakpoint 1, load_lzs (name=..., data=0x22fa30) at stageview.cpp:76
76              int tsize = load( name, (void**)&buf );
(gdb) n
77              int isize = get_u32le(buf) + 4;
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb)

So, same place.

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #12 on: 2013-03-29 18:17:58 »
In case it's of interest, I've also done so for load_background and main:

Code: [Select]
$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe...done.
(gdb) b load_background
Breakpoint 1 at 0x401a62: file stageview.cpp, line 217.
(gdb) r C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
Starting program: C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
[New Thread 1124.0xf7c]

Breakpoint 1, load_background (filename=..., bg=0x40c020) at stageview.cpp:217
217             bg->vertex_count = 0;
(gdb) n
218             bg->vertex = NULL;
(gdb) n
219             bg->element_count = 0;
(gdb) n
220             bg->element = NULL;
(gdb) n
221             bg->textures = NULL;
(gdb) n
222             bg->shader_count = 0;
(gdb) n
223             bg->shader = NULL;
(gdb) n
224             bg->mesh_count = 0;
(gdb) n
225             bg->mesh = NULL;
(gdb) n
228             int size = load_lzs(filename, (void**)&data);
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb)


Code: [Select]

$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe...done.
(gdb) b main
Breakpoint 1 at 0x402c41: file stageview.cpp, line 477.
(gdb) r C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
Starting program: C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
[New Thread 3776.0x1ac]

Breakpoint 1, main (argc=2, argv=0x3e26f8) at stageview.cpp:477
477     {
(gdb) n
478             if (argc != 2) {
(gdb) n
483                     getcwd(cwd, sizeof(cwd));
(gdb) n
485                   string path;
(gdb) n
486                     if (argv[1][0] != 0 && argv[1][0] != '/' && argv[1][1] !
= 0 && argv[1][1] != ':' && argv[1][2] != 0 && argv[1][2] != '\\') {
(gdb) n
491                     glutInit(&argc, (char **) argv);
(gdb) n
492                     glutInitWindowSize(640, 480);
(gdb) n
493                     glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH
);
(gdb) n
494                     glutCreateWindow(argv[0]);
(gdb) n
495                     glutDisplayFunc(MainRenderLoop);
(gdb) n
496                     glutIdleFunc(MainRenderLoop);
(gdb) n
497                     glutReshapeFunc(ReshapeWindowFunc);
(gdb) n
498                     glutKeyboardFunc(KeyboardHandler);
(gdb) n
499                     glutSpecialFunc(SpecialHandler);
(gdb) n
502                     load_background(path + argv[1], &battle);
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb)


...but as you can see, it's the same issue.

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #13 on: 2013-03-29 18:20:43 »
Wait, if you're using Visual Studio in Windows, couldn't you just compile a copy of the program?  :)  That's all I'm shooting for here (sure, playing with source is fun, but my goal is to get the app working).

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #14 on: 2013-03-29 18:39:19 »
Unfortunately since I installed a new HD a few weeks ago my netbook doesn't really have anything FF7 related on it. I'll have to try tomorrow if I can find some time to copy everything over again.
You are stepping over load_lzs. I assume it returns NULL in data, because it somehow fails to load the file. What is interesting is if it fails to open the file, or fails to decompress it. Though I have no experience with mingw, if it needs anything special.

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #15 on: 2013-03-29 19:09:55 »
Cool; hopefully you can figure something out. If so, I'll add it to the GameHacking.org Downloads section, for posterity.

Not sure what you mean by "stepping over load_lzs". In the first post after yours, I set a breakpoint on load_lzs, and stepped through it...did I miss something?

Cyberman

  • *
  • Posts: 1572
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #16 on: 2013-03-30 15:01:15 »
In case it's of interest, I've also done so for load_background and main:

Code: [Select]

$ gdb stageview
GNU gdb (GDB) 7.4
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-mingw32".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe...done.
(gdb) b main
Breakpoint 1 at 0x402c41: file stageview.cpp, line 477.
(gdb) r C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
Starting program: C:\MinGW\msys\1.0\home\[USER OMITTED]\stageview.exe C:\MinGW\msys\1.0\home\[USER OMITTED]\stage04.lzs
[New Thread 3776.0x1ac]

Breakpoint 1, main (argc=2, argv=0x3e26f8) at stageview.cpp:477
477     {
(gdb) n
478             if (argc != 2) {
(gdb) n
483                     getcwd(cwd, sizeof(cwd));
(gdb) n
485                   string path;
(gdb) n
486                     if (argv[1][0] != 0 && argv[1][0] != '/' && argv[1][1] !
= 0 && argv[1][1] != ':' && argv[1][2] != 0 && argv[1][2] != '\\') {
(gdb) n
491                     glutInit(&argc, (char **) argv);
(gdb) n
492                     glutInitWindowSize(640, 480);
(gdb) n
493                     glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH
);
(gdb) n
494                     glutCreateWindow(argv[0]);
(gdb) n
495                     glutDisplayFunc(MainRenderLoop);
(gdb) n
496                     glutIdleFunc(MainRenderLoop);
(gdb) n
497                     glutReshapeFunc(ReshapeWindowFunc);
(gdb) n
498                     glutKeyboardFunc(KeyboardHandler);
(gdb) n
499                     glutSpecialFunc(SpecialHandler);
(gdb) n
502                     load_background(path + argv[1], &battle);
(gdb) n

Program received signal SIGSEGV, Segmentation fault.
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
(gdb)


...but as you can see, it's the same issue.
LB: If you look carefully the debugger indicated buf was a NULL pointer (see this line)
Code: [Select]
0x00401414 in get_u32le (buf=0x0) at stageview.cpp:45
45              return ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16)
| (((u8*)buf)[3]<<24);
which means it can't dereference a reference to NULL in the function (by the way should it be checking for NULL pointers before using one?).
Erstwhile what Mickey was suggesting might be a bit difficult with straight gdb you could try debugging the application under Eclipse with MinGW. What I believe was being suggested was to inspect buf before it's used.

Cool; hopefully you can figure something out. If so, I'll add it to the GameHacking.org Downloads section, for posterity.

Not sure what you mean by "stepping over load_lzs". In the first post after yours, I set a breakpoint on load_lzs, and stepped through it...did I miss something?

It appears what is going wrong is before the get_u32le function is called because buf is being passed as a NULL pointer (0).

That is probably whats going wrong.
Code: [Select]
u16 get_u16le(void const * const buf)
{
u16 temp;

temp = 0;
if(buf)
{
temp = ((u8*)buf)[0] | (((u8*)buf)[1]<<8);
}
return temp;
}

u32 get_u32le(void const * const buf)
{
u32 temp;

temp = 0;
if(buf)
{
temp = ((u8*)buf)[0] | (((u8*)buf)[1]<<8) | (((u8*)buf)[2]<<16) | (((u8*)buf)[3]<<24);
}
return temp;
}
would prevent the error from croping up, however if it's passing NULL pointers that Is the real problem line 78 in function
Code: [Select]
int load_lzs(const string & name, void ** data ) is where get_u32le is called
Code: [Select]
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;
The prior function call is likely returning a NULL pointer in buff (hence the load function is likely too blame).
going to load
Code: [Select]
int load( const string & name, void ** data )
{
        size_t size = 0;
        *data = NULL;
        int fd = open( name.c_str(), O_RDONLY, 0 );
initializes data to NULL (or buf since this is a reference to buf in the prior function).
skipping a bit further down if it can't open the file or can't allocate the space it returns NULL for the buffer and 0 for the size.

Code: [Select]
        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;
}

So it may make sense to do something like
Code: [Select]
int load_lzs(const string & name, void ** data )
{
// read compressed data
u8 *buf;
int tsize, isize;

// attempt to open file
tsize = load( name, (void**)&buf );
// if we successfully opened the file
if(tsize)
{ // get the internal file size and add the header value
isize = get_u32le(buf) + 4;
// if the file size and the internal file size + offset aren't equal
if (isize != tsize) {
free(buf);
*data = NULL;
return 0;
}
}
else
{ // clean up
*data = NULL;
return 0;
}
I'm not sure where one would dump errors too (error.log?) granted this tool was Micky's quick hack, so yes their might be a few 'problems' with it if everything isn't perfect. Anyhow THAT is why Micky was saying it's probably having a problem with opening the file.

Cyb

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #17 on: 2013-03-30 19:30:07 »
Try this: http://bin.mypage.sk/FILES/release.zip
It works with the background I tried.
Cyberman is right, there is no error checking. I should probably put an assert into the get_le* functions, the NULL pointer should be really checked outside, in cases where a NULL can be returned.

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #18 on: 2013-03-31 01:52:54 »
Cyberman: Good tips; thanks very much. I'll keep those in mind in the future.

Micky: Awesome; it works :)  Mind sharing the current source as well?

Micky

  • *
  • Posts: 300
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #19 on: 2013-03-31 08:06:19 »
I have started to port it to python, I'll post it here once it is done.

Cyberman

  • *
  • Posts: 1572
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #20 on: 2013-03-31 14:50:28 »
I have started to port it to python, I'll post it here once it is done.
Python is cool, that should work under linux without any fiddling around.
In the mean time I'll see if I can get the "old" one working under linux (with some error checking of course). I had complete forgot about this .. duh?

Cyb

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #21 on: 2013-04-02 04:11:23 »
I've hosted a mirror of StageView's Windows executable on GameHacking.org (with credit, of course), including a quick batch script I wrote to provide a primitive menu system for StageView, in case that's desired by the user: HERE.

The script allows the user to either type the name of the stage file to load, or the word "all", which will load a separate instance of stageview.exe for each valid stage file within a directory (my system could handle loading all stage files simultaneously; who knows how that will act on slower systems). If the user types the name of a valid, existent stage file, but neglects to add the .lzs file extension, the script will append it to the provided file name and try that. If the user types the name of a file that doesn't exist, or that isn't a true stage file, the appropriate message will be displayed, and the user will be asked to try again. After successfully loading one or more stage files, if stageview.exe is closed, the batch script will remain open and allow the user to type another file name (or "all").

Hopefully that's slightly useful to someone.

In case anyone wants to see Micky's app in action:

« Last Edit: 2013-04-02 04:44:42 by Lazy Bastard »

MiLo83

  • *
  • Posts: 6
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #22 on: 2013-04-03 18:47:44 »
Micky, I'm MiLO83 working with LazyBastard.
I am animating / rendering some FMVs for him, but before I became an animator I was a ROM hacker.
I partially translated Brave Story for PSP, put Zelda as the playabe char in Minish Cap GBA, and made Link a driver in Mario Kart Super Curcuit GBA, etc etc. Patching, Lz compression, pointer tables, blah blah.

Believe it or not I can code for squat.
I can however take something thats coded and adjust it lol.

Anyway, have you heard of Noesis? Its a python model viewer / converter with a .py plugin system.
You can write your own import export scripts and the GUI, model preview controls etc are already there.
I'm am potitioning for a move to this platform as it already has import / export scripts for the best 3d model formats / software.

If you are already re-writing the Battle Stage Viewer in .py, can you please write it as an import (/ export ?) script for NOESIS?


NOESIS HOME:

http://oasis.xentax.com/index.php?content=downloads


Here are some quick docs to get you started! :D

http://forum.xentax.com/viewtopic.php?f=29&t=8319&hilit=tutorial+exploring+file+formats
http://forum.xentax.com/viewtopic.php?f=29&t=7760
http://code.google.com/p/noesis-plugins-official/source/browse/trunk/chrrox/export/beta/fmt_noepyxml.py

"http://forum.xentax.com" is an amazing hang out for all this model format reversing stuff.

I have plenty more to ask of you, cause i typed my first python hello world yesterday (trying to convert your PSX Battle Model viewer python script from 2009 to a Noesis plugin myself.) but i'll give time to read about Noesis first. The main idea is you can import and export models to / from Noesis to so many different formats.

    - MiLO83

 

Lazy Bastard

  • *
  • Posts: 290
  • I may be lazy, but I can...zzzZZZzzzZZZzzz...
    • View Profile
    • GameHacking.org
Re: (PSX) FF7 battle stage viewer?
« Reply #23 on: 2013-04-03 19:12:33 »
I have plenty more to ask of you, cause i typed my first python hello world yesterday (trying to convert your PSX Battle Model viewer python script from 2009 to a Noesis plugin myself.) but i'll give time to read about Noesis first. The main idea is you can import and export models to / from Noesis to so many different formats.

FYI, Micky's model viewer script was for field models, not battle models - http://forums.qhimm.com/index.php?topic=8969.msg122920#msg122920

And yes, I agree - Noesis looks quite promising.
« Last Edit: 2013-04-03 19:14:34 by Lazy Bastard »

MiLo83

  • *
  • Posts: 6
    • View Profile
Re: (PSX) FF7 battle stage viewer?
« Reply #24 on: 2013-04-03 19:17:37 »
@LazyBastard LOL "Thanks Jimmy!"