Final Fight Unlicensed Hack (SNES 2-Player Hack)

All talk about ROM hacks go here - your current or past projects, other people's hacks, or whatever else as long as it pertains to ROM hacking.
Post Reply
User avatar
Gnawtor

Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Gnawtor » Mon Nov 12, 2018 6:14 am

What started as an experimental side-project from my original large-scale hack of SNES Final Fight Guy (Death Fuck '89, still a work in progress) has become my hack that I am most proud of, even though it is currently still quite incomplete and broken.

In summer of 2017 I was perusing the notes page for SNES Final Fight on tcrf.net contributing some missing RAM addresses (as best as I could with their god awful wiki interface) to the RAM map that another user, JLucas, had been posting. One thing that JLucas had discovered and documented was the presence of a 2-player switch near the beginning of Final Fight's RAM. He also included a few disassembled subroutines that read from this byte, but it looked like he either reached a dead end with it or simply didn't wish to pursue it further. It all sounded interesting, so I started a new side project from a fresh copy of Final Fight Guy (J).

The "Number of Players" byte resides at $7E:00CD. By default it contains the value #01, which is forced into place by the code for the Title Screen when a new game is started. It is worth pointing out at this time that SNES Final Fight has an unused section of RAM directly after 1P's memory which was to be used for 2P. Without any modifications to the ROM or without forcing any values in RAM, 2P's memory remains unused (all zeros) except for a copy of 1P's controller inputs. Forcing $CD to #02 instead of #01 does not result in a second player appearing on the screen, but it does result in a few more values written to 2P's memory upon starting a new game. Specifically these are $0D98 (Max Health), $0DA8 (Offset for Attack damage table) and $0DAA (Offset for Hitbox table).

At this point I opted to approach the problem head-on. Clearly this "No. Of Players" byte was controlling whether or not 2P RAM gets initialized upon starting a new game, so I had to figure out what is initializing 1P RAM and add new logic to do the same for 2P if $CD == #02.

Before doing this, I made three other changes to make my job easier.

First I modified the title screen code to always write #02 to $CD. The code that does this contains an interesting redundancy which I'm interpreting as evidence of removed functionality. At the end of the title screen fade-out, a constant #01 is written to an address within the area of RAM set aside specifically for the Title Screen (in this case $128) and then read/written to $CD just a handful of instructions after that. I can imagine that the functionality for selection a 1P or 2P game was once part of the title screen, and the selection was at one time stored to $128 well before the fade-out, but was hastily edited out requiring a hacky workaround. Maybe the devs wanted to leave the door open to restoring this mode in the future? All these years later, it's anyone's guess. (By the way, if you want to observe this for yourself, set an exec breakpoint in Final Fight Guy (J) to address $0193C7). This fix was later made obsolete when I modified the Title Screen code to allow the option for a 1P or 2P game.

Second, I had to fix 2P's controller inputs to read from Joypad2 instead of Joypad1. This was a pretty simple fix, as the game already reads inputs from all joypads by default (including the seldom-used 3 and 4, oddly enough) and stores them to a "system RAM" (as I have dubbed it) area near the start of RAM. While the game is running, the program copies these inputs from System RAM to Player RAM. All I had to do was modify the section that writes to 2P RAM to read from Joypad2 instead of Joypad1.

The third and final preemptive change I had to make was to ensure that 2P's selected character was always the same as 1P's selection. This was to avoid any potential conflicts with animation data. In my prior explorations of this ROM (specifically back when I discovered the code for Cody was still lying dormant in Final Fight Guy and could be restored) I learned that all of the animation frame data for every on-screen player, enemy and boss was decompressed into RAM on the fly, and that there was only enough room for one Player's anim frame data, and that many problems (including crashes) could arise if a different character's animation indexes were used with what was currently decompressed into RAM. This was also a relatively simple fix in the Character Select code. When 1P's character ID is copied to the appropriate address upon making a selection ($D12) I forced it to also store to the equivalent address in 2P RAM ($D92). Like before, this was a quick and dirty fix that was made obsolete when I redid the character selection screen code, but it served its purpose well in those early days.

Once those three loose ends were tied up, I began the process of systematically replicating all of 1P's initializations to also apply to 2P (as long as 2P mode is set). Some of these are set at the title screen, while some others are set when the gameplay program loop mode begins. It was a matter of observing 1P RAM against 2P RAM, seeing what was missing, and restoring the missing logic.

A few sessions later, I achieved this effect:
ff2play_firstsuccess.PNG
ff2play_firstsuccess.PNG (43.32 KiB) Viewed 2086 times
This was to my utter bewilderment, as I did not really expect any of this to work at all. From that point on my goal was to see just how far I could push this hack. For the first time ever, outside of Capcom's offices, there were 2 player characters visible in SNES Final Fight, independently controllable. Unfortunately, this success came with many limitations. However early on 2P mode was cut from the game is anyone's guess, seeing as at least some pieces of it are leftover, but digging into it and seeing it in action it became painfully clear that many fundamental systems of the game were designed exclusively with only one player in mind. 2P did not have his own section of VRAM, and so any actions taken by one player character would overwrite the tiles of both players (you can see the effect in the image below, where 1P is walking forward and 2P is standing still, but 2P displays tiles from 1P's current walk frame with an incorrect arrangement). 2P was practically a ghost. Although he could walk around, he did not affect the level scroll, and would be bulldozed along by the screen's edge as 1P advances through the level. Enemies and bosses completely ignored him, and he likewise couldn't even attack or be attacked. He couldn't break item containers, but oddly enough he was able to pick up items and weapons. The only way 2P was able to affect an enemy in any capacity was by throwing a knife.
ff2play_firstsuccess2.PNG
ff2play_firstsuccess2.PNG (17.67 KiB) Viewed 2086 times
However, it was indeed a good start and proof of concept. There was a ton of fluff that I wanted to improve upon in this hack, but I decided early on that the best approach would be to restrict myself to a few high priority problems that were fundamental to the overall experience of the game. Most importantly, 2P had to become less of a ghost and be able to better interact with his surroundings. Second, he needed a spot in the overhead status bar to display his health, lives, points, and current target entity's namebar and healthbar. Third, something had to be done about the animation frame data and VRAM restrictions, as it was pretty fucking lame to just have two glitchy copies of the same character scrambling around the screen. Apart from these three top priorities, I also needed to (still need to, as of this writing) account for more than 1 player's sound effects being loaded into SPC-700 memory (only 1P's selected character's sound effects are loaded) and do something about enemy pathfinding.

That's a shitload of work.

Undeterred I started piecing together the in-game logic to get 2P to become more than just a minor novelty. My investigations into the various gameplay handlers revealed certain high-water marks where you could see evidence of multiplayer functionality left somewhat intact but ultimately implemented exclusively for 1P. To explain a bit more technically, there are many subroutines left in place where 1P's memory is modified by first setting 1P RAM ($D00) as the direct page. In these instances it was mostly straightforward to recreate logic for 2P mode and run these same subroutines with 2P RAM ($D80) as the direct page. That being said, there were at least a few cases where 1P's memory was being addressed without the direct page, which required some more creative workarounds. Eventually I got 2P working to the point where he could affect the level scroll in the same manner as the arcade game (which already has functioning 2P mode, and 3 player characters... remind me again why the fuck I'm making this hack???) and able to break item containers, doors, and attack enemies and bosses. 2P could even be attacked by enemies, but because the enemy pathfinding is hard-coded to only account for 1P, 2P can only be hit by putting himself into harm's way.

The status bar took me about a month or two to sort out (on top of a full time job and social life). I wound up reverse-engineering the entire damn thing and putting it into expanded ROM. This also required me to start filling up what little room there was leftover in RAM, as there are a number of event tables relating to updating the status bar (mostly points and hit-confirm stuff) which required copies for 2P's status to function correctly. Adding 2P to the status bar also forced a few system changes. First, it became obvious that there would be a significant palette limitation. In Final Fight, if an enemy or boss has more hit points than what can be displayed in a depleting health bar, their health bar will be replaced by an "oversize" solid color health bar that changes color the more you beat them up, eventually turning into a standard yellow/red depleting health bar. Introducing 2P to the equation not only meant displaying his health bar, but also whichever enemy he is currently interacting with. So what happens if 1P and 2P are both attacking different enemies with their own oversize health bars, but at different values? A concession had to be made so that both players can correctly display their own oversized target healthbars, which sounds easy until you realize that there is no room leftover on the BG palettes to fit this extra color. The colored enemy namebar portraits had to be axed and forced to use the same 3-tone palette as the player portraits, meaning no more blue EDI.E portrait and no more pink Poison portrait. As much as it sucked to remove a feature from the game, it became clear that this was a necessary evil, and gave me a humbling insight to the sort of dilemma that the teams responsible for this kind of console port had to face back in the day. Also, moving the target namebars and healthbars underneath their respective player's healthbars created a new problem, as this required more room on the status bar than what was available. A tweak needed to be made to the game's interrupt routine to transfer an additional horizontal row of tiles from RAM to VRAM. This was actually the least painful part of the status bar fix. Points conditions also had to be altered to account for 2P, and I think there are even still a few conditions that I have missed. Don't even get me started on the bonus round points display...
p2_statusbar.PNG
p2_statusbar.PNG (19.89 KiB) Viewed 2086 times
Probably the biggest pain in the ass of them all was getting 2 different player characters on-screen at once due to the aforementioned memory limitations. I had to add new logic to decompress animation frame data for 2P in the second RAM bank, as the first bank was full. Then I had to fully reverse-engineer the code that takes action upon this frame data and make a 2P-only version of it to account for it being in a different RAM bank than the other entity's anim frame data. Then I had to tweak the DMA routines to place 2P's tiles in a different area than 1P's tiles. Unfortunately this meant overwriting a lot of tiles for the food and points items, and some impact effect tiles. This is actually still broken and will require extensive tweaking. Also of concern was 2P's palette, which wound up overwriting the greyscale flashing effect for item pickups. Once I got this sorted out, I implemented my Cody hack into this one to get all 3 player characters.
ffight_codyportrait.PNG
ffight_codyportrait.PNG (32.6 KiB) Viewed 2086 times
So, the hack is rough around the edges, but it's close to halfway presentable. The sound effects and pathfinding issues are still a concern, as are the tile conflicts that are a result of shoehorning 2P into VRAM, but overall I'm pretty proud of what I managed to accomplish with so many limitations. A link to the current prototype version of this hack is below.
Please feel free to ask me any questions about the hack you might have, just please don't quote this entire wall of text if you decide to reply.

lolo

Re: Final Fight (SNES) 2-Player Hack

Post by lolo » Mon Nov 12, 2018 8:56 am

in all its amazing glory :D

User avatar
Gnawtor

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Gnawtor » Fri Jan 25, 2019 3:11 am

I see the boys at romhacking.net discovered that this hack exists. For the record, no, this hack is NOT dead (I don't even know how you got that impression), I have not "disappeared from the net" and I fully intend on continuing it. In fact I already have a new prototype in the works that allows enemies to target player 2. More on that later.

redrum

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by redrum » Sat Jan 26, 2019 5:13 pm

This is the best new of the day! Congrats mate about your hack. I followed your amazing storytelling since your first post on romh**** and was very worried that your account on badd*** was no more active.

Sooooo it seems that you want again to show to the world your skills? I hope that your death fuck 89 project will gain of it :)

One suggestion: if the other hacker wants to work on the rolento's stage could you mutualize your expertise or at least speak together? FYI his work on f-zero was impressive and maybe you should appreciate to gain some support.

Again Bravo for your work: you are the most advanced hacker on this snes game and no doubt that you still remains on top of the hill. All the best for the next step

lolo

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by lolo » Sat Jan 26, 2019 6:17 pm

post more post more its always lookin cooler an cooler :)

User avatar
Gnawtor

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Gnawtor » Sun Jan 27, 2019 12:37 am

redrum wrote:
Sat Jan 26, 2019 5:13 pm
This is the best new of the day! Congrats mate about your hack. I followed your amazing storytelling since your first post on romh**** and was very worried that your account on badd*** was no more active.

Sooooo it seems that you want again to show to the world your skills? I hope that your death fuck 89 project will gain of it :)

One suggestion: if the other hacker wants to work on the rolento's stage could you mutualize your expertise or at least speak together? FYI his work on f-zero was impressive and maybe you should appreciate to gain some support.

Again Bravo for your work: you are the most advanced hacker on this snes game and no doubt that you still remains on top of the hill. All the best for the next step
Thanks dude. You don't have to censor other sites' names, by the way.

This project is a solo effort. I have long term plans for it and it's also my personal love letter to the SNES port, as it caught a lot of flak over the years but I always had fun with it despite its shortcomings. So yeah, this project is kinda personal to me. Also I'm not interested in starting a collaboration with someone I don't even know, especially given this scene's track record for those kinds of projects. I don't care if someone else is doing a similar hack, I'm not one of those weirdos who "claims" games as his own. Having said all that, I know a lot about how final fight works so if he wants to pick my brain about it he's free to.

User avatar
Gnawtor

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Gnawtor » Sun Jan 27, 2019 6:03 am

Here's a screenshot of enemies targeting 2P. It's not perfect and for some attacks and movement patterns it will likely require changes to enemy AI, but it's getting there.
ff2play_2ptarget02.png
ff2play_2ptarget02.png (40.32 KiB) Viewed 1415 times

Grego2d

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Grego2d » Sun Jan 27, 2019 8:48 pm

Hey rotwang, it's Grego the guy from rhdn. Sorry about the mix-up thinking you had abandoned the project. I read through all the stuff on bhdn, amazing work dude. I'd like to pick your brain about final fight some time, if you're on discord perhaps we could swap discord info.

User avatar
Gnawtor

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by Gnawtor » Mon Jan 28, 2019 12:03 am

Heya, welcome. All good, and thanks. Our discord is here

niuus

Re: Final Fight Unlicensed Hack (SNES 2-Player Hack)

Post by niuus » Thu Feb 07, 2019 1:39 am

Gnawtor wrote:
Sun Jan 27, 2019 6:03 am
Here's a screenshot of enemies targeting 2P. It's not perfect and for some attacks and movement patterns it will likely require changes to enemy AI, but it's getting there.

ff2play_2ptarget02.png
Incredible work. Is PT07 coming soon, or are you holding until it is finished?

Are you planning to decensor and add the removed things from the game?

I always had this wrong assumption that 2 players SNES FF wasn't technically feasible for whatever reason, so your hack really blew my mind. Thanks for making it possible, this certainly would make it the best version in my book.

Post Reply