Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ FF7 ] Implement "Double input" bug fix #713

Open
potus-barret opened this issue Jul 27, 2024 · 5 comments
Open

[ FF7 ] Implement "Double input" bug fix #713

potus-barret opened this issue Jul 27, 2024 · 5 comments
Labels
enhancement New feature or request

Comments

@potus-barret
Copy link

Summary

Like the title says, as default behaviour without an option to disable. Or as an opt in feature. Or something in between.

What is double input?
I describe this as, in menus, every input triggers a tick of input repeat.

Menus as in: battle, new game, triangle/main menu, shops, naming screen, etc and their submenus. Not field dialogue boxes with cursors in them (which dont use the repeat feature), or menus from minigames.

Input repeat, aka auto fire, aka rapid fire. In game, in a menu, when a button is held and its input repeats, like holding down in a item menu.

In FF7 PC, "input repeat"'s implementation is flawed/incomplete.
If a button is pressed while a repeatable button is held:

  • On PSX only the recently pressed button is capable of being processed.
  • On PC both the held repeatable button is and the recently pressed one can get processed.

Steps to test and reproduce the bug's effects along with a verbose and rambly explanation of the bug and the fix can be found here.

https://potus-barret.github.io/ff7-pc-di-fix-web/

Basic example

When directly patching with DLB's hext tools I modify these bytes like so:
1A49E = 34 87
1A4AA = C6

In 7th heaven my mod modifies these bytes like so:
0041B09E = 34 87
0041B0AA = C6

Motivation

Aside from being obsessed. It seems to me like the right place for such a fix. A player that encounters the bug regularly may not want to go through the rigamarole to set up 7th heaven to install a mod. Or manually patch their exe. They may simply want to play vanilla with FFNx and the features it provides, and perhaps among them a fix for an irritating bug.

Not gonna die on this hill or anything, just make a case. And if the proposal is accepted, as time allows ill try make it happen. cpp will be a learning curve for me, but at a glance ff7_data and patch look like good places to start wrapping my head around.

Fair warning, I am slow, and time is hard to come by, so by no means will i be upset if someone without a learning curve ahead of them takes what I have provided and finishes the job.

@potus-barret potus-barret added the enhancement New feature or request label Jul 27, 2024
@julianxhokaxhiu
Copy link
Owner

Hello @potus-barret and thank you very much for rising this. I am trying to understand what you fixed by touching those addresses, and while looking at the game code, I have only one question: are you able to repro this bug also on the vanilla 1998 game ( without FFNx ) or Steam game ( vanilla, without FFNx )?

Thank you in advance

@potus-barret
Copy link
Author

potus-barret commented Jul 28, 2024

Hello Odin, we spoke back in February about this, I go by 'Dig' on qhimm discord. Yes this is a bug in all PC FF7 versions and ports derived from them, the only place i haven't checked or got confirmation on is the demos.

These sections will be of interest and together with their 'reference links' should answer most if not all questions. But do please ask away if something is unclear or you feel you need a tldr.

009A8734 - aka 34 - Repeat is active flag
0041B108 - aka 108 - input timer and flag setting function
0041B099 - aka 099 - check for repeatable key function
Brass tacks
The unreachable condition (condition 1 of 099)

The section on the unreachable condition does make some assumptions, and id be interested to see what people with a better understanding of FF7's binary think of it.

@julianxhokaxhiu
Copy link
Owner

Thanks a lot for the clarification, I found some time to look at the game code and the result may result disappointing to you but you basically fixed broken leftover code check.

The part you're patching is supposed to check if there was never an input from the player, if the current ( last ) key pressed before the next frame, was the one intended by the game. That check never worked because they have some leftover code that was supposed to be called for that check that never triggers ( leftover, not surprised ).

How you've fixed it? By replacing that check with another one: you compute if the final computed key ( independent of source input, keyboard, mouse, gamepad, etc. ) is the one intended by the game. Since this key is always computed at every frame, chances are the input is evaluated and the game resets internally until the next button press.

If it's ok with you I'll include the hext patches directly in the code so you don't have to ship it via hext anymore. Fine with you? I'll add you to the credits as well.

Thank you in advance,
Julian

@potus-barret
Copy link
Author

potus-barret commented Jul 29, 2024

Sounds great to me. Thanks!

I appreciate you taking the time to consider this, and accepting to include it in FFNx. I hope this wall of text doesn't changed your mind. 😄

