Tuesday, February 25, 2025

Ultima III - Try a little randomness

Most of the games need at some point to introduce some randomness in their world. In Ultima III's case, this is implemented as a routine (found at offset 0x514B in EXODUS.COM) that I named u3_rand. Strangely, the character's creation process doesn't need it. As a matter of fact ULTIMA.COM and BOOTUP.COM only uses u3_rand for sound effects.

Which leads us to EXODUS.COM. Here is a list, I hope is exhaustive, of the different calls to u3_rand:

  • position of the random explosions in Castle Exodus
  • when steal action fails, determine if this will alert the guards
  • starting position of the whirlpool in Ambrosia
  • creatures spawning (lots of calls)
  • dragons and pirates shoot on map
  • move errand creatures
  • Dexterity test (called by several other routines)
  • Player's ship cannon shoot
  • magical attack on all enemies (called by Repond, Noxum, Dag Mentar, Zxkuqyb, the unnamed spell, Pontori)
  • set random position in dungeon
  • spells: Repond, Mittar, Dag Acron, Pontori, Appar Unem, Sanctu, Sanctu Mani, Surmandum
  • Move whirlpool
  • Determination of a chest's content
  • Determine if chest is trapped, the trap's type, and the damage inflicted by it.
  • All party damage (called by other routines)
  • Random encounter in dungeon and enemy's type
  • In combat, determine the number of enemies, their characteristics
  • Player's hit resolution in combat
  • Dragon's fireball during combat
  • Enemy moves during combat
  • Enemy's magical attack]
  • Player's parade during combat
  • Player's damage when hit
  • Poison attack
  • Pilfer's determination
  • Gremlin's event in dungeon 

 And I thinks that's all.

So how does it work ? Well, basically, there is a 128 bit-long, or 16 byte-long, seed which is updated on each u3_rnd call. It is stored in big-endian. Here is my conversion in C language of the algorithm (seed_len is 16):

void update_seed(unsigned char seed[], int seed_len) {
	//-- --
	int carry = 0;
	for(int i = seed_len - 1; i > 0; i --) {
		unsigned a = seed[i] + seed[i - 1] + carry;
		seed[i - 1] = a & 0xff;
		carry = (a & 0x100) >> 8;
	}//end for
	//-- seed_128 += 1 --
	for(int i = seed_len - 1; i >= 0; i --) {
		seed[i] ++;
		if(seed[i])
			break;
	}//end for
}

After the seed is updated, u3_rnd applies a modulo to its first byte, seed[0], and returns the result in register DL. The modulo parameter is passed in register DH.

Funny fact Ultima IV uses the same algorithm but does not do the modulo. This way, the caller can have access to 0~0xff values while in Ultima III's case, the returned value cannot exceed 0xfe. This is due to the fact that the maximum modulo parameter is 0xff.

I forgot to say that the seed is "randomized" with the return values from get_date and get_time system calls. For test purpose, you can initialize it 0s, it still works.

 That's all for today.

Sunday, February 16, 2025

Ultima III - More strange things

Most of the time, an MSDOS's COM file had all its data (static and not) at the begin of it, and the code after. It begins with a JMP instruction though, to "skip" the data. But in the case of EXODUS.COM, data and code alternate several times. I used this observation to "cut" the executable into several modules -- understand several ASM files. This may or may not reflect the original development design, but at least it makes the sources a little more readable ... in my opinion at least.

Some of the data and code is local to the module in which it is declared, and some is not. Nothing strange here.

There are several text strings that are declared only once, and used by different modules; take this declaration for instance:

TXT_What    db "<-What?",0ah,0

This is the text displayed when the player issues an invalid command. This text is shared by several functions/procedures across several modules. And I think that it is a good design: It saves memory, it simplifies debugging, development, localization ...

On the other hand, you also have the same strings declared several times:

D_1707    db "Pass",0ah,0
...
D_77EA    db "Pass",0ah,0
...
D_8885    db "Pass",0ah,0

This  mixed approach on design unnecessary complicates the work of the developer. Of course, without the original source code in my possession all I can do is assumption: were those modules supposed to be separates executable files ? Was it because of the design of the apple II version that made it necessary to have redundancy ? Was there several programmers involved with different coding habits ? ... ?

Want another strange thing yet ?

All right, here are two pieces of code, the first one being called when the player casts the wizard spell Repond and the second one, the cleric spell Pontori:

;Wizard spell:"Repond"
C_5F9F:
	;-- combat only --
	CMP	party[PARTY_02],LOC_COMBAT
	JNZ	C_5FD1
	;-- --
	CMP	enemy_type,TIL_18	;Orc
	JNZ	C_5FD1
	;-- only once per combat --
	CMP	D_84E3,0
	JNZ	C_5FD1
	MOV	D_84E3,0FFh
	;-- 50% chance --
	MOV	AL,DL	;backup DL
	MOV	DH,0FFh
	CALL	u3_rand
	SHL	DL,1
	JB	C_5FD1
	MOV	DL,AL	;restore DL
	;-- --
	CALL	SpellSFX

	MOV	AL,0FFh
	CALL	C_5EAA	;magical attack(all)

	JMP	C_5E69	;cast spell:end
;----
C_5FD1:	JMP	w_Failed

...

;Cleric spell:"Pontori"
C_6153:
	;-- combat only --
	CMP	party[PARTY_02],LOC_COMBAT
	JNZ	C_6185
	;-- --
	CMP	enemy_type,TIL_19	;Skeleton
	JNZ	C_6185
	;-- only once per combat --
	CMP	D_84E3,0
	JNZ	C_6185
	MOV	D_84E3,0FFh
	;-- 50% chance --
	MOV	AL,DL	;backup DL
	MOV	DH,0FFh
	CALL	u3_rand
	SHL	DL,1
	JB	C_6185
	MOV	DL,AL	;restore DL
	;-- --
	CALL	SpellSFX

	MOV	AL,0FFh
	CALL	C_5EAA	;magical attack(all)

	JMP	C_5E69	;cast spell:end
;----
C_6185:	JMP	w_Failed

So what do we notice ? Well, the code is almost identical in both cases. Only the target of the spell differs: orc-related creatures for Repond and skeleton-related creatures for Pontori. And what do we notice also ? The "only once per combat" piece of code of course; it uses a flag that is reset at the start of each combat. The same flag for both spells, which insures that either spell can be cast only once per combat.

Well since magic is not in my main expertise field, it is hard for me to give any explanation to this, but I tried to check Ultima III's documentation and didn't find the description for this feature. Was the player supposed to guess it by himself ? And then, why make the two spells share the same flag ? Was it a copy/paste error ?

