Bugfixes and Queries

  • Thread starter Thread starter DLPB_
  • Start date Start date
Status
Not open for further replies.
It's a memory thing. If it changed modules it would have to unload itself before loading the menu THEN it would have to reload when the menu ends. That's too slow for the PSX (I guess) so the WM module sets the menu handlers and calls them itself at the bottom of its draw method.
 
But it doesn't have any problem doing it from field?  It uses less memory than the WM?
 
That'd be my guess. If you destroy the WM you'd have to destroy it and recreate it each time you'd use the menu. That's a long enough task when transitioning to/from a field. I'd imagine the team wanted menu use to be less of a hassle and just made an internal menu event.
 
Why didn't they just overlay the menu on top for field?  Surely that could have been done since the menu is next to no memory?  Or perhaps because people worked on different modules, they just didn't have time to make that idea fly.

n other news, I managed to create a way to jump to any module instantly.  I am using it to jump back to the credit screen with a master reset.  Aali's driver is interfering with screen height / width (it uses field version, which I've altered), but it's fixable.  Master reset seems to work fine.

It's initiated by certain addresses being placed at certain addresses.  It then uses

CC0D84 = 26
CBF9DC = 27

To decide what to do.  The above is "Jump to Credit screen".  CBF9DC is the main identifier.
 
Last edited:
Sorry to butt in; I searched the bug tracker database for bugin1c but couldn't see this issue there.

I'm trying to fix the problems in the Observatory scene; I've got the character placements done using offset object, but the sound is a problem. There's supposed to be 4 sound effects played in tandem using AKAO 35 but only the fourth one plays. After some testing, I found that other sound effects will behave correctly with this and play all at once but these four won't.

AKAO: 35? (param1=64, param2=110, param3=111, param4=112, param5=113)

So it seems to be something to do with these particular sound files themselves and how they're handled. I saw in Dan's notes that there's code to determine if sound files loop or not, is there maybe some kind of override that might be causing these sounds to not play together?
 
It could also be broken in original pc / aali's driver.  Since you have to remember field script is only script... it's the sound functions that do the work.  And they are definitely broken in some areas.  You'll likely find that my dll fixes the issue you have.  There is a condition to allow up to 4 sounds to play together using AKAO function.  I didn't know that field did it, but battle does - some Summons use 3 sound effects together.

The bugin visual bug is known and has been fixed with Reunion by Spy__Dragon. But this is the first time I have heard of the sound issue.

I should note, actually:  Aali doesn't do anything with the sound effects that I've seen.  So all the original errors with PC version are there.  With music, he took over the midi functions - but, by that time, the music is already broken. Which is why certain functions like tempo, fade do not work properly.  I've rewritten akao and other sound functions from scratch and fed them to bass.dll.  This fixes all the issues.
 
Last edited:
It isn't released yet.  Soon :)  bass.dll allows for dsound operation and isn't made by me.  My dll project is called ff7ddraw / ff8ddraw.  But obviously that will go in the game root as ddraw.dll.  Same thing for FF8.
 
Updated akao documentation.  This should be 100% correct now.

http://wiki.qhimm.com/view/FF7/Field/Script/Opcodes/F2_AKAO

Notes:

PC specific problems:

1. All tempo functions do not exist in original PC version. Steam added operation 0xD0 back in, but it does not have pitch correction.
2. Operation 0x30 cannot be stopped by using an effect on channel 4 like it should. 0x30 is supposed to work on channel 4.
3. Operation 0x20 to 0x23 are apparently broken.
4. Operation 0xC2 does not exist.

I have fixed these issues with my dll.

PSX issues:

Operation 0x10/0x14 and 0x18/0x19 do not seem to work. I think they have been intentionally disabled to force script programmers into using Opcode 0xF0 : http://wiki.qhimm.com/view/FF7/Field/Script/Opcodes/F0_MUSIC

No such limitation exists on the PC version. F0 calls akao Operation 0x10 anyway, so I am not sure why akao operation 0x10/14 has been disabled on the PSX version. Perhaps the design team wanted to keep it consistent.

It may also be that I don't understand how the PSX version handles music.
 
Last edited:
PC port used an older version of the source. They didn't have very good versioning control software back then, unless you count backup tapes.
 
I am looking at assembly to learn more about the field and how it is coded (especially regarding script calls).  I will note any findings.  So far, it should be noted that

a. 16 models (Model ID 0-15) are supported per field. Any more than that will cause a crash, due to overflow of data.
b. 48 Groups are supported, with Group ID of 0-47.  If you exceed this, the field will crash. It certainly does if using a Wait operation on  Group 48 (the 49th group), since this overflows and uses the address CC0960 (which is critical for running the field module).

The field does not do checks to make sure that unsupported groups and models are not used. As stated, it will crash.

About the Wait operation (Opcode 0x24).  Each Group has an instruction executed, one to the next (usually) .  Because each group can be executing, at most, one Wait operation, each group must have its own 2 byte storage address for the delay remaining (in frames).  These start at CC0900. If the delay is still in progress, Wait is called, the counter is deducted, and the instruction will only move on to the next instruction (when its turn comes around again) when the delay is 0.  For some reason, the Wait function [9055a0 + (4 * 24)] checks for both a remaining delay of 0 and 1 and does different things on both.
 
