Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: halkun on 2004-07-12 00:34:07
-
This is going to be an expansive section of Gears and so it's nice to start a new topic and keep the script-related here.
Now, this is a sample of how I'm documenting each opcode. I think this is the most elegent solution.
(http://the-afterm.ath.cx/gears/sample.png)
Now, if you guys want you can take a line from the opcode marix in Gears and assist me by posting discovered arguments in something like this style here. I'll pop it into Gears. If you can think of something more elegent than this, let me know before we hae 128 of the little boogers decypered only to find some really key information we should of included...
For example should WINDOW say the following?
WINDOW (id=00 x=0000 y=0000 h=0000 w=0000)
-----
Now, terrence, because you have had more play time with script logics (I've been working on more of the cosmetic aspects of the language) Can you tell me how memory management works? Is there a protected array in memory that the script language uses for storing variables? Does it has free reign over the the PSX's memory. I would think the kernel will allow for a degree of protection.
Also, are there flags? Are these bools different from the memory array?
I guess memory management should be the first thing to work out.
-
Just looking at it... I'm not sure it has any private addresses. It frequently accesses standard FF7 variables, which means it has full access to the save block. Outside of that, it's difficult to say... most scripts are self-contained, and what variables they need are always localised around that area's plot variables.
Now, stuff like objects on the field and setting IDs and direction... that's slightly different. But it's not something you're going to see from the scripting language itself; tracing it in memory or looking at the disassembled EXE (and hoping you can track down the script interpreter) is pretty much best bet there.
Does that answer your question? I'm not sure how some opcodes reference the same bytes... sometimes there's definitive differences in the exact way they interpret it... the Random opcode is a good example of that.
-
Random discovery, I'd thought I'd post it here... It's just a note
First a little vocabulary
SCRIPT: The logic of the field file
DIALOG: The actual "words" spoken in windows. This is in a seprite section
ENTITY: An object that's in the field, it can be a person, save point, materia, the player, or even an abitrary place.
PC: The player
From browsing the script file, there seems to be two scripts associated with an entity. One is an initalization script that places the entity on the map, and second is a script that does the interaction with the PC.
For example XYZI is the inital XYZ location of the entity. I have found that always is in the init script (It always seems to be the first one assiciated with that entity)
-
You may also note that the REQ series of commands (REQ, REQSW, REQEW, PREQ, PRQSW, PRQEW) are meant to call different scripts. It's used, for example, for conversations: like talking to a boy, then answering a question, and then suddenly one of Cloud's scripts is referenced to do an animation and state a reply, and then it comes back to the boy, who might say something, and then reference *another* of Cloud's scripts, etc., etc.
So far, although all REQ commands are in the format 'ab cd' with arguments, I only know the 'd': it's the *number* of the script associated with the object being called. How 'cab' refers to the proper object, I'm not sure; I've not seen a concrete relation between the two yet. It may be something that is best left to the script dumper to work out, if it can be modified to do it.
-
Heh, I was just gonna start in on those. I have to learn not to read too much into the opcode names.
REQ?
Request?
Request what? Request Stored Word?
-
Hmmm I think it might be refered to as a CALL to subroutine on an object.
in C++ you might be able to refer to it as
<object>.<subroutine>(Parameters)
This infers some OOP in the scripting language. Perhaps shifting to C++ to describe it will be better for me (mumbles softly). However it does make sense now that I think about it. :)
I would describe something like that as a subroutine on an object personally.
Cyb
-
After going through some of the other minigames, out of curiousity, it seems that many of the commands do switch that first byte, just like Random does. Here's a few more commands that I'm guessing at:
MPARA(bh, wid, vid, bl)
Takes what's stored in (bh)[bl] and assigns it to variable VID in the soon to be created window of id (wid). Used to print up variables in message boxes.
MPRA2(bh, wid, vid, bl(2))
Takes what's stored in the SWord(bh)[bl] and does the same as MPara.
PLUS2(bhs, bl, const(2))
Adds the number in const to SWord(bh)[bl]; bh's nibbles are switched, just like randoms.
DEC(bhs, bl)
Decreases the number stored in (bh)[bl], nibbles switched again. No idea what happens when it hits 0.
SET-WORD(bhs, bl, const(2))
Went over it before, but this is the more formal definition: stores const in SWord(bh)[bl], nibbles switched.
That's about the size of it for now. If I see anything else interesting, I'll add to it.
===============
Edit: More information on variables:
(10)[00] refers to 00DB1EC4 (v1.0?), and this byte is the plot address I talked about earlier.
(20)[00] refers to the same address. The only difference is that the (20) is associated with words rather than bytes.
(50)[00] refers to 00CB2B80 (v1.0?), and is the start of the temporary variables set aside for scripting (so they do have private addresses...) They's overwriten very often though.
(60)[00] refers to 00CB2B80, as usual, but relates to words over bytes.
I *think* (don't have concrete evidence yet but...) that the 1/2/5/6 byte doesn't count with regards to absolute addresses: it just relates to what section we need to look at and what's to be expected. (22)[58] should be offset 0x258 from 00DB1EC4, for example, and is exactly the same as (12)[58] with relates to what address we're looking at.
Keep in mind that, as stated, certain opcodes will reverse the first byte, so 62 01 will be referred to as 26 01. I've already noted a couple of opcodes that do this.
===============
2nd Edit: Update on variables.
Okay, forget what I said. It's more complicated. Much more complicated.
Right, we've got *5* numbers. 0, 1, 2, 5 and 6. They mean the following:
0: Constant
1: Permenant Plot Variable (Byte)
2: Permenant Plot Variable (Word)
5: Temporary Variable (Byte)
6: Temporary Variable (Word)
Now, here's where it gets... well... fun. Let's look at a couple of sample commands.
81 62 08 00 00: SET-WORD (62, 08, 00, 00). So, what does this mean?
It means: (6)[08] = (2)[00].
The first byte tells us 'destination type' and 'source type'.
The second byte tells us the 'destination offset'.
The third byte tells us the 'source offset' or 'constant'.
In this case, the destination is (6), so it's a temporary word variable, and the [08] means it's at Offset 0x08 in the temporary block.
The source is (2), so it's a permenant word variable, and the [00] tells us it's in Offset 0x00, which happens to point to the Plot Progression Variable.
So all in all, we've just moved the contents of a variable to another variable.
Even more fun:
88 62 08 AD 00: MINUS2 (62, 08, AD, 00)
Can you guess what this does? That's right:
(6)[08] -= (2)[AD]
On the other hand, we can also subtract constants in the same manner:
MINUS2 (60, 08, 46, 00) becomes:
(6)[08] -= 0x46 (or 70 in base 10)
It's that simple. (And that annoying.) This is also likely the source of those switched byte things... just a change between source and destination.
Enjoy.
NOTE: There are more IDs than just 0, 1, 2, 5 and 6. I know 3 and 4 exist... just haven't identified them yet.
-
Oh God, but you are a master in assembling to find out such things so quickly! Ok, I will add those informations in my dumper right now!
Edit:
Here is the tool I used to make my dumper. It can contains interesting hints. the tool : http://lasyan3.free.fr/otherstools/ff7dasm.exe (http://lasyan3.free.fr/otherstools/ff7dasm.exe)
An overview of the output : http://lasyan3.free.fr/otherstools/cargoin.asm (http://lasyan3.free.fr/otherstools/cargoin.asm)
The sources : http://lasyan3.free.fr/otherstools/ff7_dasm.zip (http://lasyan3.free.fr/otherstools/ff7_dasm.zip)
-
Thanks, but most of it was just working through the dump and trying to figure out what FF7 was trying to do rather than poring over the disassembled dump of the executable. Though that has helped in a few other topics...
Regarding the script dumper... I'd personally prefer it if, when you translate your current tool to work with the PC versions again, that it includes a copy of the raw data next to each line like the current version does. Being able to go over the raw data can help at times.
I'd also appreciate it if, with variables, that you include the 'type' in brackets before it rather than renaming all of them as Global or Local... just helps ease identification on our side, is all, as well as with comparisons and ease of recognition. Not a big issue though.
You probably already know this, but for Halkun's sake, I tracked down (3) and (4): they're the offset for the next block of 256 bytes after (1) and (2). Need to track down (D) and (E) sometime, but it's not a pressing issue.
Finally, you can see why I'm anxious to get 'frcyo' to work with the script dumper. I've already tackled two of the biggest script-based puzzles in the game, and Chocobo Breeding is one of the last to handle. I'd also like 'games2' (the second part of Wonder Square) to work, but that's far less important; I just had idle curiousity about how some of the games (like the rock/paper/scissors game) worked.
Incidentally, the code for the Basketball game is really quite amusing. I probably need to go peek at the Junon Formation minigame next for insights on keypresses and timing....
-
Man, I've been prepping for school and now I feel behind the power curve (;_;). I still haven't touched REQ (opcode 0x01) yet....
Explaning the memory and variable management is confusing the heck out of me. Now I fear I'm worse off that I started.
I think my problem is because It's a little whacked when compared to Sierra On-Lines AGI system, Enterbrain's RPGTskuru 5, or even Inforcom's Z-Machine system.
Sierra's scripting language has 255 one byte variables and 255 flags that could be set.
Enterbrains system had 999 vars and 999 flags.
Infocom had an object aria that allowed for 255 entities you could access.
All three systems did not directly access the computer's memory, but had it's own special "internal ram" that created a sandbox for the vars and flags to play in. That is not to say that there weren't system-dependant flags that you shouldn't write to. In all three systems these system flags and variables were kept rather low in memory and were useful for grabbing the current state of the virtual machine.
This scripting schema seems to be directly accessing memory, which I don't understand because the memory "accessors" (For lack of a better term) as waaay to small (max size is a long? That only give you 65536 bytes [64K] of address space) Maybe I can use a memory map or something to help sort this out. Is this just a syntaxual issue and I'm just not seeing it, or is it really that complex and close to the metal.
Laying out a memory map first is probably a good idea as then I can see the playground the scripting language can play in. Do you really have full reign to all 2 meg of the system ram? That just seem really dangerous and non-portable to me.
Before I start mapping out opcodes I need to have a good understanding on how the memory system works, from what I'm reading it's not very elegent and I'm just haveing a bear of a time groking it.
-
No, it's restricted in what it can access; global variables are limited to certain portions (or entirety?) of the savemap (the area that FF7 saves out to disk/card when you save the game), and thus comprises of a few multiples of 256 bytes. The temporary variables have their own space of 256 bytes, held seperately. Any other memory addresses affected by the scripting systems are via the other opcodes and don't count as variables as the scripting engine knows them.
For instance, it can check who's in your battle party using the IF-PRTYQ( id, offset ) (if the party member identified by id is not in your party, skip the next 'offset' bytes starting your count and *including* the offset byte itself), but it's not using a standard scripting variable to do that.
The maximum amount of addresses the scripting language could concievably access (using its standard variable routines) is 7*256 or 1792 bytes, of which 256 of those are temporary, and I think lasyan has a further 256/512 bytes marked as illegal in his current PSX script dumper or something.
-
Why 7*256? Does each entity have it's own address space?
Also something just struck me...
If we break this scripting system, how much of it do you think is intact in games like FF8 and Parasite Eve?
-
I'm willing to bet the opcodes are similar, but it wouldn't surprise me if they were dual-byte instead of single-byte.
Actually, I might take a look at some FF8 files in a short while.
-
I found these in "ebexit6.fs" (Disc 1, field.fi):
squall
zell
selphie
quistis
rinoa
irvine
edea
dic
squall
squall::default
squall::talk
squall::push
zell
zell::default
zell::talk
zell::push
zell::bami
zell::oti
selphie
selphie::default
selphie::talk
selphie::push
selphie::bami
selphie::oti
quistis
quistis::default
quistis::talk
quistis::push
quistis::bami
quistis::oti
rinoa
rinoa::default
rinoa::talk
rinoa::push
irvine
irvine::default
irvine::talk
irvine::push
irvine::bami
irvine::oti
edea
edea::default
edea::talk
edea::push
edea::bami
edea::oti
dic
dic::defalut
dic::talk
dic::push
I don't know if it's just leftovers from development if it actually has something to do with the scripting engine, though. (And yes, dic::defalut is how it was spelled)
-
Why 7*256?
(1)[00] - (1)[FF] is 256 bytes.
(2)[00] - (2)[FF] is the *same* 256 bytes using words instead (let's assume for a second that (2)[FF] is illegal because it overlaps the next section... we won't, however, confirm this until we either look at the code or see that there's no script that uses it)
(3)[00] - (3)[FF] is a different set of 256 bytes.
(4)[00] - (4)[FF] is the *same* 256 bytes using words.
And so it goes on. And then we also remember that (0)[??} means to use a constant rather than a variable.
So we have 1 number out of a possible 16 options reserved for constants, and the other 15 refer to memory spaces, with two each referring to the same memory space but giving the difference between bytes and words. That means 7 possible ranges. Of which 1 range is set aside for temporary variables, and we're not sure about the others except that 4 or 5 are definitely confirmed as global variables kept in the savemap.
-
Regarding the script dumper... I'd personally prefer it if, when you translate your current tool to work with the PC versions again, that it includes a copy of the raw data next to each line like the current version does. Being able to go over the raw data can help at times.
I'd also appreciate it if, with variables, that you include the 'type' in brackets before it rather than renaming all of them as Global or Local... just helps ease identification on our side, is all, as well as with comparisons and ease of recognition. Not a big issue though.
What are you talking about ? Are you talking about my script dumper of the ff7dasm prog (which is not from me) ? And I'm not really sure to understand what you want me to change in the dumper. Please explain again, and as soon as I understand, I'll work on it, you can rely on me :wink:
-
I don't know if it's just leftovers from development if it actually has something to do with the scripting engine, though. (And yes, dic::defalut is how it was spelled)
one question... what's dic?
MOD EDIT: Removed excessive quoted text.
-
Right, time to be harsh. And I'm already in a deliciously bad mood for it...
one question... what's dic?
a) You quoted a huge, irrelevant text mass only to post a simple question, which definitely didn't require that text mass for context.
b) This thread's purpose is to collect and summarize information on the FF7 scripting language. Do you believe your post contributed to this?
This and the growing amount of small, irrelevant postings from you and a few others have become enough to get on my nerves (and apparently other members as well). You just received your second warning. Other members are wise to take notice as well, because I'll be handing out warnings more frequently from now on for anyone posting crap excessively. Please please please think before you post, "Will the forum members find this post useful?".
As for your question on the FF8 script "offshoot" (which really should get its own thread if there's interest to continue it), dic would most likely just be an object or NPC in the current field. There. Back on topic with you all.
EDIT: Oh yeah, just to make sure my own post isn't off-topic... I've located the code that executes all the different FF7 script opcodes in the FF7 PC executable. I'm still in the process of structuring the work, but I should be able to dig out information soon. If you need information on specific opcodes then let me know so I can focus on those.
-
Ah, thought it was your work since the download was on your site. Didn't realise. Looks like they already had variables figured out at the very least. In that case, I'd rather prefer it if you worked on getting 'frcyo' working with your script dumper; we can mess around with opcodes and their real translations later.
-
ok, it works now with the psx version of frcyo (decompressed), so it should be ok with the pc version. Check it out!
http://lasyan3.free.fr/mytools/script_dump.zip (http://lasyan3.free.fr/mytools/script_dump.zip)
-
It works. Thanks. I'll get to work on Chocobo Breeding then.
Edit: Well, locked down some more variables. I'll just go over the types again fully...
(1/2) = 00DB1EC4
(3/4) = 00DB1FC4
(B/C) = 00DB20C4
(D/E) = 00DB21C4
(7/F) = 00DB22C4
(5/6) = 00CB2B80 (temporary variables)
(0) = Constants
This just leaves 8, 9 and A...
-
Didn't you just say above that bank 4/5 was the savemap? That looks to be spread between two different banks. Did I miss something?
Also does anyone have a quick copy of the FF7 save map, including how any checksums are calculated?
I'm torn where to put the memory map in gears. The save map belongs in the "Menu" section, the memory management belongs in "Kernel" and so far I've only seen "Field" make extensive use of the memory.
Should I split up the explanations?
Also I seem to have found out why I was gettng so confused about memory. Here I was thinking about the PSX memory map and not the PC one. Now that I can see the memory banks are contiguous, this is quite helpful.
The PSX has a high-speed scratchpad memory area internal to the CPU. It's 1k and between the ranges of 0x1f80_0000-0x1f80_03ff. You think there is a throwback in the PC code to represent this?
::EDIT::
Never mind, I see that the memory *ISN'T* contiguous. You have grouped the sections into blocks of memory.
::EDIT 2::
No wait, I'm confused. I'm letting my own assuptions get the best of me.
A PSX FF7 save has to fit into a single memory block of 8K. Now a PSX memory card has a finer "resolution" than that. The 8K block is actually split up into 64 "frames" made up of 128 bytes each. The first two frames are reserved for the filename and save icon. You can allocte the next two frames for additional icon animations, but isn't required. (Max allocations are 4 frames, one for the filename, and then three frames of animation)
Common sense is telling me that the savemap should be a direct mirror of what's saved on the card. This way a simple counter can attach the save data to the card stream. Writing to memory cards is a really qirkey process as it's really just another controller port. The more elegent solution, the faster the saves.
-
From what I know, the FF7 "save block" is a contiguous block of memory simply dumped to the memory card (with checksums and stuff added/updated of course). Common sense prevails again. :wink: Now, the global script variables have five banks of 256 bytes each, all of which reside inside the memory area that constitutes the save block. Thus no specific bank refers to the savedata, rather they're all already in the save data. So another way to describe the banks would be temporary variables and permanent variables.
I hope that provided some information you were looking for.
-
I'm mapping the save block with that little doc you had in the Jenova source there Qhimm. This will be in Gears and should work as a psudo-map for the field file's memory mapping.
-
I found these in "ebexit6.fs" (Disc 1, field.fi):
SNIPED
I don't know if it's just leftovers from development if it actually has something to do with the scripting engine, though. (And yes, dic::defalut is how it was spelled)
It's likely dic::defalut is a mispelling by the programer.
If I were to hazard a guess these are function references, did you check if there were anything interesting around the text like addresses?
Cyb
-
First bit of info from FF7.exe mainly confirms Terence's findings about memory variable banks. The complete mapping is:
Code Bank Size
-------------------------------------------------------------------
0 - - (16-bit constant fetched from argument list)
1 1 8 bits
2 1 16 bits
3 2 8 bits
4 2 16 bits
5 temp 8 bits (temporary variables)
6 temp 16 bits (...)
7 5 16 bits
8 - - (unused, always returns zero when read)
9 - - (...)
A - - (...)
B 3 8 bits
C 3 16 bits
D 4 8 bits
E 4 16 bits
F 5 8 bits
Any of these banks can be accessed by script commands, as long as the code nibble used to select the variable type is in one of the first three arguments. (e.g. the MINUS2 command uses two variable references, and uses the first argument to contain the two type codes)
Naturally, since variable references are 8-bit (the offset from the start of the bank, that is) while constants are 16-bit, script commands must take special care to support constant arguments (usually as the last argument where the final byte is either part of the constant or unused). It is thus not guaranteed that all commands support constant arguments.
-
First bit of info from FF7.exe mainly confirms Terence's findings about memory variable banks. The complete mapping is:
[MAP SNIPPED]
I'm going to bed and I'm going to attempt to look at that with fresh eyes when I wake up. I'm not getting this whole code <-> bank thing at all.
I'm going to talk out loud, then go to bed. Let's take the following command.
set byte(50)[07]=14
In this case the [07] means code 7, which selects bank 5 at a "depth" of 16 bits.
It then writes 0x14 at +0x50 within bank 5.
So what's the difference than doing this?
set byte(50)[0F]=14
....
Wait, I have the sentax wrong. I guess I simply don't know how to read the opcodes... I've been playing with the ones that don't deal with memory at all, like BATTLE, PRTYP, GOLD+ and others like that. I think you tried to explain it above, but you started tossing around terms like "nybbles" (I honestly thought this term was made up hacker speak for the 4 bit upper/lower half of a byte. Even on an 8 bit 6502 you can't split a register in half, everything aligns on a 8 bit byte or 16 bit word.) The script dumps also align on 8 bit bytes and 16 bit words.
I guess the script dumper is displaying memory locations in a pretty unintuitive way. I'm used to seeing explicit memory address ($0000-$FFFF), "zero-page" offsets ($00-$FF), or immidatle accessors (LDA #10)
Could you use give me some examples on how I'm supposed to properly parse these things? Thanks....
-
I'm going to talk out loud, then go to bed. Let's take the following command.
set byte(50)[07]=14
In this case the [07] means code 7, which selects bank 5 at a "depth" of 16 bits.
Don't look at the disassembled script for this. Look at the *Op Codes* and associated data. So what you really wanted to say was:
80 50 07 14, which becomes, properly:
set byte (5)[07] = (0)14
This means: Put the Constant 0x14 into the Byte at Offset 0x07 using ID 5 (which refers to the Temporary Bank and only dealing with bytes).
So what's the difference than doing this?
set byte(50)[0F]=14
I believe you want for your example:
set byte (6)[07] = (0)14
This means: Put the Constant 0x14 into the Byte at Offset 0x07 using ID 5 (which refers to the Temporary Bank and only dealing with words). Which... isn't exactly the most sensible of code... you don't *really* want to mix opcodes dealing with bytes and telling the script engines variable handling subroutine that you want to store it in a word. Who knows what might be put into that spare byte?
Wait, I have the sentax wrong. I guess I simply don't know how to read the opcodes... I've been playing with the ones that don't deal with memory at all, like BATTLE, PRTYP, GOLD+ and others like that. I think you tried to explain it above, but you started tossing around terms like "nybbles" (I honestly thought this term was made up hacker speak for the 4 bit upper/lower half of a byte. Even on an 8 bit 6502 you can't split a register in half, everything aligns on a 8 bit byte or 16 bit word.) The script dumps also align on 8 bit bytes and 16 bit words.
You're thinking about this too much. If I want to check a single bit, do I use a command to *only* get that bit? (Assume we're working in Assembler; the script engine has comparisons to check single bits) No: I grab the whole byte and store it in one of the 4-byte registers, then I'll AND it with the bit I want to check, and then test it against 0. It's the same with nibbles (which, yes, is another term for a half-byte, and is reasonably old in terms of when it was defined); I used it at the time because I thought it strange that, say: Random (05, 6D) and And (50, 6D, 01) would be associated with the same address. It was only later that I was able to parse them as:
99 05 6D: Random (5)[6D]
8F 50 6D 01: And (5)[6D], (0)01
I guess the script dumper is displaying memory locations in a pretty unintuitive way. I'm used to seeing explicit memory address ($0000-$FFFF), "zero-page" offsets ($00-$FF), or immidatle accessors (LDA #10)
Could you use give me some examples on how I'm supposed to properly parse these things? Thanks....
They're not that unintuitive. See, the script doesn't *know* all the addresses. It doesn't have nearly as much control you give it credit for. It has, as we've stated, access to 5 main banks of permenant variables, and 1 bank of temporary variables. (Please note: VARIABLES. We are currently ignoring stuff like XYZI which are specific commands that FF7 will go and do its own business with.) The permenant variables contain stuff that will be saved out in FF7's save file. Stuff like the number of Chocobos you have, or the last time you fought at Fort Condor, or how many battles you've been in since the start of the game. Temporary variables change and are reset from scene to scene and are used and thrown away as and when they're needed.
All you really have to know about the addresses is the following:
B/W
(1/2)[xx]: Bank 1
(3/4)[xx]: Bank 2
(B/C)[xx]: Bank 3
(D/E)[xx]: Bank 4
(F/7)[xx]: Bank 5
(5/6)[xx]: Temporary Bank
The 5 Banks are sequential sets of 256 addresses, and in v1.0, they start at 00DB1EC4 in memory, which starts with the Plot Progression Variable I've made note of several times.
The Temporary Bank is a set of 256 addresses that start at 00CB2B80 in v1.0, and are reserved for the script engine's use alone.
To put it simply: the scripts are interpreted by FF7, and FF7 itself will eventually convert (2)[00] to Word(0x00DB1EC4) when it's asked by the script engine to grab that particular address. Script writers are not expected to know exactly where in memory all this is going to eventually be placed... all they need to know is their offset from the start of the Global or Temporary areas.
-
I've updated Gears with the FF7 save map, it's on page 12 under "menu" You can use it to help map out variables. It me know if I missed anything and if I need any corrections.
You know, I wonder of those banks arn't object conatiners.
For example the original code was something like Chocobo::slot_1::Intelligence==100
or If Character::Cloud::Vitality==21 then jump :label3
The bank is "Character" and the offset is handeled by the script compiler, (or even a hand ful of #define)
It would be easier when the save map is mapped to the banks...
-
I've updated Gears with the FF7 save map, it's on page 12 under "menu" You can use it to help map out variables. It me know if I missed anything and if I need any corrections.
You know, I wonder of those banks arn't object conatiners.
For example the original code was something like Chocobo::slot_1::Intelligence==100
or If Character::Cloud::Vitality==21 then jump :label3
The bank is "Character" and the offset is handeled by the script compiler, (or even a hand ful of #define)
It would be easier when the save map is mapped to the banks...
Judging from the way things are being handled in terms of 'functions' within the script I would say you might be closer to the truth. Perhaps the 'global' values are really values within the game object and each character object has a set of associated variables, such as the 'love points' for example. I wonder if these are less global and more object oriented. Or I could be completely wrong (wouldn't be the first time).
Cyb
-
Script values are definitely not object-oriented, at least not on any visible level. As for the rest of the save block (and probably most of the game), a certain level of object-oriented thinking exists, but mostly its just POD (plain-old-data) structs.
-
Right it's a double-post, but they're completely unrelated and separated by a lot of time so it's warranted.
A question for people who have actually been looking at the FF7 scripts... are there any scripts that use the DM_TRA or CM_TRA commands? (delete materia and check materia, respectively)
The reason being that in the PC version, these commands are not implemented. But since no one has complained about any materia bugs, I'm speculating that the commands aren't actually used anywhere in the game... I'd like to confirm it though.
-
CM_TRA might be used for that one timer for that underwater battle.
-
I don't think the game ever removes materia. I've been diggin with MNU files as of late but I think all yuffie does is move the materia to an unaccessable part of memory.
When I finaly figure out the menu thing, I'll have to play with that.
Heck. I haven even started on the REQ opcodes, I'm kinda hoping because they are first, someone will beet me to it.... :P
-
Right it's a double-post, but they're completely unrelated and separated by a lot of time so it's warranted.
A question for people who have actually been looking at the FF7 scripts... are there any scripts that use the DM_TRA or CM_TRA commands? (delete materia and check materia, respectively)
The reason being that in the PC version, these commands are not implemented. But since no one has complained about any materia bugs, I'm speculating that the commands aren't actually used anywhere in the game... I'd like to confirm it though.
DM_TRA and CM_TRA are those for removing/deleting materia?
Do we know exactly what these do?
They may have been for things that were never implemented in the game. I know FF7 is incomplete and unused opcodes might be a good indicator of this.
Cyb
-
DM_TRA and CM_TRA are those for removing/deleting materia?
Do we know exactly what these do?
I'm fairly sure they are the delete-materia-from-inventory and check-if-materia-exists-in-inventory commands. Even if the actual command is unimplemented, the forms are very similar to the ST-ITM, DL-ITM and CK-ITM commands (which add, remove and check for items in the inventory, respectively). Also, the SM-TRA commans is fully implemented, which adds materia to the inventory. DM-TRA and CM-TRA use the same type of arguments as SM-TRA, with small differences equal to those between ST-ITM and DL-ITM/CK-ITM.
All I can't figure out is what TRA stands for... :P
-
You *are* jotting down all these arguments..... right ^_^
-
Yes I am, dear. ^_~
I'm dumping all info on the commands I find into C++ header files for now, the plan is to incorporate them into a complete script viewer capable of presenting (and later editing) the script in an easily comprehensible manner. For example, replacing variable references like "14 56 1F 00" with "(1)[56], <2>[1F]" and replacing some command names to more descriptive versions, with (simplified) syntax and description denoted. The actual "hardcode" syntax can then still be easily retrieved from reading the C++ header files, even if you don't read C++ too well.
-
All I can't figure out is what TRA stands for... :P
SM-TRA most likely stands for:
Set MaTeRiA.
-
SM-TRA most likely stands for:
Set MaTeRiA.
Ooh, that's clever that. :D I would've thought Store or Stock though, from the ST-ITM variant, but SeT is equally likely I guess. ^_^
-
Since Items and materia aren't stored in the same data space, it might be a hold over.
That is they may have originally had SET REMOVE and GET Materia commands (obligatory nuisances), then removed them and rolled them into the ITEM code. Now this brings up a bit of a problem, if materia is fetched using the item code how do the specify what the item is? Since Materia and item codes are completely different?
Cyb
-
Nonono, materia and items are still handled completely separately. Sorry for any confusion. All I'm saying is that the commands for the two share certain similarities. Now DM-TRA and CM-TRA are non-functional in the PC version. It's possible there's another command replacing them, but I haven't found one; probably the commands are just never used, and the developers didn't bother implementing them. That, or they just forgot :P
One point I figured the DM-TRA and CM-TRA commands would be used is when you touch the huge materia to replace your mastered materia with a Master Magic (or similar) materia. Probably there's a special command for that though, or it would have to rely on the dysfunctional ones...
-
Yea it's probably a SPECIAL() command that talks directly with the kernel.
That bad boy has 256 sub commands and it seems to be a bunch of hard-coded last minute commands that were put in because the opcode matrix was full.
-
Yea it's probably a SPECIAL() command that talks directly with the kernel.
That bad boy has 256 sub commands and it seems to be a bunch of hard-coded last minute commands that were put in because the opcode matrix was full.
Argh.. I hate when they do that, it reminds me of programing for microcontrollers the idiot designers didn't design with enough for thought. I should shut up on that, I really want to strangle some engineers on ocassion, but then I am probably as guilty as they are!
So I suppose the big thing would be to document the SPECIAL matrix next then?
Cyb
-
So I suppose the big thing would be to document the SPECIAL matrix next then?
Yeah, I'll get on it right after I finish documenting those pesky REQ instructions...
-
So I suppose the big thing would be to document the SPECIAL matrix next then?
Yeah, I'll get on it right after I finish documenting those pesky REQ instructions...
Well I suppose I ought to start working on the script viewing stuff as well. I have to rewrite my software almost completely (sigh) in some section.
The 'dumb' question where are the NAMEs you are giving this OPCODES coming from? Laysan3's script dumper? or are they listed someplace in FF7's various data files?
Cyb
-
They are coming from Lasyan3's script dumper, but Qhimm is renaming them.
I'm going to hold off on the field scripting bit till Qhimm's done with his side. However, I'm done with the MENU section of Gears, and I'm going to finish KERNEL tonight, after that, I'm going to move on to the FIELD section and flesh out the mechanics of that.
I'l also re-vamping the way I have my sections set up to improve readability. However, Openoffice can't export it's bookmarks into PDF format so I'm still looking for a soultion.
Maybe, when gears is done, some soul with Acrobat can put in the bookmarks for me.
-
The names I've given to the opcodes are listed in the field.bin file of ff7 PSX. Here is the file, I think it will be useful :
[MOD: LINK REMOVED][/i]
-
The names I've given to the opcodes are listed in the field.bin file of ff7 PSX. Here is the file, I think it will be useful :
Please don't post files from the game disk here. I'm pretty sure most everyone has a copy of FF7 for PSX. Saying that it's in Field.bin is enough for us to take a look for ourselves.
-
I see. Sorry for the trouble.
-
I see. Sorry for the trouble.
I don't think you'll be lynched for it Lasyan, it was an honest mistake.
Ok field.bin has the opcode names, odd that hmmm I have to finish a paying project before I start work on recoding TV, so I am thinking things through at least (heh me think things through that's an interesting statement).
So much to do and so little time. Mumble.
Cyb
-
I figured it was time for a small update, so...
I've been working mostly on mapping out the arguments for individual commands (which is proving a bit complicated for some commands like the REQ family). I've also spent some time on my soon-to-be script viewer and trying to transform the simple assembly-like commands into more readable code (particularly conditional jumps).
http://www.qhimm.com/script.gif
Still quite a way to go, but I'm getting there. In the mean time, I'm documenting the commands as best as I can in C++ header files, like this:
// Random() is actually not very random at all, it merely consists of
// picking values from a 256-byte array containing all values between
// 0 and 255 in shuffled order.
BEGIN_COMMAND(0x99, RANDOM, 3)
COMMAND_NAME("Random")
SPECIAL_SYNTAX(Arg(1) + " = Random()")
DESCRIPTION("Sets the operand to a random value between 0 and 255. Operates on unsigned bytes only.")
BEGIN_ARGUMENTS()
REFERENCE_UByte(op1, ONE|2)
END_ARGUMENTS()
END_COMMAND()
The above code is directly used by the script viewer as its source of information on script commands, as well as being decently human-readable. For example, the first line designates the op-code, name and length of the command, the other lines specify a special user-friendly syntax used in the script viewer, a description, and informs us that the function takes one argument, a non-constant variable, with the bank/type specifier stored in argument nibble 1 and the bank offset in argument byte 2. Easy as pie?
-
Ah, no wonder I was having a hard time with REQ family. That looks absolutly wonderful though.
I wonder, should we set varible names for known vars in the savemap? We can probably have that as an external file that can be edited by the user.
JUMPMAPs should also be mapped to what field file they call. I bet the look up table is in field.bin. (That is, if the PC version has one). I'm going to do a quick compare just to see if the bin files are the same.
I've been procrastonating on the kernel section of Gears. It kinda sucks when you make a breakthrough on a file format, only to open it up and find about 27 file formats inside. I really got stalled out menu and on window.bin, and that was just TIM files. I was hoping that kernel.bin would hold tim files. Boy was I wrong. That's the item/spell/materia resource for the game. Even though it's cool I found it, if forces me to stay in Kernel a little longer than I'd like.
-
I'm guessing we will never see a Linux port of that. You know what happened last time I tried to compile your code with GCC... (I use MinGW, BTW)
-
I've anticipated the need for named variables as well, support is already built into the viewer, so that "<1>[00]" might be displayed as "plotProgression" or similar. The only thing I've been wondering is how they should be displayed in the viewer, as simply text, or with some special syntax, like "<plotProgression>", or "(plotProgression" had it been of byte size... Oh yeah, this is how I mark up unnamed variables right now:
Byte-sized variable in bank n: (n)[offset]
Word-sized variable in bank n: <n>[offset]
Byte-sized temporary variable: ()[offset]
Word-sized temporary variable: <>[offset]
Does this system work? I'd appreciate comments and suggestions to make it more readable and intuitive...
And no, no Linux port by yours truly at least, but the actual script parser should be reasonably portable; only the display classes/functions will have to be rewritten.
-
These is a difference between making it intuitive and readable as opposed to what *we* are used to.
What the matter with changing this...
06D9 if ()[06] == 01 { //06E0
06DF return
06E0 }
06E0 if ()[06] == 02 {//06F7
into this
06D9 if TempVarByte[06] == 01 { //if not go foreward to 06E0
06DF return
06E0 }
06E0 if TempVarByte[06] == 02 {//if not go foreward to 06F7
Now how about user defned vars? how about this as an example
if <plotProgression> == 931 {//if not jump to 7BCA
You have tooltops, so why not have it so that when you hover over the <plotProgression> var the tooltip will display <1>0000.
This also solves how to disply correct program flow becuase, if I remeber, the if-type commands can either roll foreward, back, or to an implicit jump.
Here's another tip. You are making a *READER* not a compiler, yes? We can leave that to other toolmasters. To make an engine, you just need to be able to read data in a cohearent mannor that both human and computer can read.
-
Yes, currently it is just a reader. I am however trying to write it so that making an actual editor/compiler won't be that much of an extra step. Thus I'm trying to make the code->text translation as "strict" as possible to further help making the work of writing the compiler easier by having a well-defined language structure. One bit of extra type information you might want to display is to differentiate between signed and unsigned words, for example. I read earlier in the thread where Terence used the following form: SWord(1)[55]. I think it would be easier to read, but I'm very open for feedback.
In the mean while, how many permanent script variables have actually been mapped?
-
Lots
I have them in my new Gears upload, (Under menu), I even split up the save map to show the bank boundires (They are in orange) I'm pretty proud of what I have in there so far. However, I can't verify the vilidity of some of those vars. Some of them seem to be misplaced, like the penned chocobo field data
-
I really don't think there's a distinction, Qhimm; SWord and UByte were just what the lasyan's script dumper produced, but we already know the bank system and we know that the script itself only really knows the difference between words and bytes. I think signed/unsigned is a possible factor with a few of the If statements too. Maybe. Not looked at the disassembly to be sure.
I'd be interested in hearing what the difference is between INC/DEC and INC!/DEC! though. I assume it's something to do with either signed numbers or overflow, but can't spare the time to test.
-
Yes, while signed/unsigned matters very little in most respects of the script (only operand size is stored with arguments), a couple of commands are specific to one of the two. The best example would be the If statements which have specific forms for signed/unsigned words. I'm theorizing that the actual script language (before compilation) kept this type distinction and warned about mismatches during compilation.
You are correct in both aspects regarding the "!" forms; they are designed for UBytes and SWords (not UWords), and they have overflow control, also known as saturated addition, subtraction, etc. That is, if you add 10 to a UByte containing 250, the result is 255. Or, it you add 10,000 to an SWord containing 30,000, the result is 32,767. The engine is not very careful about this though, and only checks in one direction. Thus is you PLUS2! a variable containing -30,000 and a variable containing -10,000, overflow will occur and the result will be 25,536. The normal PLUS/MINUS operators don't care about overflow at all and thus works on either SWords/UWords, but other operators like MUL, DIV and MOD are definitely designed for SWords (and UBytes, of course).
So based on what I've seen so far, it seems that SWord is the primary data type for 16-bit variables (all arithmetic operators work on SWords), while UWords are primarly intended for bitwise operations and such (theory).
-
Commands
64 = SCR2D | 6 - screen 2 destination - change center of screen immidietly
I have no idea what first two bytes doing, everytime I tried to change theri values into something other than 0, ff7.exe crashed. After that is short (2 byte int) determining destination x in the picture. Last short is y value determining destination in picture
66 = SCR2DC | 9 - screen 2 destination - change center of screen in time in line, trajectory is not uniformly divided, you speed is increasing at the start and decreasing in the end. Agan, first 2 bytes unknown, short y, short x and last short is time you have on move.
68 = SCR2DL | 9 - screen 2 destination - change center of screen in time on line, move uniformly (speed is constant). Parameters same as in SCR2DC.
coordinates x and y are absolute, center is probably in the center of backgound/foreground image. - moves you to right/up, + to the left/down.
Maybe useless, It seems to me that some people have already decoded most of commands. If I try to decode some more commands, will it be usefull?
I will soon write some text on section 1 based from script_dump.src
Can someone plz explain about sections and scripts? To me it seems that one section is in fact for one object in field. Line One for treasure chest, one for soldier, one for chocobo and so on. But what about scripts in one section? When are they running? Normally i would thought that you have one section and one script for one object. You have to run them, but how? First section 1, script1, sec1,script2...sec1,script 5, sec2 script1,sec1 script2.... and so on in eternal loop?
I have seen a lot of parts like this one
****** Section n°2 (ba) Script n° 1 ******
000 : [00] - RET
where is absolutly nothing usefull, can someone explain?