Author Topic: Bugfixes and Queries  (Read 8794 times)

NFITC1

  • No life
  • *
  • Posts: 2727
  • Karma: 60
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Bugfixes and Queries
« Reply #75 on: 2017-03-03 20:53:27 »
Ah, cheers for that NFITC! ;)  Very nice.  Why nothing after 1B?  I remember jumps to code exist,  This code doesn't do anything?

Jumps exist and they do things, but they're used by the Wutai event. The actual event driver variable (0xCBF9DC) has a max value of 1C, but is subtracted by 1 when the event is selected. If that subtracted value is greater than 1C it goes to a non-event. In practice, this never happens. Quite strange and I'm not really sure what the point of subtracting by 1 is. It's only ever dynamically assigned in three places: loading a game (including new game), exiting the main menu and changing CDs. That's only to redirect it back to the world map and the field. As a matter of fact, this might be the source of the Yuffie warp glitch. I can't confirm that right now, however.
« Last Edit: 2017-03-03 21:02:40 by NFITC1 »

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #76 on: 2017-03-03 21:37:51 »
I've seen this subtraction nonsense a lot actually.  I'm thinking it may have been the compiler that did it.  I know that when I look at my own code it often uses fancy tricks like that, usually for efficiency.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #77 on: 2017-03-03 21:40:18 »
The warp glitch has been fixed by me actually for my dll.  It's caused by some temp vars not being reset on game over - here:

              {reset field vars to stop teleport bug}
              mov dword ptr [$CFF594],$00
              mov dword ptr [$CFFAB2],$00
              mov dword ptr [$CC0B60],$00
              mov dword ptr [$CFFC60],$00

They were a buttercup to find and I had to use some nifty comparison trickery to isolate them.

3 of these contain addresses.  If they aren't cleared on game over, then reloading to world map will not clear them either.  After world map battle, it will use these to attempt to jump back to field. When cleared, the behaviour changes and will be correct. As far as I can see, these addresses are used for a simply "jump back to previous field" operation.  And, as I noted, they take precedence over other jump field operations.

Other addresses need to be cleared to stop other bugs.  For example, countdown timer being visible in world map battle after reloading game.  World map / game over / load  do not reset any of the vars.  Only entering a field will. I've added these fixes too.
« Last Edit: 2017-03-03 21:46:00 by DLPB »

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #78 on: 2017-03-06 00:25:10 »
credit screen fix is going well, but I will have to hold off until I have got the original text in place from psx... since a lot of this is a manual changes to the table.

Anyway, here is how it works.  Each text uses 16 bytes.  Four texts to a corner means 64 bytes per fix. And, since there are 29 corners needing repair, that is 29 * 64 bytes in total (1856).
 

99B5E8 = C2 01 00 00 08 00 00 00 80 FF FF FF 08 00 00 00
99B5F8 = 02 01 00 00 00 00 00 00 03 01 00 00 00 00 00 00
99B608 = BB 00 00 00 94 FF FF FF BB 00 00 00 50 01 00 00
99B618 = 00 00 00 00 28 00 00 00 00 00 00 00 29 00 00 00


Red:  Starting X of white text1 and black text 1
Orange:  Ending X of white text1 and black text 1
Blue: Fixed Y of white text 1 and black text 1

Green: Starting Y of White text 2 and black text 2
Brown: Ending Y of white text 2 and black text 2
Purple: Fixed X of white text 2 and black text 2

When in the top right, black text 1 X pos dictates the timing. In this case, 80 FF FF FF (minus 127).

*** These are unused above as top right calculations do not need them.  Two of the corners use these values, while discarding 4 others.
Yellow:  Fixed X of of white text 1 and black text 1
Pink: Fixed Y of white text 2 and black text 2

In order to get the top right working properly, part of the calculation phase also needs amendment:

7A53FB = 90 90
7A554B = 90 90

Another 6 of these will be nopped to fix the other 3 corners.
« Last Edit: 2017-03-06 00:39:58 by DLPB »

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #79 on: 2017-03-06 21:10:55 »
NFITC1.  Why does the World Map not use the main jump at 40919E when calling the menu.  Everything else seems to use this no matter what.
« Last Edit: 2017-03-06 21:12:51 by DLPB »

NFITC1

  • No life
  • *
  • Posts: 2727
  • Karma: 60
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Bugfixes and Queries
« Reply #80 on: 2017-03-06 23:04:35 »
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.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #81 on: 2017-03-06 23:08:16 »
But it doesn't have any problem doing it from field?  It uses less memory than the WM?

NFITC1

  • No life
  • *
  • Posts: 2727
  • Karma: 60
  • I just don't know what went wrong.
    • View Profile
    • WM/PrC Blog
Re: Bugfixes and Queries
« Reply #82 on: 2017-03-07 13:13:01 »
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.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #83 on: 2017-03-07 17:42:37 »
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 Edit: 2017-03-07 21:18:08 by DLPB »

Sega Chief

  • No life
  • *
  • Posts: 2278
  • Karma: 131
  • These guys is sick
    • View Profile
Re: Bugfixes and Queries
« Reply #84 on: 2017-03-10 16:02:40 »
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?

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #85 on: 2017-03-11 10:55:19 »
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 Edit: 2017-03-11 11:03:33 by DLPB »

Sega Chief

  • No life
  • *
  • Posts: 2278
  • Karma: 131
  • These guys is sick
    • View Profile
Re: Bugfixes and Queries
« Reply #86 on: 2017-03-11 12:29:55 »
I'll check out your bass.dll; thanks for the info.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #87 on: 2017-03-11 13:00:54 »
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.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #88 on: 2017-03-18 10:22:37 »
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 Edit: 2017-03-18 10:40:35 by DLPB »

halkun

  • Global moderator
  • No life
  • *
  • Posts: 2105
  • Karma: 20
  • NicoNico :)
    • View Profile
    • Q-Gears Homepage
Re: Bugfixes and Queries
« Reply #89 on: 2017-03-18 23:50:34 »
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.

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #90 on: 2017-05-08 00:26:14 »
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 Edit: 2017-05-08 02:20:52 by DLPB »

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #91 on: 2017-05-08 02:03:17 »
Normal field operations.

Note, the following may not apply to async Script Call functions (I am going to do some in-depth testing to see how all that works)

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 may be 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 probably 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 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]
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]
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]
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]
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]
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 Edit: 2017-05-11 04:56:49 by DLPB »

DLPB

  • No life
  • *
  • Posts: 8399
  • Karma: 225
  • ‘You know who I am,’ he said.
    • View Profile
Re: Bugfixes and Queries
« Reply #92 on: 2017-05-11 08:38:34 »
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 Edit: 2017-05-11 09:11:58 by DLPB »