Monday, January 25, 2016

FF7's CHOCOBO.WAT - having fun with 3D models

While trying to understand the different sections of the WAT files, I made a tool to convert some of them to 3D models; you can download the result here:
http://www.geocities.jp/ergonomyjoe/chocobo_obj_20160126.zip

Of course it is just an interpretation of the original data, and it lacks some information, like transparency and billboards(sprites).


The converted sections are:
-LDG3, LDG4, SDG3 and SDG4: the S stands for "SHORT" and the L for "LONG" and DG3 is for triangles while DG4 is for quads.
-SDOMEG3 and LDOMEG3 which are the "skydomes" for respectively the SHORT and the LONG course; the happen to be the same.

I used a slightly improved Wavefront .obj format, to include color in the vertex information; depending on the viewer you use you may not be able to see them.

By the way, here is the C structures of these sections:

#pragma pack(1)
struct SVECTOR {
 short x,y,z,pad;
};

typedef union {//size 4
 struct {
  unsigned char r,g,b,a;
 }c;
 unsigned rgba;
}tRGBA;

struct t_chocobo_data_DOMEG3 {//size 0x24
 /*+00*/struct SVECTOR vert_0,vert_1,vert_2;
 /*+18*/unsigned color_0,color_1,color_2;
};

struct t_chocobo_data_DG3 {//size 0x1c
 /*+00*/struct SVECTOR vert_0,vert_1,vert_2;
 /*+18*/tRGBA color_0;
 //color_1 and color_2 are "hidden" in vert_0.pad, vert_1.pad, vert_2.pad
};

struct t_chocobo_data_DG4 {//size 0x28
 /*+00*/struct SVECTOR vert_0,vert_1,vert_2,vert_3;
 /*+20*/tRGBA color_0,color_1;
 //color_2 and color_3 are "hidden" in vert_1.pad, vert_2.pad, vert_3.pad, color_0.c.pad
};
#pragma pack()


Have fun!!

Friday, January 22, 2016

FF7's CHOCOBO.WAT

Here is some notes I made about the content of the file CHOCOBO.WAT (which is in the archive CHOCOBO.LGP).

CHOCOBO.WAT is a binary file. It contains two sets of data describing the track and events occuring during the chocobo racing mini-game. We first have the 'LONG' course then the 'SHORT' course.
They both have the same structure which is:
;----------
DW num_obj
    [struct t_chocobo_unknown_18(size 0x18)] (num_obj times)
    DB '{L|S}GUIDE'
    [struct t_chocobo_data_GUIDE(size 8)] (num_obj times)
DB '{L|S}MAPPOINTE'
DW num_obj
    [short(size 2)] (num_obj times)
DB '{L|S}MAPTABLE'
DW num_obj
    [int(size 4)] (num_obj times)
DB '{L|S}DOMEG3'
DW num_obj
    [struct t_chocobo_data_DOMEG3(size 0x24)] (num_obj times)
DB '{L|S}DG3'
DW num_obj
    [struct t_chocobo_data_DG3(size 0x1c)] (num_obj times)
    //LDG3 padded with EXE bits
DB '{L|S}DG4'
DW num_obj
    [struct t_chocobo_data_DG4(size 0x28)] (num_obj times)
DB '{L|S}SPRITE'
DW num_obj
    [struct t_chocobo_data_SPRITE(size 0x16)] (num_obj times)
    [pad (0x28 - 0x16) *  num_obj]
And then we have:
DB 'SHORT'    ;if after the 'LONG' section
or
DB 'END'    ;if after the 'SHORT' section
;----------
.{L|S} means the character 'L' or 'S' for Long and Short
.16-bit words are little endian

All words indicating the number of object of each section are ignored by FF7's main program: they are hard coded in the function that loads CHOCOBO.WAT.

  • About the padding

The section 'LDG3' contains only 0x1eb0 objects that make sense(while there is space for 0x2e49).
The remaining memory is filled with 0s and then with data that seems to come directly from and executable's run-time memory. Section 'SDG3' doesn't have this problem.
The funny thing is, this data contains the following strings:
    LGUIDE
    LMAPPOINTER
    LMAPTABLE
    LDOMEG3
    LDG3
    LDG4
    LSPRITE
    SHORT
    SGUIDE
    SMAPPOINTER
    SMAPTABLE
    SDOMEG3
    SDG3
    SDG4
    SSPRITE
    END
My guess is that we are looking at parts from the conversion executable (the one that generates the CHOCOBO.WAT file).
.When writing the tag "MAPPOINTER" to the file, one character was omited so the tag became "MAPPOINTE".
.The section 'LDG3' was allocated less space in memory than what was written to disk. So the memory immediately following this section was written to the file, and it seems that this memory was a part of the exe's initialized segment.
.Concerning the section LSPRITE and SSPRITE, there seems to have been another error: in this case, while the memory contained objects of size 0x16, they where written to the file with a command taking them as 0x28 byte-long objects. That's why we found some bits of the EXE file at the end of these sections too.

When I have more time, I will try to be more specific about the content of the structures.

Thursday, December 10, 2015

FF7's "is_lib.cpp"

If you carefully read the previous posts, you may have notice some function calls commented with is_lib:; these functions belong to the is_lib module which performs the read operations on .lgp files (i.e archives).

The access to an archive is basically done with the next calls:
  • C_00675511; "opens" the archive file, and associate it with and Id (must be below 0x12); subsequent calls will use this Id only.
  • C_006759D2; allows to get the starting offset of an item (identified by name) in an opened archive (identified by its Id); since the caller uses this offset only as a parameter to subsequent calls, it maybe easier to consider this function as an "opener" and the offset as a "handle".
  • C_006762EA; returns the size of an entry (identified by its "handle") inside an opened archive.
  • C_0067633E; loads an entry into a buffer provided by the caller.
  • C_00675F1D; "closes" an archive
  • C_00676064; cleans the module, i.e closes all opened archives

I don't know what LGP means, nor IS (I guess "lib" is for library), but here is the code to the module:
#define __FF7FILE__ "C:\\lib\\src\\file\\is_lib.cpp"

#include "ff7.h"

//(archive/LGP related)

////////////////////////////////////////
struct t_is_lib_fileentry {//size 0x18
 /*+00*/char name[0x10];
 /*+10*/int dwOffset;//in archive
 /*+14*/short f_14;//unused?
 /*+16*/unsigned short f_16;//index[+1] to path info
};

struct t_is_lib_fileinfo {//size 8
 /*+00*/struct t_is_lib_fileentry *pFileEntries;
 /*+04*/int dwNumEntries;
};

struct t_is_lib_pathentry {//size 0x82
 /*+00*/char f_00[0x80];//path
 /*+80*/unsigned short dwEntryIndex;
};

struct t_is_lib_pathinfo {//size 8
 /*+00*/char f_00; char __01[3];//# of entries
 /*+04*/struct t_is_lib_pathentry *f_04;
};

struct t_is_lib_index {//size 4
 /*+00*/unsigned short _first;//first index + 1
 /*+02*/unsigned short _num;//# of elements
};
////////////////////////////////////////
struct t_is_lib_fileinfo D_00D8F678[0x12];
int D_00D8F708[0x12];//# of "path info" objects?
struct t_is_lib_pathinfo D_00D8F750[0x12][0x3e8];//0x1f40=0x3e8*8
int D_00DB29D0[0x12];//file handles
char D_00DB2A18[0x80];//"virtual path"
struct t_is_lib_index *D_00DB2A98[0x12];//"fast indexes" -- 30x30 array
short D_00DB2AE0;//# of opened archives?
int D_00DB2AE4;//use "virtual path" flag
int D_00DB2AE8;//archive id[unused]
////////////////////////////////////////
//is_lib:set archive id(unused)
void C_006750E0(int bp08) {
 D_00DB2AE8 = bp08;
}

//is_lib:get archive id(unused)
int __006750ED() {
 return D_00DB2AE8;
}

//is_lib:fopen
FILE *__006750F7(const char *bp08, const char *bp0c) {
 FILE *local_1;
 
 local_1 = fopen(bp08, bp0c);

 return local_1;
}

//is_lib:fclose
short __00675115(FILE *bp08) {
 short local_1;

 if(bp08)
  local_1 = fclose(bp08);

 return local_1;
}

//is_lib:fseek
void C_00675137(FILE *bp08, long bp0c, int bp10) {
 fseek(bp08, bp0c, bp10 & 0xffff);
}

//is_lib:ftell
long C_00675155(FILE *bp08) {
 return ftell(bp08);
}

//is_lib:ffile size?
long __00675166(FILE *bp08) {
 long local_2;
 long local_1;

 local_2 = C_00675155(bp08);//is_lib:ftell
 C_00675137(bp08, 0, SEEK_END);//is_lib:fseek
 local_1 = C_00675155(bp08);//is_lib:ftell
 C_00675137(bp08, local_2, SEEK_SET);//is_lib:fseek

 return local_1;
}