Last edited:
Normal field operations.

The maximum number of field operations per group is 8 a frame, as long as the instruction is not told to wait (for example, some Move operations must complete before the next instruction is allowed to execute, like an NPC walking from A to B.).

8 field operations that have no delay (i.e., the instruction doesn't have to wait for something like a Move operation to complete) will execute for one group, each frame, where possible. This is not affected by how many total groups there are. This means that each group will execute 30 * 8 instructions a second at a maximum (240), meaning 48 * 240 (11520) instructions a second, at the most, for all groups. Each group is given control, one to the next, starting at ID 0. If it has already reached a return, the return will simply be executed on the group's every turn from that point on. All groups are therefore given control once a frame, whether they end up doing 8 instructions or 1. There will always be at least one instruction per group, each frame, even if it's the final return of group's Main. A return is therefore seen by the engine as nothing more than a "no operation - go to next group".

If the instruction is waiting for an action to complete, it is still executed again, but the opcode's function (in the exe) works out what to do.  Only when the function itself is satisfied that the operation has been completed will it mark the instruction as completed. It does this simply by advancing the script's progress by a number of bytes (the number of bytes that the opcode + arguments required).

Jumps: When a jump backwards instruction is executed, the current group is skipped and the next group is automatically given its turn. For example, if you have 8 instructions in Group 0, but the 4th one is a jump backwards, it will execute the 4th instruction for that frame, move to the next group (Group 1), and only continue with the 5th instruction for Group 0 on the next frame. There are other instructions that also have this effect.

Init and Main both have their own functions to execute instructions in field.  Inits of all groups are executed once (when you enter a field), starting from Group ID 0, at 0060C78D. Placing instructions here that require a delay will also cause a delay in the field loading (which is why these types of instructions should be avoided in the init sections). Using loops inside an init section will hang the field indefinitely. Unlike the Main function, Init will execute all instructions of a group's init section before moving to the next group.  In other words, it doesn't execute 8 instructions a frame, then move to the next group. Assuming it is still executing 8 instructions a frame in total, this does mean that a very large list of instructions in the init section will also cause a delay in the field opening (240 instructions would be 1 second delay - IF the Init function is working like Main in terms of execution time). This many total instructions for the groups' inits is very unlikely to happen (and the original game didn't come close to it), so it's not a worry regardless. I'd like to think that Init executes as fast as your processor will allow. It very likely does.

Main's function is at  0060CDAE.

Instruction Progress

Each of the 48 groups must keep their current instruction location saved, so that when focus moves to other groups and returns, the game can continue where it left off. Each group is assigned a 2 byte value to store the instruction offset, starting at 00CC0CF8. The first instruction will not be at value 0 (byte 0), because the instruction progress value includes the offset to the actual instruction list, which begins after other data (such as the field header). The current group's instruction progress value is retrieved at 60CD98.

mov dl,[ecx+eax]

Where ecx is the address of the field data itself, eax is the offset to the instructions of the field from that address (The instruction progress value), and dl is the opcode retrieved (FF7 field only has one byte opcodes) from that location.  The asm code there is a little dumb(?), but that's probably because optimization was turned off for the executable:

xor edx,edx
mov dl,[ecx+eax]
and edx,000000FF   <<< redundant.

The parameters for the operations are retrieved from inside the opcode's function itself (which is easy, since it's just the addresses after the opcode) . For example, the 8 bit jump forward opcode (0x10) retrieves the one-byte jump value at 61311B.

Because the instruction progress value is only 2 bytes, there is a limit to how big a field script can be.  Even if the headers and other data were discounted, the limit would still be 65535 bytes for ALL instruction data (for all groups). As it is, it seems to me that 65535 bytes is actually the maximum size an uncompressed field DATA section can be (not to be confused with the uncompressed field size total), and so the number of operations available per field is even more limited. It's pretty difficult to exhaust this limit, even with 48 large groups. Even 100 addition (arithmetic) operations, for 48 groups (5 * 100 * 48 [24000] bytes of data) is not a problem. The game will crash / behave incorrectly if it is not able to properly advance the instruction progress value, as it just did when I deliberately added 5 * 1000 * 48 (240000 bytes) of addition operations.

Execute a Script (Async Wait, Async NoWait, Sync Wait, Priority)

This is probably the most confusing part of field programming for modders. That's because no-one has really written anything about how the engine actually deals with these functions. 

Regardless of whether a script is called or not, the field engine still loops from ID 0 to the last ID. If a script has been called for, say, ID 6, then ID 6 will try to execute 8 instructions from that script. This mean that its MAIN script is superseded. So, the following:

