Let's look into Module Fíles (.rel) - Defensive Collision is now!

Started by pikazz, May 04, 2012, 12:58:26 PM

Previous topic - Next topic

0 Members and 6 Guests are viewing this topic.

Quote from: KingJigglypuff on February 28, 2015, 09:59:53 PM

So how are Article Floating Points handled?

Depends really. From the limited research i've done, they are for the most part done through the relevant ParamAccessor object. For example, Falco's wnFalcoBlasterBulletParamAccesser object handles accessing the blaster bullet's floating points from memory.


Quote from: PhantomWings on February 28, 2015, 09:42:29 PM
~snip~

Haven't read it yet because im at work, but this looks juicy :)

oh god all that information on the articles! really awesome information so props to you! x3
I will take a closer look at it!

...tomorrow, its 3AM right now and tired as [censored]! couldnt even read what you wrote straight xD
Stupid Tinypic :C

Continuing,

The soGenerateArticleManageModule contains an instance pool that holds reserved memory for all the article instances allowed for that character.

Sorry but you are not allowed to view spoiler contents.


The regular soGenerateArticleManageModule with 3 aura spheres is on the left and our goal which has 8 aura spheres is to the right. To get from 3 to 8, we need to add space for 5 wnInstanceHolder<wnLucarioAuraBall> objects and 5 soInstancePoolSub<... wnLucarioAuraBall> objects.

Each soInstancePoolSub regardless of type will always be 0x4 bytes. The wnInstanceHolder objects vary in size, but you can determine their size by looking into the soGenerateArticleManageModule constructor:

Sorry but you are not allowed to view spoiler contents.


Here, we can observe 3 wnInstanceHolder<wnLucarioAuraBall> constructors calls into 0x84DC. We can subtract two of the offsets passed into the constructors to get the wnInstanceHolder<wnLucarioAuraBall> size.

instanceHolderSize = 0x2060 - 0x0C = 0x2054

From here, we can calculate the size increase for adding 5 aura spheres:

sizeIncrease = 5 * (instanceHolderSize + instancePoolSubSize) = 0xA1B8

In order to continue, we also need to determine the original size of the soGenerateArticleManageModule. Because we are moving it to a completely new location, we can't use the old allocated memory for it.

Determining the size of the soGenerateArticleManageModule can easily be done by subtracting the location of the following module - soEffectModule - from the location of the soGenerateArticleManageModule. Looking a bit further down from where the soGenerateArticleManageModule constructor is called:


Sorry but you are not allowed to view spoiler contents.


We find that the soEffectModule is constructed at [soInstanceManagerFixedSimple + 0xDC40]. This means the size of the soGenerateArticleManageModule is:

moduleSize = 0xDC40 - 0x5C24 = 0x801C

From here, we can calculate the new memory allocation size of our character.

newAllocSize = oldAllocSize + moduleSize + sizeIncrease = 0x2271C


We've increase the size of our character's memory space, and repositioned our soGenerateArticleManageModule into its new location, but we still need to add those 5 extra wnInstanceHolders and InstancePoolSubs. Let's look into the module soGenerateArticleManageModule constructor again.

Sorry but you are not allowed to view spoiler contents.


As it turns out, there's enough repeated code here to merit a loop. And with a loop, we can control however many iterations we want - be it 3 or 8. Doing a little bit of refactoring gets us:


s[1] 0x844C:

li r27, 0x00000000
addi r3, r28, 0x1E5C
mulli r4, r27, 0x2054
add r3, r3, r4
mr r4, r31
bl s[1] 0x84DC
addi r27, r27, 1
cmpwi r27, 8
blt+ -0x1C
lis r3, 0x0000
addi r3, r3, 0x0000
stw r3, 0(r28)



If there weren't enough space to add duplicate instance holders - or if we were adding a completely new article, then we would need to use another hook to do the additional work required. Neither case is very much of an issue.

As was the case when we moved the soGenerateArticleManageModule, if we update the constructors, we also need to update the destructors. In this case we need to update the offsets inside each soInstancePoolSub<... wnLucarioAuraBall>Method[0][0] to reflect the new offsets. There's a bit more to it than that, but I'll save the details on it for later.