//fread_u8
int __006751B3(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fread(bp0c, 1, 1, bp08);
 local_2 = (local_1 != 1);

 return !local_2;
}

//fread_u16
int __006751E9(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fread(bp0c, 1, 2, bp08);
 local_2 = (local_1 != 2);

 return !local_2;
}

//fread_u32
int __0067521F(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fread(bp0c, 1, 4, bp08);
 local_2 = (local_1 != 4);

 return !local_2;
}

//fread_bytes
short __00675255(FILE *bp08, void *bp0c, unsigned bp10) {
 int local_2;
 unsigned local_1;

 if(bp10 < 0)//nonsense
  return 0;
 local_1 = fread(bp0c, 1, bp10, bp08);
 local_2 = (local_1 != bp10);

 return !local_2;
}

//is_lib:_open
int C_0067529A(const char *bp08, int bp0c) {
 int local_1;
 
 local_1 = _open(bp08, bp0c);

 return local_1;
}

//is_lib:_close
short C_006752B8(int bp08) {
 short local_1;

 if(bp08 != -1)
  local_1 = _close(bp08);

 return local_1;
}

//is_lib:_lseek
void C_006752DA(int bp08, int bp0c, int bp10) {
 _lseek(bp08, bp0c, bp10 & 0xffff);
}

//is_lib:_tell
int C_006752F8(int bp08) {
 return _tell(bp08);
}

//is_lib:file size?
long __00675309(int bp08) {
 int local_2;
 int local_1;
 
 local_2 = C_006752F8(bp08);//is_lib:_tell
 C_006752DA(bp08, 0, SEEK_END);//is_lib:_lseek
 local_1 = C_006752F8(bp08);//is_lib:_tell
 C_006752DA(bp08, local_2, SEEK_SET);//is_lib:_lseek

 return local_1;
}

//read_u8
int C_00675356(int bp08, void *bp02) {
 int local_2;
 int local_1;

 local_1 = _read(bp08, bp02, 1);
 local_2 = (local_1 != 1);

 return !local_2;
}

//read_u16
int C_0067538A(int bp08, void *bp02) {
 int local_2;
 int local_1;

 local_1 = _read(bp08, bp02, 2);
 local_2 = (local_1 != 2);

 return !local_2;
}

//read_u32
int C_006753BE(int bp08, void *bp02) {
 int local_2;
 int local_1;

 local_1 = _read(bp08, bp02, 4);
 local_2 = (local_1 != 4);

 return !local_2;
}

//read_bytes
unsigned short C_006753F2(int bp08, void *bp0c, unsigned bp10) {
 int local_2;
 unsigned local_1;

 if(bp10 < 0)//nonsense
  return 0;
 local_1 = _read(bp08, bp0c, bp10);
 local_2 = (local_1 != bp10);

 return !local_2;
}

//fwrite_bytes
int __00675435(FILE *bp08, void *bp0c, int bp10) {
 int local_2;
 int local_1;

 local_1 = fwrite(bp0c, 1, bp10, bp08);
 local_2 = (local_1 != bp10);

 return !local_2;
}

//fwrite_u8
int __0067546F(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fwrite(&bp0c, 1, 1, bp08);
 local_2 = (local_1 != 1);

 return !local_2;
}

//fwrite_u16
int __006754A5(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fwrite(&bp0c, 1, 2, bp08);
 local_2 = (local_1 != 2);

 return !local_2;
}

//fwrite_u32
int __006754DB(FILE *bp08, void *bp0c) {
 int local_2;
 int local_1;

 local_1 = fwrite(&bp0c, 1, 4, bp08);
 local_2 = (local_1 != 4);

 return !local_2;
}

int C_0067564E(int file, int id);//read archive header?
void C_0067577A(int file, int id);//read path infos?

//is_lib:open archive?
int C_00675511(const char *bp08, int dwArchiveId/*bp0c*/) {
 struct {
  int j;//local_3
  int i;//local_2
  int local_1;
 }lolo;

 if(dwArchiveId >= 0x12)
  return 0;
 lolo.local_1 = C_0067529A(bp08, _O_RDONLY|_O_BINARY);//is_lib:_open
 if(lolo.local_1 == -1)
  return 0;
 D_00DB29D0[dwArchiveId] = lolo.local_1;
 if(C_0067564E(lolo.local_1, dwArchiveId) == 0)
  return 0;
 //-- read "fast indexes" --
 D_00DB2A98[dwArchiveId] = (struct t_is_lib_index *)C_0065FDA1(30 * 30 * sizeof(struct t_is_lib_index), /*0090D16C*/__FF7FILE__, 0x192);
 if(D_00DB2A98[dwArchiveId] == 0)
  return 0;
 for(lolo.i = 0; lolo.i < 30; lolo.i ++) {
  for(lolo.j = 0; lolo.j < 30; lolo.j ++) {
   /*read_u16*/C_0067538A(lolo.local_1, &((D_00DB2A98[dwArchiveId] + lolo.i * 30 + lolo.j)->_first));
   /*read_u16*/C_0067538A(lolo.local_1, &((D_00DB2A98[dwArchiveId] + lolo.i * 30 + lolo.j)->_num));
  }
 }
 C_0067577A(lolo.local_1, dwArchiveId);//read path infos?
 D_00DB2AE0 ++;

 return 1;
}

//read archive header?
int C_0067564E(int bp08, int bp0c) {
 struct {
  unsigned i;//local_7
  short local_6;
  unsigned dwNumEntries;//local_5
  char local_4[0xa+6];
 }lolo;

 /*read_u16*/C_0067538A(bp08, &lolo.local_6);//usually [00 00]
 /*read_bytes*/C_006753F2(bp08, lolo.local_4, 0xa);//'SQUARESOFT'
 /*read_u32*/C_006753BE(bp08, &lolo.dwNumEntries);
 D_00D8F678[bp0c].dwNumEntries = lolo.dwNumEntries;
 D_00D8F678[bp0c].pFileEntries = (struct t_is_lib_fileentry *)C_0065FDA1(lolo.dwNumEntries * sizeof(struct t_is_lib_fileentry), /*0090D188*/__FF7FILE__, 0x1cc);
 if(D_00D8F678[bp0c].pFileEntries == 0)
  return 0;
 for(lolo.i = 0; lolo.i < lolo.dwNumEntries; lolo.i ++) {
  /*read_bytes*/C_006753F2(bp08, D_00D8F678[bp0c].pFileEntries[lolo.i].name, 0x14);//0x14 > 0x10: they like risks
  /*read_u32*/C_006753BE(bp08, &(D_00D8F678[bp0c].pFileEntries[lolo.i].dwOffset));
  /*read_u8*/C_00675356(bp08, &(D_00D8F678[bp0c].pFileEntries[lolo.i].f_14));
  /*read_u16*/C_0067538A(bp08, &(D_00D8F678[bp0c].pFileEntries[lolo.i].f_16));
 }

 return 1;
}

////////////////////////////////////////

//read path infos?
void C_0067577A(int bp08, int bp0c) {
 int local_2;
 int local_1;

 D_00D8F708[bp0c] = 0;
 /*read_u16*/C_0067538A(bp08, &D_00D8F708[bp0c]);
 for(local_1 = 0; local_1 < D_00D8F708[bp0c]; local_1 ++) {
  /*read_u16*/C_0067538A(bp08, &D_00D8F750[bp0c][local_1].f_00);//read "int" as "u16":they like risks
  D_00D8F750[bp0c][local_1].f_04 = (struct t_is_lib_pathentry *)C_0065FDA1(D_00D8F750[bp0c][local_1].f_00 * sizeof(struct t_is_lib_pathentry), /*0090D1A4*/__FF7FILE__, 0x1f3);
  for(local_2 = 0; local_2 < D_00D8F750[bp0c][local_1].f_00; local_2 ++) {
   /*read_bytes*/C_006753F2(bp08, D_00D8F750[bp0c][local_1].f_04[local_2].f_00, 0x80);
   /*read_u16*/C_0067538A(bp08, &(D_00D8F750[bp0c][local_1].f_04[local_2].dwEntryIndex));
  }
 }
}

#if 0 //with MACRO
#define cleanPathInfo_inline(_archive_id, _file_, _line_) {                    \
 int _var_;                                                                 \
                                                                            \
 for(_var_ = 0; _var_ < D_00D8F708[_archive_id]; _var_ ++) {                \
  if(D_00D8F750[_archive_id][_var_].f_04) {                              \
   C_0065FB40(D_00D8F750[_archive_id][_var_].f_04, _file_, _line_);   \
   D_00D8F750[_archive_id][_var_].f_04 = 0;                           \
  }                                                                      \
 }                                                                          \
}
#else //with inline
static inline void cleanPathInfo_inline(int _archive_id, const char *_file_, int _line_) {
 int _var_;

 for(_var_ = 0; _var_ < D_00D8F708[_archive_id]; _var_ ++) {
  if(D_00D8F750[_archive_id][_var_].f_04) {
   C_0065FB40(D_00D8F750[_archive_id][_var_].f_04, _file_, _line_);
   D_00D8F750[_archive_id][_var_].f_04 = 0;
  }
 }
}
#endif