There may still be things to consider, for example there are likely those that expect and prefer the buggy behaviour of repeat on PC FF7. Those people may dislike what people call double input, but enjoy repeats behaviour during battles while inputs are spamming. Ive at least seen one example of this in the speed running community.

So the ability to disable the fix sounds like a good idea. For the few rare types of casual players that use speed run like technique in battles and would rather keep the bug. I expect this number of people to be quite low, but I might be wrong.

Thanks a lot for the clarification, I found some time to look at the game code and the result may result disappointing to you but you basically fixed broken leftover code check.

Yes as the linked doc says, the condition I hijacked is unreachable. Not sure what would be disappointing about that.

The part you're patching is supposed to check if there was never an input from the player, if the current ( last ) key pressed before the next frame, was the one intended by the game.

Eh, it might have been left over from testing/debugging input repeat, considering what address its checking. Right?

Also "current last key"(009A85E0) sounds like your confusing my fix with the original code. The unreachable condition would have checked ALL currently pressed keys (009A85D4) without my fix applied.

[ Edit: removed unrelated stuff about E0, and stuff I'm likely misremembering about exchange menu, think it was D4. And it read as argumentative rather than informative. which was the intention.]

That check never worked because they have some leftover code that was supposed to be called for that check that never triggers ( leftover, not surprised ).

Is that the zeroing function I referred to? Or something else? There would be very wrong things happening with inputs if that condition was true, even more so if the zeroing function was called to make that condition true.

How you've fixed it? By replacing that check with another one:

Yes. Though it is not what I was fixing. I was fixing input repeat, a feature of menu inputs with a great many moving parts.

I replaced the address being checked (009A85E4 - Input repeat rate) with 009A8734. What I call "Repeat is active flag".

It is 1 when repeatable inputs should be pulled from all held keys. 009A85D4
And 0 when repeatable inputs should be pulled from keys pressed this frame. 009A85E0

009A8734 is never read by the game at any point in time.

And I change a call to 0041AB67 with a call to 0041AB74. Which is equally as important as the 009A8734 change.

you compute if the final computed key ( independent of source input, keyboard, mouse, gamepad, etc. ) is the one intended by the game.

No.
Clarify please, sounds like your mistaken. Final, what's final? Intended by the game... what. And yes hardware does not matter, I said something similar in the doc, including a dance dance mat in the list. 😆

Are you referring to my changing a call to 0041AB67 with a call to 0041AB74?

Since this key is always computed at every frame

In menus, yes, this function, 0041B099, is ran several times a frame. The amount of times depends on how many repeatable keys their are for that menus/submenus context.

chances are the input is evaluated and the game resets internally until the next button press.

I'm not sure what you mean by this, but as the previous quote states, this code is run every frame, and as I clarify, several times a frame. That does not change, so the word 'until' is confusing to me here. Checking 009A8734 doesn't cause anything to reset. Calling 0041AB74 doesn't cause anything to reset.

This function, 0041B099, begins with a key to check for in the stack:

If 009A8734 is 0, a list of keys pressed this frame is checked for that key. (0041AB74)
If the key is found, 0041B099 returns to the caller with a non 0 value in eax.
If the key is not found, 0041B099 returns to the caller with a 0 value in eax.

If 009A8734 is 1, a list of all currently pressed/held keys is checked for that key. (0041AB67)
If the key is found, 0041B099 returns to the caller with the value of 009A872C placed in eax.
If the key is not found, 0041B099 returns to the caller with a 0 value in eax.

This eax value is either passed up a chain of calling functions and then used as a condition and processes that key as pressed if eax is non 0.
Or is immediately used as a condition and processes that key as pressed if eax is non 0.

What is 009A872C, i call it the repeat gate, it is 1 when a key is pressed, and every time the 'current wait' elapses. 'Current wait' is 200ms on key press, and 50ms every time 'current wait' elapses while a button is held. This is repeat.

Or as i put it in the doc:

It is what keeps repeat firing on 50ms ticks, 
it is what keeps it from firing before the 200ms wait has passed. 
It gates repeat from occurring.

That's as simple as i can put it, and I'm still leaving stuff out. The fix is simple, what is happening is not. Hence the length of the doc.

If you or anyone else would like more explained in a more tldr fashion than the doc, ask away.

@zaphod77
Copy link

double input is actually usually caused by emulation of xinput controllers without hiding the original controller. the game sees both xniput and dinput inputs at te same time. I'm guessing ds4windows is being used. :)

The fix is to make the game 100% ignore dinput if xinput is detected.

Now i'm not sure that this is what's happening, but i'd say it's a pretty good guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants