• Welcome to Final Fantasy Hacktics. Please login or sign up.

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - Glain

Recently I began investigating Effects because I wanted to use some of them for abilities in ways that wouldn't normally work.  For example, some effects only animate on one target, even if the ability hits multiple targets.  Some of them only center around the caster, even if the ability was targeted away from the caster (like the Draw Out effects). 

Here I'm going through three different ways to modify effects:

    * Changing where an Effect animates (target panel vs. source unit, for example)
    * Changing Effects that animate on a single target to animate on multiple targets
    * Changing Effect colors/palette at runtime

There wasn't a whole lot of previous documentation about this, so I had to do quite a bit of investigation, and I'm documenting what I found for posterity.  Choto's notes on effect data locations and routines (from years before!) were really helpful.
Completed Mods / Auto-SCC Patch
September 07, 2018, 07:25:01 pm
This patch makes the game a Straight Character Challenge (SCC) immediately from the start of the game.  In the opening sequence, the player chooses an initial job.  Your starting characters are automatically in that job (and will have appropriate equipment).  You even start the opening battle with Ramza as the SCC job.

This eliminates the tedium of unlocking the job you want to play as, but also forces the player into that job earlier than normal.

At the start of the game, there's a job to select...

Looks like we're starting in the Geomancer Academy.

Since you only have access to your chosen job, there's no need to see all kinds of other jobs in the job list...

And, well... you can't change jobs.  This is an SCC.

Also, the only selectable abilities are the ones from your job.

The soldier office appears to now be the Geomancer Office.

Note: Soldier office prices will vary based on whatever equipment is rolled for the unit!  The calculation for the hiring price is actually 1000 + (Equipment Cost).  Since equipment can vary, the price will read 1000 until either Male Fighter or Female Fighter is selected (and the equipment is determined), at which point the price will update with an increased value.

Characters will still join the party as normal... but will be in the SCC job, and cannot be changed out of it.  This applies to special characters as well as generics!  Monsters can join the party, but will not create eggs and cannot be fielded in battles.

Level caps are automatically enforced, and use the standard:
    Chapter 1: 20
    Chapter 2: 35
    Chapter 3: 50
    Chapter 4: 70 (Before Orbonne)
This means you actually don't have to worry about a unit going over the level limit because they just won't be able to level if they would normally have done so.

Abilities to be learned from crystals only include abilities for your job.
Invited units will not join the party at the end of the battle, but their equipment will silently be added to your inventory (much like what happens with a dismissed unit).

This patch uses the "Random unit equipment more selective" ASM patch, and modifies the levels of some equipment so that the AI will, in general, have access to more varied equipment but not equipment that's obsolete.

There are also a few scattered changes:
    Gafgarion at Zirekile doesn't load from the party
    Rubber Shoes become available in Chapter 3
    "Defeat Dycedarg!" at Igros Chapter 4

In addition to employing its own extensive ASM changes, this patch also makes use of the following ASMs:
    Random unit equipment more selective
    Fix Reraise Graphic Width
    Start button skips events, effects, battle and event text, etc.
    JP scroll glitch actual fix (Disable paging on confirm menu)
    Unit slots backfilled when unit is removed from party
    Speed up text crawls
    Switch unit number with L1 and R1 buttons (formation)
    Equipment duplication glitch fixes
    Level down fix
    Crits always deal bonus damage
This patch is actually completed, as far as I'm aware!  The PPF is attached.  I've played through on console without issue.
Feedback is welcome!
I recently went through the code that handles ATTACK.OUT battle event conditionals and made a reference for all of the conditional commands contained therein... and there were quite a few more than I've seen documented anywhere, and some were a bit different than how they'd been described before.

Some of these are well known and others less so!  I added a table to the Wiki page here for the ATTACK.OUT conditionals since the table didn't seem to fit very well as a post.
(Updated with newest version from June 9th)

This patch requires the latest version of FFTorgASM (0.492), available in the FFTPatcher suite here.

To use it, download the attached file and unzip it into the XmlPatches directory within the FFTorgASM base directory and apply the patch. 

This ASM patch allows you to press the Start button to skip:

        When skipping an event, the code will determine what kind of event is being skipped, and depending on a few factors, one of two "skip types" will be picked.
            1.  "Normal skipping" or fast-forwarding: Mostly for pre-battle events.  There are a bunch of event instructions in pre-battle events that can't be skipped because they'd mess up the ensuing battle,
                so those instructions are still run, but very quickly.  As a result, the event looks like it's being fast-forwarded.  (Note: This can sometimes look rather hilarious.)
            2. "Total skipping" or true skipping: Skips nearly everything in the event, running only what's required to get out of the event smoothly and update required variables. 
                Used for out-of-battle cutscenes and the like where very little actually has to be run since a battle isn't being set up.  In this mode, it basically just looks like the entire event was skipped.
        You may not see damage and healing numbers, etc. if you skip a mid-battle effect.  Can also skip an event in the middle of an effect playing. 
        There are a few rare cases where you won't be allowed to skip an effect (e.g. Bethla sluice) once it starts playing because it wouldn't be safe to skip.
    Battle and Event Text
        The Start button can be used to skip dialog text (as well as the text crawl in cutscenes, even "l i t t l e   m o n e y" mid-sequence).  If in an event,
        it will skip the entire remaining part of the event.  Otherwise, Start should just advance the text.
    Map Titles
        Applies to both the ones you see pre-battle and in cutscenes (although if in a cutscene, Start will skip the entire thing and not just the map title).
    Unit movement
        If skipped, the unit will suddenly appear at the destination.  Works mid-battle when units move during their turns.
    Chapter title and end graphics
        Since these show up in events, pressing Start will break out of the graphic and skip the entire rest of the event.  If something is loading in the background, there may be some wait time.
    Gameover screen
        There may still be a bit of wait time until the titlescreen comes up, probably because something is being loaded.
    Ending credits
        Again, there can be a bit of wait time before the titlescreen comes back up.