(To be honest, I just realized that one's party can only be attacked by one type of creature at the time, which means that having the flag shared by the two spells might not be a problem after all)

 Ok, that's all for this time. Stay tuned for more tech-related stuff on Ultima III.

Monday, February 3, 2025

Ultima III - Strange things going on

That's right, I'm having fun with the MS-DOS version of Origin system's breakthrough title: Ultima III Exodus.

Having almost entirely reversed the executable part of the game I felt eager to share some of the information I gathered through this process.

First, the basics. There are 3 executable files that constitute the core of the game, plus one "overlay" file. They are:

  • ULTIMA.COM: the opening animation and splash screen
  • BOOTUP.COM: allows to create and manage characters and the party
  • EXODUS.COM: the main game
  • DUNGEON.DAT: and "overlay" loaded by EXODUS.COM when the player enters a dungeon; it does the 3D-sort-of rendering of the dungeon.

The executable files seem to have been written in assembly language exclusively. I use Microsoft's Macro Assembler (MASM) version 4.0 for the build and it works fine, though I have no way to be sure which assembler was used by the original coder (who by the way is none other than James R. Van Artsdalen, the same who did the MS-DOS port of Ultima IV).

Being MSDOS's COM files, the start address of the 3 executable files is 100h (256 in decimal) and this address cannot be changed to another arbitrary address, unlike the MZ-type executable. And so here is one of the first strange thing about this game, happening in EXODUS.COM. This program's entry point is not at address 100h; as a matter of fact, there is no executable code at this address and if you try to launch it from the MSDOS prompt, chances are you will lock your PC or cause a crash.

So what is going on here ? EXODUS.COM is meant to be loaded at address 100h, like any other COM file, but its entry point is then at address 24C4h. I think this was intended as some kind of "protection", to prevent users from launching the main part of the game directly. Actually, BOOTUP.COM is responsible for launching EXODUS.COM. When option "Journey Onward" is selected, BOOTUP.COM loads (there is a little hack involving part of the PSP used to create a loader but I will not explain the details here) EXODUS.COM at address 100h and jumps to the address 24C4h.

Well, it is not that simple; BOOTUP.COM actually reads the 16-bit value at address 1328h which contains the correct entry point address: 24C4h. Why not directly jump to the entry point ? That it just an hypothesis, but I think that the address 24C4h is really arbitrary and the programmer didn't want to change/rebuild its BOOTUP.COM code every time there was a change in EXODUS.COM, so he chose to store this arbitrary address at a fixed address in the program. Why is 1328h less arbitrary ? Because 1328h is 1228h + 100h and 1228h this is the exact size of the resources files (SOSARIA.ULT, AMBROSIA.ULT, LCB.ULT, ...) that are meant to be loaded at address 100h.

 Another strange thing ? Ok here we go (this one I originally posted on BlueSky):

I found this intriguing piece of code in EXODUS.COM. It starts at offset 73F0h (when loaded in memory):

xxxx:73F0 B42C MOV    AH,2C
xxxx:73F2 CD15 INT    15

It took me some time to understand that it was a mistake from the programmer. Can you spot it ?‪
I am almost sure that this is what was intended:

xxxx:73F2 CD21 INT    21

The programmer must have forgotten to put the h-prefix in his source code (instead of "int 21h", he wrote "int 21").

Fortunately, the consequence is minor. To explain it may take some time though.

If you know a little MS-DOS programming, you may know that "INT 21h" is the standard way to call a system service who will be identified by the value of register AH.
In this case, 2Ch, the system time is requested. The value is returned as:

  • hours in register CH
  • minutes in register CL
  • seconds in register DH
  • hundredths in register DL

EXODUS.COM only uses the value from register DH, seconds.
Here is another piece of code found at 3 different places:

MOV    AH,2Ch
INT    21h
MOV    BL,DH
ADD    BL,5
CMP    BL,60
JB    _below60
SUB    BL,60
_below60:

(aka, get the current second count and add 5 secs.)

Then the program enters a loop which will be exited on one of the two conditions:

  • a key has been pressed
  • the current second count is equal to the value computed previously

In other words: gives the player 5 seconds to press a key.

This code is used when the program is in "prompt mode":
World map, Dungeon, or Combat.

Let's focus on the World Map prompt; there is a call to the function at 7347h at each iteration of the prompt loop we just described. This function manages a particular game object: the Whirlpool !

On entering this function, the value of register BX (containing the counts of second + 5) is pushed on the stack, and, most of the time, after the Whirlpool's information has been refreshed, the value of register BX is popped from the stack, and no harm is done.

BUT in case the player's boat is engulfed ...
‪... function at 2168h is called; in this function, some text is displayed, some disk accessed, some graphic stuff done ... let's say that this takes time. And when the function returns, remember that we are still inside the prompt loop. Since the pushed value of register BX is certainly overdue, we need to compute a new one.

The previously pushed value of register BX is popped and ... ignored, and we have then the piece of code, the one at 73F0h.
Which means that in this particular case, function 7347h, which is expected to leave a valid timer value in register BX, may returns something completely random.

What are the consequences ?

Fortunately, this is not a big deal. Let's say we have the best case, the bad case, and the worst case:

  • best case: register BL contains a value different from the current seconds counter; the player has a delay of 5 to 255 seconds.
  • bad case: the player has less than 5 seconds until the next timeout.
  • worst case: register BL value is equal to the current seconds timer; there is no delay left.

And what happens when the delay is overdue ? The same thing as when the player issues a "pass" command. The entire game's world is updated of 1 step while the player does nothing.

As I said before, no big deal...

 

Well, that's it for today. I hope I will be able to post more Strange Things on Ultima III very soon.

Sunday, November 5, 2017

A map to ff7.exe

Here is an exceprt from the program I use to compare the object files generated from my "decompiled code" with the original exe file. One line correspond to one source file.
__diffAt is the comparison function which takes 4 parameters:
  • the code segment address
  • the constant data address
  • the initialized data address
  • the object file path

When I could guess the object file name from the assembly code (originally through the __FILE__ macro) I kept this name. Otherwise I would name the file from the object code address prefixed with C_.


As you can see, most of the exe file is reversed now. Only the battle effects modules are still to be decompiled. And there are many of them. So it won't be for tomorrow.

Anyway I'm pretty happy with this "map" because it gives some clean view to the original exe file. You can see that there is a clean separation of the different parts of the file. For instance, the "highway minigame" files are contiguous, same for the "field" part, or the "world map" part.

Here it is, I hope you have fun with it !



 //--------------------
 //---- 0x00401000 ----
 //--------------------
// __diffAt(0x00401000, 0x007BA070,          0, OBJPATH "init.obj");//ok
// __diffAt(0x00401500, 0x007BA1A8, 0x007B63E0, OBJPATH "C_00401500.obj");//ok -- "swirl effect module"
// __diffAt(0x00402EF0, 0x007BA278, 0x007B6408, OBJPATH "C_00402EF0.obj");//ok -- "request CD"
// __diffAt(0x00404D80,          0, 0x007B6448, OBJPATH "C_00404D80.obj");//ok -- windows registry stuff
// __diffAt(0x00404E00, 0x007BA668, 0x007B6490, OBJPATH "C_00404E00.obj");//ok -- "Game Over"
// __diffAt(0x004067A0, 0x007BA738, 0x007B64A0, OBJPATH "C_004067A0.obj");//ok -- windows registry stuff
// __diffAt(0x00406D10, 0x007BA748, 0x007B6550, OBJPATH "initpath.obj");//ok[contains bug]
// __diffAt(0x00408010, 0x007BAB24, 0x007B6658, OBJPATH "main.obj");//ok
// __diffAt(0x0040A460,          0, 0x007B6730, OBJPATH "C_0040A460.obj");//ok -- windows registry stuff
// __diffAt(0x0040AAB0,          0, 0x007B6818, OBJPATH "C_0040AAB0.obj");//ok -- timeSetEvent related
// __diffAt(0x0040AC90, 0x007BAE80,          0, OBJPATH "sm_movie.obj");//ok
 //--------------------
 //---- 0x0040B400 ----
 //--------------------
 //        libc
 //--------------------
 //---- 0x00414EE0 ----
 //--------------------
// __diffAt(0x00414EE0,          0,          0, OBJPATH "C_00414EE0.obj");//ok -- movie related debug module
// __diffAt(0x00414F00, 0x007BF640, 0x007B7468, OBJPATH "movie.obj");//ok[using DXMEDIA 6]
// __diffAt(0x004186E0, 0x007C05B8,          0, OBJPATH "C_004186E0.obj");//ok
// __diffAt(0x00419210, 0x007C0A68, 0x007B7470, OBJPATH "smcdfile.obj");//ok
// __diffAt(0x00419360, 0x007C0AE8, 0x007B7478, OBJPATH "C_00419360.obj");//ok
// __diffAt(0x00419E50,          0,          0, OBJPATH "C_00419E50.obj");//ok -- part of gzip inflater?
// __diffAt(0x0041A090,          0,          0, OBJPATH "C_0041A090.obj");//ok -- used by SWIRL?
// __diffAt(0x0041A1B0,          0,          0, OBJPATH "C_0041A1B0.obj");//ok -- high-level input module
// __diffAt(0x0041B300, 0/*0x007C0AF0*/, 0x007B74B8, OBJPATH "Battle.obj");//hobo ok[data]
// __diffAt(0x0041CF10,          0,          0, OBJPATH "C_0041CF10.obj");//ok -- some inflate function
// __diffAt(0x0041D090, 0x00000000, 0x00000000, OBJPATH "C_0041D090.obj");//ok
// __diffAt(0x0041D300, 0x007C0D08,          0, OBJPATH "C_0041D300.obj");//ok -- inflate.cpp?
// __diffAt(0x0041EEA0, 0x007C0E84, 0x007B74D0, OBJPATH "input.obj");//ok
 //====---- BATTLE ----==== almost 300 files
// __diffAt(0x0041FA70, 0/*0x007C1028*/,          0, OBJPATH "C_0041FA70.obj");//hobo ok[data]
// __diffAt(0x00428500, 0x007C1FC8, 0x007B74E0, OBJPATH "b3ddata.obj");//ok
// __diffAt(0x00429AC0, 0x007C21C8, 0x007B74E4, OBJPATH "C_00429AC0.obj");//ok
// __diffAt(0x00430DD0,          0,          0, OBJPATH "C_00430DD0.obj");//ok
// __diffAt(0x00431890, 0x007C28C0,          0, OBJPATH "coloss.obj");//ok
// __diffAt(0x00431C80,          0,          0, OBJPATH "C_00431C80.obj");//ok
// __diffAt(0x00432040,          0,          0, OBJPATH "C_00432040.obj");//ok
// __diffAt(0x00432C60,          0,          0, OBJPATH "C_00432C60.obj");//ok
// __diffAt(0x00433020,          0,          0, OBJPATH "C_00433020.obj");//ok
// __diffAt(0x00433510, 0x007C2980, 0x007B74F0, OBJPATH "C_00433510.obj");//hobo ok[data duplicate problem]
// __diffAt(0x00434690, 0x007C2A00, 0x007B7500, OBJPATH "C_00434690.obj");//hobo ok[data duplicate problem]
// __diffAt(0x00435250,          0,          0, OBJPATH "C_00435250.obj");//ok
// __diffAt(0x00435770, 0x007C2ACC, 0x007B7510, OBJPATH "C_00435770.obj");//ok
// __diffAt(0x00436C30,          0,          0, OBJPATH "C_00436C30.obj");//ok
// __diffAt(0x004372B0,          0,          0, OBJPATH "C_004372B0.obj");//ok
// __diffAt(0x00437370, 0x007C2AD0,          0, OBJPATH "C_00437370.obj");//ok
// __diffAt(0x00437DB0,          0,          0, OBJPATH "C_00437DB0.obj");//ok
 //-- 284 files --
// __diffAt(0x00438610, 0x007C2AE0,          0, OBJPATH "C_00438610.obj");//ok "EFFECT/HITMARK"
// __diffAt(0x00438C80, 0x007C3248,          0, OBJPATH "C_00438C80.obj");//ok "EFFECT/S_BARIA"
// __diffAt(0x004390F0, 0x007C32D0,          0, OBJPATH "C_004390F0.obj");//ok "MAGIC/MGUN"
 //__diffAt(0x00439A00, 0x007C33A0,          0, OBJPATH "lasboss3.obj");//[TODO] "weapon/s_nova"
 //0x004429B0 "weapon/dia_u"
 //0x00444730 "weapon/weapon4"
 //0x00447AB0 "weapon/beam1"
 //0x00448A60 "weapon/weapon3"
 //0x0044A510 "weapon/weapon1" "C:\FF7\src\battle\yasui\sting.cpp"?
 //0x0044EC10 "WEAPON/WEAPON2"
// __diffAt(0x00452020, 0x007E2CA8,          0, OBJPATH "deadsef.obj");//ok "SUMMON/LASTBOSS"
 //0x004559B0 "SPECIAL/TOSEKI"
 //0x00457040 "SPECIAL/MAGEND"
 //0x00457410 "MAGIC/WALL"
 //0x00457A90 "SPECIAL/LASBOSS1"
 //0x0045A2C0 "LIMIT/S_GUARD"
 //0x0045ADF0 "LIMIT/DAICHI"
 //0x0045B8A0 "SPECIAL/PUNCH"
 //0x0045C030 "limit2/satan"
 //0x00461660 "limit2/night"
 //0x00463E50 "LIMIT2/SPARK"
 //0x004658A0 "ITEM/KEMURI"
 //0x00465B30 "ITEM/YAMABIKO"
 //0x00465F70 "LIMIT/A_MAX"
 //0x00466990 "LIMIT/HAZARD"
 //0x00467490 "LIMIT/HAMBLOW"
 //0x004685F0 "LIMIT/CATASTRO"
 //0x0046B6C0 "LIMIT/BUSIN"
 //0x0046E7F0 "LIMIT/HAKOU"
 //0x0046FF00 "LIMIT/S_BEAM"
 //0x00471FD0 "LIMIT/GLENADE"
 //0x00472EC0 "LIMIT/JAKI"
 //0x004739E0 "LIMIT/EVA"
 //__diffAt(0x004766C0, 0x00000000, 0x00000000, OBJPATH "C_004766C0.obj");//[TODO] "SUMMON/KNIGHTS"
 //0x00483520 "SUMMON/VAHAMUT3" "C:\FF7\src\Battle\yasui\vahamut0.cpp"?
 //0x0048C200 "SUMMON/VAHAMUT2"
 //0x004943E0 "LIMIT2/GAISEI"
 //0x00496B50 "LIMIT/RAKUIN"
 //0x00497790 "SUMMON/VAHAMUT"
 //__diffAt(0x0049B040, 0x00000000, 0x00000000, OBJPATH "C_0049B040.obj");//[TODO] "magic/brizag2"
 //0x0049FA40 "limit2/dise"
 //0x004A0A40 "SUMMON/odin1"
// __diffAt(0x004A5AC0, 0x00803E48,          0, OBJPATH "C_004A5AC0.obj");//ok "SUMMON/odin2"
 //0x004AAB50 "limit2/beast"
 //0x004AB910 "LIMIT/KODOU"
 //0x004AC3F0 "LIMIT/MIND"
 //0x004ACE20 "LIMIT2/HITUMETU"
 //0x004AD950 "LIMIT2/ISSHOKU"
 //0x004AE240 "LIMIT2/MATSURI"
 //0x004AE840 "LIMIT2/JINRAI"
 //0x004AEB60 "LIMIT3/RANNTOU"
 //0x004AF1E0 "LIMIT3/D_DIVE"
 //0x004AFD20 "LIMIT3/H_JUMP"
 //0x004B1800 "LIMIT3/D_M"
 //0x004B2A50 "LIMIT3/B_JUMP"
 //0x004B2F40 "LIMIT3/SINRA"
 //0x004B38A0 "LIMIT3/HIWIND"
 //0x004B46B0 "LIMIT2/MEIKYO"
 //0x004B5120 "LIMIT3/DYNAMITE"
 //0x004B62B0 "summon/hardeath"
 //0x004BE8A0 "limit2/garyoten"
 //0x004C15C0 "limit2/meteo_r"
 //0x004C3F80 "limit/lgirl"
 //0x004C4BE0 "LIMIT3/C_M"
 //0x004C6860 "limit3/e_r"
 //0x004C8200 "limit2/lunatic"
 //0x004C8E90 "limit3/s_d"
 //0x004CB120 "limit3/h_moon"
 //0x004CBDF0 "LIMIT2/TOYBOX"
 //0x004CCF30 "item/wp_bi"
 //0x004CD810 "item/wp_ke"
 //__diffAt(0x004CE0F0, 0x00000000, 0x00000000, OBJPATH "C_004CE0F0.obj");//[TODO] "item/wp_yu"
 //0x004CFEB0 "item/wp_si"
 //0x004D1B90 "item/wp_ea"
 //0x004D2830 "item/wp_ba"
 //0x004D36A0 "item/wp_cu"
 //0x004D3F10 "BLUE/KAENN"
 //0x004D4790 "MAGIC/FULL"
 //0x004D56B0 "SUMMON/tupon"
 //0x004DB2E0 "SPECIAL/TYOKYU"
 //0x004DCEB0 "SPECIAL/SYAKU"
 //0x004DE3E0 "SPECIAL/RE_DO"
 //0x004DF8F0 "SPECIAL/HADOHO"
 //0x004E0E40 "SPECIAL/SHIELD"
 //__diffAt(0x004E1580, 0x00000000, 0x00000000, OBJPATH "C_004E1580.obj");//[TODO] "limit2/tifa1"
 //0x004EA970 "limit3/sld_fang"
 //0x004ECE70 "special/joker2"
 //0x004EE870 "special/joker1"
 //0x004EF6A0 "SPECIAL/DIA2"
 //0x004F06F0 "SPECIAL/DIA1"
 //0x004F1E20 "SPECIAL/CLUB2"
 //0x004F4370 "SPECIAL/club1"
 //0x004F52D0 "special/heart2"
 //0x004F6AF0 "special/heart1"
 //0x004F7AF0 "SPECIAL/SPADE2"
 //0x004F8AD0 "SPECIAL/SPADE1"
 //0x004F97F0 "SUMMON/KJATA"
 //0x005013F0 "SUMMON/ALEXAND"
 //0x00507AF0 "SUMMON/CHOCO2"
 //0x005097C0 "SUMMON/CHOCO"
 //0x0050B850 "SPECIAL/HADO"
 //0x0050CD90 "SPECIAL/ENERGY"
 //0x0050E2B0 "SPECIAL/KAKUSAN"
 //0x0050F110 "SPECIAL/SAIKYO"
 //0x00510230 "SPECIAL/HO_DEN"
 //0x00511230 "special/s_bless"
 //0x005125E0 "SPECIAL/GESUI1"
 //0x005135B0 "SPECIAL/MIZUFUKI"
 //0x00514710 "ITEM/VEGET"
 //0x00515060 "SUMMON/FENIC"
 //0x005190F0 "special/korosi"
 //0x00519830 "SPECIAL/FROG"
 //0x0051A500 "SPECIAL/DAIONPA"
 //0x0051B250 "SPECIAL/RENMISA"
 //0x0051C410 "SPECIAL/SEKIKA"
 //0x0051E190 "SPECIAL/NAPARM"
 //0x0051F5F0 "SPECIAL/KONA"
 //0x00520290 "SPECIAL/PINK"
 //0x005212B0 "SPECIAL/JAMMER2"
 //0x00522010 "SPECIAL/JAMMER"
 //0x00522B00 "SPECIAL/HAKUGEK2"
 //0x00523850 "SPECIAL/HAKUGEK1"
 //0x005246A0 "SPECIAL/GAS"
 //0x00525190 "SPECIAL/SHOWER"
 //0x00525EC0 "SPECIAL/JIBASIRI"
 //0x00526DA0 "SPECIAL/TELE"
 //0x00528200 "SPECIAL/DENGEKI"
 //0x00528FF0 "SPECIAL/NADARE"
 //0x0052A550 "SPECIAL/ROKET"
 //0x0052B920 "SPECIAL/KOUDAN2"
 //0x0052CB40 "SPECIAL/KOUDAN1"
 //0x0052D8B0 "SPECIAL/REITO"
 //0x0052F170 "special/yami"
 //0x0052FE10 "special/renbaku"
 //0x005305F0 "special/zenbaku"
 //0x00530D30 "SPECIAL/UTAGOE"
 //0x005313F0 "special/daisenp"
 //0x00531FE0 "special/ayasii"
 //0x005324A0 "special/heatbu"
 //0x00532B10 "magic/seald"
 //0x00533920 "SPECIAL/MAGMA2"
 //0x00533FE0 "SPECIAL/HIKARI3"
 //0x00534710 "SPECIAL/HIKARI2"
 //0x00534E80 "SPECIAL/REIKI"
 //0x00535C30 "SPECIAL/KAEN2"
 //0x00536850 "SPECIAL/KAEN"
 //0x00537340 "SPECIAL/JIBAKU2"
 //0x00538480 "special/turara"
 //0x00539040 "SPECIAL/WLASER"
 //0x00539770 "SPECIAL/HAKAI"
 //0x00539B10 "SPECIAL/GEKI2"
 //0x0053B2B0 "SPECIAL/GEKI1"
 //0x0053BFA0 "SPECIAL/NETU"
 //0x0053CCD0 "SPECIAL/MAGMA"
 //0x0053D3D0 "SPECIAL/ONSEN"
 //0x0053DAA0 "special/sander"
 //0x0053EEC0 "SPECIAL/AWA2"
 //0x0053FE90 "SPECIAL/CHIKASUI"
 //0x00540500 "SPECIAL/KINZAN"
 //0x00540B10 "SPECIAL/SMOG"
 //0x00541810 "SPECIAL/TUNAMI"
 //0x005426E0 "SPECIAL/KAHUN"
 //0x005434E0 "special/toboe2"
 //0x00543D40 "SPECIAL/SCORP"
 //0x00544590 "SPECIAL/NOZAN"
 //0x005452E0 "SPECIAL/SAZAN"
 //0x00546030 "SPECIAL/KUEISA"
 //0x00547460 "SPECIAL/HUBUKI"
 //0x005482A0 "SPECIAL/DOKUEKI"
 //0x00548AC0 "SPECIAL/DOKUFUKI"
 //0x005493F0 "SPECIAL/OIL"
 //0x00549D50 "SPECIAL/NONOSI"
 //0x0054B150 "SPECIAL/ATMIC"
 //0x0054C8E0 "SPECIAL/COLD1"
 //0x0054D6F0 "SPECIAL/RAKU"
 //0x0054E6D0 "SPECIAL/HARRIER"
 //0x0054F290 "SPECIAL/MIZU"
 //0x0054FA50 "SPECIAL/TRIANGLE"
 //0x00550CB0 "SPECIAL/OSEN"
 //0x00551B80 "SPECIAL/KYUU2"
 //0x005529F0 "SPECIAL/JIBAKU1"
 //0x00552CF0 "SPECIAL/KAMA2"
 //0x005535E0 "SPECIAL/SUMI"
 //0x00554010 "SPECIAL/KYUU"
 //0x00554E80 "SPECIAL/KAMA"
 //0x00555760 "SPECIAL/MISAIRU"
 //0x00556130 "SPECIAL/B_2"
 //0x005579E0 "special/yuwaku"
 //0x005586C0 "SPECIAL/REZA2"
 //0x00559470 "SPECIAL/HIKARI"
 //0x00559AC0 "SPECIAL/ITO"
 //0x0055A710 "SPECIAL/SUNA"
 //0x0055B350 "SPECIAL/KONOHA"
 //0x0055BA70 "SPECIAL/KIENGIRI"
 //0x0055C3B0 "special/tan"
 //0x0055C940 "SPECIAL/MGBEAM"
 //0x0055D240 "SPECIAL/TANE"
 //0x0055DA60 "SPECIAL/HARI"
 //0x0055E2E0 "SPECIAL/SARCHI"
 //0x0055EAE0 "SPECIAL/BAKYU"
 //0x0055F7E0 "SPECIAL/ONPA"
 //0x0055FDD0 "blue/chyobo"
 //0x00561B90 "magic/deth"
 //0x00564390 "magic/aleiz"
 //0x00565EC0 "blue/ruretto"
 //0x00566740 "blue/nanntoka"
 //0x00568D30 "blue/mytyg"
 //0x005696A0 "blue/lv4jibak"
 //0x0056A800 "blue/matra"
 //0x0056C1A0 "blue/reazer"
 //__diffAt(0x0056D880, 0x00000000, 0x00000000, OBJPATH "C_0056D880.obj");//[TODO] "magic/faiga3"
 //0x0056ECF0 "blue/lv5deth"
 //0x0056F2B0 "blue/dethfo"
 //0x0056FFD0 "blue/kaeru"
 //0x00570850 "blue/dragonn"
 //0x005711E0 "blue/hanmmer"
 //0x00571940 "blue/kusai"
 //0x00571E20 "blue/hatena"
 //0x00572B90 "blue/goblinp"
 //0x00573720 "blue/aqua"
 //0x00573D80 "blue/magikal"
 //0x005743C0 "BLUE/TRAINN"
 //0x00574B00 "MAGIC/TODO"
 //0x00574F90 "magic/minimam"
 //0x00575460 "magic/dethper"
 //0x00576F40 "blue/sflea"
 //0x00578520 "magic/debaria"
 //0x005796B0 "magic/altema"
 //0x0057A6E0 "blue/beata"
 //0x0057B630 "magic/brizag"
 //0x0057E260 "magic/faiga"
 //0x0057F210 "magic/flea"
 //0x00580750 "MAGIC/STOP"
 //0x00581550 "magic/sailess"
 //0x005819D0 "magic/queik"
 //0x00581D50 "magic/queig"
 //0x00585330 "magic/queir"
 //0x00587C90 "magic/heist"
 //0x00588D10 "MAGIC/SLIPL"
 //0x00589550 "SPECIAL/TIMEDMG"
 //0x0058ADB0 "SPECIAL/UZUMAKI"
 //0x0058BD10 "BLUE/KAENN"
 //0x0058CE50 "MAGIC/FREEZE"
 //0x0058E370 "SUMMON/SIVA"
 //0x00592720 "SUMMON/IFU"
 //0x00596F50 "SUMMON/LAMU"
 //0x0059B090 "SUMMON/TAITAN"
 //0x005A01A0 "MAGIC/HITMARK"
 //0x005A0DC0 "BLUE/HOWIN"
 //0x005A1320 "MAGIC/REZIST"
 //0x005A1850 "MAGIC/ESNA"
 //0x005A1D60 "MAGIC/BREAK"
// __diffAt(0x005A2570, 0x008C9978, 0x00000000, OBJPATH "C_005A2570.obj");//ok "MAGIC/BIO4"
 //0x005A2E60 "MAGIC/BIO3"
 //0x005A4220 "MAGIC/COMETEO"
 //0x005A5520 "MAGIC/BIO2"
 //0x005A66A0 "MAGIC/BIO"
 //0x005A6BF0 "MAGIC/COMET"
 //__diffAt(0x005A7DD0, 0x00000000, 0x00000000, OBJPATH "C_005A7DD0.obj");//[TODO] "MAGIC/GRAV5"
 //0x005A9630 "MAGIC/GRAV3_1"
 //0x005AA910 "MAGIC/GRAV2"
 //0x005AB420 "MAGIC/TORNADO"
 //0x005AC3A0 "MAGIC/DEJON"
 //0x005ACEF0 "SPECIAL/REZA1"
 //0x005AD5C0 "LIMIT/HVSHOT"
 //0x005AE4C0 "LIMIT/KYOGIRI"
// __diffAt(0x005AEC40, 0x008F34B8, 0x00000000, OBJPATH "C_005AEC40.obj");//ok "LIMIT/BLAVER"
 //0x005AF350 "LIMIT/IYASHI"
 //0x005AFF70 "SPECIAL/KEMU1"
// __diffAt(0x005B02E0, 0x008F36D8,          0, OBJPATH "C_005B02E0.obj");//ok "SUMMON/riva"
 //0x005B31F0 "MAGIC/BARSAC"
 //0x005B3970 "MAGIC/RIFREK"
 //0x005B3E40 "MAGIC/MBARIA"
 //0x005B42F0 "MAGIC/BARIA"
 //0x005B4D80 "MAGIC/SLOU"
 //0x005B5720 "MAGIC/GRAV1"
 //0x005B5E10 "MAGIC/BRIZAR2"
// __diffAt(0x005B6540, 0x008FD958, 0x00000000, OBJPATH "C_005B6540.obj");//ok "MAGIC/BRIZAD"
 //__diffAt(0x005B68A0, 0x00000000, 0x00000000, OBJPATH "C_005B68A0.obj");//[TODO] "MAGIC/THUNDER4"
 //0x005B7730 "MAGIC/THUNDER3"
 //0x005B8570 "MAGIC/THUNDERA"
// __diffAt(0x005B8E30, 0x008FDE88, 0x00000000, OBJPATH "C_005B8E30.obj");//ok "MAGIC/THUNDER"
// __diffAt(0x005B9480, 0x008FDF50, 0x00000000, OBJPATH "C_005B9480.obj");//ok "MAGIC/FAIRA"
// __diffAt(0x005B9810, 0x008FE0A8, 0x00000000, OBJPATH "C_005B9810.obj");//ok "MAGIC/FIRE"
 //-- --
// __diffAt(0x005B9B30, 0x008FE110, 0x007B7670, OBJPATH "C_005B9B30.obj");//ok
// __diffAt(0x005BE490, 0x008FE260,          0, OBJPATH "C_005BE490.obj");//ok
// __diffAt(0x005BE9F0,          0,          0, OBJPATH "C_005BE9F0.obj");//ok
// __diffAt(0x005BEC50,          0,          0, OBJPATH "C_005BEC50.obj");//ok "task.cpp"?
// __diffAt(0x005BF340,          0,          0, OBJPATH "C_005BF340.obj");//ok
// __diffAt(0x005BF9D0, 0x008FE2AC,          0, OBJPATH "C_005BF9D0.obj");//hobo ok[code]
// __diffAt(0x005BFAA0, 0x00000000,          0, OBJPATH "C_005BFAA0.obj");//ok
// __diffAt(0x005C01A0, 0x008FE2B8,          0, OBJPATH "C_005C01A0.obj");//ok "MAGIC/LIMIT"
// __diffAt(0x005C0C20, 0x00000000,          0, OBJPATH "C_005C0C20.obj");//ok
// __diffAt(         0, 0x008FE398,          0, OBJPATH "D_008FE398.obj");//ok
// __diffAt(0x005C0CA0, 0x008FEBA0,          0, OBJPATH "C_005C0CA0.obj");//ok
// __diffAt(0x005C2070, 0x008FEE2C,          0, OBJPATH "C_005C2070.obj");//ok
// __diffAt(0x005C67A0,          0,          0, OBJPATH "C_005C67A0.obj");//ok
// __diffAt(0x005C6B60,          0,          0, OBJPATH "C_005C6B60.obj");//ok -- character progression?
// __diffAt(0x005C7090,          0,          0, OBJPATH "C_005C7090.obj");//ok -- character progression?
// __diffAt(0x005C7260,          0,          0, OBJPATH "C_005C7260.obj");//ok -- character progression?
// __diffAt(0x005C7C50,          0,          0, OBJPATH "C_005C7C50.obj");//ok
// __diffAt(0x005C8000,          0,          0, OBJPATH "C_005C8000.obj");//ok -- some helper functions?
// __diffAt(0x005C8180,          0,          0, OBJPATH "C_005C8180.obj");//ok
// __diffAt(0x005C86E0, 0x008FEE38,          0, OBJPATH "C_005C86E0.obj");//ok
// __diffAt(0x005C8B80,          0,          0, OBJPATH "C_005C8B80.obj");//ok
// __diffAt(0x005C8C90,          0, 0x007B7688, OBJPATH "C_005C8C90.obj");//ok
// __diffAt(0x005C8FB0, 0x008FEE48, 0x007B76A0, OBJPATH "C_005C8FB0.obj");//ok
// __diffAt(0x005CA860,          0, 0x007B76C0, OBJPATH "C_005CA860.obj");//ok
// __diffAt(0x005CAD70, 0x008FEEC8, 0x00000000, OBJPATH "C_005CAD70.obj");//ok -- materia/equip/... related?
// __diffAt(0x005CF650,          0, 0x007B76C8, OBJPATH "C_005CF650.obj");//ok
// __diffAt(0x005D0690,          0, 0x00000000, OBJPATH "C_005D0690.obj");//ok
// __diffAt(0x005D0CA0,          0, 0x007B76E8, OBJPATH "C_005D0CA0.obj");//ok
// __diffAt(0x005D1050, 0x008FF038, 0x007B76F0, OBJPATH "inits.obj");//ok
// __diffAt(0x005D1520,          0, 0x00000000, OBJPATH "C_005D1520.obj");//ok
// __diffAt(0x005D16D0, 0x008FF068, 0x007B7700, OBJPATH "C_005D16D0.obj");//ok
// __diffAt(0x005D1910,          0,          0, OBJPATH "C_005D1910.obj");//ok -- random related too?
// __diffAt(0x005D1A30,          0, 0x007B772C, OBJPATH "C_005D1A30.obj");//ok
// __diffAt(0x005D2A30,          0, 0x00000000, OBJPATH "C_005D2A30.obj");//ok
// __diffAt(0x005D2E00,          0, 0x00000000, OBJPATH "C_005D2E00.obj");//ok
// __diffAt(0x005D2F40,          0,          0, OBJPATH "C_005D2F40.obj");//ok -- helper functions for magic FX?
// __diffAt(0x005D3240,          0,          0, OBJPATH "C_005D3240.obj");//ok -- helper functions for magic FX?
// __diffAt(0x005D3650,          0,          0, OBJPATH "C_005D3650.obj");//ok -- helper functions for "SUMMON/KNIGHTS"
// __diffAt(0x005D38D0,          0,          0, OBJPATH "C_005D38D0.obj");//ok -- helper function for "SUMMON/riva"
// __diffAt(0x005D4240,          0,          0, OBJPATH "C_005D4240.obj");//ok -- more magic callbacks?
// __diffAt(0x005D4390, 0x008FF078, 0x00000000, OBJPATH "C_005D4390.obj");//ok -- items,potion related?
// __diffAt(0x005D4F90, 0x008FF098, 0x00000000, OBJPATH "C_005D4F90.obj");//ok -- more magic callbacks?
// __diffAt(0x005D5720, 0x008FF0E8, 0x00000000, OBJPATH "C_005D5720.obj");//ok -- more magic callbacks?
// __diffAt(0x005D6A70, 0x008FF100, 0x00000000, OBJPATH "C_005D6A70.obj");//ok -- more magic callbacks?
// __diffAt(0x005D6E70, 0x008FF138, 0x00000000, OBJPATH "C_005D6E70.obj");//ok -- more magic callbacks?
// __diffAt(0x005D7720, 0x008FF170, 0x00000000, OBJPATH "C_005D7720.obj");//ok -- more magic callbacks?
// __diffAt(0x005D7CF0, 0x008FF1D8, 0x00000000, OBJPATH "C_005D7CF0.obj");//ok
// __diffAt(0x005D9550, 0x008FF1E8, 0x007B7738, OBJPATH "C_005D9550.obj");//ok
// __diffAt(0x005DC390,          0, 0x00000000, OBJPATH "C_005DC390.obj");//ok
// __diffAt(0x005DC880,          0, 0x007B7748, OBJPATH "C_005DC880.obj");//ok
// __diffAt(0x005DD700,          0, 0x007B7790, OBJPATH "C_005DD700.obj");//ok
// __diffAt(0x005DDBB0,          0, 0x007B77A8, OBJPATH "C_005DDBB0.obj");//ok
// __diffAt(0x005DE5C0, 0x008FF1F8, 0x00000000, OBJPATH "C_005DE5C0.obj");//ok
// __diffAt(0x005DF080,          0, 0x00000000, OBJPATH "C_005DF080.obj");//ok
// __diffAt(0x005DF0F0,          0, 0x00000000, OBJPATH "C_005DF0F0.obj");//ok -- damage management?
// __diffAt(0x005DF460, 0x008FF278, 0x007B77C8, OBJPATH "C_005DF460.obj");//ok
// __diffAt(0x005DFDC0, 0x008FF290, 0x00000000, OBJPATH "bdata.obj");//ok
// __diffAt(0x005E0200, 0/*0x008FF2E8*/, 0x00000000, OBJPATH "char.obj");//hobo ok[data]
// __diffAt(0x005E2A10, 0x008FFA50, 0x00000000, OBJPATH "limitbrk.obj");//ok
// __diffAt(0x005E2F00, 0x008FFB00, 0x007B77D0, OBJPATH "stage.obj");//ok
// __diffAt(0x005E3900, 0x008FFC00, 0x00000000, OBJPATH "enemy.obj");//ok
// __diffAt(0x005E4310, 0x008FFC78, 0x007B77DC, OBJPATH "lmd.obj");//ok
// __diffAt(0x005E7680, 0x00900084, 0x007B77EC, OBJPATH "amptoanm.obj");//ok
// __diffAt(0x005E8550, 0x00900120, 0x007B77F4, OBJPATH "mdl.obj");//ok
// __diffAt(         0, 0x00900198,          0, OBJPATH "D_00900198.obj");
 //====---- coaster ----====
// __diffAt(0x005E8A70, 0x0090140C, 0x007B77F8, OBJPATH "C_005E8A70.obj");//ok -- launcher
// __diffAt(0x005E9150, 0x00901410,          0, OBJPATH "C_005E9150.obj");//ok -- resources/sound?
// __diffAt(0x005E98E0, 0x009014A8, 0x007B780C, OBJPATH "C_005E98E0.obj");//ok -- main?
// __diffAt(0x005EA8C0, 0x009014B0,          0, OBJPATH "C_005EA8C0.obj");//ok -- track matrix?
// __diffAt(0x005EAB70,          0,          0, OBJPATH "C_005EAB70.obj");//ok -- game objects
// __diffAt(0x005ED8F0,          0,          0, OBJPATH "C_005ED8F0.obj");//ok -- "static" objects
// __diffAt(0x005EE150,          0,          0, OBJPATH "C_005EE150.obj");//ok -- input module
// __diffAt(0x005EE620, 0x009014F0,          0, OBJPATH "psxdata_c.obj");//ok
// __diffAt(0x005EE7F0,          0,          0, OBJPATH "C_005EE7F0.obj");//ok -- 3d models?
// __diffAt(0x005EEA50,          0,          0, OBJPATH "C_005EEA50.obj");//hobo ok[code] -- hit/clip?
// __diffAt(0x005EF1C0,          0,          0, OBJPATH "C_005EF1C0.obj");//ok -- nodes?
// __diffAt(0x005EF4D0, 0x00901550, 0x007B7810, OBJPATH "C_005EF4D0.obj");//ok -- Class_coaster_D8
 //====---- condor ----====
// __diffAt(0x005F2BF0, 0x00901644,          0, OBJPATH "cd_init.obj");//ok
// __diffAt(0x005F4630,          0,          0, OBJPATH "C_005F4630.obj");//ok
// __diffAt(0x005F4740, 0x00901A40, 0x007B7820, OBJPATH "cd_app.obj");//ok
// __diffAt(         0, 0x00901A90,          0, OBJPATH "D_00901A90.obj");//ok
// __diffAt(0x005F4C70, 0x00901AB0, 0x007B7828, OBJPATH "C_005F4C70.obj");//ok
// __diffAt(0x005F5C40, 0x00901AB4,          0, OBJPATH "cd_tim.obj");//ok
// __diffAt(0x005F76A0, 0x00901B70,          0, OBJPATH "C_005F76A0.obj");//ok
// __diffAt(0x005FA280, 0x00901B80,          0, OBJPATH "C_005FA280.obj");//ok
// __diffAt(0x005FA660, 0x00905464,          0, OBJPATH "cd_ddraw.obj");//ok
// __diffAt(0x005FAB70, 0x00905480,          0, OBJPATH "C_005FAB70.obj");//ok
// __diffAt(0x005FB300, 0x00905528,          0, OBJPATH "C_005FB300.obj");//ok
// __diffAt(0x005FD3A0,          0, 0x007B7830, OBJPATH "C_005FD3A0.obj");//ok
// __diffAt(0x005FF2E0,          0,          0, OBJPATH "C_005FF2E0.obj");//ok
// __diffAt(0x00603230,          0,          0, OBJPATH "C_00603230.obj");//ok
// __diffAt(0x006049D0,          0,          0, OBJPATH "C_006049D0.obj");//ok
// __diffAt(0x00606A50,          0,          0, OBJPATH "C_00606A50.obj");//ok
// __diffAt(0x00607880, 0x00905560, 0x007B7838, OBJPATH "C_00607880.obj");//ok
// __diffAt(0x0060A450,          0,          0, OBJPATH "C_0060A450.obj");//ok
 //====---- FIELD ----====
// __diffAt(0x0060B260,          0,          0, OBJPATH "C_0060B260.obj");//ok
// __diffAt(0x0060B9D0, 0x009055A0,          0, OBJPATH "C_0060B9D0.obj");//ok
// __diffAt(0x0060D810, 0x00905AC8, 0x007B7840, OBJPATH "ad_app.obj");//ok
// __diffAt(0x0060F750, 0x00905E50,          0, OBJPATH "C_0060F750.obj");//ok
// __diffAt(0x00620510,          0,          0, OBJPATH "C_00620510.obj");//ok
// __diffAt(0x00620790, 0x00905E58, 0x007B7850, OBJPATH "ad_data.obj");//ok
// __diffAt(0x0062D240, 0x009066F0,          0, OBJPATH "ad_image.obj");//ok
// __diffAt(0x006305C0, 0x00906750,          0, OBJPATH "ad_cdr.obj");//ok
// __diffAt(0x00630940,          0,          0, OBJPATH "C_00630940.obj");//ok
// __diffAt(0x006339C0, 0x009067C0,          0, OBJPATH "ad_human.obj");//ok
// __diffAt(0x006384B0, 0x009069C4, 0x007B7898, OBJPATH "C_006384B0.obj");//ok
// __diffAt(0x0063AA30, 0x009069CC,          0, OBJPATH "tutaddr.obj");//ok
// __diffAt(0x0063AC20, 0x00906A70,          0, OBJPATH "C_0063AC20.obj");//ok
// __diffAt(0x0063BC90, 0x00906B88,          0, OBJPATH "C_0063BC90.obj");//ok
// __diffAt(0x0063CD40, 0/*0x00906C68*/, 0x007B78AC, OBJPATH "ad_obj.obj");//hobo ok[data]
// __diffAt(0x0063F350, 0x00907C20,          0, OBJPATH "ad_tile.obj");//ok
// __diffAt(0x00641760, 0x00907D8C,          0, OBJPATH "ad_ddraw.obj");//ok
// __diffAt(0x00642950, 0x00907E10,          0, OBJPATH "ad_bk.obj");//ok
// __diffAt(0x00644A80,          0,          0, OBJPATH "C_00644A80.obj");//ok
// __diffAt(0x00645150, 0x00907F60, 0x007B78B8, OBJPATH "C_00645150.obj");//ok
// __diffAt(0x00646310,          0,          0, OBJPATH "C_00646310.obj");//ok
// __diffAt(0x006463F0, 0x00907F64, 0x007B7918, OBJPATH "ad_list.obj");//ok
// __diffAt(0x006470E0, 0x00907FC4,          0, OBJPATH "ad_pal.obj");//ok
// __diffAt(0x006496E0, 0x00908150,          0, OBJPATH "C_006496E0.obj");//ok
// __diffAt(0x00649770,          0,          0, OBJPATH "C_00649770.obj");//ok
// __diffAt(0x00649B30,          0,          0, OBJPATH "C_00649B30.obj");//ok
// __diffAt(0x00649B50, 0x00908E28,          0, OBJPATH "C_00649B50.obj");//ok
// __diffAt(0x00649DE0,          0, 0x007B7920, OBJPATH "C_00649DE0.obj");//ok
// __diffAt(0x0064A070,          0,          0, OBJPATH "C_0064A070.obj");//ok
// __diffAt(0x0064B090, 0x00908E30, 0x007B7930, OBJPATH "C_0064B090.obj");//ok
// __diffAt(0x0064D670,          0,          0, OBJPATH "C_0064D670.obj");//ok
// __diffAt(0x0064E0C0,          0,          0, OBJPATH "C_0064E0C0.obj");//ok
// __diffAt(0x0064E1B0,          0,          0, OBJPATH "C_0064E1B0.obj");//ok
// __diffAt(0x0064E940,          0,          0, OBJPATH "C_0064E940.obj");//ok
// __diffAt(0x0064EB50,          0,          0, OBJPATH "C_0064EB50.obj");//ok
// __diffAt(0x0064EC60,          0,          0, OBJPATH "C_0064EC60.obj");//ok
// __diffAt(0x0064EEE0,          0,          0, OBJPATH "C_0064EEE0.obj");//ok
// __diffAt(0x0064F420,          0,          0, OBJPATH "C_0064F420.obj");//ok
// __diffAt(0x0064F860,          0,          0, OBJPATH "C_0064F860.obj");//ok
// __diffAt(0x0064FB20,          0,          0, OBJPATH "C_0064FB20.obj");//ok
 //====---- HighWay ----====
// __diffAt(0x00650310,          0,          0, OBJPATH "C_00650310.obj");//ok -- launcher
// __diffAt(0x006504C0, 0x009092b0, 0x007B7990, OBJPATH "C_006504C0.obj");//ok -- main
// __diffAt(0x006520C0,          0,          0, OBJPATH "C_006520C0.obj");//ok -- input module
// __diffAt(0x006523B0, 0x0090930C,          0, OBJPATH "psxdata.obj");//ok
// __diffAt(0x00652670,          0,          0, OBJPATH "C_00652670.obj");//ok -- sparkles
// __diffAt(0x00652D40, 0x00909398,          0, OBJPATH "C_00652D40.obj");//ok -- Player & AI?
// __diffAt(0x00656EB0, 0x00909568, 0x007B79B0, OBJPATH "C_00656EB0.obj");//hobo ok[code] -- Class_HW_B0 & Class_HW_Road
// __diffAt(0x00658700, 0x009096A0, 0x007B79C8, OBJPATH "C_00658700.obj");//ok -- Class_HW_2DDisplay
// __diffAt(0x006595E0,          0,          0, OBJPATH "C_006595E0.obj");//ok -- Class_HW_Background
// __diffAt(0x00659770,          0, 0x007B79F4, OBJPATH "C_00659770.obj");//ok -- Class_HW_Node & Class_HW_C87C
// __diffAt(0x00659F50, 0x00909728,          0, OBJPATH "C_00659F50.obj");//ok -- Class_HW_ModelCache
// __diffAt(0x0065A3A0,          0, 0x007B7A00, OBJPATH "C_0065A3A0.obj");//ok -- Class_HW_Camera -- camera & trajectories
// __diffAt(0x0065BCE0, 0x0090A280,          0, OBJPATH "C_0065BCE0.obj");//ok -- Class_HW_AnimatedModel & Class_HW_ListAnimations
// __diffAt(0x0065C910, 0x0090A6C8, 0x007B7A18, OBJPATH "C_0065C910.obj");//ok[+__asm] -- Class_HW_20
// __diffAt(0x0065DC20,          0,          0, OBJPATH "C_0065DC20.obj");//ok -- Class_HW_4E0
// __diffAt(0x0065E520,          0,          0, OBJPATH "C_0065E520.obj");//ok -- part of PSXData?!?
// __diffAt(0x0065E560,          0, 0x007B7A20, OBJPATH "C_0065E560.obj");//ok -- Class_HW_Sprites
 //====---- some library? ----====
// __diffAt(0x0065EC20, 0x0090A808,          0, OBJPATH "memory.obj");//ok[check again]
// __diffAt(0x0065EC60,          0,          0, OBJPATH "C_0065EC60.obj");//ok
// __diffAt(0x0065ED50,          0, 0x007B7A30, OBJPATH "C_0065ED50.obj");//ok[+__asm]
// __diffAt(0x0065FAC0, 0/*0x0090A838*/,          0, OBJPATH "mem.obj");//hobo ok[data]
// __diffAt(0x00660370,          0, 0x007B7A48, OBJPATH "C_00660370.obj");//ok -- timing functions?
// __diffAt(0x00660540, 0x0090AA2C,          0, OBJPATH "g_drv.obj");//ok
// __diffAt(0x00661000, 0x0090AA90, 0x007B7A50, OBJPATH "psx.obj");//ok
// __diffAt(0x00664160, 0x0090AB64,          0, OBJPATH "dx_dbg.obj");//ok
// __diffAt(0x00664E90, 0x0090C86C, 0x007B7A80, OBJPATH "dx_3d2d.obj");//ok
// __diffAt(0x00666AB0, 0x0090C930,          0, OBJPATH "C_00666AB0.obj");//ok
// __diffAt(0x00666F60, 0x0090C9D8, 0x007B7A98, OBJPATH "psxgraph.obj");//ok
// __diffAt(0x0066C3D0,          0,          0, OBJPATH "C_0066C3D0.obj");//ok
// __diffAt(0x0066C4F0,          0,          0, OBJPATH "C_0066C4F0.obj");//ok -- optimized matrix functions
// __diffAt(0x0066D060, 0x0090CA84, 0x007B7AC0, OBJPATH "dx_spr.obj");//ok
// __diffAt(0x006700E0, 0/*0x0090CBC8*/, 0x007B7ACC, OBJPATH "dx_sfx.obj");//hobo ok[data]
// __diffAt(0x00674530, 0x0090CF38,          0, OBJPATH "rsd.obj");//ok
// __diffAt(0x006750E0, 0/*0x0090D16C*/,          0, OBJPATH "is_lib.obj");//hobo ok[data]
// __diffAt(0x006763F0, 0/*0x0090D268*/,          0, OBJPATH "directx.obj");//hobo ok[data]
// __diffAt(0x0067A050, 0x0090DE4C, 0x007B7AD8, OBJPATH "dx_mat.obj");//ok[+__asm]
// __diffAt(0x0067DB30,          0, 0x007B7B00, OBJPATH "WinMain.obj");//ok
// __diffAt(0x0067DED0, 0x0090E06C,          0, OBJPATH "C_0067DED0.obj");//ok
// __diffAt(0x0067F5F0, 0x0090F480,          0, OBJPATH "C_0067F5F0.obj");//ok[+__asm] -- sw:
// __diffAt(0x0067FA90, 0x0090F490, 0x007B7B08, OBJPATH "sw.obj");//ok
// __diffAt(0x00681FF0, 0x0090F690,          0, OBJPATH "file.obj");//ok[contains bug]
// __diffAt(0x00682D80,          0,          0, OBJPATH "C_00682D80.obj");//ok
// __diffAt(0x006833D0, 0x0090F964, 0x007B7B18, OBJPATH "anm.obj");//ok
// __diffAt(0x00685110, 0x0090F984, 0x007B7B20, OBJPATH "dx_stat.obj");//ok
// __diffAt(0x00685DA0, 0/*0x0090FBB8*/,          0, OBJPATH "dx_graph.obj");//hobo ok[data]
// __diffAt(0x006892B0, 0x00910480,          0, OBJPATH "thread.obj");//ok
// __diffAt(0x00689820, 0x00910658,          0, OBJPATH "render.obj");//ok
// __diffAt(0x0068AAC0, 0x00910798,          0, OBJPATH "dx_rendi.obj");//ok
// __diffAt(0x0068CF70,          0, 0x007B7B34, OBJPATH "C_0068CF70.obj");//ok
// __diffAt(0x0068D6F0, 0x00910A08, 0x007B7B3C, OBJPATH "C_0068D6F0.obj");//ok -- light related?
// __diffAt(0x0068F7A0, 0x00910A1C,          0, OBJPATH "instance.obj");
// __diffAt(0x0068F960, 0x00910A64,          0, OBJPATH "direct.obj");//ok
// __diffAt(0x00690240, 0/*0x00910B90*/, 0x007B7B50, OBJPATH "plytopd.obj");//hobo ok[data]
// __diffAt(0x00696D00, 0x009116DC,          0, OBJPATH "tim.obj");//ok
// __diffAt(0x00699470, 0/*0x00911A9C*/, 0x007B7B60, OBJPATH "polygon.obj");//hobo ok[data]
// __diffAt(0x0069AFC0, 0x00911FDC,          0, OBJPATH "list.obj");//ok
// __diffAt(0x0069B660, 0x00912088,          0, OBJPATH "C_0069B660.obj");//ok
// __diffAt(0x0069BC10,          0,          0, OBJPATH "C_0069BC10.obj");//ok[+__asm]
// __diffAt(0x0069BF90, 0x0091212C, 0x007B7B64, OBJPATH "light.obj");//ok
// __diffAt(0x0069CBD0, 0x009121EC,          0, OBJPATH "TexCache.obj");//hobo ok[code]
// __diffAt(0x0069F910, 0x00912234, 0x007B7B70, OBJPATH "trans.obj");//ok
// __diffAt(0x0069FCA0, 0/*0x00912288*/, 0x007B7B80, OBJPATH "dx_rend.obj");//hobo ok[data]
// __diffAt(0x006A28F0, 0x009129A8,          0, OBJPATH "stack.obj");//ok
// __diffAt(0x006A2AB0, 0x00912A64,          0, OBJPATH "shp.obj");//ok
// __diffAt(0x006A3280, 0x00912C50, 0x007B7B84, OBJPATH "dx_rend5.obj");//ok
// __diffAt(0x006A4F10, 0x00912E78,          0, OBJPATH "sort.obj");//ok
// __diffAt(0x006A5440, 0x00912F90,          0, OBJPATH "heap.obj");//ok
// __diffAt(0x006A5A70,          0,          0, OBJPATH "C_006A5A70.obj");//ok
// __diffAt(0x006A5D80, 0x00913050,          0, OBJPATH "dx_pal.obj");//ok
// __diffAt(0x006A6740, 0x009132B0,          0, OBJPATH "driver.obj");//ok
// __diffAt(0x006A6790, 0x009132F0,          0, OBJPATH "dx_rendx.obj");//ok
// __diffAt(0x006A68F0, 0x00917214, 0x007B7B88, OBJPATH "gl_code.obj");//ok
// __diffAt(0x006A9060, 0x009177EC,          0, OBJPATH "registry.obj");//ok
// __diffAt(0x006A91F0, 0x0091782C, 0x007B7B90, OBJPATH "dx_view.obj");//ok
// __diffAt(0x006A9B60,          0,          0, OBJPATH "C_006A9B60.obj");//ok -- setup OpenGL driver
// __diffAt(0x006A9DF0,          0,          0, OBJPATH "C_006A9DF0.obj");//ok -- setup SW driver
// __diffAt(0x006AA080,          0,          0, OBJPATH "C_006AA080.obj");//ok -- setup D3D/HW driver
// __diffAt(0x006AA340,          0,          0, OBJPATH "C_006AA340.obj");//ok -- sw:line clipper
// __diffAt(0x006AAAE0,          0,          0, OBJPATH "C_006AAAE0.obj");//ok[+__asm] -- sw:fast lines[debug]
// __diffAt(0x006AB1D0, 0x009179F0,          0, OBJPATH "C_006AB1D0.obj");//ok -- sw:
// __diffAt(0x006AB9C0, 0x00917C20,          0, OBJPATH "z.obj");//ok -- sw:
// __diffAt(0x006ABC60, 0x00917CC0, 0x007B7B98, OBJPATH "sw_vert.obj");//ok[+__asm] -- sw:
// __diffAt(0x006AC860,          0, 0x007B7BA0, OBJPATH "C_006AC860.obj");//ok -- sw:
// __diffAt(0x006AD110,          0,          0, OBJPATH "C_006AD110.obj");//ok -- sw:setup functions?
// __diffAt(0x006AF160, 0x00917D88,          0, OBJPATH "C_006AF160.obj");//ok -- sw:creates "c:/lib/h/graphics/sw/offset.hpp" file
// __diffAt(0x006B1D60, 0x00919514,          0, OBJPATH "C_006B1D60.obj");//ok -- sw:renderer?
// __diffAt(0x006B26C0, 0x0091951C, 0x007B7BB0, OBJPATH "dx_mesh.obj");//ok
// __diffAt(0x006B35D0, 0x0091955C,          0, OBJPATH "token.obj");//ok
// __diffAt(0x006B3CA0,          0,          0, OBJPATH "C_006B3CA0.obj");//ok -- blend mode related for plytopd
// __diffAt(0x006B3D10, 0x00919608,          0, OBJPATH "gl.obj");//ok
// __diffAt(0x006B6180, 0x009196C0, 0x007B7BC0, OBJPATH "C_006B6180.obj");//ok -- OpenGL:implementation of "dx_spr.cpp"
// __diffAt(0x006B6A10,          0,          0, OBJPATH "C_006B6A10.obj");//ok -- sw:implementation of "dx_spr.cpp"
// __diffAt(0x006B76F0, 0x009196C8,          0, OBJPATH "C_006B76F0.obj");//ok -- hw:blend mode
// __diffAt(0x006B77F0, 0x00919800, 0x007B7BC4, OBJPATH "C_006B77F0.obj");//ok -- sw:clipping functions?
// __diffAt(0x006B9200,          0,          0, OBJPATH "C_006B9200.obj");//ok[+__asm] -- sw:triangle drawing functions
// __diffAt(0x006BBBD0,          0, 0x007B7BCC, OBJPATH "C_006BBBD0.obj");//ok[+__asm] -- sw:some fixed_32_32//float conversions?
// __diffAt(0x006BBDC0,          0,          0, OBJPATH "C_006BBDC0.obj");//ok -- sw:more gradient
// __diffAt(0x006BC180,          0, 0x007B7BD0, OBJPATH "C_006BC180.obj");//ok -- sw:gradient related
// __diffAt(0x006BC2E0,          0,          0, OBJPATH "C_006BC2E0.obj");//ok[+__asm] -- sw:some gradient(for unused function)
// __diffAt(0x006BC350, 0x00919880,          0, OBJPATH "C_006BC350.obj");//ok[+__asm] -- sw:aligned quad rendering?
// __diffAt(0x006BE300, 0x009198E0, 0x007B7BD4, OBJPATH "C_006BE300.obj");//ok[+__asm] -- sw:render lines?
// __diffAt(0x006BF220,          0,          0, OBJPATH "C_006BF220.obj");//ok -- sw:2D clipping for aligned quads?
 //====---- Menu System ----====
// __diffAt(         0, 0x00919928,          0, OBJPATH "menu_data.obj");//
// __diffAt(0x006BF4E0, 0x00919960, 0x007B7BD8, OBJPATH "C_006BF4E0.obj");//ok -- "Exit box"
// __diffAt(0x006C0F60, 0x00919C18,          0, OBJPATH "C_006C0F60.obj");//ok -- icon resources
// __diffAt(0x006C1F60, 0x00919EA8,          0, OBJPATH "C_006C1F60.obj");//ok -- config submenu
// __diffAt(0x006C4970, 0x0091A8C0, 0x007B7BE0, OBJPATH "C_006C4970.obj");//ok -- main
// __diffAt(0x006CDC20, 0x0091ACB0,          0, OBJPATH "C_006CDC20.obj");//ok -- key config
 //-- combat specific --
// __diffAt(0x006CE7D0, 0x0091BD68,          0, OBJPATH "callback.obj");//ok[must check BSS]
// __diffAt(0x006CF5C0, 0x0091BE90, 0x007B7C08, OBJPATH "C_006CF5C0.obj");//ok
// __diffAt(0x006DC910, 0x0091E828, 0x007B7C58, OBJPATH "C_006DC910.obj");//ok
// __diffAt(0x006E5980,          0,          0, OBJPATH "C_006E5980.obj");//ok[must check BSS]
//                      0x0091EF90~0x0091EFC8
 //-- --
// __diffAt(0x006E6C30, 0x0091EFC8, 0x007B7CA8, OBJPATH "C_006E6C30.obj");//ok -- more graphic functions
// __diffAt(0x006F4D30,          0,          0, OBJPATH "C_006F4D30.obj");//ok -- input
// __diffAt(0x006F5490, 0x0091F034, 0x007B7CF8, OBJPATH "C_006F5490.obj");//ok -- graphic functions
// __diffAt(0x006FDCA0,          0,          0, OBJPATH "C_006FDCA0.obj");//ok -- "C" to "FF7" string
// __diffAt(0x006FE2B0, 0x0091F038,          0, OBJPATH "C_006FE2B0.obj");//ok -- unused menu?
// __diffAt(0x006FEB00,          0,          0, OBJPATH "C_006FEB00.obj");//ok -- empty menu?
// __diffAt(0x006FEB20, 0x0091F0B0,          0, OBJPATH "C_006FEB20.obj");//ok -- save submenu
// __diffAt(0x00700090, 0x0091F140,          0, OBJPATH "C_00700090.obj");//ok -- phs submenu
// __diffAt(0x007020A0, 0x0091F4D8,          0, OBJPATH "C_007020A0.obj");//ok -- limit menu
// __diffAt(0x00703610, 0x00920520,          0, OBJPATH "C_00703610.obj");//ok -- unused submenu
// __diffAt(0x00703760, 0x00920540,          0, OBJPATH "C_00703760.obj");//ok -- status menu
// __diffAt(0x00705C80, 0x009209A8,          0, OBJPATH "C_00705C80.obj");//ok -- equip menu
// __diffAt(0x00708670, 0x00920B18,          0, OBJPATH "C_00708670.obj");//ok -- materia menu
// __diffAt(0x00710C50, 0x00920FE8,          0, OBJPATH "C_00710C50.obj");//ok -- magic menu
// __diffAt(0x00714640, 0x00921168,          0, OBJPATH "C_00714640.obj");//ok -- item/.../... menu
// __diffAt(0x00718200,          0,          0, OBJPATH "C_00718200.obj");//ok -- input for tutorial
// __diffAt(0x00718710, 0x00921CB8,          0, OBJPATH "C_00718710.obj");//ok -- "please enter a name"
// __diffAt(0x00719D20, 0x009229B0,          0, OBJPATH "C_00719D20.obj");//ok -- shops
// __diffAt(0x00720080, 0x00925760, 0x007B7D28, OBJPATH "loadmenu.obj");//ok
// __diffAt(0x007228C0,          0,          0, OBJPATH "C_007228C0.obj");//ok
// __diffAt(0x00722BB0, 0x00926288,          0, OBJPATH "C_00722BB0.obj");//ok
 //====---- Snowboard ----====
// __diffAt(0x00722C10,          0,          0, OBJPATH "C_00722C10.obj");//ok
// __diffAt(0x00722E60, 0x00926290, 0x007B7D38, OBJPATH "C_00722E60.obj");//hobo ok[code]
// __diffAt(         0, 0x00926500,          0, OBJPATH "snobo_data.obj");//hobo ok[must convert data]
// __diffAt(         0, 0x0093A280,          0, OBJPATH "snobo_data2.obj");//hobo ok[must convert data]
// __diffAt(0x0072A170, 0x0094A340, 0x007B7D70, OBJPATH "C_0072A170.obj");//ok
// __diffAt(0x0072CA90, 0/*0x00956638*/, 0x007B7DA8, OBJPATH "C_0072CA90.obj");//hobo ok[data]
// __diffAt(0x0072FE00, 0x00956F00, 0/*0x007B7DC0*/, OBJPATH "C_0072FE00.obj");//hobo ok[code/data]
// __diffAt(0x00731AA0, 0x009624A0, 0x007B7DF4, OBJPATH "tmd.obj");//ok
// __diffAt(0x00733340,          0,          0, OBJPATH "C_00733340.obj");//ok
// __diffAt(0x00733360, 0x00962B40, 0x007B7E14, OBJPATH "C_00733360.obj");//ok
// __diffAt(0x00735220,          0,          0, OBJPATH "C_00735220.obj");//ok
// __diffAt(0x00735370,          0,          0, OBJPATH "C_00735370.obj");//ok
// __diffAt(0x00735400, 0x00962B60, 0x007B7E1C, OBJPATH "C_00735400.obj");//ok
// __diffAt(0x0073BB60, 0x00964CE8, 0x007B7E28, OBJPATH "C_0073BB60.obj");//ok
 //====---- Midi/sound ----====
// __diffAt(0x00740D80, 0x00966B14,          0, OBJPATH "C_00740D80.obj");//ok
// __diffAt(0x00741780, 0/*0x00966B48*/, 0x007B7E40, OBJPATH "midi1.obj");//hobo ok[data]
// __diffAt(0x00744400, 0x00967660, 0x007B7E70, OBJPATH "sound.obj");//ok
// __diffAt(0x00749940, 0x00967FA0,          0, OBJPATH "C_00749940.obj");
// __diffAt(0x00749C50,          0,          0, OBJPATH "C_00749C50.obj");//ok
// __diffAt(0x00749EE0, 0x0096805C,          0, OBJPATH "C_00749EE0.obj");
// __diffAt(0x0074A0D0, 0x009684C8,          0, OBJPATH "C_0074A0D0.obj");//ok
// __diffAt(0x0074A0F0, 0x00968920,          0, OBJPATH "dx_snd.obj");//ok
// __diffAt(0x0074A900, 0x00968BF8,          0, OBJPATH "acm.obj");//ok
// __diffAt(0x0074AB90, 0x00968CF4,          0, OBJPATH "C_0074AB90.obj");//ok
// __diffAt(0x0074ADE0, 0x00969198,          0, OBJPATH "sfutils.obj");//ok
// __diffAt(0x0074B8C0, 0x00969228,          0, OBJPATH "C_0074B8C0.obj");//ok
 //====---- World Map ----====
// __diffAt(0x0074BA80, 0x00969948, 0x007B7EC0, OBJPATH "C_0074BA80.obj");//ok
// __diffAt(0x0074C9A0, 0/*0x009699B0*/,          0, OBJPATH "C_0074C9A0.obj");//hobo ok[data]
// __diffAt(0x0074FFC0, 0x00969B30,          0, OBJPATH "C_0074FFC0.obj");//ok
// __diffAt(0x00753860, 0x00969B58, 0x007B7ED0, OBJPATH "C_00753860.obj");//ok
// __diffAt(0x007592E0, 0x00969CE8, 0x007B7ED8, OBJPATH "wmfile.obj");//ok
// __diffAt(0x0075AB50, 0x0096D55C,          0, OBJPATH "wmdefine.obj");//ok
// __diffAt(0x0075AC80, 0x0096D598, 0x007B7EE0, OBJPATH "C_0075AC80.obj");//ok
// __diffAt(0x0075E7A0,          0,          0, OBJPATH "C_0075E7A0.obj");//ok
// __diffAt(0x0075EE50,          0,          0, OBJPATH "C_0075EE50.obj");//ok
// __diffAt(0x0075F090,          0, 0x007B7EF0, OBJPATH "C_0075F090.obj");//ok
// __diffAt(0x00760FB0, 0x0096DDB0,          0, OBJPATH "C_00760FB0.obj");//ok
// __diffAt(0x007663E0,          0,          0, OBJPATH "C_007663E0.obj");//ok
// __diffAt(0x00766B70, 0x0096DEA0,          0, OBJPATH "C_00766B70.obj");//ok
// __diffAt(0x00767D40, 0x0096DEF0, 0x007B7F08, OBJPATH "C_00767D40.obj");//ok
// __diffAt(0x007688E0, 0x0096DF10,          0, OBJPATH "C_007688E0.obj");//ok
// __diffAt(0x00768C70, 0x0096E0B0,          0, OBJPATH "C_00768C70.obj");//ok
 //====---- chocobo ----====
// __diffAt(0x0076B850, 0/*0x0096E0F0*/, 0x007B7F10, OBJPATH "ch_init.obj");//hobo ok[data]
// __diffAt(0x0076D320, 0x0096E34C, 0x007B7F18, OBJPATH "ch_app.obj");//ok
// __diffAt(0x0076DDF0, 0x0096E518,          0, OBJPATH "C_0076DDF0.obj");//ok
// __diffAt(0x0076E1F0, 0x0096E5C0,          0, OBJPATH "C_0076E1F0.obj");//ok
// __diffAt(0x0076E2B0, 0x0096E878,          0, OBJPATH "C_0076E2B0.obj");//ok
// __diffAt(0x0076EFF0, 0/*0x0096EBE0*/, 0x007B7F20, OBJPATH "ch_chr.obj");//hobo ok[data]
// __diffAt(0x0076FF60, 0x00971D08,          0, OBJPATH "C_0076FF60.obj");//ok
// __diffAt(0x00770800, 0x00971DE0,          0, OBJPATH "C_00770800.obj");//ok
// __diffAt(0x00771660, 0x00972A78,          0, OBJPATH "C_00771660.obj");//ok
// __diffAt(0x00771680,          0,          0, OBJPATH "C_00771680.obj");//ok
// __diffAt(0x00772340,          0, 0x007B7F28, OBJPATH "C_00772340.obj");//ok
// __diffAt(0,          0x0097A108,          0, OBJPATH "D_0097A108.obj");//ok
// __diffAt(0x00776370, 0x0097A440, 0x007B7F2C, OBJPATH "C_00776370.obj");//ok
// __diffAt(0,          0x0097A4F8,          0, OBJPATH "D_0097A4F8.obj");//ok
// __diffAt(0x007794F0, 0x0097C748,          0, OBJPATH "ch_ddraw.obj");//ok
// __diffAt(0x00779850, 0x0097C768,          0, OBJPATH "C_00779850.obj");//ok
// __diffAt(0,          0x0097c8a8,          0, OBJPATH "D_0097C8A8.obj");//ok
// __diffAt(0x00779C90, 0x00980DA0, 0x007B7F38, OBJPATH "C_00779C90.obj");//ok
// __diffAt(0x0077CF40,          0,          0, OBJPATH "C_0077CF40.obj");//ok
 //====---- submarine ----====
// __diffAt(0x0077D030, 0x00980DA8, 0x007B7F60, OBJPATH "C_0077D030.obj");//ok
// __diffAt(         0, 0x00981000,          0, OBJPATH "D_00981000.obj");//ok
// __diffAt(0x0078CBD0, 0x00987018, 0x007B7F78, OBJPATH "C_0078CBD0.obj");//ok
// __diffAt(0x0078F4A0, 0x00987028,          0, OBJPATH "C_0078F4A0.obj");//ok
// __diffAt(0x00798160, 0x00987058,          0, OBJPATH "C_00798160.obj");//ok
// __diffAt(0x00798580, 0x009870B0, 0x007B7F90, OBJPATH "C_00798580.obj");//ok
// __diffAt(0x0079A550, 0x009873F8, 0x007B7FA0, OBJPATH "C_0079A550.obj");//ok
// __diffAt(0x0079C030, 0x00987478, 0x007B7FB8, OBJPATH "C_0079C030.obj");//ok
// __diffAt(         0, 0x009874F0,          0, OBJPATH "D_009874F0.obj");//ok
// __diffAt(         0, 0x00989D38,          0, OBJPATH "D_00989D38.obj");//ok
// __diffAt(         0, 0x0098A1A8,          0, OBJPATH "D_0098A1A8.obj");//ok
// __diffAt(         0, 0x0098A6E8,          0, OBJPATH "D_0098A6E8.obj");//ok
// __diffAt(0x0079F5C0,          0,          0, OBJPATH "C_0079F5C0.obj");//ok
// __diffAt(0x0079FB20, 0x00989CF8, 0x007B7FC8, OBJPATH "C_0079FB20.obj");//ok
// __diffAt(0x007A0190,          0, 0x007B7FD4, OBJPATH "C_007A0190.obj");//ok
// __diffAt(0x007A2CA0,          0,          0, OBJPATH "C_007A2CA0.obj");//ok
// __diffAt(0x007A3110,          0, 0x007B7FD8, OBJPATH "C_007A3110.obj");//ok
// __diffAt(         0, 0x00999718,          0, OBJPATH "D_00999718.obj");//ok
 //====---- credits ----====
// __diffAt(0x007A5060, 0x0099B4D0, 0x007B7FE0, OBJPATH "C_007A5060.obj");//ok
// __diffAt(0x007A8150, 0x0099C5F0, 0x007B7FF0, OBJPATH "C_007A8150.obj");//ok
// __diffAt(0x007A98F0, 0x0099C6B0,          0, OBJPATH "credfile.obj");//ok
// __diffAt(0x007AA820,          0, 0x007B8028, OBJPATH "C_007AA820.obj");//ok
 //------------------
 //--- 0x007AD7A0 ---
 //------------------
 //     more libc
 //------------------
 //--- 0x007B6000 ---
 //------------------

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.