Now that we have 8 instance holders to store our 8 Aura Spheres, we need to change the character's ftArticleMediator to actually allow us to make those 8. Before continuing, let's take a look at the OpenSA documentation on ftArticleMediatorImpl

http://opensa.dantarion.com/wiki/SoArticleMediatorImpl

There are 4 methods in this class that need to be changed: GetInstanceCount, GetInstanceCap, and the two versions of ClearInstances. These are just numeric caps on the number of instances for the aura ball type that we need to change from 3 to 8.

s[1] 0xAD24 cmpwi r31, 8   // ClearInstances
s[1] 0xAF84 cmpwi r31, 8      // GetInstanceCount
s[1] 0xB188 li r3, 8         // GetInstanceCap
s[1] 0xB534 cmpwi r31, 8   // ClearInstances (All)

Finally, we need to change the instantiation function itself that actually puts an instance of the article into the instance holder. This is the GenerateArticle method of the ftArticleMediator. Changing it it more or less the same process as how we changed the construction process for the instance holders - that is, we replace hard coded references to each instance holder with a loop that iterates over them.



And that's about it. I ged over a bit more than I would have liked, and I think I may have missed a few things all together, but I'll leave a file here with all the changes I made to the Lucario module.

Changelist

I've also made a near complete memory map of ftLucario in-game. It may come in handy for future references:


============== ftLucario ===================
POS: 0x0000
SIZE: 0x10548

0x00000 p_nodeName // "LUCARIO"
0x00004 Prev Node
0x00008 Next Node
0x0000C Prev Node
0x00010 Next Node
0x00014 Prev Node
0x00018 Next Node
0x0003C p_classMTable    // ftLucario
0x00040 p_ancestorMTable
0x00048 p_ancestorMTable
0x00054 p_ancestorMTable
0x00060 p_soModuleAccessor
0x00064 p_ancestorMTable
0x00070 p_ancestorMTable
0x0007C p_ancestorMTable
0x00088 p_ancestorMTable
0x00094 p_ancestorMTable
0x000A0 p_ancestorMTable
0x000AC p_ancestorMTable
0x000B8 p_ancestorMTable
0x000C4 p_ancestorMTable
0x000D0 p_ancestorMTable
0x000DC p_ancestorMTable
0x000E8 p_ancestorMTable
0x000F4 p_ancestorMTable
0x00100 p_ancestorMTable
0x00110 Fighter ID
0x0013C ftOutsideEventPresenter

0x00194 INSTANCE MANAGER DATA

0x0FF98 ftCancelModuleImpl
0x0FFD4 ftVirtualNodeMatrixPoolImpl

0x1048C GIMMICK DATA

0x10524 ?
0x10528 0x00000001
0x1052C ?
0x10530 0x00000001
0x10534 soArrayContractibleTable<const soStatusData>
0x10544 ?

===== INSTANCE MANAGER DATA ======
POS:  0x0194
SIZE: 0xFE04

0x0000 soInstanceManagerFixedSimple

0x0014 soArrayVector<soInstanceUnit<soEventUnit *>, 19>
0x0020 soInstanceUnit<...> [00]
0x0028 soInstanceUnit<...> [01]
...
0x00A8 soInstanceUnit<...> [17]
0x00B0 soInstanceUnit<...> [18]

0x00B8 soEventManageModuleImpl
0x09D0 soModuleAccessor
0x0AB0 soHeapModuleImpl
0x0AC8 ftLucarioParamCustomizeModule
0x115C soResourceModuleImpl
0x1180 soModelModuleImpl
0x1440 soMotionModuleImpl
0x17D4 soPostureModuleImpl
0x1888 soGroundModuleImpl
0x1930 soSituationModuleImpl
0x196C soTeamModuleImpl
0x19E0 soCollisionAttackModuleImpl
0x1FFC ===== [BASE] soCollisionAttackModuleImpl
0x209C soCollisionHitModuleImpl
0x2990 ===== [BASE]soCollisionHitModuleImpl
0x29F8 soCollisionShieldModuleImpl
0x2D4C ===== [BASE]soCollisionShieldModuleImpl
0x2DA0 soCollisionShieldModuleImpl
0x37B4 ===== [BASE]soCollisionShieldModuleImpl
0x380C soCollisionCatchModuleImpl
0x3988 ===== [BASE]soCollisionCatchModuleImpl
0x3A70 soDamageModuleActor
0x3B1C ===== [BASE]soDamageModuleActor
0x3C20 soCatchModuleImpl
0x3C84 soCaptureModuleImpl
0x3CB8 ftStopModuleImpl
0x3CDC soTurnModuleImpl
0x3D14 soShakeModuleImpl
0x3D90 ===== [BASE]soShakeModuleImpl
0x3DC0 ===== [BASE]soSoundModuleImpl
0x3E1C soLinkModuleImpl
0x3F60 ===== [BASE]soLinkModuleImpl
0x3FB4 soVisibilityModuleImpl
0x3FE4 ftControllerModuleImpl
0x459C ===== [BASE]ftControllerModuleImpl
0x4708 soCameraModuleImpl
0x4758 ===== [BASE]soCameraModuleSimple
0x477C soWorkManageModuleImpl
0x47B0 soAnimCmdModuleImpl
0x48A4 soStatusModuleImpl
0x5688 ===== [BASE]soStatusModuleImpl
0x5738 soKineticModuleGenericImpl
0x5C00 ===== [BASE]soGeneralWorkSimple
0x5C24 soGenerateArticleManageModuleImpl
0xDC04 ===== [BASE]soGenerateArticleManageModuleImpl
0xDC40 soEffectModuleImpl
0xDCAC ===== [BASE]soEffectModuleImpl
0xDDE4 ftComboModuleImpl
0xDE14 ftAreaModuleImpl
0xDE24 ===== [BASE]ftAreaModuleImpl
0xE188 soPhysicsModuleImpl
0xE204 ===== [BASE]soPhysicsModuleImpl
0xE24C soSlopeModuleImpl
0xE2CC soShadowModuleImpl
0xE314 soItemManageModuleImpl
0xE3B8 ===== [BASE]nhsoItemManageModuleImpl
0xE424 soColorBlendModuleImpl
0xE578 soJostleModuleImpl
0xE5C4 ftAbnormalModuleImpl
0xE62C soSlowModuleImpl
0xE668 ftGlowModuleImpl

0xE7E8 soArrayContractibleTable<const soStatusData>
0xE7F8 ANIM CMD DATA

============== ANIM CMD DATA =============
POS:  0xE7F8
SIZE: 0x160C

0x0000 soArrayVector<const acAnimCmdConv *, 293>
0x000C acAnimCmdConv[000]
0x0010 acAnimCmdConv[001]
...
0x049C acAnimCmdConv[292]

0x04A0 soArrayVector<const acAnimCmdConv *, 293>
0x04AC acAnimCmdConv[000]
0x04B0 acAnimCmdConv[001]
...
0x093C acAnimCmdConv[292]

0x0944 soArrayVector<acCmdInterpreterStackData, 8>
0x0950 acCmdInterpreterStackData [0]
0x0964 acCmdInterpreterStackData [1]
...
0x09DC acCmdInterpreterStackData [7]

0x09F0 soAnimCmdAddressPackArraySeparate
0x0A0C soAnimCmdInterpreter
0x0A5C soArrayContractibleTable<const acAnimCmdConv *>
0x0B88 soArrayContractibleTable<const acAnimCmdConv *>
0x0CB4 soArrayContractibleTable<const acAnimCmdConv *>
0x0DE0 soArrayContractibleTable<const acAnimCmdConv *>
0x0F0C soArrayContractibleTable<const acAnimCmdConv *>
0x1038 soArrayContractibleTable<const acAnimCmdConv *>
0x1164 soArrayContractibleTable<const acAnimCmdConv *>
0x1290 soArrayContractibleTable<const acAnimCmdConv *>

0x13BC soAnimCmdInterpreter
0x14E0 soArrayContractibleTable<const acAnimCmdConv *>


========= GIMMICK DATA =========
POS:  0x1048C
SIZE: 0x00098

0x00 ftStatusGimmickUniqProcessPoolImpl
0x08 ftStatusUniqProcessGimmickBarrel
0x20 ftStatusUniqProcessGimmickDoor
0x34 ftStatusUniqProcessGimmickCatapult
0x48 ftStatusUniqProcessGimmickLadder
0x5C ftStatusUniqProcessGimmickSpring
0x70 ftStatusUniqProcessGimmickTruck
0x84 ftStatusUniqProcessGimmickEaten