As always, use at your own risk.  I don't know of any remaining bugs (a few fast-forwarded scenes looking rather silly aside), but I may have missed something!
Let me know what you think if you use this and/or if you find anything obviously wrong!
Tutorials and Learning / Glain's ASM learning segment #1
February 03, 2012, 11:38:09 pm
Assembly language

Assembly language (ASM) refers to program code that a computer processor can "understand" and interpret as commands to follow out. Encoded assembly language is sometimes called machine code. Different computer architectures have different assembly languages.

Computers don't understand anything but assembly language. So how can you code in other languages like C, C++, VB, etc.? Those code files have to be compiled into an executable file that the computer can understand. Ever wondered was was inside an EXE file? It's ASM. So basically, ASM controls everything every computer ever does. No big deal!

The PSX processor is built on the MIPS architecture, so MIPS assembly language is the one we're looking at. Specifically, the instruction set the PSX processor (MIPS R3000A) can understand.

ASM Instructions

Processors operate on registers, which are small areas of memory inside the processor itself that can be manipulated quickly. The PSX has 32 registers available for general use by the processor (and a few others used behind the scenes).

ASM instructions will typically either modify either registers or memory. I'll explain what various instructions do as we run into them. Here are a few examples:

add r2,r4,r5         #   r2 = r4 + r5
or r3,r3,r5            #   r3 = r3 | r5 (Bitwise OR)

addi r3,r3,4         #   r3 = r3 + 4

lui r4,0x8019         #   r4 = 0x80190000 (This instruction loads the 0x8019 into the upper half of the register)
lb r5,0x24cc(r4)      #   r5 = (Value at memory location 0x801924cc - The first digit is insignificant, so this is memory at 0x1924cc)
sb r5,0x08cc(r4)      #   Value at memory location 0x801908cc = r5

The registers are simply referred by number (so r2,r3,r4,r5,etc. are all registers). Those familiar with some of the memory locations FFT uses might recognize what's happening in the last example...

Setting the base class of the first enemy in the battle to Ramza's base class. Pretty bizarre, really.


Let's look at a formula, why don't we?

When we're looking at formulas, we have some memory addresses that tend to be used a lot:
0x80192d90 Action
0x80192d94 Caster unit (User of action)
0x80192d98 Target unit

It's also useful to know the offsets of data as they relate to the units, which can be found on the wiki here. There are also some offsets to the action that change what it does. These are the properties I know about (and I find invaluable as a reference when formula hacking):

0x00: 1 if the action hit, 0 if it missed
0x04: HP Damage
0x06: HP Healing
0x08: MP Damage
0x0A: MP Healing
0x25: Effect flags - these are used for displaying the projected damage
   0x01 = Status? (Status proc)
   0x10 = MP Healing
   0x20 = MP Damage
   0x40 = HP Healing
   0x80 = HP Damage
These values are what we'll manipulate in the formulas.

Example formula : 44 (Difference)

So the Byblos has an ability, Difference, that deals damage equal to the MP of the target. Here's the formula, on which I've commented every line in a literal fashion:

lui r2,0x8019         #    r2 = 0x80190000
lw r2,0x2d98(r2)      #   r2 = Memory value at 0x80192d98
lui r3,0x8019         #   r3 = 0x80190000
lw r3,0x2d90(r3)      #   r3 = Memory value at 0x80192d90
lhu r4,0x002c(r2)      #   r4 = Memory at r2 + 0x002c
ori r2,r0,0x0080      #   r2 = 0x80 (r2 = r0 | 0x80)
sb r2,0x0025(r3)      #   Memory at r3 + 0x0025 = r2
jr r31               #   (Return, but run next command first)
sh r4,0x0004(r3)      #   Memory at r3 + 0x0004 = r4

So that's the technical idea of what's happening. But what significance does it have?

lui r2,0x8019
lw r2,0x2d98(r2)   

This is a very common pattern to load in a value from a memory address to a register. The first command loads the upper half of the memory address into a register, then a value is loaded in at that location + the lower half of the memory address as the offset. Note that r2's value is not ready until two commands after this one due to load delay.

lw syntax looks like this: lw (destination_register), (offset)(source_register), where you're loading memory into the destination register at (value of source register) + (offset).

What's the significance of loading this into r2?

r2 = Target unit (See memory addresses I listed at the start of the section)

lui r3,0x8019
lw r3,0x2d90(r3)

The same pattern.

r3 = Action

lhu r4,0x002c(r2)

Here, r4 = Memory value at r2 + 0x002c. We know r2 is the target unit, so what is offset 0x2c for a unit? See the formula hacking page for the unit offsets.

r4 = Target unit's MP

ori r2,r0,0x0080
sb r2,0x0025(r3)

The first command is a way to set r2 = 0x80. Using a command of the pattern ori (destination_register),r0,(value) is a way to set (destination_register) = (value), since a bitwise OR operation on zero and a value results in the value. You could also use addi to do this (0 + value = value).

The second command says to save r2 to the memory location at r3 + 0x25. Why might we do that?

Action type = HP Damage. (Check the top of the section where I listed out the action offsets)

jr r31
sh r4,0x0004(r3)

jr is a command that will jump to the address in a register; r31 is the return address. This is a return statement, where we return to the code that called this formula... but yet, we have a statement under it that needs to be run. This is because jump/branch statements are subject to branch delay, which means that the statement after them is run before the jump truly occurs.

sh is like sb, the only difference being that we save out a 2 byte value instead of a 1 byte value.

Action HP Damage = Target unit's MP

lui r2,0x8019         
lw r2,0x2d98(r2)      #   r2 = Target
lui r3,0x8019         
lw r3,0x2d90(r3)      #   r3 = Action
lhu r4,0x002c(r2)      #   r4 = Target MP
ori r2,r0,0x0080      
sb r2,0x0025(r3)      #   Action type = HP Damage
jr r31                  
sh r4,0x0004(r3)      #   Action HP Damage = Target MP

The comments here are more minimal, but it's a lot easier to understand what's going on here.

So... what if we changed it so that r4 loads in the target HP? So, change:

lhu r4,0x002c(r2)

to load in HP instead of MP. (look up the unit offset and replace it)

lhu r4,0x0028(r2)

Putting that into MassHexASM we get a little-endian encoded value of 28004494. That's at RAM address 0x186e64, which is in BATTLE.BIN at 0x11fe64 (subtract 0x67000 to get a BATTLE.BIN address from an appropriate RAM address). We can use FFTorgASM to patch the ISO using a patch like this:

  <Patch name="Formula 44: Use HP instead of MP">
    <Description>Formula 44: Use HP instead of MP</Description>
    <Location file="BATTLE_BIN" offset="11FE64">
  Note we just specified the file, offset, and changed code.

And if I use FFTPatcher to change Ramza's Wish to use formula 0x44...

This is certainly not overpowered at all.

Next time we'll fiddle with formulas some more.
This is my miscellaneous ASM thread, for assorted ASM hacks I've done that don't have a thread, or are scattered throughout miscellaneous threads. Basically I'm starting this thread at Pride's suggestion, as sometimes I'll write some ASM in some other section and it will get buried somewhere in a thread that's hard to find. Also, so I have a place to post some things I've done that don't fit anywhere else.

The latest version of my .XML also comes with (and requires) the latest FFTPatcher suite 0.493, currently available here.


MassHexASM - For encoding ASM commands into PSX-ready binary commands (with the little-endian option), or decoding them from binary commands to ASM instructions. This way you don't have to manually switch byte order like you did with Renegade64. Ideally, the output can just be pasted into a patch file.
Thread: http://ffhacktics.com/smf/index.php?topic=7130.0

LEDecoder - Disassemble binary files into ASM commands, with an option for using little endian byte order. I couldn't find any MIPS disassemblers out there that would let you specify little endian, so I wrote this. (I used this program to disassemble some of the files in the EFFECT subdirectory of the ISO and found ASM commands in there...)
Thread: http://ffhacktics.com/smf/index.php?topic=7758.0

Spreadsheet ASM Hacks

UWEntries - Change which base jobs use which UNIT.BIN and WLDFACE.BIN entries. For changing special characters, not generics.
Thread: http://ffhacktics.com/smf/index.php?topic=7833.0

Gear Level ASM Hacks

Gear Level bugfix - Fixes a bug in FFT where the random gear routine could select extremely low level equipment in certain situations (e.g. Linen Robe in Chapter 4).
Post: http://ffhacktics.com/smf/index.php?topic=6971.msg142849#msg142849

Gear level based on Shop Availability (Story Progression) with override (0x16b)
This is the standard gear level patch (based on shop availability) with an FFTPatcher override.  My latest version.

Gear level based on Shop Availability (Story Progression) - Picks random equipment based on what's currently in the shops instead of the levels of the units. Allows enemy levels to scale with you without their equipment scaling.
Post: http://ffhacktics.com/smf/index.php?topic=6971.msg146276#msg146276

Other ASM Hacks

All formulas apply elemental (v2)
All formulas apply elemental.
Affects HP Damage, MP Damage, HP Healing, and MP Healing.

Ability element takes precedent over weapon element, and will be used unless
the ability's "Weapon Strike" and flag under "Hit Allies" are both checked, in which
case the Ability and Weapon elements will be combined.

Optional: You can specify a multiplier to be applied to damage and/or a separate multiplier for healing. 
The format for the multipliers is 16.16 fixed point (e.g. 1.25 = 00014000).
A multiplier of zero (default) will be ignored.

Swordskill strengthen (v2) - Strengthen should work with ability element for Holy/Dark sword; Dark Sword gains strengthen, status proc, (WP+Y)
This version has a variable flag to decide whether to use PA or MA.

Defense (Equipment grants damage reduction) (v2)
In the item data, this patch interprets Unknown 1 as Physical damage reduction (percentage) and Unknown 2 as Magical damage reduction (percentage).
The damage reduction percentage for each piece of equipment is added to determine the total damage reduction.
An ability's classification as physical or magical is determined by the 'Physical Attack' and 'Magical Attack' AI flags for the ability.
If an ability is flagged with both (this shouldn't happen, really), both damage reduction percentages (physical AND magical) will be applied
(the sum of the two)!

Crystals - Learn from any base job
This hack's for learning from crystals where the enemy base job is different from yours. For example, Night Sword from Gafgarion. You'll need to put the ability to learn in the same slot as the skillset you're learning into. Since this one did appear in another thread, there's more info on it here.

Hide excessive HP/MP recovery
Excessive HP/MP recovery is not displayed.
Post: http://ffhacktics.com/smf/index.php?topic=10535.msg203041#msg203041

Signed Ability Y Values
This allows you to use negative numbers for Y values in formulas. Any Y > 127 is considered as the negative number Y - 256 (e.g. 253 becomes -3). The source for this one is just changing lbu to lb where this particular address (0x801938fa) is read.

Target's Faith in Spell damage calculations becomes XX%

[Half of MP] becomes [No MP Cost]
Might be useful as an enemy-only ability for bosses, to use MP-heavy abilities without depleting their MP.

Require Materia Blade -> Require Item Type X (Default Lance)

Formula Hacks

Formula 3B: Mana Burn/Feedback (v2)
Version 2 of the mana burn hack, which caps damage at MA * Y.  Damages MP and deals equivalent HP damage.

Formula 11: Damage = MA * (WP+Y)

Elemental cancel: Formulas 17,3e,44
This makes formulas [17], [3e], and [44] subject to elemental cancel. Useful if you don't want bosses to be affected.

Formula 54 becomes HealMP_(Y%)

Formula 44: Use HP instead of MP

Fix Reraise Graphic Width
The graphic width for the Reraise status text is slightly too short; this patch fixes that.

Start button skips events, effects, battle and event text, etc.
Thread: http://ffhacktics.com/smf/index.php?topic=11608.0

Speed shortens ability CT
This one uses a curve where CT is affected more at lower speeds, and the effect depends on the CT factor you choose (default 5).  With CT Factor = 5, it begins taking effect at Speed = 5 and ability CT halves by Speed = 10, and would quarter by Speed = 20. 
Basically, for any ability, the CT is affected by a multiplier where ((CT Multiplier) = (CT Factor) / Speed).

Geomancy single ability
Creates a single ability for Geomancy that changes its properties based on the tile the caster is standing on.
Should be used inside a Default skillset.  Ability ID can be specified; default is 0x7E (Pitfall)

JP scroll glitch actual fix (Disable paging on confirm menu)
This patch disables paging up or down while the confirm menu is up on the ability learn screen.  As a result, there is no way to do the JP scroll glitch.

Unit slots backfilled when unit is removed from party
Removing a unit from the party no longer leaves a gap in the party!

Speed up text crawls
Speeds up text crawls significantly.
Text delays longer than a certain amount are changed to that amount.  Default is 2.  Lower is faster.

Switch unit number with L1 and R1 buttons (formation)
Switch unit number with L1 and R1 buttons in the formation screen.  Changes unit order as appropriate.  L1 and R1 still work normally (switching the selected unit) in menus.

Press buttons to win!
Press a button combination (default L1 + Select) to win the current battle.  It needs to be done while the game is processing the battle event conditionals, so between turns, or between a unit moving and acting, or somesuch.  Works even in Bethla Sluice.

Load Formation ignored if unit not in party
Doesn't try to load a unit from the party from the ENTD (Load Formation flag set) if they're not in the party, and instead uses whatever data is in the ENTD for that unit.  You won't normally encounter this scenario, but if it were to happen in the normal game, it would create a messed up unit that would overwrite Ramza when the battle/event was over.

Break out of multi-battle sequences
Break out of multi-battle sequences by pressing a button combination directly after exiting the formation screen.  Default is L1 + R1.
Includes the "Load Formation ignored if unit not in party" patch, because that situation can occur if breaking out of a sequence where a Guest has already left the party.

Equipment duplication glitch fixes
Fixes the equipment duplication glitches that can be triggered from using Best Fit in the shops.

Level down fix
Fixes leveling down to subtract raw stats based on old level instead of new level, so it should subtract the same amount gained in the previous level.  Also fixes level up abilities so that they add the same amount as leveling up via experience gain.

Crits always deal bonus damage
Crit XA bonus minimum is 1 instead of 0.  Maximum is still XA - 1.

Always crit
Always get a critical hit.  Can be useful if you're testing crits or somesuch.

Brave story can point to non-setup events

Enforce level caps
Varies by chapter, like in SCC challenges, but enforced by the game.

Change camera zoom values

Disable Now Loading Message
This hack disables the Now Loading routine entirely, saving the most SCUS space.

Now Loading Routine Rewrite
Rewrites the Now Loading routine to save space.  Includes variables to modify the color of the message.

Expand load and save data
Expands the load and save data to use all 64 blocks of the save file, instead of only 60.

Unit bench (10 extra unit slots)
Add the unit bench, which can hold 10 extra units.  Accessible from the formation screen, with Bench Unit and Unbench Unit menu items.  Units sent to the bench lose EXP and JP.  Joining units will join to the bench if party is full.

----- Latest -----

Jump single ability
Makes an ability function as Jump.  It should be placed in a normal skillset with Default action menu.  Recommended to use the default ability ID, 0x018A Level Jump2.
Range (Both level and vertical) are based on Lancer job level.

Skillset learned ability fix
When determining usable abilities in a skillset, finds the learned abilities based on the job instead of skillset ID.  Fixes an issue where changing the skillset for a generic job to an unused skillset resulted in those abilities all being auto-learned.

Skillset menu hardcoding fix
Skillset menu actions based on action menu instead of being hardcoded to skillset ID.  Fixes some issues resulting from changing action menus on generic skillsets.  Similar to (and mutually exclusive with) Generic Skillset Fix / Skillset Behaviors hack, but based on action menu.

Save between battles after formation screen
Allows saving between battles AFTER the formation screen. 
Loading a save made this way will still load the formation screen, but with the latest changes saved.
Saving before the formation screen still functions as normal.

Gained JP functions as if Gained JP UP was always active

Equippable R/S/M only limited by job innates

All (and only) player human units can find items on moving

Blade Grasp dodge percentage is (Brave * Multiplier) / 1024 and only affects certain weapons

Abandon multiplies evasion by 1024 / Divisor

Require Sword and Require Materia Blade check only first weapon

ENTD Job Level also applies to Job and secondary skillset job

Enhanced ENTD
ENTD can now specify extra parameters for units.  (See description in XML.  Can use extra ENTD slots to modify previous slots.)

Defense Up and Defend status multiply damage by Multiplier / 1024

Stat variation
Base stats for PA and HP vary based on Brave, while MA and MP vary based on Faith.  Starting job stat multipliers also cause further variation.

Base stat decrease
Base speed is 5 for humans and 4 for monsters (instead of 6 and 5, respectively).  Base PA and MA use values of 3.25 and 4 instead of 4 and 5.  Male and female soldier office recruits both have Broad Swords.
So a while back I wanted to be able to use jobs 3A and 3B for some characters, and not have to go through the process of using another character's job like Simon's, replacing 3A with Simon, replacing Simon with my character, making the ENTD edits, etc. Much easier if I can just use jobs 3A and 3B, and point them to unused entries in unit.bin and wldface.bin (Like 08 or 09).

In the help section I whipped up this patch, which is a quick and dirty way to get exactly that (Job 3A uses 08, Job 3B uses 09). 
(EDIT: Included the entry for the REQUIRE.OUT table.)

  <Patch name="Jobs [3A,3B] use unit/wldface [08,09]">
      Jobs [3A,3B] use unit/wldface [08,09] for formation screen and battle preparation sprites.
    <Location file="WORLD_WORLD_BIN" offset="458B0"> 
    <Location file="WORLD_WORLD_BIN" offset="74B88">
    <Location file="WORLD_WORLD_BIN" offset="AA1DC">
    <Location file="WORLD_WORLD_BIN" offset="AA92C">
    <Location file="WORLD_WORLD_BIN" offset="ADE6E">
    <Location file="EVENT_ATTACK_OUT" offset="16C24">
    <Location file="EVENT_REQUIRE_OUT" offset="11BB0">

But then we figured it'd be useful to be able to change these entries for any job. So we discussed making a spreadsheet for it. Raven was going to write one, but then it seems I pre-empted him.

This spreadsheet should allow you to change which entries in unit.bin and wldface.bin are used for each base job. So, if you want to make Gafgarion's guest job (0x17) look like Reis in the formation screen, job wheel, battle prepation screens, etc? Just change the his entry from 0D to 07. Want to overwrite unused entries in unit.bin/wldface.bin and tie them to specific base jobs? Sure.

Attached is the spreadsheet. Basically you just edit the yellow column, then copy the XML from the XML tab into a .XML file in the same directory as FFTorgASM and use it to apply the patch.

I labelled the base jobs in the spreadsheet with what they're used for in the game currently, but interestingly, I couldn't figure out what jobs 0x35 - 0x39 are actually used for, if anything. Are those all unused? That might open up some possibilites for adding more characters without having to take jobs that are in use.

EDIT (8/28/2018): Attaching new version of the spreadsheet!  Fixes a problem with default values, notably for Holy Dragon Reis.
There are a lot of program files in FFT. Why is anyone's guess, but for a long time I've thought it would be really useful to be able to scan various files for program code (ASM). The problem is, the MIPS disassemblers you find online assume big endian byte ordering.

Enter LEDecoder, my newest program, that will decode files with a little endian option. You just select the input/output files and have it do its thing. This'll let us find the ASM in any FFT file. The output is very similar to what you'd see in the disassembly portion of the pSX debugger.

LEDecoder basically means 'little endian decoder', but of course, I couldn't call it LEDecoder without including an LED. A software LED, perhaps. ...Well, okay, maybe it's just a filled in circle. But anyway, it indicates the status of the program by its color. Most of the time it'll go orange and then green quite quickly when it's told to process. It's fine to run another decode as long as the LED isn't orange.

    Blue = Ready (Program start)
    Red = Decode failed (Usually if the input file/path is invalid)
    Green = Decode succeeded!
    Orange = Processing

So, what would be an interesting target file to run the decoder on? Say... perhaps an effect (animation) file?
...Yup, they've got ASM in them. Might explain why some work one way and others work another regardless of the graphics/palette/what have you!

EDIT: Version 2 added to fix some problems with the decoder... It didn't recognize srav, jalr, or break. This uses the MassHexASM decoder, so anytime I update that, I have to update this, basically. New version is attached.

EDIT: Version 3 added:
     * Fixed a problem where the program counter was incorrect, resulting in incorrect hex for branch instructions (thanks fdc)
     * Used a save file dialog for the output file, so now you should be able to just type in output filenames.

EDIT: Version 4 added:
     * When decoding, immediates will now properly show as signed or unsigned based on the instruction. These are the affected commands:
          Unsigned: andi, ori, xori, sltiu
          Signed: addi, addiu, slti

EDIT: Version 5 added: Check the MassHexASM thread's original post for details.  This now uses the same engine as MassHexASM.  (A shared DLL, not just copied functions between binaries!)

EDIT 1/11/2017: LEDecoder has now been rolled into MassHexASM.  The newest version is available in that thread.
Latest version now included with FFTPatcher suite! (Thread: https://ffhacktics.com/smf/index.php?topic=7163)


After being frustrated with manually switching ASM byte order, I've decided to write MassHexASM, a program that lets you encode directly to little endian, and furthermore, should be able to encode ASM in a format where you just copy/paste the output to an XML patch (or what have you).

This program was written in C#/.NET, so you'll need some version of the .NET framework to get this to run. I believe it should work with .NET version 2.0 or later. Other than that, should be all good. Let me know if you find bugs and I'll do what I can to fix 'em.

Note on using the program: If you define a label, you need to put a colon ( : ) after it. If there is a command on the same line, separate it from the label by at least one space.

Some differences between MassHexASM and the Renegade64 assembler:
     * The little endian format uses the correct byte order: B4,B3,B2,B1. It also outputs the hex directly instead of trying to format for Gameshark.
     * My program encodes BEQ and BNE instructions properly. Renegade64 switches the order of the registers for some reason, which it can get away with because they're just used for an equality check, but it is still weird.
     * Encoding and decoding in the same form with little endian format. In testing, I've looked in the various XML files used by FFTorgASM, pasted patches straight into my program, entered a starting address, specified little endian and decoded it. Useful if you've got encoded hex but can't remember what the ASM is.
     * Ability to specify a starting address without using .org (Important for decoding as well)
     * You can pad the output with spaces if you want to preserve formatting when pasting into an FFTorgASM patch.
     * You should be able to use same-line labels. (i.e. put a label and the next instruction on the same line)
     * Registers can use the $[num] format. (e.g. $4 instead of r4). You can still use names like $s0, $t2, etc.
     * You can specify negatives as offsets in save/load instructions (i.e. -0x7fad, as the screenshot shows). The negative sign has to be before the 0x, not after.
     * Encodes statements without regard to whether or not they make sense. This means Renegade64 will stop you in certain cases where my program won't, primarily when BEQ/BNE addresses are invalid.

Java version now available... check the attachment list. To use it, run "java -jar JMassHexASM.jar" in the command line in the directory that contains the jar file. (Updated: I fixed a bug with the Java version)


      * LEDecoder has been added to MassHexASM, accessed via a menu item (Form -> LEDecoder), and LEDecoder is no longer a standalone program.
      * MassHexASM now checks the ASM upon encoding or decoding and displays warnings for various possible hazards, listed below.
      * MassHexASM uses the latest version of the ASM Encoder/Decoder, which includes numerous fixes, as well as the latest changes:

     * Added check functionality that warns about various possible hazards:
        * Load delay (Attempt to use a loaded value from memory immediately after loading it; ignored in PSP mode)
        * Unaligned offset (When doing a memory load or store)
        * Using mult or div within 2 commands of mflo/mfhi
        * Adding or subtracting a value from the stack pointer register ($sp, r29) that is not a multiple of 8
        * Putting a branch inside a branch delay slot
    * Added the ability to use %hi(address) and %lo(address) to get the high and low 16-bit parts of an address.  The low 16-bit portion is considered to be signed.  e.g.:
        * .label @address_current_action, 0x80192d90
        * # (...)
        * lui   t0, %hi(@address_current_action)
        * # (...)
        * lw    t0, %lo(@address_current_action)(t0)
        * Note that %hi(address) will be one higher than first 16 bits when the low 16 bit value is negative.
        *   e.g. %hi(0x8018f5f0) = 0x8019; %lo(0x8018f5f0) = -0x0a10 (0xf5f0).
        * This means that the %hi,%lo combination works well with load and store commands, and also with the (lui, addiu) pattern, but not with the (lui, ori) pattern.
    * Fixed some issues with the load and store psuedoinstructions when using values between 0x8000 and 0x10000.
    * Modified the error checking functionality to report more errors, particularly when an undefined label is referenced.
    * Now recognizes GTE and COP0 register names as used by pSX's disassembly.
    * Added beqz (branch if equal to zero) and bnez (branch if not equal to zero) psuedoinstructions.
    * Fixed potential issues with byte order that could have occurred if running on a big endian machine.
    * Added "replaceLabels" optional attribute to the <Location> tag in XML patches, which, if set to "true", will replace any labels found in the hex with the correct hex address.  The idea here is basically to be able to create dispatch tables using a list of labels if so desired.
    * (Undocumented from last release)
    * Added "equivalences" which function similar to macros. Defined with .eqv and are replaced with the specified value before encoding.  e.g.:
        * .label @address_controller_input, 0x80045944
        * .eqv   %start_button_flag, 0x0800
        * # (...)
        * lw     t0, @address_controller_input
        * nop
        * andi   t0, t0, %start_button_flag
        * # (...)

    * The "li" pseudoinstruction now accepts negative values correctly.


     * Changed the window anchoring so that resizing the window causes the ASM and/or Hex textboxes to expand.  Expanding the window to the right now causes the ASM textbox to expand (as well as the messages window); expanding downward causes both ASM and Hex to expand.
     * Any label starting with the '@' character is now a "persistent label" that will stay around even if not defined in the current code block.
     * Added ".label" directive to define label addresses without putting them inside the code.  The format is: .label (name), (address).  Example: .label  @clear_data_block, 0x05e644
     * Decoding no longer requires newlines; you should just be able to paste straight from a hex editor and it will decode every 4 bytes (8 hex digits) as a command. 
     * Fixed some bugs relating to labels that contained a command on the same line.
     * The encode/decode textboxes are no longer limited to 32767 characters.



     * Both textboxes are now using a fixed-size font (Courier New) at a bigger size. Widened the ASM textbox to accommodate the bigger font.
     * Error reporting (in the "Messages" area) should now report more errors and be more specific.  You should at least get a warning if it skips a line entirely (e.g. an unrecognized command when encoding).  Decoding will also report unknown commands.


     * Can now specify starting address.

     Both (MassHexASM and LEDecoder)

     * "Mode" dropdown added to select between PSX/PSP modes (as well as a generic Base mode).  This will primarily affect coprocessor 2 instructions (GTE, VFPU) which differ between the two.  This won't restrict the instruction set completely to ones supported by the CPU.
     * Added support for many new MIPS instructions up to the MIPS32R2 instruction set.
     * PSX mode should contain GTE instructions. Format is slightly different from psxfin's disassembly. GTE registers (both data and control registers) can be referred to simply using the generic format $(num), which works with all register types.
     * Coprocessor 0 registers can also be referred to using $(num).
     * PSP mode should contain the VFPU commands as well as other PSP-specific commands (bitrev, max, min, etc).
     * With the new instructions recognized, there should be no more unknowns in PSX/PSP code!
     * MassHexASM and LEDecoder now both use the same encoding/decoding engine (as well as FFTorgASM, upon release of the next FFTP version).
     * You should be able to specify addresses >= 0x10000000 (either with the starting address textbox, or a .org statement).
     * Encoding/decoding uses an entirely new process and should be significantly faster (In my testing, a decoding of the PSP's EBOOT.BIN going from about 10s to < 3s on LEDecoder). You'll probably only notice the speed if encoding/decoding a LOT (thousands) of commands at once.
     * The input file formats have changed significantly with a lot more options/flexibility. I can give more detail if needed, though it's somewhat complicated...
     * Decoding a line can produce "illegal" if the decoder found the instruction and determined that the inputs are illegal/impossible (typically this can only happen with ins/ext instructions). An unrecognized encoding is still "unknown".  If the program hits an unexpected error trying to decode a line, it can produce "error", but this shouldn't happen if the program is working properly.
     * You can use ';' to denote comments, just like '#'.
     * Syscall and break should now properly use 20-bit immediates.
     * Decoding is less lenient; invalid code will produce more unknowns.

     * Fixed a bug where load/store instructions with negative offsets were encoded with incorrect hex (and were treated incorrectly as pseudoinstructions).

     * Fixed a bug where newlines in the assembly section were erroneously removed when encoding.
     * Added a "name registers" option that uses register names like r2 = v0, r4 = a0, r29 = sp, r31 = ra, etc, when decoding.

     * MassHexASM can now support many psuedoinstructons even if they take multiple lines. The list of psuedoinstructions here should mostly be supported. You can also do fun things like lw r2,0x80192d90 and lbu r3,0x801908cc(r4), although these commands always generate the entire address, so this will be less efficient in cases where you can reuse part of the address. Those two statements would become 5 lines of regular ASM, but this example can be done in 4. As a sidenote, these pseudoinstruction patterns are very apparent in FFT's ASM code.

     * When decoding, immediates will now properly show as signed or unsigned based on the instruction. These are the affected commands:
         Unsigned: andi, ori, xori, sltiu
         Signed: addi, addiu, slti

     * Added a "Show Addresses" option which will show the appropriate addresses on encode/decode in the left textbox.

     * Added support for 'select all' using ctrl+A.
     * Little Endian is now the default byte order.

     * Added support for srav, jalr, break (10-bit immediate), syscall (10-bit immediate) and trap (26-bit immediate) commands.
     * Fixed a bug where label addresses could be calculated incorrectly when the input contained instructions on the same lines as labels, resulting in incorrect hex for branch/jump instructions.
     * Fixed a bug where an instruction on the same line as a label could be parsed incorrectly.

     * Fixed a bug with encoding long immediates (such as in the j command) when all eight hex digits were not specified.

     * Fixed a bug where the program failed to encode certain I-type instructions (bgez,blez,bgtz,bltz,bgezal,bltzal).
     * Fixed a bug where the program could incorrectly decode certain I-type instructions (bgez,blez,bgtz,bltz,bgezal,bltzal).
     * Added support for mthi/mtlo instructions.

     * Added support for comments on the same line as commands. (They'll need to preceded by #)
     * Fixed a bug where labels would only be recognized if they were from earlier points in the code.
     * Fixed a bug involving placement of tab characters that could cause the program not to recognize lines of code.
     * Added support for nor/not instructions.

     * Can now decode to negative offsets in the appropriate cases for addi and similar instructions.

EDIT: I'm putting the Gear Level ASM patches here for convenience. Also, I'm attaching them as an XML at the bottom of this post.

  <Patch name="Random unit gear based on story progression">
      Random unit gear based on story progression
    <Location file="BATTLE_BIN" offset="F59BC"> 
    <Location file="SCUS_942_21" offset="4D49C">
    <Location file="SCUS_942_21" offset="4D52C">
    <Location file="SCUS_942_21" offset="4D5D8">

  <Patch name="Random unit equipment more selective">
   Random unit equipment will now be more selective
        of secondary item types. (You shouldn't see
        knights in Chapter 2 using linen robes)
    <Location file="SCUS_942_21" offset="4D53C">
    <Location file="SCUS_942_21" offset="4D5F0">
    <Location file="SCUS_942_21" offset="4D628">


Recently I've been thinking about an idea for enemy unit levels scaling with the party and how that affects random equipment. By default, that means enemy gear will scale up with the player's level, meaning that after a certain point, the player is going to be outmatched by equipment he can't obtain except through stealing. On the other side of the coin, if the player manages to level very little and/or use degenerators, he can keep the shop equipment ahead of the enemy equipment and gain an advantage that way. My objective: fix the gear of enemies at a certain tier, depending on the battle... without fixing the equipment itself.

Using the pSX debugger, I was able to find where random equipment was being determined. It happens while you're placing your units before the battle, and there is a subroutine at 0x05cc98 in RAM (0x04d498 in SCUS_942.21) that chooses random equipment. It appears to loop through a list of items and check them against the current unit's (r4,r13) level based on the given equipment type (r5). The unit offset relating to the current unit is loaded into the register r13, and sure enough, we find this:

0x0005cd2c: lbu r14,0x0022(r13)

Loading the current unit's level into a register (r14)! r14 is then used for a check in the loop, so this is the number being used to determine the quality of enemy equipment.

My first thought was that this could be replaced with something relating to the shop availability for items, using some kind of multiplier. ([Shop availability variable] * 2? 3? 2.5?) Once again, I used the pSX debugger and went into a shop, to trace to where a check is made against this number. At first, I find this:

0x001251c0: lw r8,0x0028(r29)

I'm testing at the start of chapter 4, and r8 here is loaded with 0x0d (13). That would be... correct! That's the number it'd check against the item's shop availability, and I can see it doing this in the code:

0x001251bc: lbu r2,0x2ec2(r1)     Load in the item's shop availability number... (pSX debugger breakpoint)
0x001251c0: lw r8,0x0028(r29)               Load the player's shop availability (based on story progression) from the stack
0x001251c4: nop
0x001251c8: slt r2,r8,r2     If the player's number is less than the item's, the item is too high quality, so...
0x001251cc: bne r2,r0,0x00125238            ...skip adding it to the shop item list for purchase.

But, well, we loaded r8's value (the player's number) from the stack somewhere, it looks like. How did it get there?

0x00125128: jal 0x000ef1a8
0x00125134: sw r2,0x0028(r29)

r2 changed to the correct value (0x0d) after the call to subroutine 0x0ef1a8.

At least under some circumstances, this subroutine at 0x0ef1a8 seems to load r2 with the variable I wanted... but unfortunately, we're in a shop, so probably we're in WORLD.BIN. When I tried to call this subroutine in a battle, the memory at 0x0ef1a8 looked completely different... the subroutine wasn't even loaded into memory anymore! (Leading to fantastic results). Besides, I wasn't sure of the circumstances under which that subroutine would return the storyline variable (it uses a billion registers and is also recursive, so it's a barrel of fun to try to trace through).

So my next thought... forgetting subroutine 0x0ef1a8... was I overcomplicating things? We already have an offset to the current unit in subroutine 0x05cc98 (where the random equipment is loaded). So what other offsets can I use? Is there some other property of the current unit I can use to check for what the equipment quality should be? Looking at the formula hacking reference, there's basically nothing. I can't use HP, MP, Brave, Faith, etc. So what to do?

Venture into the unknown. In the ENTD in FFTPatcher, there are unknown fields for each unit, in that little box down in the bottom left. Some of them seem to be AI flags, but what of the one down in the very bottom left (last row, leftmost)? I couldn't find a single instance of it being anything other than zero (though I only checked the story battles). So if it's unused... why not use it? This number could function as a "give the enemy gear as if his level was X" variable, without actually changing the unit's level. I located it in RAM at unit offset 0x0169.

This is the number I'm talking about. In my patch, I've made Gafgarion here Party Level + 5, but his "gear level" is 0x12 = 18, which is his default level for this battle. (Gate of Lionel)

We could simply change the offset checked by subroutine 0x05cc98 to use 0x0169 instead of 0x0022 (level). While that's viable, we'd have to make sure we changed every single instance of this variable in the ENTD. What I really want is for it use 0x0169 unless it's zero, and otherwise use 0x0022.

Well...okay, I can write a block of ASM that does that. Here is the biggest oustanding problem with this whole thing though... where to put it? It just so happened that in my patch, I had ASM hacked formula 41 into something else, and I happened to have a bunch of nop's at the end of it. I used that and wrote my own subroutine at 0x0018a164. (I'll get to this shortly)

If we look at subroutine 0x05cc98, we have to rewrite a little bit to make our own subroutine call work. I want to make the call to my subroutine near the beginning of this one, but the return address wasn't saved yet. Originally, we have:

0x0005cc9c: 00806821 addu r13,r4,r0 r13 = r4 (reference to unit)
0x0005cca0: 30a500ff andi r5,r5,0xff r5 = (Lowest byte of r5)
0x0005cca4: 34020020 ori r2,r0,0x0020 r2 = 0x0020
0x0005cca8: afbf0094 sw r31,0x0094(r29)         (Save return address)

I changed mine to this:

0x0005cc9c: afbf0094 sw r31,0x0094(r29)         (Save return address)
0x0005cca0: 00806821 addu r13,r4,r0 r13 = r4 (reference to unit)
0x0005cca4: 0c062859 jal 0x0018a164 Call my subroutine!
0x0005cca8: 34020020 ori r2,r0,0x0020 r2 = 0x0020

I had to cut out the andi statement, but I execute that in my subroutine, as shown here:

0x0018a164: 91ae0169 lbu r14,0x0169(r13)      Load in unit offset 0x0169
0x0018a168: 140e0002 bne r14,r0,0x0018a174      If it's not zero, use it, so jump to the return statement
0x0018a16c: 30a500ff andi r5,r5,0xff      (Statement from calling subroutine that I cut and wanted to execute here)
0x0018a170: 91ae0022 lbu r14,0x0022(r13)      Otherwise, load in the unit's level, and
0x0018a174: 03e00008 jr r31      Return
0x0018a178: 00000000 nop

Now r14 (the number used to check quality of equipment) already has what I wanted in it. The only thing left to do is stop it from trying to load in the unit offset later in the subroutine. In other words, changing this:

0x0005cd2c: 91ae0022 lbu r14,0x0022(r13)

to this:

0x0005cd2c: 00000000 nop

And we've done it! Random enemy equipment would now load based on the "gear level" you give them at unit offset 0x0169, analogous to what their gear would be if they were at that level... without changing the actual level. Thus, the level can scale with the party, but the gear can remain fixed at a certain tier.

I implemented this in my own patch swimmingly enough (after lots of debugging, of course)! It's pretty cool, but making it into a general patch anyone can use presents some difficulties. The biggest problems at this point, as I see it, are:

(1) Where do I put the new subroutine? (I can't just assume everyone has a bunch of nop's at the end of their formula 41)
(2) What if unit offset 0x0169 is used by something and now it won't work the same way? (Doubtful, they're all zero, and my testing doesn't show any problems here)

Honestly, the simplest answer to (1) may be to just go with the first approach, just straight changing the instruction at 0x05cd2c to load offset 0x0169 instead of 0x0022, beause that would be a one-line ASM hack, and it would work... it's just that you'd have to change that 0x0169 number in the ENTD for every single unit that loads random equipment, or they'd have none at all (I presume). It's just that I'd prefer the (Use 0x0169 if it's there, otherwise the level) approach, but am not sure where I could put the subroutine that does that.

Thoughts? Concerns? Ideas on other ways to make this happen? Different offsets to use? Random comments?