Code: [Select]
Code:
Execute script #2 in external group Untitled (No6) (priority 1/6) - Only if the script is not already running
means that Group 6 (ID 6) will begin running through its Script 2 list of instructions INSTEAD OF its Main instructions when ID 6 gets its turn (remember, each ID/Group gets one turn every frame). Once all the instructions in Script 2 have been completed, it automatically switches back to its own Main. This is a difficult thing to explain with words, so, at a later time, I will make a video.

The 'Execute a Script' instruction has 3 modes:

Sync Wait, Async Wait, Async No Wait. 

Sync Wait:

Code: [Select]
Code:
Execute script #2 in group Untitled (No6) (priority 1/6) - Waiting for end of execution to continue
Each loop (each frame) will execute up to 8 instructions in ID 6. Until this is done, the calling script (for example, ID 0 > Main) will not continue when it gets its turn.

Async Wait:

Code: [Select]
Code:
Execute script #2 in external group Untitled (No6) (priority 1/6)
Each loop (each frame) will execute up to 8 instructions in ID 6. If ID 6 is already busy running through one of its scripts from a previous 'Execute a Script' instruction, the calling script (for example, ID 0 > Main) will not continue when it gets its turn. So, this instruction differs with Sync Wait only because it will wait if an ID is busy running through a called script, and not wait otherwise.  This means that Async Wait, like Sync Wait, is guaranteed to eventually run.

Async No Wait:

Code: [Select]
Code:
Execute script #2 in external group Untitled (No6) (priority 1/6) - Only if the script is not already running
This is the same as Async Wait, except... it doesn't wait. The next instruction (of the script that is calling the 'Execute a Script' instruction) will always be executed as soon as possible.  If the request for ID 6 (in my example) to run through Script 2 cannot be granted, it will never be initiated. This instruction should only be used when you require a script to be skipped if already running.

Some things to consider:

 - If a target ID's script is looped / does not end, Async Wait and Sync Wait will cause the calling ID's execution to be put on hold permanently. 

- If an ID has one of its scripts (Script 0-31) looped / unable to complete, all calls to execute any of its other scripts from any other location will fail. An ID can only have one of its scripts (Script 0-31) executing at a time.

- Every time an ID has one it its scripts (Script 0-31) executing, it will be in place of its Main script (Script 0). Only when all script call operations are completed will the Main script resume for that ID.

The game queues any scripts to be run at 612B5F

Code: [Select]
Code:
mov [ecx+eax*8+00CBF9E8],dl
Where ecx is the priority (0-6), eax is the ID (0-47), and dl is the script (0-31) that has been told to execute. This means that each ID can queue up to 7 "Execute a Script" instructions. Priorities are, basically, queued scripts to be run - with a priority order. Although parts of the engine support 0-7 queued items, only 0-6 will work. If priority 7 is called, it will be completely ignored.

Note: Calling Script 0 of an ID is supported, but it will be the "init" part of Script 0 (since it always ends with a return). It is not recommended nor essential to ever do this.
 
Last edited:
Finally, 8 instructions a frame per Group can be increased at 0060CD77.  A modern CPU can easily do 256 instructions a frame for each group. This would only have side effects where a. math counters have been used (if they have), compared to using Wait.  and b. Where the script programmer perhaps wanted the slight delay in computation.  Although, I am doubtful this is ever the case.  The benefits may be things like better controller response during "Has X been pushed", and no frame delays at all when executing instructions that place objects. Sync between instructions from different groups would also be improved.  I can't see increasing it being a problem for the most part... but maybe someone would like to test :P

And whether a function will cause the group to be skipped (like Move  [until completed] and Jump Back do) depends on the return value from the opcode function at 0060CDAE. 

0 = Do not skip current Group. Execute next instruction.
1 = Skip current group by going to next group.

This is, of course, hard coded for each function.

The Wait function, for example, simply returns 1 until the frames-to-wait counter reaches 0, when it will return 0 and increment the script progress value for the Group.
 
Last edited:
So, as my SFX mod finally nears a close - here are the unused IDs from the original PSX game (PC game differs slightly due to error. I have fixed this by using the correct SFX in the correct place.)  My dll won't allow you to use these IDs at all - so you'll have to use 751 - 65535 if you want to add more SFX / voice acting.  There's plenty there, no?

000, 199, 210, 476, 477, 488, 524, 575, 576, 580, 618, 732 - 750.

PC differs as follows: 

1. Effect 731 does not exist.  This is part of Sepher Sephiroth's death.  In the PC version, this isn't heard (it's the disintegrating part) It may have been  missed because it starts with a long silence.  Perhaps the sound programmer for the port thought it was a silent file.

2. The Kefka laugh in the Ghost Hotel is not looped.
 
Last edited:
740-743 are called, as it turns out.  The PSX version uses some of the other effects to create some variations for this attack.  Since PC can only use streamed data, the porting team have created the 4 effects as streamed files. Clever of them to realize.

edit.

Nope.  They weren't clever.  They just couldn't get this part working right so created these 4 effects to do the job that existing effects already do when called right.
 
Last edited:
I am not the original person who wanted you to start a tut, but I would be ever grateful if you did.
 
Status
Not open for further replies.
Back
Top