========= ARTICLE MEDIATOR MODULE ========
POS:  0x5C24
SIZE: 0x801C
============
0x0000: soArrayVector<soArticle *, 5>
0x0020: soArrayVector<soArticleEventObserver *, 5>

0x007C: soArticleMediator<...> T1
0x0080: soArticleMediator<...> T2

0x0084: == INSTANCE POOL ===
0x0084: == LINE HIERARCHY ==
0x0084: Decl

0x0088: soInstancePoolSub<...>
0x008C: x
0x0090: == INSTANCE HOLDER DATA == [0x1E40]
0x0090: soInstanceHolder<...>.decl
0x0094: ? (0x8508)

0x1ED0: soInstancePoolSub<..., 3>
0x1ED4: soInstancePoolSub<..., 2>
0x1ED8: soInstancePoolSub<..., 1>
0x1EDC: x
0x1EE0: == INSTANCE HOLDER DATA == [0x2054]
0x1EE4: soInstanceHolder<...>.decl

0x3F34: == INSTANCE HOLDER DATA == [0x2054]
0x3F38: soInstanceHolder<...>.decl

0x5F88: == INSTANCE HOLDER DATA == [0x2054]
0x5F8C: soInstanceHolder<...>.decl

0x7FDC: byte Flag (END OF soArticleMediator)

0x7FE0: == ARTICLE MODULE BASE == [0x3C]


It also comes with a visual! Buy 1 for the low price of 0 dollars and get the second 1 50% off!

Buy Now!


I hope this helps anyone looking to get serious with modules. Let me know if there's any questions.

Quote from: KingJigglypuff on February 28, 2015, 09:59:53 PM
So how are Article Floating Points handled?

And a bit unrelated, but which part of the rel tells the character to use an animation for their DamageFace Sub Action? I'd like to get a character of mine to use a tail animation (like Charizard's tail and wings) when being thrown, but I just couldn't figure it out. So I'm assuming it's module-related.

As Sammi-Husky mentioned, article floating point parameters are mostly to do with the ParamAccessor objects. That being said, I haven't done too much research into them either, so Sammi-Husky probably knows more than me at this point.

As for the damage face animations, I haven't touched on those at all, so I can't really say I can help you there. You would probably be correct in assuming that they are managed by the character module though.

Thanks for answering.

As for the Article subject, could we use this information to create new custom articles, rather than increase the amount of existing articles on the stage?
I don't take requests.

My PSA Thread
[move][/move]

Quote from: KingJigglypuff on March 01, 2015, 03:09:16 AM
As for the Article subject, could we use this information to create new custom articles, rather than increase the amount of existing articles on the stage?

I'm sure this is the question we're all thinking about.

Although, I wonder... how did PW start with all this module stuff to begin with?


Sorry but you are not allowed to view spoiler contents.

Thank you very much for the info PW!  I'm gonna see if I can implement this on G&W.

Thanks very much indeed. I'll also be seeing about implementing this somewhere. If i do, i'll post about it here :)

Quote from: KingJigglypuff on March 01, 2015, 03:09:16 AM
Thanks for answering.

As for the Article subject, could we use this information to create new custom articles, rather than increase the amount of existing articles on the stage?

Technically yes. Doing so would require a custom soInstanceHolder constructor and custom instantiation function, but that doesn't mean it would be impossible to use another article as a template. I imagine porting articles to other characters would be the first step in that direction. The easiest articles to port are the ones that can be copied by Kirby as their soInstanceHolder constructors and instantiation functions are stored in sora_melee and therefore always loaded. All other articles are completely encapsulated inside their character's module file, so porting them over to another character would require copying over all the assembly related to making them run.

Quote from: drogoth on March 01, 2015, 03:44:53 AM
I'm sure this is the question we're all thinking about.

Although, I wonder... how did PW start with all this module stuff to begin with?

Sometimes I wonder the same. :O


I was going through my mail archives and I don't think I ever mentioned this publicly. Maybe someone will find it interesting:

Quote from: PhantomWings on May 15, 2014, 05:55:19 AM
Hi Pikazz, thanks for being patient with me getting back to you.