//clean path info[all archives]?
/**/static void C_00676172() {
 int local_1;

 for(local_1 = 0; local_1 < 0x12; local_1 ++)
  cleanPathInfo_inline(local_1, /*0090D1C0*/__FF7FILE__, 0x224);
}

//clean path info?
/**/static void C_00675FDE(int dwArchiveId/*bp08*/) {
 if(dwArchiveId >= 0x12)
  return;
 cleanPathInfo_inline(dwArchiveId, /*0090D1DC*/__FF7FILE__, 0x233);
}

////////////////////////////////////////

//is_lib:set "virtual path"
int C_006758C3(const char *bp08) {
 int local_1;

 strcpy(D_00DB2A18, bp08);
 local_1 = strlen(D_00DB2A18);
 if(D_00DB2A18[local_1 - 1] != '\\' && D_00DB2A18[local_1 - 1] != '/') {
  D_00DB2A18[local_1] = '/';
  D_00DB2A18[local_1 + 1] = 0;
 }
 D_00DB2AE4 = 1;

 return 1;
}

//is_lib:"virtual path" off
int C_00675949() {
 D_00DB2AE4 = 0;

 return 1;
}

char C_0067595D(char bp08) {
 char local_1;
 
 local_1 = tolower(bp08);
 if(local_1  >= 'a' && local_1 <= 'z')
  return local_1;
 if(local_1  >= '0' && local_1 <= '9')
  return local_1 + ('a' - '0');//0x31
 if(local_1 == '_')
  return 0x6b;//'k'?
 if(local_1 == '-')
  return 0x6c;//'l'?
 if(local_1 == '.')
  return local_1;

 return local_1;
}


//is_lib:get entry offset?
int C_006759D2(const char *bp08, int dwArchiveId/*bp0c*/) {
 struct {
  char bp_5b0[128];
  char drive[4];//bp_530
  int bp_52c;
  char dir[256];//bp_528
  char fname[256];//bp_428
  char ext[256];//bp_328
  int dwFirst;//bp_228//local_138
  int bp_224;
  int j;//bp_220//local_136
  char bp_21c[256];
  int i;//bp_11c//local_71
  char bp_118[256];
  int dwEntryIndex;//bp_018
  char name[0x10];//bp_014
  int dwNum;//bp_004//local_1
 }lolo;

 if(dwArchiveId >= 0x12)
  return 0;
 _splitpath(bp08, lolo.drive, lolo.dir, lolo.fname, lolo.ext);
 //-- append "virtual path" --
 if(D_00DB2AE4) {
  strcpy(lolo.bp_5b0, lolo.dir);
  strcpy(lolo.dir, D_00DB2A18);
  strcat(lolo.dir, lolo.bp_5b0);
 }
 //-- --
 strcpy(lolo.name, lolo.fname);
 strcat(lolo.name, lolo.ext);
 lolo.name[strlen(lolo.name)] = 0;
 //-- get "fast index" infos --
 lolo.i = C_0067595D(lolo.name[0]) - 'a';
 if(lolo.name[1] == '.')
  lolo.j = 0;
 else
  lolo.j = C_0067595D(lolo.name[1]) - 'a' + 1;

 lolo.dwFirst = (D_00DB2A98[dwArchiveId] + lolo.i * 30 + lolo.j)->_first;
 if(lolo.dwFirst <= 0)
  return 0;
 lolo.dwFirst --;
 lolo.dwNum = (D_00DB2A98[dwArchiveId] + lolo.i * 30 + lolo.j)->_num;
 //-- --
 for(lolo.i = lolo.dwFirst; lolo.i < lolo.dwFirst + lolo.dwNum; lolo.i ++) {
  if(_strcmpi(lolo.name, D_00D8F678[dwArchiveId].pFileEntries[lolo.i].name) == 0) {
   //-- is file name unique? --
   if(D_00D8F678[dwArchiveId].pFileEntries[lolo.i].f_16 == 0)
    return D_00D8F678[dwArchiveId].pFileEntries[lolo.i].dwOffset;
   //-- compare pathes --
   lolo.bp_52c = D_00D8F678[dwArchiveId].pFileEntries[lolo.i].f_16 - 1;
   if(lolo.dir[0] == 0) {//else 00675CDF
    GetCurrentDirectory(0x100, lolo.bp_21c);
    lolo.bp_21c[strlen(lolo.bp_21c)] = '/';
    _splitpath(lolo.bp_21c, lolo.drive, lolo.dir, lolo.fname, lolo.ext);
   }
   if(lolo.dir[0] == '\\' || lolo.dir[0] == '/') {//else 00675D3C
    strncpy(lolo.bp_118, lolo.dir + 1, strlen(lolo.dir) - 1);
    lolo.bp_118[strlen(lolo.dir) - 1] = 0;
   } else {
    strcpy(lolo.bp_118, lolo.dir);
   }
   lolo.bp_224 = strlen(lolo.bp_118);
   if(lolo.bp_118[lolo.bp_224 - 1] == '\\' || lolo.bp_118[lolo.bp_224 - 1] == '/') {
    lolo.bp_118[lolo.bp_224 - 1] = 0;
    lolo.bp_224 --;
   } else {
    lolo.bp_118[lolo.bp_224] = 0;
   }
   //-- --
   for(lolo.j = 0; lolo.j < D_00D8F750[dwArchiveId][lolo.bp_52c].f_00; lolo.j ++) {
    strncpy(lolo.bp_21c, D_00D8F750[dwArchiveId][lolo.bp_52c].f_04[lolo.j].f_00, strlen(D_00D8F750[dwArchiveId][lolo.bp_52c].f_04[lolo.j].f_00));
    lolo.bp_21c[strlen(D_00D8F750[dwArchiveId][lolo.bp_52c].f_04[lolo.j].f_00)] = 0;
    if(_strcmpi(lolo.bp_118, lolo.bp_21c) == 0) {
     lolo.dwEntryIndex = D_00D8F750[dwArchiveId][lolo.bp_52c].f_04[lolo.j].dwEntryIndex;
     return D_00D8F678[dwArchiveId].pFileEntries[lolo.dwEntryIndex].dwOffset;
    }
   }
   //-- --
  }
 }

 return 0;
}

//is_lib:close archive
int C_00675F1D(int dwArchiveId/*bp08*/) {
 if(dwArchiveId >= 0x12)
  return 0;
 //-- --
 if(D_00DB29D0[dwArchiveId] != -1) {
  C_006752B8(D_00DB29D0[dwArchiveId]);//is_lib:_close
  D_00DB29D0[dwArchiveId] = 0;//should be -1?
 }
 if(D_00D8F678[dwArchiveId].pFileEntries) {
  C_0065FB40(D_00D8F678[dwArchiveId].pFileEntries, /*0090D1F8*/__FF7FILE__, 0x2d7);
  D_00D8F678[dwArchiveId].pFileEntries = 0;
 }
 if(D_00DB2A98[dwArchiveId]) {
  C_0065FB40(D_00DB2A98[dwArchiveId], /*0090D214*/__FF7FILE__, 0x2dc);
  D_00DB2A98[dwArchiveId] = 0;
 }
 C_00675FDE(dwArchiveId);//clean path info?

 return 1;
}

//void C_00675FDE(int);//clean path info?

//is_lib:clean?
int C_00676064() {
 unsigned short i;//local_1
 
 for(i = 0; i < 0x12; i ++) {
  if(D_00DB29D0[i] != -1) {
   C_006752B8(D_00DB29D0[i]);//is_lib:_close
   D_00DB29D0[i] = 0;
  }
  if(D_00D8F678[i].pFileEntries) {
   C_0065FB40(D_00D8F678[i].pFileEntries, /*0090D230*/__FF7FILE__, 0x2fa);
   D_00D8F678[i].pFileEntries = 0;
  }
  if(D_00DB2A98[i]) {
   C_0065FB40(D_00DB2A98[i], /*0090D24C*/__FF7FILE__, 0x2ff);
   D_00DB2A98[i] = 0;
  }
 }
 C_00676172();//clean path info[all archives]?

 return 1;
}

//void C_00676172();//clean path info[all archives]?

//is_lib:get archive's handle[unused]
int __0067620F(int dwArchiveId/*bp08*/) {
 if(dwArchiveId >= 0x12)
  return 0;

 return D_00DB29D0[dwArchiveId];
}