It turns out that there isn't a whole lot that goes into initializing the soStatusUniqProcess objects for a character. For regular actions of Id 0 up to 111, the unique processes are initialized using a generic function called for all characters when they are created.

For the unique processes corresponding to a character's special moves - that is, Action Ids 112 and above - they are initialized inside the character's constructor located inside their module. Luckily it's not too hard to find the place where this particular bit of code is located:

Sorry but you are not allowed to view spoiler contents.


If you're looking to swap out any of the unique processes for Action Ids 111 and below, those are set at the instructions located at m1b[1] 0x128504 (80832F18 in memory). The code used there is fairly similar to what's used inside the character's constructor.



This is a pretty interesting discovery. It seems unique processes are fairly easy to swap too. Here's the unique process for Lucario's up-B, ftLucarioStatusUniqProcessSpecialHiRush, placed on his neutral-B allowing controller based directional movement:





Anyways, I hope you found this helpful. Good luck with your modding Pikazz.

I actually did it for you o: been showing that gif of yours to Wip PSA Thread and one more thread inhere I think o:
been using the StatusUniq Swapping quite effectly 'w'

and super great info for all the articles! I want to see if I can create something from it!
Stupid Tinypic :C

So I was able to change g&w's projectile count using PW's info, but unfortunately the game crashes trying to generate the 9th onscreen projectile.  It doesn't seem to be due to issues with where the projectile is being loaded/instantized (as the game doesn't crash when any projectile other than the 9th is instantized into any of the constructed objects); I'm assuming it's a memory issue, although it seems kind of odd that that little extra loaded stuff could cause a crash.  I'll go ahead and post a g&w with 8 chef projectiles once I resize the cap and clean stuff up.  Also thanks for the awesome info PW!

Edit:  Yeah, it does seem like there's some crazy stuff going on behind the scenes that's causing stuff to behave oddly.  No idea how, but the 15 count had weird t-poses at the beginning of up-b (in addition to aforementioned crashing) and the 8 count somehow shaved off 90% of the la-float space that g&w had allocated (although maybe that just happens because la-floats end up writing into unused memory sections under normal circumstances and the change in object structure causes that memory to become unusable).  In any case though, definitely possible to implement this on other fighters.

Also, download link for anyone who's interested: https://www.dropbox.com/s/2yv6tgkst3mhp5e/ft_gamewatch.rel?dl=0

Double post, but this doesn't fit with my previous post and is probably interesting enough to share:
Each fighter has 11 AnimCmd threads (projectiles can have less, which is why I believe concurrent infinite loops have such a range of thread types; articles don't always have the thread 9 that fighters use), with various types that are used for various things.  Threads are held in a fixed size array with the threads stored in a structure of [ soAnimCmdInterpreter thread, soAnimCmdAddressPackArraySeparate something, word threadID, halfword threadType ].  Thread id is directly referenced by at least some commands (flash overlays in probably behave slightly differently for thread A), but thread type affects how each thread processes stuff; it makes intangibility and charged move flashing not terminate on (sub)action change and ignore frame speed modifiers (type 0x10) or makes commands be run every frame instead of terminating when the action is finshed (type 0x4), and changing the thread type does cause them to gain the new thread type properties.  Some threads can be directly interacted with by psa commands (type 0x4 threads for instance), but I don't think all of them can.  In any case here's some info about how the threads generally look for fighters:
Thread ID - Thread Type (hex) - What thread is used for
Sorry but you are not allowed to view spoiler contents.


Also, I found where 00 and 01 commands are run.  00 commands are handled starting at 0x801398c8 in memory (so somewhere in main.dol, I think) and 01 commands are handled starting from 0x706F8 in section 1 of sora_melee.  Oh, and if anyone wants to dig through PSA code soAnimCmdImpl's methods, which are used for accessing info about the current command, are almost all fully documented on the OpenSA wiki (only missing method [0][10]).

thats really good info of the soAnimCmd o:
so basically, Warios Wart/ROB laser is using a soAnimCmd Concurrent Infinite Loops to charge their attacks no matter what action is used? o:

really interested into soAnimCmd! do you have any more info about it? :3
Stupid Tinypic :C

The charging is probably done via asm, although that's probably something that's worth checking.