//is_lib:seek
int C_00676228(int dwOffset/*bp08*/, int dwArchiveId/*bp0c*/) {
 int _ocal_1;

 if(dwArchiveId >= 0x12)
  return 0;
 if(D_00DB29D0[dwArchiveId] == 0)
  return 0;
 C_006752DA(D_00DB29D0[dwArchiveId], 0, SEEK_SET);//is_lib:_lseek
 C_006752DA(D_00DB29D0[dwArchiveId], dwOffset, SEEK_CUR);//is_lib:_lseek
 if(C_006752F8(D_00DB29D0[dwArchiveId]) != dwOffset) {//is_lib:_tell
  C_006752DA(D_00DB29D0[dwArchiveId], dwOffset, SEEK_SET);//is_lib:_lseek
  /*??? = */C_006752F8(D_00DB29D0[dwArchiveId]);//is_lib:_tell
 }

 return 1;
}

//is_lib:advance seek[unused]
int __006762C7(int bp08, int dwArchiveId/*bp0c*/) {
 C_006752DA(D_00DB29D0[dwArchiveId], bp08, SEEK_CUR);//is_lib:_lseek

 return 1;
}

//is_lib:get entry size
int C_006762EA(int dwEntryOffset/*bp08*/, int dwArchiveId/*bp0c*/) {
 int dwEntrySize;//local_1

 dwEntrySize = 0;
 if(C_00676228(dwEntryOffset, dwArchiveId)) {//is_lib:seek
  C_006752DA(D_00DB29D0[dwArchiveId], 0x14, SEEK_CUR);//is_lib:_lseek
  /*read_u32*/C_006753BE(D_00DB29D0[dwArchiveId], &dwEntrySize);
 }

 return dwEntrySize;
}

//is_lib:load entry
int C_0067633E(int dwEntryOffset/*bp08*/, int dwArchiveId/*bp0c*/, void *bp10, int dwEntrySize/*bp14*/) {
 if(bp10 == 0)
  return 0;
 if(C_00676228(dwEntryOffset, dwArchiveId)) {//is_lib:seek
  C_006752DA(D_00DB29D0[dwArchiveId], 0x14 + 4, SEEK_CUR);//is_lib:_lseek
  if(/*read_bytes*/C_006753F2(D_00DB29D0[dwArchiveId], bp10, dwEntrySize) == 0)
   return 0;
 }

 return 1;
}

//is_lib:...
//[used by file.cpp]
int C_006763A5(int bp08, void *bp0c, int bp10) {
 if(bp0c == 0)
  return 0;
 if(/*read_bytes*/C_006753F2(D_00DB29D0[bp08], bp0c, bp10) == 0)
  return 0;

 return 1;
}

You can see a lot of functions defined but not used (I put a double underscore in front of their name);
Also notice the clever "fast indexes" array, which allows to get information about an entry by using the first two character of its name.

Usually, entries are referred to only by their name, but in the case of more complex archives (magic.lgp comes to mind) entries may also include some path information; this allows to have several entries with the same name, but in a different path of course. Since I didn't reverse the battle system yet, I didn't put a lot of commentary yet.

Wednesday, December 9, 2015

FF7's "Insert Disc #" screen

Let's have a look at another system:
//"request cd"

#include "ff7.h"

#include "loadmenu.h"

#include 

////////////////////////////////////////
int D_007BA278 = 0xff;//fade out alpha

const char *D_007BA280[] = {
 /*007BA35C*/"disk1_a.tim",/*007BA368*/"disk1_b.tim",
 /*007BA374*/"disk2_a.tim",/*007BA380*/"disk2_b.tim",
 /*007BA38C*/"disk3_a.tim",/*007BA398*/"disk3_b.tim"
};
const char *D_007BA298[] = {
 /*007BA3A4*/"disk1_x.tim",
 /*007BA3B0*/"disk2_x.tim",
 /*007BA3BC*/"disk3_x.tim"
};

const char *D_007BA2A8[] = {
 "aeris_a.tim","aeris_b.tim","aeris_c.tim","aeris_d.tim",
 "barr_a.tim","barr_b.tim","barr_c.tim","barr_d.tim",
 "cid_a.tim","cid_b.tim","cid_c.tim","cid_d.tim",
 "cloud_a.tim","cloud_b.tim","cloud_c.tim","cloud_d.tim",
 "kets_a.tim","kets_b.tim","kets_c.tim","kets_d.tim",
 "red_a.tim","red_b.tim","red_c.tim","red_d.tim",
 "tifa_a.tim","tifa_b.tim","tifa_c.tim","tifa_d.tim",
 "vinc_a.tim","vinc_b.tim","vinc_c.tim","vinc_d.tim",
 "yuff_a.tim","yuff_b.tim","yuff_c.tim","yuff_d.tim"
};
const char *D_007BA338[] = {
 /*007BA578*/"aeris_x.tim",
 /*007BA584*/"barr_x.tim",
 /*007BA590*/"cid_x.tim",
 /*007BA59C*/"cloud_x.tim",
 /*007BA5A8*/"kets_x.tim",
 /*007BA5B4*/"red_x.tim",
 /*007BA5C0*/"tifa_x.tim",
 /*007BA5CC*/"vinc_x.tim",
 /*007BA5D8*/"yuff_x.tim"
};
////////////////////////////////////////
char D_009A04F8[4];
int D_009A04FC;
int D_009A0500;//viewport:height
int D_009A0504;//viewport:left
int D_009A0508;//viewport:width
struct t_dx_sfx_e0 *D_009A0510[6];
struct t_dx_sfx_e0 *D_009A0528[2];
struct t_dx_sfx_e0 *D_009A0530;
int D_009A0534;
int D_009A0538;
int D_009A053C;//viewport:top
int D_009A0540;
int D_009A0544;//fade in alpha
//009A0548//maybe ""?
////////////////////////////////////////
extern void C_006C09C0(const char *);//[menu related]?
////////////////////////////////////////
//set picture alpha(fade in/out)?
void C_00402EF0(unsigned char bp08) {
 struct {
  struct t_dx_rend_vertex_20 *local_6[4];
  float local_2;
  tRGBA local_1;
 }lolo;
 
 lolo.local_1.c.b = lolo.local_1.c.g = lolo.local_1.c.r = 0;
 lolo.local_1.c.a = bp08;
 lolo.local_2 = 0;
 if(C_0066E272(1, D_009A0530)) {
  MK_QUAD_2D_NOTEXTURE(
   D_009A0530->f_70.asVertex, lolo.local_6,
   (float)D_009A0504, (float)D_009A053C, (float)D_009A0508, (float)D_009A0500,
   lolo.local_2, 1.0f,
   lolo.local_1.rgba, 0xff000000
  );
 }
}

//image[gr mode 0]
void C_00403073() {
 struct {
  struct t_dx_rend_vertex_20 *local_13[4];
  struct t_dx_rend_vertex_20 *local_9[4];
  float tex_height;//local_5
  float tex_width;//local_4
  float tex_v;//local_3
  float tex_u;//local_2
  float local_1;
 }lolo;

 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = (200.0f/256.0f);//0.78125f
 lolo.tex_height = (150.0f/256.0f);//0.5859375f
 lolo.local_1 = 0.01f;
 if(C_0066E272(1, D_009A0528[0])) {
  MK_QUAD_2D(
   D_009A0528[0]->f_70.asVertex, lolo.local_9,
   60.0f, 8.0f, 200.f, 150.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_height = (148.0f/256.0f);//0.578125f
 if(C_0066E272(1, D_009A0528[1])) {
  MK_QUAD_2D(
   D_009A0528[1]->f_70.asVertex, lolo.local_13,
   60.0f, 158.0f, 200.f, 74.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

//image[gr mode 1]
void C_004033BF() {
 struct {
  struct t_dx_rend_vertex_20 *local_13[4];
  struct t_dx_rend_vertex_20 *local_9[4];
  float tex_height;//local_5
  float tex_width;//local_4
  float tex_v;//local_3
  float tex_u;//local_2
  float local_1;
 }lolo;

 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = (200.0f/256.0f);//0.78125f
 lolo.tex_height = (150.0f/256.0f);//0.5859375f
 lolo.local_1 = 0.01f;
 if(C_0066E272(1, D_009A0528[0])) {
  MK_QUAD_2D(
   D_009A0528[0]->f_70.asVertex, lolo.local_9,
   220.0f, 128.0f, 200.f, 150.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_height = (148.0f/256.0f);//0.578125f
 if(C_0066E272(1, D_009A0528[1])) {
  MK_QUAD_2D(
   D_009A0528[1]->f_70.asVertex, lolo.local_13,
   220.0f, 278.0f, 200.f, 74.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

//image[gr mode 2]
void C_0040370B() {
 struct {
  struct t_dx_rend_vertex_20 *local_30[4];
  struct t_dx_rend_vertex_20 *local_26[4];
  struct t_dx_rend_vertex_20 *local_22[4];
  struct t_dx_rend_vertex_20 *local_18[4];
  struct t_dx_rend_vertex_20 *local_14[4];
  struct t_dx_rend_vertex_20 *local_10[4];

  float tex_height;//local_6
  float tex_width;//local_5
  float tex_v;//local_4
  float tex_u;//local_3
  float local_2;
  int local_1;
 }lolo;

 lolo.local_1 = 0x18;

 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = 1.0f;
 lolo.tex_height = 1.0f;
 lolo.local_2 = 0.01f;
 if(C_0066E272(1, D_009A0510[0])) {
  MK_QUAD_2D(
   D_009A0510[0]->f_70.asVertex, lolo.local_10,
   120.0f, (float)lolo.local_1, 256.f, 256.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_width = (144.0f/256.0f);//0.5625f
 if(C_0066E272(1, D_009A0510[1])) {
  MK_QUAD_2D(
   D_009A0510[1]->f_70.asVertex, lolo.local_14,
   376.0f, (float)lolo.local_1, 144.0f, 256.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_height = (176.0f/256.0f);//0.6875f
 lolo.tex_width = 1.0f;
 if(C_0066E272(1, D_009A0510[2])) {
  MK_QUAD_2D(
   D_009A0510[2]->f_70.asVertex, lolo.local_18,
   120.0f, (float)lolo.local_1 + 256.0f, 256.0f, 44.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_width = (144.0f/256.0f);//0.5625f
 if(C_0066E272(1, D_009A0510[3])) {
  MK_QUAD_2D(
   D_009A0510[3]->f_70.asVertex, lolo.local_22,
   376.0f, (float)lolo.local_1 + 256.0f, 144.0f, 44.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_height = (148.0f/256.0f);//0.578125f
 lolo.tex_width = 1.0f;
 if(C_0066E272(1, D_009A0510[4])) {
  MK_QUAD_2D(
   D_009A0510[4]->f_70.asVertex, lolo.local_26,
   120.0f, (float)lolo.local_1 + 300.0f, 256.0f, 148.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_width = (144.0f/256.0f);//0.5625f
 if(C_0066E272(1, D_009A0510[5])) {
  MK_QUAD_2D(
   D_009A0510[5]->f_70.asVertex, lolo.local_30,
   376.0f, (float)lolo.local_1 + 300.0f, 144.0f, 148.0f,
   lolo.local_2, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

void C_0040414A() {
 struct {
  int local_162;
  const char *local_161;
  unsigned local_160;
  struct t_rsd_74 local_159;
  int local_130;
  struct t_aa0 *local_129;
  char local_128[256];
  char local_64[256];
 }lolo;

 lolo.local_129 = C_00676578();
 lolo.local_162 = C_00404D80();//Get "Graphics/Mode" Key
 //-- pick a "random" number --
 lolo.local_160 = C_0040AC84();//get frame count
 lolo.local_130 = lolo.local_160 % 9;
 //-- open archive --
 strcpy(lolo.local_64, C_004076EA());//initpath:...
 strcat(lolo.local_64, "cd/disc_us.lgp");
 if(C_00675511(lolo.local_64, 0x10) == 0) {//is_lib:open archive?
  sprintf(lolo.local_128, "Failed to load: %s\n", lolo.local_64);
  C_00664E30(lolo.local_128);
 }
 //-- --
 C_0067453A(0);//rsd:set some flag
 C_006745E6(4, &lolo.local_159);//rsd:set blend mode?
 lolo.local_159.f_40.f_04 = 1;
 lolo.local_159.f_40.f_08 = 0x10;
 lolo.local_159.f_40.f_0c = 0;
 lolo.local_159.f_24 = "";//009A0548
 if(lolo.local_162 == 2) {//else 00404572
  lolo.local_130 <<= 2;
  C_00674659(4, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
  lolo.local_161 = D_007BA2A8[lolo.local_130];
  lolo.local_130 ++;
  D_009A0510[0] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  lolo.local_161 = D_007BA2A8[lolo.local_130];
  lolo.local_130 ++;
  D_009A0510[1] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  lolo.local_161 = D_007BA2A8[lolo.local_130];
  lolo.local_130 ++;
  D_009A0510[2] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  lolo.local_161 = D_007BA2A8[lolo.local_130];
  lolo.local_130 ++;
  D_009A0510[3] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  lolo.local_159.f_50 |= 1;
  lolo.local_159.f_54.rgba = 0xFF141414;
  if(D_00DC0BDC == 1) {
   lolo.local_161 = D_007BA280[0];
   D_009A0510[4] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
   lolo.local_161 = D_007BA280[1];
   D_009A0510[5] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  } else  if(D_00DC0BDC == 2) {
   lolo.local_161 = D_007BA280[2];
   D_009A0510[4] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
   lolo.local_161 = D_007BA280[3];
   D_009A0510[5] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  } else  if(D_00DC0BDC == 3) {
   lolo.local_161 = D_007BA280[4];
   D_009A0510[4] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
   lolo.local_161 = D_007BA280[5];
   D_009A0510[5] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  }
 } else {
  C_00674659(4, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
  lolo.local_161 = D_007BA338[lolo.local_130];
  D_009A0528[0] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
  if(D_00DC0BDC == 1)
   lolo.local_161 = D_007BA298[0];
  else if(D_00DC0BDC == 2)
   lolo.local_161 = D_007BA298[1];
  else if(D_00DC0BDC == 3)
   lolo.local_161 = D_007BA298[2];
  D_009A0528[1] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_161, lolo.local_129->f_910);//dx_sfx:alloc/create?
 }
 lolo.local_159.f_70 |= 0x4000;
 C_00674659(0, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
 D_009A0530 = C_006710AC(0, 8, &lolo.local_159, 0, lolo.local_129->f_910);//dx_sfx:alloc/create?
}

//Request CD[START][callback]
void C_00404688(struct t_aa0 *_p0c) {
 int local_1;

 local_1 = C_00404D80();//Get "Graphics/Mode" Key
 strncpy(D_009A04F8, D_009A06C0, 2);
 D_009A0540 = 1;
 D_009A0534 = 1;
 D_009A04FC = 1;
 D_009A0544 = 0;
 D_007BA278 = 0xff;
 if(local_1 == 0) {
  D_009A0504 = 0;
  D_009A053C = 0;
  D_009A0508 = 0x140;
  D_009A0500 = 0xF0;
 } else if(local_1 == 1) {
  D_009A0504 = 0xa0;
  D_009A053C = 0x78;
  D_009A0508 = 0x140;
  D_009A0500 = 0xF0;
 } else {
  D_009A0504 = 0;
  D_009A053C = 0;
  D_009A0508 = 0x280;
  D_009A0500 = 0x1e0;
 }
 C_0040414A();
}

//Request CD[END][callback]
void C_0040476C(struct t_aa0 *_p0c) {
 int local_1;


 C_004082BF();//main:open main archives?
 for(local_1 = 0; local_1 < 6; local_1 ++)
  C_00671082(&(D_009A0510[local_1]));//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0528[0]);//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0528[1]);//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0530);//dx_sfx:release "struct t_dx_sfx_e0 **"

 D_00CBF9DC = D_00CC0D84;

 D_00CC0D84 = 0x0c;
 C_00675F1D(0x10);//is_lib:close archive
 D_009A04FC = 0;
}

//Request CD[UPDATE][callback]
void C_004047F6(struct t_aa0 *bp08) {
 struct {
  struct fBGRA local_13;
  int local_9;
  int local_8;
  struct tMainCallbacks local_7;
 }lolo;

 lolo.local_9 = C_00404D80();//Get "Graphics/Mode" Key
 C_00666DA3(bp08);//calls "instance:reset"
 C_00666DC0(bp08);//calls "dx_sfx:reset heaps(1)"
 lolo.local_13.r = 0; lolo.local_13.g = 0; lolo.local_13.b = 0; lolo.local_13.a = 1.0f;
 C_0066075C(&lolo.local_13, bp08);//G_DRV_20:ClearColor
 C_00660626(bp08);//G_DRV_18:ClearAll
 lolo.local_8 = 0;
 C_00660C3A(G_DRV_STATE_02, 0, bp08);//G_DRV_64?
 if(C_00660EC0(lolo.local_8, bp08)) {//G_DRV_88:BeginScene
  if(lolo.local_9 == 2) {
   C_0040370B();//image[gr mode 2]
   C_0066E641(D_009A0510[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0510[1], bp08);//dx_spr:render something?
   C_0066E641(D_009A0510[2], bp08);//dx_spr:render something?
   C_0066E641(D_009A0510[3], bp08);//dx_spr:render something?
   C_0066E641(D_009A0510[4], bp08);//dx_spr:render something?
   C_0066E641(D_009A0510[5], bp08);//dx_spr:render something?
  } else if(lolo.local_9 == 0) {
   C_00403073();//image[gr mode 0]
   C_0066E641(D_009A0528[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0528[1], bp08);//dx_spr:render something?
  } else if(lolo.local_9 == 1) {
   C_004033BF();//image[gr mode 1]
   C_0066E641(D_009A0528[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0528[1], bp08);//dx_spr:render something?
  }





  if(C_00404A7D() == D_00DC0BDC && D_009A0534) {//else 00404A0A
   //--------------
   //-- fade out --
   C_00402EF0(D_009A0544);//set picture alpha(fade in/out)?
   C_00660E95(1, bp08);//G_DRV_84?change_layer
   C_0066E641(D_009A0530, bp08);//dx_spr:render something?
   D_009A0544 += 0x33;
   if(D_009A0544 > 0xff) {
    D_009A0534 = 0;
    lolo.local_7.f_08 = C_00408FA6;//MainDispatcher[BEGIN][callback]
    lolo.local_7.f_10 = C_004090E6;//MainDispatcher[UPDATE][callback]
    lolo.local_7.f_14 = C_00409DF1;//MainDispatcher[ONMOUSE][callback]
    lolo.local_7.f_18 = C_00409E39;//MainDispatcher[ONKEY][callback]
    lolo.local_7.f_0c = C_004090C7;//MainDispatcher[END][callback]
    C_00666CF2(&lolo.local_7, bp08);//set main loop callbacks?
   }
  }
  if(D_009A0540) {
   //-------------
   //-- fade in --
   C_00402EF0(D_007BA278);//set picture alpha(fade in/out)?
   C_00660E95(1, bp08);//G_DRV_84?change_layer
   C_0066E641(D_009A0530, bp08);//dx_spr:render something?
   D_007BA278 -= 0x33;
   if(D_007BA278 <= 0)
    D_009A0540 = 0;
  }
  C_00660EEB(bp08);//G_DRV_8C:EndScene
 }
}

//[callback]
void C_00404A73(int, int, int, struct t_aa0 *) {
}

//[callback]
void C_00404A78(int, int, int, struct t_aa0 *) {
}

//currently inserted CD #?
int C_00404A7D() {
 struct {
  char _ocal_193[256];
  char local_129[256];
  char _ocal_65[256];
  int local_1;
 }lolo;

 D_009A0538 = 0;
 strncpy(D_009A04F8, D_009A06C0, 2);
 SetErrorMode(SEM_FAILCRITICALERRORS/*1*/);
 lolo.local_1 = GetVolumeInformation(D_009A06C0, lolo.local_129, 0x100, 0, 0, 0, 0, 0);
 if(lolo.local_1) {
  if(strcmp(lolo.local_129, "FF7DISC1") == 0)
   D_009A0538 = 1;
  else if(strcmp(lolo.local_129, "FF7DISC2") == 0)
   D_009A0538 = 2;
  else if(strcmp(lolo.local_129, "FF7DISC3") == 0)
   D_009A0538 = 3;
 }

 return D_009A0538;
}

const char D_007B6408[] = "Software\\Square Soft, Inc.\\Final Fantasy VII";

//called on WM_DEVICECHANGE
void C_00404B4A(int bp08, struct t_aa0 *_p0c) {
 struct {
  HKEY local_156;
  int local_155;
  STARTUPINFO local_154;
  int local_137;
  int local_136;
  char local_135[12];
  char local_132[256];
  PROCESS_INFORMATION local_68;
  char local_64[256];
 }lolo;

 if(D_009A04FC || bp08 == DBT_DEVICEARRIVAL)//0x8000
  return;
 if(bp08 == DBT_DEVICEREMOVECOMPLETE) {//0x8004
  lolo.local_154.cb = 0; memset(&(lolo.local_154.lpReserved), 0, sizeof(STARTUPINFO) - 4);//STARTUPINFO local_154 = {0}
  lolo.local_68.hProcess = 0; memset(&(lolo.local_68.hThread), 0, sizeof(PROCESS_INFORMATION) - 4);//PROCESS_INFORMATION local_68 = {0};
  lolo.local_137 = 1;
  memcpy(lolo.local_135, "DiscNo", 7);//char local_135[] = "DiscNo";
  D_009A04FC = 0;
  sprintf(lolo.local_132, "Please insert Disc %i to continue.", D_00DC0BDC);
  C_006C09C0(lolo.local_132);//[menu related]?
  //-- set "DiscNo" Key value ? (used by "cdcheck.exe"?)
  lolo.local_136 = RegOpenKeyEx(HKEY_LOCAL_MACHINE, D_007B6408, 0, KEY_WRITE/*0x20006*/, &lolo.local_156);
  if(lolo.local_136 == 0) {
   lolo.local_137 = 1;
   lolo.local_155 = D_00DC0BDC;
   lolo.local_136 = RegSetValueEx(lolo.local_156, lolo.local_135, 0, 4, (LPBYTE)&lolo.local_155, 4);
   if(lolo.local_136 == 0)
    lolo.local_137 = 0;
   lolo.local_136 = RegCloseKey(lolo.local_156);
  }
  lolo.local_154.cb = sizeof(STARTUPINFO);//0x44
  lolo.local_154.lpReserved = 0;
  lolo.local_154.lpReserved2 = 0;
  lolo.local_154.cbReserved2 = 0;
  lolo.local_154.lpDesktop = 0;
  lolo.local_154.dwFlags = 0;
  strcpy(lolo.local_64, D_009A0698);
  strcat(lolo.local_64, "cdcheck.exe");
  CreateProcess(0, lolo.local_64, 0, 0, 1, NORMAL_PRIORITY_CLASS, 0, 0, &lolo.local_154, &lolo.local_68);
  WaitForSingleObject(lolo.local_68.hProcess, -1);
 }
}

This one is executed when the program requires a specific CDRom (there are 3 for the game) which is not yet inserted in the player; As you can see it is almost a copy/paste of the Game Over screen.

And now for the differences:
  • The background image is randomly picked in a list of 9 different images (the main characters of the game)
  • Part of the background image is drawn according to the request CDRom's number
  • The system doesn't leave on a user's event, but rather polls each frame for the currently inserted CDRom's label to check if it is the one that is requested.

And that's all !!!

Also, there is a callback function C_00404B4A, which is called from the WndProc in case of a WM_DEVICECHANGE event. Basically, if during the game you decide to eject the CDRom, this function will be called: it then displays a warning message, set the registry's key DiscNo to the recquired CDRom's number, calls the program cdcheck.exe and wait for it to return.
As for cdcheck.exe it will just loop until a CDRom is inserted in the player and its number fits the one from the registry.

FF7's Game Over screen

As a promise, here is the decompiled source code from the "Game Over" screen of the game Final Fantasy VII:
//"Game Over" module

#include "ff7.h"

////////////////////////////////////////
int D_007BA668 = 0xff;//fade out alpha

const char *D_007BA670[] = {
 /*007BA690*/"e_over_a.tim",
 /*007BA6A0*/"e_over_b.tim",
 /*007BA6B0*/"e_over_c.tim",
 /*007BA6C0*/"e_over_d.tim",
 /*007BA6D0*/"e_over_e.tim",
 /*007BA6E0*/"e_over_f.tim"
};
const char *D_007BA688[] = {
 /*007BA6F0*/"e_over_la.tim",
 /*007BA700*/"e_over_lb.tim"
};
////////////////////////////////////////
struct t_dx_sfx_e0 *D_009A0550[6];
struct t_dx_sfx_e0 *D_009A0568[2];
struct t_dx_sfx_e0 *D_009A0570;

int D_009A0574;//viewport:height
int D_009A0578;
int D_009A057C;//viewport:left
int D_009A0580;//viewport:width
int D_009A0584;//fade out flag
int D_009A0588;//viewport:top
int D_009A058C;//fade in flag
int D_009A0590;//fade in alpha
//009A0594:""
////////////////////////////////////////
//set picture alpha(fade in/out)?
void C_00404E00(unsigned char bp08) {
 struct {
  struct t_dx_rend_vertex_20 *local_6[4];
  float local_2;
  tRGBA local_1;
 }lolo;
 
 lolo.local_1.c.b = lolo.local_1.c.g = lolo.local_1.c.r = 0;
 lolo.local_1.c.a = bp08;
 lolo.local_2 = 0;
 if(C_0066E272(1, D_009A0570)) {
  MK_QUAD_2D_NOTEXTURE(
   D_009A0570->f_70.asVertex, lolo.local_6,
   (float)D_009A057C, (float)D_009A0588, (float)D_009A0580, (float)D_009A0574,
   lolo.local_2, 1.0f,
   lolo.local_1.rgba, 0xff000000
  );
 }
}

//image[gr mode 0]
void C_00404F83() {
 struct {
  struct t_dx_rend_vertex_20 *local_13[4];
  struct t_dx_rend_vertex_20 *local_9[4];
  float tex_height;//local_5
  float tex_width;//local_4
  float tex_v;//local_3
  float tex_u;//local_2
  float local_1;
 }lolo;

 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = 1.0f;
 lolo.tex_height = (216.0f/256.0f);//0.84375f
 lolo.local_1 = 0.01f;
 if(C_0066E272(1, D_009A0568[0])) {
  MK_QUAD_2D(
   D_009A0568[0]->f_70.asVertex, lolo.local_9,
   16.0f, 12.0f, 256.f, 216.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }

 if(C_0066E272(1, D_009A0568[1])) {
  MK_QUAD_2D(
   D_009A0568[1]->f_70.asVertex, lolo.local_13,
   272.0f, 12.0f, 32.f, 216.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

//image[gr mode 1]
void C_004052C8() {
 struct {
  struct t_dx_rend_vertex_20 *local_13[4];
  struct t_dx_rend_vertex_20 *local_9[4];
  float tex_height;//local_5
  float tex_width;//local_4
  float tex_v;//local_3
  float tex_u;//local_2
  float local_1;
 }lolo;

 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = 1.0f;
 lolo.tex_height = (216.0f/256.0f);//0.84375f
 lolo.local_1 = 0.01f;
 if(C_0066E272(1, D_009A0568[0])) {
  MK_QUAD_2D(
   D_009A0568[0]->f_70.asVertex, lolo.local_9,
   176.0f, 132.0f, 256.f, 216.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }

 if(C_0066E272(1, D_009A0568[1])) {
  MK_QUAD_2D(
   D_009A0568[1]->f_70.asVertex, lolo.local_13,
   432.0f, 132.0f, 32.0f, 216.0f,
   lolo.local_1, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

//image[gr mode 2]
void C_0040560D() {
 struct {
  struct t_dx_rend_vertex_20 *local_31[4];
  struct t_dx_rend_vertex_20 *local_27[4];
  struct t_dx_rend_vertex_20 *local_23[4];
  struct t_dx_rend_vertex_20 *local_19[4];
  struct t_dx_rend_vertex_20 *local_15[4];
  struct t_dx_rend_vertex_20 *local_11[4];
  float tex_height;//local_7
  float tex_width;//local_6
  float tex_v;//local_5
  float tex_u;//local_4
  float local_3;
  int local_2;
  int local_1;
 }lolo;

 lolo.local_2 = 0x18;
 lolo.local_1 = 0x20;
 lolo.tex_u = 0.0f;
 lolo.tex_v = 0.0f;
 lolo.tex_width = 1.0f;
 lolo.tex_height = 1.0f;
 lolo.local_3 = 0.01f;
 if(C_0066E272(1, D_009A0550[0])) {
  MK_QUAD_2D(
   D_009A0550[0]->f_70.asVertex, lolo.local_11,
   (float)lolo.local_1, (float)lolo.local_2, 256.f, 256.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }

 if(C_0066E272(1, D_009A0550[1])) {
  MK_QUAD_2D(
   D_009A0550[1]->f_70.asVertex, lolo.local_15,
   (float)lolo.local_1 + 256.f, (float)lolo.local_2, 256.0f, 256.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }


 if(C_0066E272(1, D_009A0550[2])) {
  MK_QUAD_2D(
   D_009A0550[2]->f_70.asVertex, lolo.local_19,
   (float)lolo.local_1 + 256.f + 256.0f, (float)lolo.local_2, 64.0f, 256.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
 lolo.tex_height = (176.0f/256.0f);//0.6875f
 if(C_0066E272(1, D_009A0550[3])) {
  MK_QUAD_2D(
   D_009A0550[3]->f_70.asVertex, lolo.local_23,
   (float)lolo.local_1, (float)lolo.local_2 + 256.0f, 256.f, 176.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }


 if(C_0066E272(1, D_009A0550[4])) {
  MK_QUAD_2D(
   D_009A0550[4]->f_70.asVertex, lolo.local_27,
   (float)lolo.local_1 + 256.0f, (float)lolo.local_2 + 256.0f, 256.f, 176.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }

 if(C_0066E272(1, D_009A0550[5])) {
  MK_QUAD_2D(
   D_009A0550[5]->f_70.asVertex, lolo.local_31,
   (float)lolo.local_1 + 256.0f + 256.0f, (float)lolo.local_2 + 256.0f, 64.f, 176.0f,
   lolo.local_3, 1.0f,
   0x80ffffff, 0xff000000,
   lolo.tex_u, lolo.tex_v, lolo.tex_width, lolo.tex_height
  );
 }
}

void C_004060D1() {
 struct {
  int local_161;
  const char *local_160;

  struct t_rsd_74 local_159;
  unsigned local_130;
  struct t_aa0 *local_129;
  char local_128[256];
  char local_64[256];
 }lolo;

 lolo.local_129 = C_00676578();
 lolo.local_161 = C_00404D80();//Get "Graphics/Mode" Key
 //-- --


 //-- open archive--
 strcpy(lolo.local_64, C_004076EA());//initpath:...
 strcat(lolo.local_64, "cd/disc_us.lgp");
 if(C_00675511(lolo.local_64, 0x10) == 0) {//is_lib:open archive?
  sprintf(lolo.local_128, "Failed to load: %s\n", lolo.local_64);
  C_00664E30(lolo.local_128);
 }
 //-- --
 C_0067453A(0);//rsd:set some flag
 C_006745E6(4, &lolo.local_159);//rsd:...
 lolo.local_159.f_40.f_04 = 1;
 lolo.local_159.f_40.f_08 = 0x10;
 lolo.local_159.f_40.f_0c = 0;
 lolo.local_159.f_24 = "";//009A0594
 if(lolo.local_161 == 2) {//else 00406284
  lolo.local_159.f_50 |= 1;
  lolo.local_159.f_54.rgba = 0xff141414;
  C_00674659(4, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
  for(lolo.local_130 = 0; lolo.local_130 < 6; lolo.local_130 ++) {
   lolo.local_160 = D_007BA670[lolo.local_130];
   D_009A0550[lolo.local_130] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_160, lolo.local_129->f_910);//dx_sfx:alloc/create?
  }
 } else {
  lolo.local_159.f_50 |= 1;
  lolo.local_159.f_54.rgba = 0xff141414;
  C_00674659(4, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
  for(lolo.local_130 = 0; lolo.local_130 < 2; lolo.local_130 ++) {
   lolo.local_160 = D_007BA688[lolo.local_130];
   D_009A0568[lolo.local_130] = C_006710AC(1, 0xc, &lolo.local_159, lolo.local_160, lolo.local_129->f_910);//dx_sfx:alloc/create?
  }
 }
 lolo.local_159.f_70 |= 0x4000;
 C_00674659(0, &lolo.local_159);//rsd:set struct t_rsd_74::f_20
 D_009A0570 = C_006710AC(0, 8, &lolo.local_159, 0, lolo.local_129->f_910);//dx_sfx:alloc/create?
}

//GameOver[START][callback]
void C_00406367(struct t_aa0 *bp08) {
 int local_1;

 local_1 = C_00404D80();//Get "Graphics/Mode" Key

 D_009A058C = 1;//start fade in
 D_009A0584 = 1;//start fade out
 D_009A0578 = 0;
 D_009A0590 = 0;//fade in alpha
 D_007BA668 = 0xff;//fade out alpha
 if(local_1 == 0) {
  D_009A057C = 0;
  D_009A0588 = 0;
  D_009A0580 = 0x140;
  D_009A0574 = 0xf0;
 } else if(local_1 == 1) {
  D_009A057C = 0xa0;
  D_009A0588 = 0x78;
  D_009A0580 = 0x140;
  D_009A0574 = 0xf0;
 } else {
  D_009A057C = 0;
  D_009A0588 = 0;
  D_009A0580 = 0x280;
  D_009A0574 = 0x1e0;
 }
 C_004060D1();
}

//GameOver[END][callback]
void C_00406436(struct t_aa0 *bp08) {
 int local_1;

 C_00740D80(0xc1, 0x3c, 0, 0, 0, 0, 0, 0, 0);
 C_006C12B1();
 for(local_1 = 0; local_1 < 6; local_1 ++)
  C_00671082(&(D_009A0550[local_1]));//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0568[0]);//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0568[1]);//dx_sfx:release "struct t_dx_sfx_e0 **"
 C_00671082(&D_009A0570);//dx_sfx:release "struct t_dx_sfx_e0 **"

 D_00CC0D88.f_01 = 0;
 D_00CBF9DC = 0x1b;
 D_00CC0D84 = 0x1a;
 C_00675F1D(0x10);//is_lib:close archive

}

//GameOver[UPDATE][callback]
void C_004064D7(struct t_aa0 *bp08) {
 struct {
  struct fBGRA local_13;
  int local_9;
  int local_8;
  struct tMainCallbacks local_7;
 }lolo;

 lolo.local_9 = C_00404D80();//Get "Graphics/Mode" Key
 C_00666DA3(bp08);//calls "instance:reset"
 C_00666DC0(bp08);//calls "dx_sfx:reset heaps(1)"
 lolo.local_13.r = 0; lolo.local_13.g = 0; lolo.local_13.b = 0; lolo.local_13.a = 1.0f;
 C_0066075C(&lolo.local_13, bp08);//G_DRV_20:ClearColor
 C_00660626(bp08);//G_DRV_18:ClearAll
 lolo.local_8 = 0;
 C_00660C3A(G_DRV_STATE_02, 0, bp08);//G_DRV_64?
 if(C_00660EC0(lolo.local_8, bp08)) {//G_DRV_88:BeginScene
  if(lolo.local_9 == 2) {
   C_0040560D();//image[gr mode 2]
   C_0066E641(D_009A0550[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0550[1], bp08);//dx_spr:render something?
   C_0066E641(D_009A0550[2], bp08);//dx_spr:render something?
   C_0066E641(D_009A0550[3], bp08);//dx_spr:render something?
   C_0066E641(D_009A0550[4], bp08);//dx_spr:render something?
   C_0066E641(D_009A0550[5], bp08);//dx_spr:render something?
  } else if(lolo.local_9 == 0) {
   C_00404F83();//image[gr mode 0]
   C_0066E641(D_009A0568[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0568[1], bp08);//dx_spr:render something?
  } else if(lolo.local_9 == 1) {
   C_004052C8();//image[gr mode 1]
   C_0066E641(D_009A0568[0], bp08);//dx_spr:render something?
   C_0066E641(D_009A0568[1], bp08);//dx_spr:render something?
  }
  //-- manage input --
  C_0041A21E(0);//Refresh input driver?
  if(C_0041AB74(0xffffffff))//test input mask[trigger]?
   D_009A0578 = 1;
  //-- --
  if(D_009A0578 && D_009A0584) {
   //--------------
   //-- fade out --
   C_00404E00(D_009A0590);//set picture alpha(fade in/out)?
   C_00660E95(1, bp08);//G_DRV_84?change_layer
   C_0066E641(D_009A0570, bp08);//dx_spr:render something?
   D_009A0590 += 0x33;
   if(D_009A0590 > 0xff) {
    D_009A0584 = 0;
    lolo.local_7.f_08 = C_00408FA6;//MainDispatcher[BEGIN][callback]
    lolo.local_7.f_10 = C_004090E6;//MainDispatcher[UPDATE][callback]
    lolo.local_7.f_14 = C_00409DF1;//MainDispatcher[ONMOUSE][callback]
    lolo.local_7.f_18 = C_00409E39;//MainDispatcher[ONKEY][callback]
    lolo.local_7.f_0c = C_004090C7;//MainDispatcher[END][callback]
    C_00666CF2(&lolo.local_7, bp08);//set main loop callbacks?
   }
  }
  if(D_009A058C) {
   //-------------
   //-- fade in --
   C_00404E00(D_007BA668);//set picture alpha(fade in/out)?
   C_00660E95(1, bp08);//G_DRV_84?change_layer
   C_0066E641(D_009A0570, bp08);//dx_spr:render something?
   D_007BA668 -= 0x33;
   if(D_007BA668 <= 0x33)
    D_009A058C = 0;
  }
  C_00660EEB(bp08);//G_DRV_8C:EndScene
 }
}

//[callback]
void C_00406770(int bp08, int bp0c, int _p10, struct t_aa0 *_p14) {
 unsigned char local_1;

 switch(bp08) {
  case WM_CHAR://0x102
   local_1 = (unsigned char)bp0c;
   switch(local_1) {
    case 0x00: break;//0x00 or whatever ...
   }
  break;
 }
}

//[callback]
void C_00406797(int _p08, int _p0c, int _p10, struct t_aa0 *_p14) {
}

This is one of the simplest system in the program. You can see the three main callbacks, START, UPDATE and END, plus two empty callbacks which are called on keyboard's and mouse's events.

The function C_00404D80 returns the current graphic mode (from Windows'registry);
The three modes available are:
  • 0 - 640x480
  • 1 - 320x200 centered
  • 2 - 320x200
And according to the mode, you can see several places where specific code is executed.

For instance, those strange struct t_dx_sfx_e0 objects, what are they ?
Let's say they hold information about polygons to be rendered. Each object is configured to render one type of polygon (triangle, quad or line, textured or not, shaded or not, ...); this allows the renderer algorithm to batch more easily.
In mode 0 you need six of them to draw a fullscreen image. That is because the maximum size of a texture is 256x256 pixels. So 3 of them are necessary to cover 640 pixels horizontally and 2 to cover 480 pixels vertically; and 3x2 = 6 !
In the other modes, only 2 objects are necessary !

In order to have perform the fade in/out effects, the system renders a semi-transparent polygon on top of the background image and makes its alpha channel varies.
That is what the function C_00660E95 helps us to do: by changing the current rendering target (I wrote layer in the source's comments). Target 0 is solid while target 1 allows for transparent polygons.

This system also uses the archive manager (from the module is_lib.cpp). First by indicating that it wants to associate the index 0x10 to the archive file "cd/disc_us.lgp", and then by getting information on files inside this archive (such as offest, and size) by using their names.

So you see, even for a task as simple as displaying a picture and waiting for some user's interaction, we have a lot of code to go through.

Thursday, December 3, 2015

Decompiling Final Fantasy VII

Some years ago, I started the task of reversing back to C code the executable file from the game "Final Fantasy VII". More exactly, from the patched version 1.02.

From a technical point of view, the program is divided into major "systems":
- main dispatcher: the first system of the game, and the one that makes the transition between the others.
- field
- world map
- battle
- menu
- chocobo race[minigame]
- highway[minigame]
- roller coaster[minigame]
- submarine[minigame]
- condor fort[minigame]
- snowboard[minigame]
- credits: logo+opening and ending
- swirl: the animation before a battle
- request CD
- game over

Each system can be view as a standalone program (only one system at a time is running) and is defined by 5 callback functions:
- BEGIN: called once to initialize the system
- UPDATE: called every frame to update state and display
- END: called once to cleanup the system
- ONKEY: called in case of a keyboard event(almost never used)
- MOUSE: called in case of a mouse event(almost never used)

Some of the systems, like request CD or game over, are defined in only one ".C" file(or module), while others, like battle, can take dozens.

Those systems are pretty high-level stuff, and can rely on a big library to do the low-level tasks: file access, memory management, lists, heaps, inflating, matrix operations, graphic stuff ...

This library can be found at the runtime adresses ranging from 0x0065ec20 to 0x006bf4e0.
For music/sound, the library is between 0x0740d80 and 0x0074ba80.

Let's have a look at the WinMain:

#include "ff7.h"

#include "sw.h"

#include 



if(lolo.bp_53cc->f_9f0.f_10) {
 lolo.bp_53cc->f_9f0.f_10(lolo.bp_53cc);
 lolo.bp_53cc->f_040 += 1.0;
}
the current "REFRESH" callback is called (and some frame counter is incremented)


Then we have:
if(lolo.bp_53cc->f_020 == 0)
 C_0066059C(lolo.bp_53cc);//G_DRV_10:Flip
That is the graphic's driver's "Flip" functions is called; i.e the backbuffer which has been updated during the "REFRESH" call is presented to the used.

That is the core of the program file. Pretty simple, isn't it ?

To give you an idea of how far I went, let's say that except for the Battle system (the biggest of all), the Condor Fort system and part of the Menu system related to the battle system, I could reverse all the executable file to C source code (some parts harder to read than others) and recompile to a runnable file. Which means that the fun starts ... now !

Next time, we will take a look at the Game Over system, the simplest of all.

Monday, December 19, 2011

LC 3.00 strikes again

Hi there, sorry to make you wait, I was doing research on the C library part (plus reverse-coding on Commander Keen, Moebius, Ultima IV, ... I wish I can make a blog about them too).

Remember one of the first posts here ? The string "LC 3.00" at the beginning of the data segment gives us the hint that it have been compiled with Lattice C version 3.
Thanks to the internet, I found an almost complete version of the compiler (copyrighted from 1982, so you can say it's an old one).
In this version, there is a file C.ASM which is the source file to the very first piece of code that will be called when launching MGT.
The problem is, although it contains the line:
_VER DB "LC 3.00",0
confirming the fact that it is the right compiler, the source differs from what found in MGT's executable in some places.

For instance:
MOV _MSIZE,BX
MOV _MSIZE+2,AX
CALL RBRK ; reset memory pool
OR AX,AX
becomes:
MOV _MSIZE,BX
MOV _MSIZE+2,AX
PUSH ES
PUSH SI
CALL RBRK ; reset memory pool C_3B65_0000
POP SI
POP ES
OR AX,AX
in MGT.

And that's not all.

When checking the C library too, we can find some differences. In the floating point library for instance, there is a module named CXNDPL (don't ask me what it does... yet).

Inside you can find:
FLDZ
FSTP QWORD PTR _FPA
PUSH BX
PUSH AX
CALL CXFERR
POP AX
POP BX
and for MGT:
PUSH BX
PUSH AX
CALL CXFERR
POP AX
POP BX
the two upper instructions have disappeared.

If you want my opinion, the version of Lattice I have may be one of the first released. And it has some bugs left, which may have been corrected/patched in a later release. Though I don't understand why the version string is the same. Why not change it to "LC 3.00b" or "LC 3.01" ?

So if somewhere, someone reading this post has any information about this verions/bug/patch issue, I would be really great to hear from her (or him, it may be a guy too). Don't hesitate to comment !

That's all for today ! Thanks for reading !