2021-06-12 06:23:15 +08:00
using System ;
using System.IO ;
using System.Collections.Generic ;
using TC = System . ComponentModel . TypeConverterAttribute ;
using EXP = System . ComponentModel . ExpandableObjectConverter ;
2022-08-09 03:54:23 +08:00
using System.Linq ;
2021-06-12 06:23:15 +08:00
namespace CodeWalker.GameFiles
{
2022-02-01 19:59:24 +08:00
[TC(typeof(EXP))] public class MrfFile : GameFile , PackedFile
{
public byte [ ] RawFileData { get ; set ; }
public uint Magic { get ; set ; } = 0x45566F4D ; // 'MoVE'
public uint Version { get ; set ; } = 2 ;
public uint HeaderUnk1 { get ; set ; } = 0 ;
public uint HeaderUnk2 { get ; set ; } = 0 ;
public uint HeaderUnk3 { get ; set ; } = 0 ;
2022-08-09 03:54:23 +08:00
public uint DataLength { get ; set ; } // doesn't include the header (Magic to Unk1_Items) nor UnkBytes
public uint UnkBytesCount { get ; set ; }
2022-02-01 19:59:24 +08:00
public uint Unk1_Count { get ; set ; }
2022-08-09 03:54:23 +08:00
public uint MoveNetworkTriggerCount { get ; set ; }
public uint MoveNetworkFlagCount { get ; set ; }
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
public MrfHeaderUnk1 [ ] Unk1_Items { get ; set ; }
public MrfMoveNetworkTrigger [ ] MoveNetworkTriggers { get ; set ; }
public MrfMoveNetworkFlag [ ] MoveNetworkFlags { get ; set ; }
public byte [ ] UnkBytes { get ; set ; }
2022-02-01 19:59:24 +08:00
public MrfNode [ ] AllNodes { get ; set ; }
2022-08-09 03:54:23 +08:00
public MrfNodeStateBase RootState { get ; set ; }
// DOT graphs to visualize the move network
public string DebugTreeGraph { get ; set ; }
public string DebugStateGraph { get ; set ; }
2022-02-01 19:59:24 +08:00
public MrfFile ( ) : base ( null , GameFileType . Mrf )
{
}
public MrfFile ( RpfFileEntry entry ) : base ( entry , GameFileType . Mrf )
{
RpfFileEntry = entry ;
}
public void Load ( byte [ ] data , RpfFileEntry entry )
{
RawFileData = data ;
if ( entry ! = null )
{
RpfFileEntry = entry ;
Name = entry . Name ;
}
using ( MemoryStream ms = new MemoryStream ( data ) )
{
DataReader r = new DataReader ( ms , Endianess . LittleEndian ) ;
Read ( r ) ;
} ;
}
public byte [ ] Save ( )
{
MemoryStream s = new MemoryStream ( ) ;
DataWriter w = new DataWriter ( s ) ;
2022-08-09 03:54:23 +08:00
NoOpDataWriter nw = new NoOpDataWriter ( ) ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
Write ( nw , updateOffsets : true ) ; // first pass to calculate relative offsets
DataLength = ( uint ) ( nw . Length - 32 - ( Unk1_Items ? . Sum ( i = > i . Size ) ? ? 0 ) - UnkBytesCount ) ;
Write ( w , updateOffsets : false ) ; // now write the MRF
2022-02-01 19:59:24 +08:00
var buf = new byte [ s . Length ] ;
s . Position = 0 ;
s . Read ( buf , 0 , buf . Length ) ;
return buf ;
}
2022-08-09 03:54:23 +08:00
private void Write ( DataWriter w , bool updateOffsets )
2022-02-01 19:59:24 +08:00
{
if ( Magic ! = 0x45566F4D | | Version ! = 2 | | HeaderUnk1 ! = 0 | | HeaderUnk2 ! = 0 )
throw new Exception ( "Failed to write MRF, header is invalid!" ) ;
w . Write ( Magic ) ;
w . Write ( Version ) ;
w . Write ( HeaderUnk1 ) ;
w . Write ( HeaderUnk2 ) ;
w . Write ( HeaderUnk3 ) ;
w . Write ( DataLength ) ;
2022-08-09 03:54:23 +08:00
w . Write ( UnkBytesCount ) ;
2022-02-01 19:59:24 +08:00
// Unused in final game
w . Write ( Unk1_Count ) ;
if ( Unk1_Count > 0 )
{
foreach ( var entry in Unk1_Items )
{
w . Write ( entry . Size ) ;
w . Write ( entry . Bytes ) ;
}
}
2022-08-09 03:54:23 +08:00
w . Write ( MoveNetworkTriggerCount ) ;
if ( MoveNetworkTriggerCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var entry in MoveNetworkTriggers )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Name ) ;
w . Write ( entry . BitPosition ) ;
2022-02-01 19:59:24 +08:00
}
}
2022-08-09 03:54:23 +08:00
w . Write ( MoveNetworkFlagCount ) ;
if ( MoveNetworkFlagCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var entry in MoveNetworkFlags )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Name ) ;
w . Write ( entry . BitPosition ) ;
2022-02-01 19:59:24 +08:00
}
}
if ( AllNodes ! = null )
{
foreach ( var node in AllNodes )
2022-08-09 03:54:23 +08:00
{
if ( updateOffsets ) node . FileOffset = ( int ) w . Position ;
2022-02-01 19:59:24 +08:00
node . Write ( w ) ;
2022-08-09 03:54:23 +08:00
if ( updateOffsets ) node . FileDataSize = ( int ) ( w . Position - node . FileOffset ) ;
}
if ( updateOffsets )
{
foreach ( var node in AllNodes )
{
node . UpdateRelativeOffsets ( ) ;
}
}
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < UnkBytesCount ; i + + )
w . Write ( UnkBytes [ i ] ) ;
2022-02-01 19:59:24 +08:00
}
private void Read ( DataReader r )
{
Magic = r . ReadUInt32 ( ) ; // Should be 'MoVE'
Version = r . ReadUInt32 ( ) ; // GTA5 = 2, RDR3 = 11
HeaderUnk1 = r . ReadUInt32 ( ) ; // Should be 0
HeaderUnk2 = r . ReadUInt32 ( ) ;
HeaderUnk3 = r . ReadUInt32 ( ) ; // Should be 0
DataLength = r . ReadUInt32 ( ) ;
2022-08-09 03:54:23 +08:00
UnkBytesCount = r . ReadUInt32 ( ) ;
2022-02-01 19:59:24 +08:00
if ( Magic ! = 0x45566F4D | | Version ! = 2 | | HeaderUnk1 ! = 0 | | HeaderUnk2 ! = 0 )
throw new Exception ( "Failed to read MRF, header is invalid!" ) ;
// Unused in final game
Unk1_Count = r . ReadUInt32 ( ) ;
if ( Unk1_Count > 0 )
{
2022-08-09 03:54:23 +08:00
Unk1_Items = new MrfHeaderUnk1 [ Unk1_Count ] ;
2022-02-01 19:59:24 +08:00
for ( int i = 0 ; i < Unk1_Count ; i + + )
2022-08-09 03:54:23 +08:00
Unk1_Items [ i ] = new MrfHeaderUnk1 ( r ) ;
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
MoveNetworkTriggerCount = r . ReadUInt32 ( ) ;
if ( MoveNetworkTriggerCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
MoveNetworkTriggers = new MrfMoveNetworkTrigger [ MoveNetworkTriggerCount ] ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < MoveNetworkTriggerCount ; i + + )
MoveNetworkTriggers [ i ] = new MrfMoveNetworkTrigger ( r ) ;
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
MoveNetworkFlagCount = r . ReadUInt32 ( ) ;
if ( MoveNetworkFlagCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
MoveNetworkFlags = new MrfMoveNetworkFlag [ MoveNetworkFlagCount ] ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < MoveNetworkFlagCount ; i + + )
MoveNetworkFlags [ i ] = new MrfMoveNetworkFlag ( r ) ;
2022-02-01 19:59:24 +08:00
}
var nodes = new List < MrfNode > ( ) ;
while ( true )
{
var index = nodes . Count ;
var node = ReadNode ( r ) ;
if ( node = = null ) break ;
node . FileIndex = index ;
nodes . Add ( node ) ;
}
AllNodes = nodes . ToArray ( ) ;
2022-08-09 03:54:23 +08:00
if ( UnkBytesCount ! = 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
UnkBytes = new byte [ UnkBytesCount ] ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < UnkBytesCount ; i + + )
UnkBytes [ i ] = r . ReadByte ( ) ;
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
RootState = AllNodes . Length > 0 ? ( MrfNodeStateBase ) AllNodes [ 0 ] : null ; // the first node is always a state or state machine node (not inlined state machine)
ResolveRelativeOffsets ( ) ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
DebugTreeGraph = DumpTreeGraph ( ) ;
DebugStateGraph = DumpStateGraph ( ) ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
if ( r . Length ! = ( DataLength + 32 + ( Unk1_Items ? . Sum ( i = > i . Size ) ? ? 0 ) + UnkBytesCount ) )
{ } // no hits
2022-02-01 19:59:24 +08:00
if ( r . Position ! = r . Length )
throw new Exception ( $"Failed to read MRF ({r.Position} / {r.Length})" ) ;
}
private MrfNode ReadNode ( DataReader r )
{
var startPos = r . Position ;
var nodeType = ( MrfNodeType ) r . ReadUInt16 ( ) ;
r . Position = startPos ;
if ( nodeType < = MrfNodeType . None | | nodeType > = MrfNodeType . Max )
{
if ( r . Position ! = r . Length ) //should only be at EOF
{ }
return null ;
}
var node = CreateNode ( nodeType ) ;
2022-08-09 03:54:23 +08:00
node . FileOffset = ( int ) startPos ;
2022-02-01 19:59:24 +08:00
node . Read ( r ) ;
2022-08-09 03:54:23 +08:00
node . FileDataSize = ( int ) ( r . Position - node . FileOffset ) ;
2022-02-01 19:59:24 +08:00
return node ;
}
private MrfNode CreateNode ( MrfNodeType infoType )
{
switch ( infoType )
{
2022-08-09 03:54:23 +08:00
case MrfNodeType . StateMachine :
return new MrfNodeStateMachine ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Tail :
return new MrfNodeTail ( ) ;
case MrfNodeType . InlinedStateMachine :
return new MrfNodeInlinedStateMachine ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Animation :
return new MrfNodeAnimation ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Blend :
return new MrfNodeBlend ( ) ;
case MrfNodeType . AddSubtract :
2022-08-09 03:54:23 +08:00
return new MrfNodeAddSubtract ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Filter :
return new MrfNodeFilter ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Mirror :
return new MrfNodeMirror ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Frame :
return new MrfNodeFrame ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Ik :
return new MrfNodeIk ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . BlendN :
return new MrfNodeBlendN ( ) ;
case MrfNodeType . Clip :
return new MrfNodeClip ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Pm :
return new MrfNodePm ( ) ;
case MrfNodeType . Extrapolate :
return new MrfNodeExtrapolate ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Expression :
return new MrfNodeExpression ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Capture :
return new MrfNodeCapture ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . Proxy :
return new MrfNodeProxy ( ) ;
case MrfNodeType . AddN :
return new MrfNodeAddN ( ) ;
case MrfNodeType . Identity :
return new MrfNodeIdentity ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . Merge :
return new MrfNodeMerge ( ) ;
case MrfNodeType . Pose :
return new MrfNodePose ( ) ;
2022-02-01 19:59:24 +08:00
case MrfNodeType . MergeN :
return new MrfNodeMergeN ( ) ;
case MrfNodeType . State :
return new MrfNodeState ( ) ;
case MrfNodeType . Invalid :
return new MrfNodeInvalid ( ) ;
2022-08-09 03:54:23 +08:00
case MrfNodeType . JointLimit :
return new MrfNodeJointLimit ( ) ;
case MrfNodeType . SubNetwork :
return new MrfNodeSubNetwork ( ) ;
case MrfNodeType . Reference :
return new MrfNodeReference ( ) ;
2022-02-01 19:59:24 +08:00
}
throw new Exception ( $"A handler for ({infoType}) mrf node type is not valid" ) ;
}
2022-08-09 03:54:23 +08:00
private void ResolveRelativeOffsets ( )
{
foreach ( var n in AllNodes )
{
n . ResolveRelativeOffsets ( this ) ;
}
}
public MrfNode FindNodeAtFileOffset ( int fileOffset )
{
foreach ( var n in AllNodes )
{
if ( n . FileOffset = = fileOffset ) return n ;
}
return null ;
}
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
public MrfMoveNetworkTrigger FindMoveNetworkTriggerForBit ( int bitPosition )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
if ( MoveNetworkTriggers = = null )
{
return null ;
}
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
foreach ( var trigger in MoveNetworkTriggers )
{
if ( trigger . Name ! = 0xFFFFFFFF & & trigger . BitPosition = = bitPosition ) return trigger ;
}
return null ;
}
public MrfMoveNetworkFlag FindMoveNetworkFlagForBit ( int bitPosition )
{
if ( MoveNetworkFlags = = null )
{
return null ;
}
foreach ( var flag in MoveNetworkFlags )
{
if ( flag . Name ! = 0xFFFFFFFF & & flag . BitPosition = = bitPosition ) return flag ;
}
return null ;
}
// MoveNetworkTriggers and MoveNetworkFlags getters by name for reference of how the arrays should be sorted in buckets
public MrfMoveNetworkTrigger FindMoveNetworkTriggerByName ( MetaHash name )
{
if ( MoveNetworkTriggers = = null )
{
return null ;
}
for ( int i = ( int ) ( name . Hash % MoveNetworkTriggers . Length ) ; ; i = ( i + 1 ) % MoveNetworkTriggers . Length )
{
var trigger = MoveNetworkTriggers [ i ] ;
if ( trigger . Name = = 0xFFFFFFFF ) break ;
if ( trigger . Name = = name ) return trigger ;
}
return null ;
}
public MrfMoveNetworkFlag FindMoveNetworkFlagByName ( MetaHash name )
{
if ( MoveNetworkFlags = = null )
{
return null ;
}
for ( int i = ( int ) ( name . Hash % MoveNetworkFlags . Length ) ; ; i = ( i + 1 ) % MoveNetworkFlags . Length )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var flag = MoveNetworkFlags [ i ] ;
if ( flag . Name = = 0xFFFFFFFF ) break ;
if ( flag . Name = = name ) return flag ;
}
return null ;
}
/// <summary>
/// Dump a DOT graph with the whole node hierarchy as a tree.
/// </summary>
public string DumpTreeGraph ( )
{
using ( var w = new StringWriter ( ) )
{
w . WriteLine ( $@"digraph ""{Name}"" {{" ) ;
w . WriteLine ( $@" label=""{Name}""" ) ;
w . WriteLine ( $@" labelloc=""t""" ) ;
w . WriteLine ( $@" concentrate=true" ) ;
w . WriteLine ( $@" rankdir=""LR""" ) ;
w . WriteLine ( $@" graph[fontname = ""Consolas""];" ) ;
w . WriteLine ( $@" edge[fontname = ""Consolas""];" ) ;
w . WriteLine ( $@" node[fontname = ""Consolas""];" ) ;
w . WriteLine ( ) ;
w . WriteLine ( " root [label=\"root\"];" ) ;
// nodes
foreach ( var n in AllNodes )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var id = n . FileOffset ;
var label = $"{n.NodeType} '{n.Name}'" ;
w . WriteLine ( " n{0} [label=\"{1}\"];" , id , label ) ;
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
w . WriteLine ( ) ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
// edges
w . WriteLine ( " n{0} -> root [color = black]" , RootState . FileOffset ) ;
2022-02-01 19:59:24 +08:00
foreach ( var n in AllNodes )
{
2022-08-09 03:54:23 +08:00
MrfStateTransition [ ] transitions = null ;
MrfNode initial = null ;
if ( n is MrfNodeStateBase sb )
{
initial = sb . InitialNode ;
if ( n is MrfNodeStateMachine sm )
{
transitions = sm . Transitions ;
}
if ( n is MrfNodeState sn )
{
transitions = sn . Transitions ;
}
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
2022-02-01 19:59:24 +08:00
if ( n is MrfNodeInlinedStateMachine im )
{
2022-08-09 03:54:23 +08:00
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"fallback\"]" , n . FileOffset , im . FallbackNode . FileOffset ) ;
}
if ( n is MrfNodeWithChildBase f )
{
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"child\"]" , n . FileOffset , f . Child . FileOffset ) ;
}
if ( n is MrfNodePairBase p )
{
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"#0\"]" , n . FileOffset , p . Child0 . FileOffset ) ;
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"#1\"]" , n . FileOffset , p . Child1 . FileOffset ) ;
}
if ( n is MrfNodeNBase nn & & nn . Children ! = null )
{
for ( int i = 0 ; i < nn . Children . Length ; i + + )
{
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"#{2}\"]" , n . FileOffset , nn . Children [ i ] . FileOffset , i ) ;
}
}
if ( transitions ! = null )
{
foreach ( var transition in transitions )
{
var conditions = transition . Conditions = = null ? "[]" : "[" + string . Join ( " & " , transition . Conditions . Select ( c = > c . ToExpressionString ( this ) ) ) + "]" ;
var target = transition . TargetState ;
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"T {2}\"]" , n . FileOffset , target . FileOffset , conditions ) ;
}
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
if ( initial ! = null )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"init\"]" , n . FileOffset , initial . FileOffset ) ;
2022-02-01 19:59:24 +08:00
}
}
2022-08-09 03:54:23 +08:00
// footer
w . WriteLine ( "}" ) ;
return w . ToString ( ) ;
}
}
/// <summary>
/// Dump a DOT graph of the state machines where nodes are placed inside their corresponding state.
/// </summary>
public string DumpStateGraph ( )
{
using ( var w = new StringWriter ( ) )
{
w . WriteLine ( $@"digraph ""{Name}"" {{" ) ;
w . WriteLine ( $@" label=""{Name}""" ) ;
w . WriteLine ( $@" labelloc=""t""" ) ;
w . WriteLine ( $@" concentrate=true" ) ;
w . WriteLine ( $@" compound=true" ) ;
w . WriteLine ( $@" rankdir=""LR""" ) ;
w . WriteLine ( $@" graph[fontname = ""Consolas""];" ) ;
w . WriteLine ( $@" edge[fontname = ""Consolas""];" ) ;
w . WriteLine ( $@" node[fontname = ""Consolas""];" ) ;
w . WriteLine ( ) ;
DumpNode ( RootState , w , null ) ;
w . WriteLine ( " root [label=\"root\",shape=\"diamond\"];" ) ;
w . WriteLine ( " root -> S{0} [color = black][lhead=\"clusterS{0}\"]" , RootState . FileOffset ) ;
// footer
w . WriteLine ( "}" ) ;
return w . ToString ( ) ;
}
}
private void DumpStateMachineSubGraph ( MrfNodeStateMachine sm , TextWriter w )
{
// header
w . WriteLine ( $@"subgraph ""clusterS{sm.FileOffset}"" {{" ) ;
w . WriteLine ( $@" label=""State Machine '{sm.Name}'""" ) ;
w . WriteLine ( $@" labelloc=""t""" ) ;
w . WriteLine ( $@" concentrate=true" ) ;
w . WriteLine ( $@" rankdir=""RL""" ) ;
w . WriteLine ( $@" S{sm.FileOffset}[shape=""none""][style=""invis""][label=""""]" ) ; // hidden node to be able to connect subgraphs
w . WriteLine ( ) ;
if ( sm . States ! = null )
{
foreach ( var state in sm . States )
{
var stateNode = state . State ;
if ( stateNode is MrfNodeState ns ) DumpStateSubGraph ( ns , w ) ;
if ( stateNode is MrfNodeStateMachine nsm ) DumpStateMachineSubGraph ( nsm , w ) ;
if ( stateNode is MrfNodeInlinedStateMachine ism ) DumpInlinedStateMachineSubGraph ( ism , w ) ;
}
foreach ( var state in sm . States )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var stateNode = state . State ;
MrfStateTransition [ ] transitions = null ;
if ( stateNode is MrfNodeState ns ) transitions = ns . Transitions ;
if ( stateNode is MrfNodeStateMachine nsm ) transitions = nsm . Transitions ;
DumpStateTransitionsGraph ( stateNode , transitions , w ) ;
2022-02-01 19:59:24 +08:00
}
}
2022-08-09 03:54:23 +08:00
w . WriteLine ( " startS{0} [label=\"start\",shape=\"diamond\"];" , sm . FileOffset ) ;
w . WriteLine ( " startS{0} -> S{1} [color = black][lhead=\"clusterS{1}\"]" , sm . FileOffset , sm . InitialNode . FileOffset ) ;
// footer
w . WriteLine ( "}" ) ;
}
private void DumpInlinedStateMachineSubGraph ( MrfNodeInlinedStateMachine sm , TextWriter w )
{
// header
w . WriteLine ( $@"subgraph ""clusterS{sm.FileOffset}"" {{" ) ;
w . WriteLine ( $@" label=""Inlined State Machine '{sm.Name}'""" ) ;
w . WriteLine ( $@" labelloc=""t""" ) ;
w . WriteLine ( $@" concentrate=true" ) ;
w . WriteLine ( $@" rankdir=""RL""" ) ;
w . WriteLine ( $@" S{sm.FileOffset}[shape=""none""][style=""invis""][label=""""]" ) ; // hidden node to be able to connect subgraphs
w . WriteLine ( ) ;
if ( sm . States ! = null )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var state in sm . States )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var stateNode = state . State ;
if ( stateNode is MrfNodeState ns ) DumpStateSubGraph ( ns , w ) ;
if ( stateNode is MrfNodeStateMachine nsm ) DumpStateMachineSubGraph ( nsm , w ) ;
if ( stateNode is MrfNodeInlinedStateMachine ism ) DumpInlinedStateMachineSubGraph ( ism , w ) ;
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
foreach ( var state in sm . States )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var stateNode = state . State ;
MrfStateTransition [ ] transitions = null ;
if ( stateNode is MrfNodeState ns ) transitions = ns . Transitions ;
if ( stateNode is MrfNodeStateMachine nsm ) transitions = nsm . Transitions ;
DumpStateTransitionsGraph ( stateNode , transitions , w ) ;
2022-02-01 19:59:24 +08:00
}
}
2022-08-09 03:54:23 +08:00
w . WriteLine ( " startS{0} [label=\"start\",shape=\"diamond\"];" , sm . FileOffset ) ;
w . WriteLine ( " startS{0} -> S{1} [color = black][lhead=\"clusterS{1}\"]" , sm . FileOffset , sm . InitialNode . FileOffset ) ;
if ( sm . FallbackNode ! = null )
{
var fn = sm . FallbackNode ;
DumpNode ( fn , w , null ) ;
w . WriteLine ( " fallbackS{0} [label=\"fallback\",shape=\"diamond\"];" , sm . FileOffset ) ;
if ( fn is MrfNodeStateBase ) w . WriteLine ( " fallbackS{0} -> S{1} [color = black][lhead=\"clusterS{1}\"]" , sm . FileOffset , fn . FileOffset ) ;
else w . WriteLine ( " fallbackS{0} -> n{1} [color = black]" , sm . FileOffset , fn . FileOffset ) ;
}
// footer
w . WriteLine ( "}" ) ;
}
private void DumpStateTransitionsGraph ( MrfNodeStateBase from , MrfStateTransition [ ] transitions , TextWriter w )
{
if ( transitions ! = null )
{
int i = 0 ;
foreach ( var transition in transitions )
{
var conditions = transition . Conditions = = null ? "[]" : "[" + string . Join ( " & " , transition . Conditions . Select ( c = > c . ToExpressionString ( this ) ) ) + "]" ;
var target = transition . TargetState ;
w . WriteLine ( " S{0} -> S{1} [color = black, xlabel=\"T#{2} {3}\"][ltail=\"clusterS{0}\"][lhead=\"clusterS{1}\"]" , from . FileOffset , target . FileOffset , i , conditions ) ;
i + + ;
}
}
}
private void DumpStateSubGraph ( MrfNodeStateBase state , TextWriter w )
{
if ( state . InitialNode = = null )
{
return ;
}
// header
w . WriteLine ( $@"subgraph clusterS{state.FileOffset} {{" ) ;
w . WriteLine ( $@" label=""State '{state.Name}'""" ) ;
w . WriteLine ( $@" labelloc=""t""" ) ;
w . WriteLine ( $@" concentrate=true" ) ;
w . WriteLine ( $@" rankdir=""LR""" ) ;
w . WriteLine ( $@" S{state.FileOffset}[shape=""none""][style=""invis""][label=""""]" ) ; // hidden node to be able to connect subgraphs
w . WriteLine ( ) ;
var initial = state . InitialNode ;
DumpNode ( initial , w , null ) ;
w . WriteLine ( " outputS{0} [label=\"output\",shape=\"point\"];" , state . FileOffset ) ;
if ( initial is MrfNodeStateBase ) w . WriteLine ( " S{0} -> outputS{1} [color = black][ltail=\"clusterS{0}\"]" , initial . FileOffset , state . FileOffset ) ;
else w . WriteLine ( " n{0} -> outputS{1} [color = black]" , initial . FileOffset , state . FileOffset ) ;
// footer
w . WriteLine ( "}" ) ;
}
private void DumpNodeGraph ( MrfNode n , TextWriter w , HashSet < MrfNode > visitedNodes )
{
if ( ! visitedNodes . Add ( n ) )
{
return ;
}
var label = $"{n.NodeType} '{n.Name}'" ;
if ( n is MrfNodeSubNetwork sub )
{
label + = $"\\n'{sub.SubNetworkParameterName}'" ;
}
w . WriteLine ( " n{0} [label=\"{1}\"];" , n . FileOffset , label ) ;
void writeConnection ( MrfNode target , string connectionLabel )
{
if ( target is MrfNodeStateBase ) w . WriteLine ( " S{1} -> n{0} [color = black, xlabel=\"{2}\"][ltail=\"clusterS{1}\"]" , n . FileOffset , target . FileOffset , connectionLabel ) ;
else w . WriteLine ( " n{1} -> n{0} [color = black, xlabel=\"{2}\"]" , n . FileOffset , target . FileOffset , connectionLabel ) ;
}
if ( n is MrfNodeInlinedStateMachine im )
{
DumpNode ( im . FallbackNode , w , visitedNodes ) ;
writeConnection ( im . FallbackNode , "fallback" ) ;
}
if ( n is MrfNodeWithChildBase f )
{
DumpNode ( f . Child , w , visitedNodes ) ;
writeConnection ( f . Child , "" ) ;
}
if ( n is MrfNodePairBase p )
{
DumpNode ( p . Child0 , w , visitedNodes ) ;
DumpNode ( p . Child1 , w , visitedNodes ) ;
writeConnection ( p . Child0 , "#0" ) ;
writeConnection ( p . Child1 , "#1" ) ;
}
if ( n is MrfNodeNBase nn & & nn . Children ! = null )
{
for ( int i = 0 ; i < nn . Children . Length ; i + + )
{
DumpNode ( nn . Children [ i ] , w , visitedNodes ) ;
writeConnection ( nn . Children [ i ] , $"#{i}" ) ;
}
}
}
private void DumpNode ( MrfNode n , TextWriter w , HashSet < MrfNode > visitedNodes )
{
if ( n is MrfNodeState ns ) DumpStateSubGraph ( ns , w ) ;
else if ( n is MrfNodeStateMachine nsm ) DumpStateMachineSubGraph ( nsm , w ) ;
else if ( n is MrfNodeInlinedStateMachine ism ) DumpInlinedStateMachineSubGraph ( ism , w ) ;
else DumpNodeGraph ( n , w , visitedNodes ? ? new HashSet < MrfNode > ( ) ) ;
}
/// <summary>
/// Writer used to calculate where the nodes will be placed, so the relative offsets can be calculated before using the real writer.
/// </summary>
private class NoOpDataWriter : DataWriter
{
private long length ;
private long position ;
public override long Length = > length ;
public override long Position { get = > position ; set = > position = value ; }
public NoOpDataWriter ( ) : base ( null , Endianess . LittleEndian )
{
}
protected override void WriteToStream ( byte [ ] value , bool ignoreEndianess = false )
{
position + = value . Length ;
length = Math . Max ( length , position ) ;
}
2022-02-01 19:59:24 +08:00
}
}
2021-11-06 08:17:53 +08:00
// Unused node indexes by GTAV: 11, 12, 14, 16
// Exist in GTAV but not used in MRFs: 4, 8, 10, 17, 21, 22, 28, 29, 31, 32
2022-01-27 00:35:50 +08:00
public enum MrfNodeType : ushort
2021-06-12 06:23:15 +08:00
{
None = 0 ,
2022-08-09 03:54:23 +08:00
StateMachine = 1 ,
2021-06-12 06:23:15 +08:00
Tail = 2 ,
InlinedStateMachine = 3 ,
2022-08-09 03:54:23 +08:00
Animation = 4 ,
2021-06-12 06:23:15 +08:00
Blend = 5 ,
AddSubtract = 6 ,
Filter = 7 ,
2022-08-09 03:54:23 +08:00
Mirror = 8 ,
2021-06-12 06:23:15 +08:00
Frame = 9 ,
2022-08-09 03:54:23 +08:00
Ik = 10 ,
2021-06-12 06:23:15 +08:00
BlendN = 13 ,
Clip = 15 ,
2022-08-09 03:54:23 +08:00
Pm = 17 ,
Extrapolate = 18 ,
2021-06-12 06:23:15 +08:00
Expression = 19 ,
2022-08-09 03:54:23 +08:00
Capture = 20 ,
2021-06-12 06:23:15 +08:00
Proxy = 21 ,
AddN = 22 ,
Identity = 23 ,
2022-08-09 03:54:23 +08:00
Merge = 24 ,
Pose = 25 ,
2021-06-12 06:23:15 +08:00
MergeN = 26 ,
State = 27 ,
Invalid = 28 ,
2022-08-09 03:54:23 +08:00
JointLimit = 29 ,
SubNetwork = 30 ,
Reference = 31 ,
2021-06-12 06:23:15 +08:00
Max = 32
}
2022-08-09 03:54:23 +08:00
public enum MrfNodeParameterId : ushort // node parameter IDs are specific to the node type
{
// StateMachine
// none
// Tail
// none
// InlinedStateMachine
// none
// Animation
Animation_Animation = 0 , // rage::crAnimation (only setter)
Animation_Unk1 = 1 , // float
Animation_Unk2 = 2 , // float
Animation_Unk3 = 3 , // float
Animation_Unk4 = 4 , // bool
Animation_Unk5 = 5 , // bool
// Blend
Blend_Filter = 0 , // rage::crFrameFilter
Blend_Weight = 1 , // float
// AddSubtract
AddSubtract_Filter = 0 , // rage::crFrameFilter
AddSubtract_Weight = 1 , // float
// Filter
Filter_Filter = 0 , // rage::crFrameFilter
// Mirror
Mirror_Filter = 0 , // rage::crFrameFilter
// Frame
Frame_Frame = 0 , // rage::crFrame
// Ik
// none
// BlendN
BlendN_Filter = 1 , // rage::crFrameFilter
BlendN_ChildWeight = 2 , // float (extra arg is the child index)
BlendN_ChildFilter = 3 , // rage::crFrameFilter (extra arg is the child index)
// Clip
Clip_Clip = 0 , // rage::crClip
Clip_Phase = 1 , // float
Clip_Rate = 2 , // float
Clip_Delta = 3 , // float
Clip_Looped = 4 , // bool
// Pm
Pm_Motion = 0 , // rage::crpmMotion
Pm_Unk1 = 1 , // float
Pm_Unk2 = 2 , // float
Pm_Unk3 = 3 , // float
Pm_Unk4 = 4 , // float
// Extrapolate
Extrapolate_Damping = 0 , // float
// Expression
Expression_Expression = 0 , // rage::crExpressions
Expression_Weight = 1 , // float
Expression_Variable = 2 , // float (extra arg is the variable name)
// Capture
Capture_Frame = 0 , // rage::crFrame
// Proxy
Proxy_Node = 0 , // rage::crmtNode (the type resolver returns rage::crmtObserver but the getter/setter expect a rage::crmtNode, R* bug?)
// AddN
AddN_Filter = 1 , // rage::crFrameFilter
AddN_ChildWeight = 2 , // float (extra arg is the child index)
AddN_ChildFilter = 3 , // rage::crFrameFilter (extra arg is the child index)
// Identity
// none
// Merge
Merge_Filter = 0 , // rage::crFrameFilter
// Pose
Pose_IsNormalized = 0 , // bool (getter hardcoded to true, setter does nothing)
// MergeN
MergeN_Filter = 1 , // rage::crFrameFilter
MergeN_ChildWeight = 2 , // float (extra arg is the child index)
MergeN_ChildFilter = 3 , // rage::crFrameFilter (extra arg is the child index)
// State
// none
// Invalid
// none
// JointLimit
JointLimit_Filter = 0 , // rage::crFrameFilter (only setter exists)
// SubNetwork
// none
// Reference
// none
}
public enum MrfNodeEventId : ushort // node event IDs are specific to the node type
{
// StateMachine
// none
// Tail
// none
// InlinedStateMachine
// none
// Animation
Animation_Unk0 = 0 ,
Animation_Unk1 = 1 ,
// Blend
// none
// AddSubtract
// none
// Filter
// none
// Mirror
// none
// Frame
// none
// Ik
// none
// BlendN
// none
// Clip
Clip_IterationFinished = 0 , // triggered when a looped clip iteration finishes playing
Clip_Finished = 1 , // triggered when a non-looped clip finishes playing
Clip_Unk2 = 2 ,
Clip_Unk3 = 3 ,
Clip_Unk4 = 4 ,
// Pm
Pm_Unk0 = 0 ,
Pm_Unk1 = 1 ,
// Extrapolate
// none
// Expression
// none
// Capture
// none
// Proxy
// none
// AddN
// none
// Identity
// none
// Merge
// Pose
// none
// MergeN
// none
// State
// none
// Invalid
// none
// JointLimit
// none
// SubNetwork
// none
// Reference
// none
}
2021-06-12 06:23:15 +08:00
#region mrf node abstractions
2022-08-09 03:54:23 +08:00
2022-01-27 00:35:50 +08:00
[TC(typeof(EXP))] public abstract class MrfNode
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
public MrfNodeType NodeType { get ; set ; }
2022-08-09 03:54:23 +08:00
public ushort NodeIndex { get ; set ; } //index in the parent state node
public MetaHash Name { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
public int FileIndex { get ; set ; } //index in the file
public int FileOffset { get ; set ; } //offset in the file
public int FileDataSize { get ; set ; } //number of bytes read from the file (this node only)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfNode ( MrfNodeType type )
{
NodeType = type ;
}
2022-01-27 00:35:50 +08:00
public virtual void Read ( DataReader r )
{
NodeType = ( MrfNodeType ) r . ReadUInt16 ( ) ;
NodeIndex = r . ReadUInt16 ( ) ;
2022-08-09 03:54:23 +08:00
Name = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
}
public virtual void Write ( DataWriter w )
{
w . Write ( ( ushort ) NodeType ) ;
w . Write ( NodeIndex ) ;
2022-08-09 03:54:23 +08:00
w . Write ( Name ) ;
2022-01-27 00:35:50 +08:00
}
2021-11-06 08:17:53 +08:00
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
return /* FileIndex.ToString() + ":" + FileOffset.ToString() + "+" + FileDataSize.ToString() + ": " + */
2022-08-09 03:54:23 +08:00
NodeType . ToString ( ) + " - " + NodeIndex . ToString ( ) + " - " + Name . ToString ( ) ;
}
public virtual void ResolveRelativeOffsets ( MrfFile mrf )
{
}
public virtual void UpdateRelativeOffsets ( )
{
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public abstract class MrfNodeWithFlagsBase : MrfNode
2022-01-27 00:35:50 +08:00
{
public uint Flags { get ; set ; }
2022-08-09 03:54:23 +08:00
public MrfNodeWithFlagsBase ( MrfNodeType type ) : base ( type ) { }
2022-01-27 00:35:50 +08:00
public override void Read ( DataReader r )
{
base . Read ( r ) ;
Flags = r . ReadUInt32 ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( Flags ) ;
}
2022-08-09 03:54:23 +08:00
public uint GetFlagsSubset ( int bitOffset , uint mask )
{
return ( Flags > > bitOffset ) & mask ;
}
public void SetFlagsSubset ( int bitOffset , uint mask , uint value )
{
Flags = ( Flags & ~ ( mask < < bitOffset ) ) | ( ( value & mask ) < < bitOffset ) ;
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return base . ToString ( ) + " - " + Flags . ToString ( "X8" ) ;
2022-01-27 00:35:50 +08:00
}
}
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
[TC(typeof(EXP))] public abstract class MrfNodeStateBase : MrfNode
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public int InitialNodeOffset { get ; set ; } // offset from the start of this field
public int InitialNodeFileOffset { get ; set ; }
2022-01-27 00:35:50 +08:00
public uint StateUnk3 { get ; set ; }
2022-08-09 03:54:23 +08:00
public bool HasEntryParameter { get ; set ; }
public bool HasExitParameter { get ; set ; }
public byte StateChildCount { get ; set ; } // for Node(Inlined)StateMachine the number of states, for NodeState the number of children excluding NodeTails
public byte TransitionCount { get ; set ; }
public MetaHash EntryParameterName { get ; set ; } // bool parameter set to true when the network enters this node
public MetaHash ExitParameterName { get ; set ; } // bool parameter set to true when the network leaves this node
public int TransitionsOffset { get ; set ; } // offset from the start of this field
public int TransitionsFileOffset { get ; set ; }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNode InitialNode { get ; set ; } // for Node(Inlined)StateMachine this is a NodeStateBase, for NodeState it can be any node
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeStateBase ( MrfNodeType type ) : base ( type ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
InitialNodeOffset = r . ReadInt32 ( ) ;
InitialNodeFileOffset = ( int ) ( r . Position + InitialNodeOffset - 4 ) ;
2022-01-27 00:35:50 +08:00
StateUnk3 = r . ReadUInt32 ( ) ;
2022-08-09 03:54:23 +08:00
HasEntryParameter = r . ReadByte ( ) ! = 0 ;
HasExitParameter = r . ReadByte ( ) ! = 0 ;
2022-01-27 00:35:50 +08:00
StateChildCount = r . ReadByte ( ) ;
2022-08-09 03:54:23 +08:00
TransitionCount = r . ReadByte ( ) ;
EntryParameterName = r . ReadUInt32 ( ) ;
ExitParameterName = r . ReadUInt32 ( ) ;
TransitionsOffset = r . ReadInt32 ( ) ;
TransitionsFileOffset = ( int ) ( r . Position + TransitionsOffset - 4 ) ;
2022-01-27 00:35:50 +08:00
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
w . Write ( InitialNodeOffset ) ;
2022-01-27 00:35:50 +08:00
w . Write ( StateUnk3 ) ;
2022-08-09 03:54:23 +08:00
w . Write ( ( byte ) ( HasEntryParameter ? 1 : 0 ) ) ;
w . Write ( ( byte ) ( HasExitParameter ? 1 : 0 ) ) ;
2022-01-27 00:35:50 +08:00
w . Write ( StateChildCount ) ;
2022-08-09 03:54:23 +08:00
w . Write ( TransitionCount ) ;
w . Write ( EntryParameterName ) ;
w . Write ( ExitParameterName ) ;
w . Write ( TransitionsOffset ) ;
2022-01-27 00:35:50 +08:00
}
2022-08-09 03:54:23 +08:00
public override void ResolveRelativeOffsets ( MrfFile mrf )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
base . ResolveRelativeOffsets ( mrf ) ;
var initNode = mrf . FindNodeAtFileOffset ( InitialNodeFileOffset ) ;
if ( initNode = = null )
{ } // no hits
if ( ( this is MrfNodeStateMachine | | this is MrfNodeInlinedStateMachine ) & & ! ( initNode is MrfNodeStateBase ) )
{ } // no hits, state machines initial node is always a MrfNodeStateBase
InitialNode = initNode ;
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
InitialNodeFileOffset = InitialNode . FileOffset ;
InitialNodeOffset = InitialNodeFileOffset - ( FileOffset + 0x8 ) ;
}
protected void ResolveNodeOffsetsInTransitions ( MrfStateTransition [ ] transitions , MrfFile mrf )
{
if ( transitions = = null )
{
return ;
}
foreach ( var t in transitions )
{
var node = mrf . FindNodeAtFileOffset ( t . TargetStateFileOffset ) ;
if ( node = = null )
{ } // no hits
if ( ! ( node is MrfNodeStateBase ) )
{ } // no hits
t . TargetState = ( MrfNodeStateBase ) node ;
}
}
protected void ResolveNodeOffsetsInStates ( MrfStateRef [ ] states , MrfFile mrf )
{
if ( states = = null )
{
return ;
}
foreach ( var s in states )
{
var node = mrf . FindNodeAtFileOffset ( s . StateFileOffset ) ;
if ( node = = null )
{ } // no hits
if ( ! ( node is MrfNodeStateBase ) )
{ } // no hits
s . State = ( MrfNodeStateBase ) node ;
}
}
protected int UpdateNodeOffsetsInTransitions ( MrfStateTransition [ ] transitions , int transitionsArrayOffset , bool offsetSetToZeroIfNoTransitions )
{
int offset = transitionsArrayOffset ;
TransitionsFileOffset = offset ;
TransitionsOffset = TransitionsFileOffset - ( FileOffset + 0x1C ) ;
if ( transitions ! = null )
{
foreach ( var transition in transitions )
{
transition . TargetStateFileOffset = transition . TargetState . FileOffset ;
transition . TargetStateOffset = transition . TargetStateFileOffset - ( offset + 0x14 ) ;
transition . CalculateDataSize ( ) ;
offset + = ( int ) transition . DataSize ;
}
}
else if ( offsetSetToZeroIfNoTransitions )
{
// Special case for some MrfNodeStateMachines with no transitions and MrfNodeInlinedStateMachines.
// Unlike MrfNodeState, when these don't have transtions the TransititionsOffset is 0.
// So we set it to 0 too to be able to compare Save() result byte by byte
TransitionsOffset = 0 ;
TransitionsFileOffset = FileOffset + 0x1C ; // and keep FileOffset consistent with what Read() does
}
return offset ;
}
protected int UpdateNodeOffsetsInStates ( MrfStateRef [ ] states , int statesArrayOffset )
{
int offset = statesArrayOffset ;
if ( states ! = null )
{
foreach ( var state in states )
{
state . StateFileOffset = state . State . FileOffset ;
state . StateOffset = state . StateFileOffset - ( offset + 4 ) ;
offset + = 8 ; // sizeof(MrfStructStateMachineStateRef)
}
}
return offset ;
}
public override string ToString ( )
{
return base . ToString ( ) + " - " + Name . ToString ( )
+ " - Init:" + InitialNodeOffset . ToString ( )
+ " - CC:" + StateChildCount . ToString ( )
+ " - TC:" + TransitionCount . ToString ( )
2022-01-27 00:35:50 +08:00
+ " - " + StateUnk3 . ToString ( )
2022-08-09 03:54:23 +08:00
+ " - OnEntry(" + HasEntryParameter . ToString ( ) + "):" + EntryParameterName . ToString ( )
+ " - OnExit(" + HasExitParameter . ToString ( ) + "):" + ExitParameterName . ToString ( )
+ " - TO:" + TransitionsOffset . ToString ( ) ;
2021-06-12 06:23:15 +08:00
}
2022-01-27 00:35:50 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public abstract class MrfNodePairBase : MrfNodeWithFlagsBase
{
public int Child0Offset { get ; set ; }
public int Child0FileOffset { get ; set ; }
public int Child1Offset { get ; set ; }
public int Child1FileOffset { get ; set ; }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNode Child0 { get ; set ; }
public MrfNode Child1 { get ; set ; }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodePairBase ( MrfNodeType type ) : base ( type ) { }
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
Child0Offset = r . ReadInt32 ( ) ;
Child0FileOffset = ( int ) ( r . Position + Child0Offset - 4 ) ;
Child1Offset = r . ReadInt32 ( ) ;
Child1FileOffset = ( int ) ( r . Position + Child1Offset - 4 ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
w . Write ( Child0Offset ) ;
w . Write ( Child1Offset ) ;
}
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
var child0 = mrf . FindNodeAtFileOffset ( Child0FileOffset ) ;
if ( child0 = = null )
{ } // no hits
var child1 = mrf . FindNodeAtFileOffset ( Child1FileOffset ) ;
if ( child1 = = null )
{ } // no hits
Child0 = child0 ;
Child1 = child1 ;
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
Child0FileOffset = Child0 . FileOffset ;
Child0Offset = Child0FileOffset - ( FileOffset + 0xC + 0 ) ;
Child1FileOffset = Child1 . FileOffset ;
Child1Offset = Child1FileOffset - ( FileOffset + 0xC + 4 ) ;
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
public enum MrfSynchronizerType
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
Phase = 0 , // attaches a rage::mvSynchronizerPhase instance to the node
Tag = 1 , // attaches a rage::mvSynchronizerTag instance to the node, sets the SynchronizerTagFlags field too
None = 2 ,
}
[Flags] public enum MrfSynchronizerTagFlags : uint
{
LeftFootHeel = 0x20 , // adds rage::mvSynchronizerTag::sm_LeftFootHeel to a rage::mvSynchronizerTag instance attached to the node
RightFootHeel = 0x40 , // same but with rage::mvSynchronizerTag::sm_RightFootHeel
}
public enum MrfValueType
{
None = 0 ,
Literal = 1 , // specific value (in case of expressions, clips or filters, a dictionary/name hash pair)
Parameter = 2 , // lookup value in the network parameters
}
public enum MrfInfluenceOverride
{
None = 0 , // influence affected by weight (at least in NodeBlend case)
Zero = 1 , // influence = 0.0
One = 2 , // influence = 1.0
}
[TC(typeof(EXP))] public abstract class MrfNodePairWeightedBase : MrfNodePairBase
{
// rage::mvNodePairDef
public MrfSynchronizerTagFlags SynchronizerTagFlags { get ; set ; }
public float Weight { get ; set ; }
public MetaHash WeightParameterName { get ; set ; }
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MetaHash FrameFilterParameterName { get ; set ; }
// flags getters and setters
public MrfValueType WeightType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType FrameFilterType
{
get = > ( MrfValueType ) GetFlagsSubset ( 2 , 3 ) ;
set = > SetFlagsSubset ( 2 , 3 , ( uint ) value ) ;
}
public bool UnkFlag6 // Transitional? RDR3's rage::mvNodePairDef::GetTransitionalFlagFrom(uint) reads these bits
{ // always 0
get = > GetFlagsSubset ( 6 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 6 , 1 , value ? 1 : 0 u ) ;
}
public uint UnkFlag7 // Immutable? RDR3's rage::mvNodePairDef::GetImmutableFlagFrom(unsigned int) reads these bits
{ // 0 or 1
get = > GetFlagsSubset ( 7 , 3 ) ;
set = > SetFlagsSubset ( 7 , 3 , value ) ;
}
public MrfInfluenceOverride Child0InfluenceOverride
{
get = > ( MrfInfluenceOverride ) GetFlagsSubset ( 12 , 3 ) ;
set = > SetFlagsSubset ( 12 , 3 , ( uint ) value ) ;
}
public MrfInfluenceOverride Child1InfluenceOverride
{
get = > ( MrfInfluenceOverride ) GetFlagsSubset ( 14 , 3 ) ;
set = > SetFlagsSubset ( 14 , 3 , ( uint ) value ) ;
}
public MrfSynchronizerType SynchronizerType
{
get = > ( MrfSynchronizerType ) GetFlagsSubset ( 19 , 3 ) ;
set = > SetFlagsSubset ( 19 , 3 , ( uint ) value ) ;
}
public uint UnkFlag21 // OutputParameterRuleSet? RDR3's rage::mvNodePairDef::GetOutputParameterRuleSetFrom(uint) reads these bits
{ // always 0
get = > GetFlagsSubset ( 21 , 3 ) ;
set = > SetFlagsSubset ( 21 , 3 , value ) ;
}
public uint UnkFlag23 // FirstFrameSyncOnly? RDR3's rage::mvNodePairDef::GetFirstFrameSyncOnlyFrom(uint) reads these bits
{ // always 0
get = > GetFlagsSubset ( 23 , 3 ) ;
set = > SetFlagsSubset ( 23 , 3 , value ) ;
}
public bool UnkFlag25 // SortTargets? RDR3's rage::mvNodePairDef::GetSortTargetsFrom(uint) reads these bits
{ // always 0
get = > GetFlagsSubset ( 25 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 25 , 1 , value ? 1 : 0 u ) ;
}
public bool MergeBlend
{
get = > GetFlagsSubset ( 31 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 31 , 1 , value ? 1 : 0 u ) ;
}
public MrfNodePairWeightedBase ( MrfNodeType type ) : base ( type ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
SynchronizerTagFlags = ( MrfSynchronizerTagFlags ) r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( WeightType )
{
case MrfValueType . Literal :
Weight = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
WeightParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
FrameFilterName = r . ReadUInt32 ( ) ;
2021-11-06 08:17:53 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
FrameFilterParameterName = r . ReadUInt32 ( ) ;
2021-11-06 08:17:53 +08:00
break ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
w . Write ( ( uint ) SynchronizerTagFlags ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( WeightType )
{
case MrfValueType . Literal :
w . Write ( Weight ) ;
break ;
case MrfValueType . Parameter :
w . Write ( WeightParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
w . Write ( FrameFilterDictionaryName ) ;
w . Write ( FrameFilterName ) ;
2021-11-06 08:17:53 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( FrameFilterParameterName ) ;
2021-11-06 08:17:53 +08:00
break ;
}
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public abstract class MrfNodeWithChildBase : MrfNodeWithFlagsBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public int ChildOffset { get ; set ; }
public int ChildFileOffset { get ; set ; }
public MrfNode Child { get ; set ; }
public MrfNodeWithChildBase ( MrfNodeType type ) : base ( type ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
ChildOffset = r . ReadInt32 ( ) ;
ChildFileOffset = ( int ) ( r . Position + ChildOffset - 4 ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( ChildOffset ) ;
}
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
var node = mrf . FindNodeAtFileOffset ( ChildFileOffset ) ;
if ( node = = null )
{ } // no hits
Child = node ;
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
ChildFileOffset = Child . FileOffset ;
ChildOffset = ChildFileOffset - ( FileOffset + 0xC ) ;
}
}
[TC(typeof(EXP))] public abstract class MrfNodeWithChildAndFilterBase : MrfNodeWithChildBase
{
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MetaHash FrameFilterParameterName { get ; set ; }
// flags getters and setters
public MrfValueType FrameFilterType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfNodeWithChildAndFilterBase ( MrfNodeType type ) : base ( type ) { }
public override void Read ( DataReader r )
{
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
FrameFilterName = r . ReadUInt32 ( ) ;
2021-11-06 08:17:53 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
FrameFilterParameterName = r . ReadUInt32 ( ) ;
2021-11-06 08:17:53 +08:00
break ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
w . Write ( FrameFilterDictionaryName ) ;
w . Write ( FrameFilterName ) ;
2021-11-06 08:17:53 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( FrameFilterParameterName ) ;
2021-11-06 08:17:53 +08:00
break ;
}
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public abstract class MrfNodeNBase : MrfNodeWithFlagsBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeNDef
public MrfSynchronizerTagFlags SynchronizerTagFlags { get ; set ; }
public byte [ ] Unk2 { get ; set ; } // unused
public MetaHash Unk2ParameterName { get ; set ; } // unused
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MetaHash FrameFilterParameterName { get ; set ; }
public int [ ] ChildrenOffsets { get ; set ; }
public int [ ] ChildrenFileOffsets { get ; set ; }
public MrfNodeNChildData [ ] ChildrenData { get ; set ; }
public uint [ ] ChildrenFlags { get ; set ; } // 8 bits per child
public MrfNode [ ] Children { get ; set ; }
// flags getters and setters
public MrfValueType Unk2Type
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType FrameFilterType
{
get = > ( MrfValueType ) GetFlagsSubset ( 2 , 3 ) ;
set = > SetFlagsSubset ( 2 , 3 , ( uint ) value ) ;
}
public bool ZeroDestination // name is correct based on RDR3 symbols but not sure about its use
{ // only difference I found is when true the weight of child #0 returns 1.0 instead of the weight in ChildrenData
get = > GetFlagsSubset ( 4 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 4 , 1 , value ? 1 : 0 u ) ;
}
public MrfSynchronizerType SynchronizerType
{
get = > ( MrfSynchronizerType ) GetFlagsSubset ( 19 , 3 ) ;
set = > SetFlagsSubset ( 19 , 3 , ( uint ) value ) ;
}
public uint ChildrenCount
{
get = > GetFlagsSubset ( 26 , 0x3F ) ;
set = > SetFlagsSubset ( 26 , 0x3F , value ) ;
}
// ChildrenFlags getters and setters
public byte GetChildFlags ( int index )
{
int blockIndex = 8 * index / 32 ;
int bitOffset = 8 * index % 32 ;
uint block = ChildrenFlags [ blockIndex ] ;
return ( byte ) ( ( block > > bitOffset ) & 0xFF ) ;
}
public void SetChildFlags ( int index , byte flags )
{
int blockIndex = 8 * index / 32 ;
int bitOffset = 8 * index % 32 ;
uint block = ChildrenFlags [ blockIndex ] ;
block = ( block & ~ ( 0xFF u < < bitOffset ) ) | ( ( uint ) flags < < bitOffset ) ;
ChildrenFlags [ blockIndex ] = block ;
}
public MrfValueType GetChildWeightType ( int index )
{
return ( MrfValueType ) ( GetChildFlags ( index ) & 3 ) ;
}
public void SetChildWeightType ( int index , MrfValueType type )
{
var flags = GetChildFlags ( index ) ;
flags = ( byte ) ( flags & ~ 3 u | ( ( uint ) type & 3 u ) ) ;
SetChildFlags ( index , flags ) ;
}
public MrfValueType GetChildFrameFilterType ( int index )
{
return ( MrfValueType ) ( ( GetChildFlags ( index ) > > 4 ) & 3 ) ;
}
public void SetChildFrameFilterType ( int index , MrfValueType type )
{
var flags = GetChildFlags ( index ) ;
flags = ( byte ) ( flags & ~ ( 3 u < < 4 ) | ( ( ( uint ) type & 3 u ) < < 4 ) ) ;
SetChildFlags ( index , flags ) ;
}
public MrfNodeNBase ( MrfNodeType type ) : base ( type ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
SynchronizerTagFlags = ( MrfSynchronizerTagFlags ) r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( Unk2Type )
{
case MrfValueType . Literal :
Unk2 = r . ReadBytes ( 76 ) ; // Unused?
break ;
case MrfValueType . Parameter :
Unk2ParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
FrameFilterName = r . ReadUInt32 ( ) ;
break ;
case MrfValueType . Parameter :
FrameFilterParameterName = r . ReadUInt32 ( ) ;
break ;
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
var childrenCount = ChildrenCount ;
if ( childrenCount > 0 )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
ChildrenOffsets = new int [ childrenCount ] ;
ChildrenFileOffsets = new int [ childrenCount ] ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < childrenCount ; i + + )
{
ChildrenOffsets [ i ] = r . ReadInt32 ( ) ;
ChildrenFileOffsets [ i ] = ( int ) ( r . Position + ChildrenOffsets [ i ] - 4 ) ;
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
var childrenFlagsBlockCount = ( ( ( 2 * childrenCount ) | 7 ) + 1 ) > > 3 ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( childrenFlagsBlockCount > 0 )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
ChildrenFlags = new uint [ childrenFlagsBlockCount ] ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < childrenFlagsBlockCount ; i + + )
ChildrenFlags [ i ] = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
if ( ChildrenCount = = 0 )
2021-06-12 06:23:15 +08:00
return ;
2022-08-09 03:54:23 +08:00
ChildrenData = new MrfNodeNChildData [ childrenCount ] ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < childrenCount ; i + + )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
var item = new MrfNodeNChildData ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( GetChildWeightType ( i ) )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
item . Weight = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
item . WeightParameterName = r . ReadUInt32 ( ) ;
break ;
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
switch ( GetChildFrameFilterType ( i ) )
{
case MrfValueType . Literal :
item . FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
item . FrameFilterName = r . ReadUInt32 ( ) ;
break ;
case MrfValueType . Parameter :
item . FrameFilterParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
ChildrenData [ i ] = item ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
w . Write ( ( uint ) SynchronizerTagFlags ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( Unk2Type )
{
case MrfValueType . Literal :
w . Write ( Unk2 ) ;
break ;
case MrfValueType . Parameter :
w . Write ( Unk2ParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
w . Write ( FrameFilterDictionaryName ) ;
w . Write ( FrameFilterName ) ;
break ;
case MrfValueType . Parameter :
w . Write ( FrameFilterParameterName ) ;
break ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
var childrenCount = ChildrenCount ;
if ( childrenCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var value in ChildrenOffsets )
2021-11-06 08:17:53 +08:00
w . Write ( value ) ;
}
2022-08-09 03:54:23 +08:00
var childrenFlagsBlockCount = ( ( ( 2 * childrenCount ) | 7 ) + 1 ) > > 3 ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( childrenFlagsBlockCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var value in ChildrenFlags )
2021-11-06 08:17:53 +08:00
w . Write ( value ) ;
}
2022-08-09 03:54:23 +08:00
if ( childrenCount = = 0 )
2021-11-06 08:17:53 +08:00
return ;
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < childrenCount ; i + + )
{
var item = ChildrenData [ i ] ;
switch ( GetChildWeightType ( i ) )
{
case MrfValueType . Literal :
w . Write ( item . Weight ) ;
break ;
case MrfValueType . Parameter :
w . Write ( item . WeightParameterName ) ;
break ;
}
switch ( GetChildFrameFilterType ( i ) )
{
case MrfValueType . Literal :
w . Write ( item . FrameFilterDictionaryName ) ;
w . Write ( item . FrameFilterName ) ;
break ;
case MrfValueType . Parameter :
w . Write ( item . FrameFilterParameterName ) ;
break ;
}
}
}
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
if ( ChildrenFileOffsets ! = null )
{
Children = new MrfNode [ ChildrenFileOffsets . Length ] ;
for ( int i = 0 ; i < ChildrenFileOffsets . Length ; i + + )
{
var node = mrf . FindNodeAtFileOffset ( ChildrenFileOffsets [ i ] ) ;
if ( node = = null )
{ } // no hits
Children [ i ] = node ;
}
}
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
var offset = FileOffset + 0xC /*sizeof(MrfNodeWithFlagsBase)*/ ;
offset + = SynchronizerType = = MrfSynchronizerType . Tag ? 4 : 0 ;
offset + = Unk2Type = = MrfValueType . Literal ? 76 : 0 ;
offset + = Unk2Type = = MrfValueType . Parameter ? 4 : 0 ;
offset + = FrameFilterType = = MrfValueType . Literal ? 8 : 0 ;
offset + = FrameFilterType = = MrfValueType . Parameter ? 4 : 0 ;
if ( Children ! = null )
{
ChildrenOffsets = new int [ Children . Length ] ;
ChildrenFileOffsets = new int [ Children . Length ] ;
for ( int i = 0 ; i < Children . Length ; i + + )
{
var node = Children [ i ] ;
ChildrenFileOffsets [ i ] = node . FileOffset ;
ChildrenOffsets [ i ] = node . FileOffset - offset ;
offset + = 4 ;
}
}
}
}
[TC(typeof(EXP))] public struct MrfNodeNChildData
{
public float Weight { get ; set ; }
public MetaHash WeightParameterName { get ; set ; }
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MetaHash FrameFilterParameterName { get ; set ; }
public override string ToString ( )
{
return $"{FloatUtil.ToString(Weight)} - {WeightParameterName} - {FrameFilterDictionaryName} - {FrameFilterName} - {FrameFilterParameterName}" ;
}
}
#endregion
#region mrf node structs
[TC(typeof(EXP))] public class MrfHeaderUnk1
{
public uint Size { get ; set ; }
public byte [ ] Bytes { get ; set ; }
public MrfHeaderUnk1 ( DataReader r )
{
Size = r . ReadUInt32 ( ) ;
Bytes = r . ReadBytes ( ( int ) Size ) ;
}
public void Write ( DataWriter w )
{
w . Write ( Size ) ;
w . Write ( Bytes ) ;
}
public override string ToString ( )
{
return Size . ToString ( ) + " bytes" ;
}
}
/// <summary>
/// Parameter that can be triggered by the game to control transitions.
/// Only active for 1 tick.
/// The native `REQUEST_TASK_MOVE_NETWORK_STATE_TRANSITION` uses these triggers but appends "request" to the passed string,
/// e.g. `REQUEST_TASK_MOVE_NETWORK_STATE_TRANSITION(ped, "running")` will trigger "runningrequest".
/// </summary>
[TC(typeof(EXP))] public class MrfMoveNetworkTrigger
{
public MetaHash Name { get ; set ; }
public int BitPosition { get ; set ; }
public MrfMoveNetworkTrigger ( DataReader r )
{
Name = r . ReadUInt32 ( ) ;
BitPosition = r . ReadInt32 ( ) ;
}
public void Write ( DataWriter w )
{
w . Write ( Name ) ;
w . Write ( BitPosition ) ;
}
public override string ToString ( )
{
return Name = = 0xFFFFFFFF ? "--- bucket separator ---" : $"{Name} - {BitPosition}" ;
}
}
/// <summary>
/// Parameter that can be toggled by the game to control transitions.
/// Can be enabled with fwClipSet.moveNetworkFlags too (seems like only if the game uses it as a MrfClipContainerType.VariableClipSet).
/// </summary>
[TC(typeof(EXP))] public class MrfMoveNetworkFlag
{
public MetaHash Name { get ; set ; }
public int BitPosition { get ; set ; }
public MrfMoveNetworkFlag ( DataReader r )
{
Name = r . ReadUInt32 ( ) ;
BitPosition = r . ReadInt32 ( ) ;
}
public void Write ( DataWriter w )
{
w . Write ( Name ) ;
w . Write ( BitPosition ) ;
}
public override string ToString ( )
{
return Name = = 0xFFFFFFFF ? "--- bucket separator ---" : $"{Name} - {BitPosition}" ;
}
}
public enum MrfWeightModifierType
{
SlowInSlowOut = 0 ,
SlowOut = 1 ,
SlowIn = 2 ,
None = 3 ,
}
[TC(typeof(EXP))] public class MrfStateTransition
{
// rage::mvTransitionDef
public uint Flags { get ; set ; }
public MrfSynchronizerTagFlags SynchronizerTagFlags { get ; set ; }
public float Duration { get ; set ; } // time in seconds it takes for the transition to blend between the source and target states
public MetaHash DurationParameterName { get ; set ; }
public MetaHash ProgressParameterName { get ; set ; } // parameter where to store the transition progress percentage (0.0 to 1.0)
public int TargetStateOffset { get ; set ; } // offset from the start of this field
public int TargetStateFileOffset { get ; set ; }
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MrfCondition [ ] Conditions { get ; set ; }
public MrfNodeStateBase TargetState { get ; set ; }
// flags getters and setters
public bool HasProgressParameter // if set, the transition progress percentage (0.0 to 1.0) is stored in ProgressParameterName
{
get = > GetFlagsSubset ( 1 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 1 , 1 , value ? 1 : 0 u ) ;
}
public bool UnkFlag2_DetachUpdateObservers // if set, executes rage::DetachUpdateObservers on the source state
{
get = > GetFlagsSubset ( 1 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 1 , 1 , value ? 1 : 0 u ) ;
}
public bool HasDurationParameter // if set use DurationParameterName instead of DurationValue. DurationValue is used as default if the paramter is not found
{
get = > GetFlagsSubset ( 3 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 3 , 1 , value ? 1 : 0 u ) ;
}
public uint DataSize // number of bytes this transition takes, used to iterate the transitions array
{
get = > GetFlagsSubset ( 4 , 0x3FFF ) ;
set = > SetFlagsSubset ( 4 , 0x3FFF , value ) ;
}
public uint UnkFlag19
{
get = > GetFlagsSubset ( 19 , 1 ) ;
set = > SetFlagsSubset ( 19 , 1 , value ) ;
}
public uint ConditionCount
{
get = > GetFlagsSubset ( 20 , 0xF ) ;
set = > SetFlagsSubset ( 20 , 0xF , value ) ;
}
public MrfWeightModifierType BlendModifier // modifier for the blend between source and target states
{
get = > ( MrfWeightModifierType ) GetFlagsSubset ( 24 , 7 ) ;
set = > SetFlagsSubset ( 24 , 7 , ( uint ) value ) ;
}
public MrfSynchronizerType SynchronizerType
{
get = > ( MrfSynchronizerType ) GetFlagsSubset ( 28 , 3 ) ;
set = > SetFlagsSubset ( 28 , 3 , ( uint ) value ) ;
}
public bool HasFrameFilter
{
get = > GetFlagsSubset ( 30 , 1 ) ! = 0 ;
set = > SetFlagsSubset ( 30 , 1 , value ? 1 : 0 u ) ;
}
public MrfStateTransition ( )
{
}
public MrfStateTransition ( DataReader r )
{
var startReadPosition = r . Position ;
Flags = r . ReadUInt32 ( ) ;
SynchronizerTagFlags = ( MrfSynchronizerTagFlags ) r . ReadUInt32 ( ) ;
Duration = r . ReadSingle ( ) ;
DurationParameterName = r . ReadUInt32 ( ) ;
ProgressParameterName = r . ReadUInt32 ( ) ;
TargetStateOffset = r . ReadInt32 ( ) ;
TargetStateFileOffset = ( int ) ( r . Position + TargetStateOffset - 4 ) ;
if ( ConditionCount > 0 )
{
Conditions = new MrfCondition [ ConditionCount ] ;
for ( int i = 0 ; i < ConditionCount ; i + + )
{
var startPos = r . Position ;
var conditionType = ( MrfConditionType ) r . ReadUInt16 ( ) ;
r . Position = startPos ;
MrfCondition cond ;
switch ( conditionType )
{
case MrfConditionType . ParameterInsideRange : cond = new MrfConditionParameterInsideRange ( r ) ; break ;
case MrfConditionType . ParameterOutsideRange : cond = new MrfConditionParameterOutsideRange ( r ) ; break ;
case MrfConditionType . MoveNetworkTrigger : cond = new MrfConditionMoveNetworkTrigger ( r ) ; break ;
case MrfConditionType . MoveNetworkFlag : cond = new MrfConditionMoveNetworkFlag ( r ) ; break ;
case MrfConditionType . EventOccurred : cond = new MrfConditionEventOccurred ( r ) ; break ;
case MrfConditionType . ParameterGreaterThan : cond = new MrfConditionParameterGreaterThan ( r ) ; break ;
case MrfConditionType . ParameterGreaterOrEqual : cond = new MrfConditionParameterGreaterOrEqual ( r ) ; break ;
case MrfConditionType . ParameterLessThan : cond = new MrfConditionParameterLessThan ( r ) ; break ;
case MrfConditionType . ParameterLessOrEqual : cond = new MrfConditionParameterLessOrEqual ( r ) ; break ;
case MrfConditionType . TimeGreaterThan : cond = new MrfConditionTimeGreaterThan ( r ) ; break ;
case MrfConditionType . TimeLessThan : cond = new MrfConditionTimeLessThan ( r ) ; break ;
case MrfConditionType . BoolParameterExists : cond = new MrfConditionBoolParameterExists ( r ) ; break ;
case MrfConditionType . BoolParameterEquals : cond = new MrfConditionBoolParameterEquals ( r ) ; break ;
default : throw new Exception ( $"Unknown condition type ({conditionType})" ) ;
}
Conditions [ i ] = cond ;
}
}
if ( HasFrameFilter )
{
FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
FrameFilterName = r . ReadUInt32 ( ) ;
}
else
{
FrameFilterDictionaryName = 0 ;
FrameFilterName = 0 ;
}
if ( ( r . Position - startReadPosition ) ! = DataSize )
{ } // not hits
}
public void Write ( DataWriter w )
{
ConditionCount = ( uint ) ( Conditions ? . Length ? ? 0 ) ;
w . Write ( Flags ) ;
w . Write ( ( uint ) SynchronizerTagFlags ) ;
w . Write ( Duration ) ;
w . Write ( DurationParameterName ) ;
w . Write ( ProgressParameterName ) ;
w . Write ( TargetStateOffset ) ;
if ( Conditions ! = null )
for ( int i = 0 ; i < Conditions . Length ; i + + )
Conditions [ i ] . Write ( w ) ;
if ( HasFrameFilter )
{
w . Write ( FrameFilterDictionaryName ) ;
w . Write ( FrameFilterName ) ;
}
}
public uint GetFlagsSubset ( int bitOffset , uint mask )
{
return ( Flags > > bitOffset ) & mask ;
}
public void SetFlagsSubset ( int bitOffset , uint mask , uint value )
{
Flags = ( Flags & ~ ( mask < < bitOffset ) ) | ( ( value & mask ) < < bitOffset ) ;
}
public void CalculateDataSize ( )
{
uint dataSize = 0x18 ;
if ( Conditions ! = null )
{
dataSize + = ( uint ) Conditions . Sum ( c = > c . DataSize ) ;
}
if ( HasFrameFilter )
{
dataSize + = 8 ;
}
DataSize = dataSize ;
}
public override string ToString ( )
{
return $"{TargetState?.Name.ToString() ?? TargetStateFileOffset.ToString()} - {FloatUtil.ToString(Duration)} - {Conditions?.Length ?? 0} conditions" ;
}
}
public enum MrfConditionType : ushort
{
ParameterInsideRange = 0 , // condition = Param > MinValue && Param < MaxValue
ParameterOutsideRange = 1 , // condition = Param < MinValue || Param > MaxValue
MoveNetworkTrigger = 2 , // condition = bittest(rage::mvMotionWeb.field_8, BitPosition) != Invert (each bit of field_8 represents a MrfMoveNetworkTrigger)
MoveNetworkFlag = 3 , // condition = bittest(rage::mvMotionWeb.field_C, BitPosition) != Invert (each bit of field_C represents a MrfMoveNetworkFlag)
EventOccurred = 4 , // condition = same behaviour as BoolParamExists but seems to be used with event names only
ParameterGreaterThan = 5 , // condition = Param > Value
ParameterGreaterOrEqual = 6 , // condition = Param >= Value
ParameterLessThan = 7 , // condition = Param < Value
ParameterLessOrEqual = 8 , // condition = Param <= Value
TimeGreaterThan = 9 , // condition = Time > Value (time since tha state started in seconds)
TimeLessThan = 10 , // condition = Time < Value
BoolParameterExists = 11 , // condition = exists(Param) != Invert
BoolParameterEquals = 12 , // condition = Param == Value
}
[TC(typeof(EXP))] public abstract class MrfCondition
{
// rage::mvConditionDef
public MrfConditionType Type { get ; set ; }
public short Unk2 { get ; set ; }
public abstract uint DataSize { get ; }
public MrfCondition ( MrfConditionType type )
{
Type = type ;
}
public MrfCondition ( DataReader r )
{
Type = ( MrfConditionType ) r . ReadUInt16 ( ) ;
Unk2 = r . ReadInt16 ( ) ;
}
public virtual void Write ( DataWriter w )
{
w . Write ( ( ushort ) Type ) ;
w . Write ( Unk2 ) ;
}
public override string ToString ( )
{
return $"{Type} - {Unk2}" ;
}
/// <summary>
/// Returns the condition as a C-like expression. Mainly to include it in the debug DOT graphs.
/// </summary>
public abstract string ToExpressionString ( MrfFile mrf ) ;
}
[TC(typeof(EXP))] public abstract class MrfConditionWithParameterAndRangeBase : MrfCondition
{
public override uint DataSize = > 16 ;
public MetaHash ParameterName { get ; set ; }
public float MaxValue { get ; set ; }
public float MinValue { get ; set ; }
public MrfConditionWithParameterAndRangeBase ( MrfConditionType type ) : base ( type ) { }
public MrfConditionWithParameterAndRangeBase ( DataReader r ) : base ( r )
{
ParameterName = r . ReadUInt32 ( ) ;
MaxValue = r . ReadSingle ( ) ;
MinValue = r . ReadSingle ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( ParameterName ) ;
w . Write ( MaxValue ) ;
w . Write ( MinValue ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + $" - {{ {nameof(ParameterName)} = {ParameterName}, {nameof(MaxValue)} = {FloatUtil.ToString(MaxValue)}, {nameof(MinValue)} = {FloatUtil.ToString(MinValue)} }}" ;
}
}
[TC(typeof(EXP))] public abstract class MrfConditionWithParameterAndValueBase : MrfCondition
{
public override uint DataSize = > 12 ;
public MetaHash ParameterName { get ; set ; }
public float Value { get ; set ; }
public MrfConditionWithParameterAndValueBase ( MrfConditionType type ) : base ( type ) { }
public MrfConditionWithParameterAndValueBase ( DataReader r ) : base ( r )
{
ParameterName = r . ReadUInt32 ( ) ;
Value = r . ReadSingle ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( ParameterName ) ;
w . Write ( Value ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + $" - {{ {nameof(ParameterName)} = {ParameterName}, {nameof(Value)} = {FloatUtil.ToString(Value)} }}" ;
}
}
[TC(typeof(EXP))] public abstract class MrfConditionWithValueBase : MrfCondition
{
public override uint DataSize = > 8 ;
public float Value { get ; set ; }
public MrfConditionWithValueBase ( MrfConditionType type ) : base ( type ) { }
public MrfConditionWithValueBase ( DataReader r ) : base ( r )
{
Value = r . ReadSingle ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( Value ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + $" - {{ {nameof(Value)} = {FloatUtil.ToString(Value)} }}" ;
}
}
[TC(typeof(EXP))] public abstract class MrfConditionWithParameterAndBoolValueBase : MrfCondition
{
public override uint DataSize = > 12 ;
public MetaHash ParameterName { get ; set ; }
public bool Value { get ; set ; }
public MrfConditionWithParameterAndBoolValueBase ( MrfConditionType type ) : base ( type ) { }
public MrfConditionWithParameterAndBoolValueBase ( DataReader r ) : base ( r )
{
ParameterName = r . ReadUInt32 ( ) ;
Value = r . ReadUInt32 ( ) ! = 0 ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( ParameterName ) ;
w . Write ( Value ? 1 : 0 u ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + $" - {{ {nameof(ParameterName)} = {ParameterName}, {nameof(Value)} = {Value} }}" ;
}
}
[TC(typeof(EXP))] public abstract class MrfConditionBitTestBase : MrfCondition
{
public override uint DataSize = > 12 ;
public int BitPosition { get ; set ; }
public bool Invert { get ; set ; }
public MrfConditionBitTestBase ( MrfConditionType type ) : base ( type ) { }
public MrfConditionBitTestBase ( DataReader r ) : base ( r )
{
BitPosition = r . ReadInt32 ( ) ;
Invert = r . ReadUInt32 ( ) ! = 0 ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( BitPosition ) ;
w . Write ( Invert ? 1 u : 0 u ) ;
}
public string FindBitName ( MrfFile mrf )
{
MetaHash ? bitNameHash = null ;
if ( mrf ! = null )
{
bitNameHash = Type = = MrfConditionType . MoveNetworkTrigger ?
mrf . FindMoveNetworkTriggerForBit ( BitPosition ) ? . Name :
mrf . FindMoveNetworkFlagForBit ( BitPosition ) ? . Name ;
}
return bitNameHash . HasValue ? $"'{bitNameHash.Value}'" : BitPosition . ToString ( ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + $" - {{ {nameof(BitPosition)} = {BitPosition}, {nameof(Invert)} = {Invert} }}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterInsideRange : MrfConditionWithParameterAndRangeBase
{
public MrfConditionParameterInsideRange ( ) : base ( MrfConditionType . ParameterInsideRange ) { }
public MrfConditionParameterInsideRange ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return $"{FloatUtil.ToString(MinValue)} < '{ParameterName}' < {FloatUtil.ToString(MaxValue)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterOutsideRange : MrfConditionWithParameterAndRangeBase
{
public MrfConditionParameterOutsideRange ( ) : base ( MrfConditionType . ParameterOutsideRange ) { }
public MrfConditionParameterOutsideRange ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' < {FloatUtil.ToString(MinValue)} < {FloatUtil.ToString(MaxValue)} < '{ParameterName}'" ;
}
}
[TC(typeof(EXP))] public class MrfConditionMoveNetworkTrigger : MrfConditionBitTestBase
{
public MrfConditionMoveNetworkTrigger ( ) : base ( MrfConditionType . MoveNetworkTrigger ) { }
public MrfConditionMoveNetworkTrigger ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return ( Invert ? "!" : "" ) + $"trigger({FindBitName(mrf)})" ;
}
}
[TC(typeof(EXP))] public class MrfConditionMoveNetworkFlag : MrfConditionBitTestBase
{
public MrfConditionMoveNetworkFlag ( ) : base ( MrfConditionType . MoveNetworkFlag ) { }
public MrfConditionMoveNetworkFlag ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return ( Invert ? "!" : "" ) + $"flag({FindBitName(mrf)})" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterGreaterThan : MrfConditionWithParameterAndValueBase
{
public MrfConditionParameterGreaterThan ( ) : base ( MrfConditionType . ParameterGreaterThan ) { }
public MrfConditionParameterGreaterThan ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' > {FloatUtil.ToString(Value)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterGreaterOrEqual : MrfConditionWithParameterAndValueBase
{
public MrfConditionParameterGreaterOrEqual ( ) : base ( MrfConditionType . ParameterGreaterOrEqual ) { }
public MrfConditionParameterGreaterOrEqual ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' >= {FloatUtil.ToString(Value)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterLessThan : MrfConditionWithParameterAndValueBase
{
public MrfConditionParameterLessThan ( ) : base ( MrfConditionType . ParameterLessThan ) { }
public MrfConditionParameterLessThan ( DataReader r ) : base ( r ) { }
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' < {FloatUtil.ToString(Value)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionParameterLessOrEqual : MrfConditionWithParameterAndValueBase
{
public MrfConditionParameterLessOrEqual ( ) : base ( MrfConditionType . ParameterLessOrEqual ) { }
public MrfConditionParameterLessOrEqual ( DataReader r ) : base ( r ) { }
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' <= {FloatUtil.ToString(Value)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionTimeGreaterThan : MrfConditionWithValueBase
{
public MrfConditionTimeGreaterThan ( ) : base ( MrfConditionType . TimeGreaterThan ) { }
public MrfConditionTimeGreaterThan ( DataReader r ) : base ( r ) { }
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return $"Time > {FloatUtil.ToString(Value)}" ;
}
}
[TC(typeof(EXP))] public class MrfConditionTimeLessThan : MrfConditionWithValueBase
{
public MrfConditionTimeLessThan ( ) : base ( MrfConditionType . TimeLessThan ) { }
public MrfConditionTimeLessThan ( DataReader r ) : base ( r ) { }
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return $"Time < {FloatUtil.ToString(Value)}" ;
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfConditionEventOccurred : MrfConditionWithParameterAndBoolValueBase
{
public bool Invert { get = > Value ; set = > Value = value ; }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfConditionEventOccurred ( ) : base ( MrfConditionType . EventOccurred ) { }
public MrfConditionEventOccurred ( DataReader r ) : base ( r ) { }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return ( Invert ? "!" : "" ) + $"event('{ParameterName}')" ;
}
}
[TC(typeof(EXP))] public class MrfConditionBoolParameterExists : MrfConditionWithParameterAndBoolValueBase
{
public bool Invert { get = > Value ; set = > Value = value ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfConditionBoolParameterExists ( ) : base ( MrfConditionType . BoolParameterExists ) { }
public MrfConditionBoolParameterExists ( DataReader r ) : base ( r ) { }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public override string ToExpressionString ( MrfFile mrf )
{
return ( Invert ? "!" : "" ) + $"exists('{ParameterName}')" ;
}
}
[TC(typeof(EXP))] public class MrfConditionBoolParameterEquals : MrfConditionWithParameterAndBoolValueBase
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
public MrfConditionBoolParameterEquals ( ) : base ( MrfConditionType . BoolParameterEquals ) { }
public MrfConditionBoolParameterEquals ( DataReader r ) : base ( r ) { }
public override string ToExpressionString ( MrfFile mrf )
{
return $"'{ParameterName}' == {Value}" ;
}
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
/// <summary>
/// Before the target node updates, sets the target node parameter to the source network parameter value.
/// </summary>
[TC(typeof(EXP))] public class MrfStateInputParameter
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeStateDef::InputParameter
public MetaHash SourceParameterName { get ; }
public ushort TargetNodeIndex { get ; }
public /*MrfNodeParameterId*/ ushort TargetNodeParameterId { get ; }
public uint TargetNodeParameterExtraArg { get ; } // some node parameters require an additional argument to be passed (e.g. a name hash)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateInputParameter ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
SourceParameterName = r . ReadUInt32 ( ) ;
TargetNodeIndex = r . ReadUInt16 ( ) ;
TargetNodeParameterId = r . ReadUInt16 ( ) ;
TargetNodeParameterExtraArg = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( SourceParameterName ) ;
w . Write ( TargetNodeIndex ) ;
w . Write ( TargetNodeParameterId ) ;
w . Write ( TargetNodeParameterExtraArg ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return SourceParameterName . ToString ( ) + " - " + TargetNodeIndex . ToString ( ) + " - " + TargetNodeParameterId . ToString ( ) + " - " + TargetNodeParameterExtraArg . ToString ( ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
/// <summary>
/// Sets a network bool parameter named <see cref="ParameterName"/> to true when the event occurs on the specified node.
/// </summary>
[TC(typeof(EXP))] public class MrfStateEvent
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeStateDef::Event
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public ushort NodeIndex { get ; }
public /*MrfNodeEventId*/ ushort NodeEventId { get ; }
public MetaHash ParameterName { get ; }
public MrfStateEvent ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
NodeIndex = r . ReadUInt16 ( ) ;
NodeEventId = r . ReadUInt16 ( ) ;
ParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( NodeIndex ) ;
w . Write ( NodeEventId ) ;
w . Write ( ParameterName ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return NodeIndex . ToString ( ) + " - " + NodeEventId . ToString ( ) + " - " + ParameterName . ToString ( ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
/// <summary>
/// After the source node updates, sets the target network parameter to the source node parameter value.
/// </summary>
[TC(typeof(EXP))] public class MrfStateOutputParameter
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeStateDef::OutputParameter
public MetaHash TargetParameterName { get ; }
public ushort SourceNodeIndex { get ; }
public /*MrfNodeParameterId*/ ushort SourceNodeParameterId { get ; }
public uint SourceNodeParameterExtraArg { get ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOutputParameter ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
TargetParameterName = r . ReadUInt32 ( ) ;
SourceNodeIndex = r . ReadUInt16 ( ) ;
SourceNodeParameterId = r . ReadUInt16 ( ) ;
SourceNodeParameterExtraArg = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( TargetParameterName ) ;
w . Write ( SourceNodeIndex ) ;
w . Write ( SourceNodeParameterId ) ;
w . Write ( SourceNodeParameterExtraArg ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return TargetParameterName . ToString ( ) + " - " + SourceNodeIndex . ToString ( ) + " - " + SourceNodeParameterId . ToString ( ) + " - " + SourceNodeParameterExtraArg . ToString ( ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfStateRef
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public MetaHash StateName { get ; set ; }
public int StateOffset { get ; set ; } // offset from the start of this field
public int StateFileOffset { get ; set ; }
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeStateBase State { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateRef ( )
2021-06-12 06:23:15 +08:00
{
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateRef ( DataReader r )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
StateName = r . ReadUInt32 ( ) ;
StateOffset = r . ReadInt32 ( ) ;
StateFileOffset = ( int ) ( r . Position + StateOffset - 4 ) ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
{
w . Write ( StateName ) ;
w . Write ( StateOffset ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return StateName . ToString ( ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
public enum MrfOperatorType : uint
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
Finish = 0 , // finish the operation
PushLiteral = 1 , // push a specific value, not used in vanilla MRFs
PushParameter = 2 , // push a network parameter value
Add = 3 , // adds the two values at the top of the stack, not used in vanilla MRFs
Multiply = 4 , // multiplies the two values at the top of the stack
Remap = 5 , // remaps the value at the top of the stack to another range
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public abstract class MrfStateOperator
{
// rage::mvNodeStateDef::Operator
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfOperatorType Type { get ; set ; } //0, 2, 4, 5
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperator ( DataReader r )
{
Type = ( MrfOperatorType ) r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public virtual void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( ( uint ) Type ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
return Type . ToString ( ) ;
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorFinish : MrfStateOperator
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public uint Unk1_Unused { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorFinish ( DataReader r ) : base ( r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
Unk1_Unused = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
w . Write ( Unk1_Unused ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorPushLiteral : MrfStateOperator
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public float Value { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorPushLiteral ( DataReader r ) : base ( r )
2021-06-12 06:23:15 +08:00
{
2021-11-06 08:17:53 +08:00
}
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
w . Write ( Value ) ;
2021-06-12 06:23:15 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return Type + " " + FloatUtil . ToString ( Value ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorPushParameter : MrfStateOperator
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public MetaHash ParameterName { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorPushParameter ( DataReader r ) : base ( r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
ParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
w . Write ( ParameterName ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return Type + " '" + ParameterName + "'" ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorAdd : MrfStateOperator
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public uint Unk1_Unused { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorAdd ( DataReader r ) : base ( r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
Unk1_Unused = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
w . Write ( Unk1_Unused ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorMultiply : MrfStateOperator
{
public uint Unk1_Unused { get ; set ; }
public MrfStateOperatorMultiply ( DataReader r ) : base ( r )
{
Unk1_Unused = r . ReadUInt32 ( ) ;
}
public override void Write ( DataWriter w )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
w . Write ( Unk1_Unused ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorRemap : MrfStateOperator
2021-07-17 01:24:01 +08:00
{
2022-08-09 03:54:23 +08:00
public int DataOffset { get ; set ; } = 4 ; // offset from the start of this field to Min field (always 4)
public float Min { get ; set ; } // minimum of input range
public float Max { get ; set ; } // maximum of input range
public uint RangeCount { get ; set ; }
public int RangesOffset { get ; set ; } = 4 ; // offset from the start of this field to Ranges array (always 4)
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorRemapRange [ ] Ranges { get ; set ; } // output ranges to choose from
public MrfStateOperatorRemap ( DataReader r ) : base ( r )
2021-07-17 01:24:01 +08:00
{
2022-08-09 03:54:23 +08:00
DataOffset = r . ReadInt32 ( ) ;
Min = r . ReadSingle ( ) ;
Max = r . ReadSingle ( ) ;
RangeCount = r . ReadUInt32 ( ) ;
RangesOffset = r . ReadInt32 ( ) ;
Ranges = new MrfStateOperatorRemapRange [ RangeCount ] ;
for ( int i = 0 ; i < RangeCount ; i + + )
{
Ranges [ i ] = new MrfStateOperatorRemapRange ( r ) ;
}
2021-11-06 08:17:53 +08:00
}
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
base . Write ( w ) ;
RangeCount = ( uint ) ( Ranges ? . Length ? ? 0 ) ;
w . Write ( DataOffset ) ;
w . Write ( Min ) ;
w . Write ( Max ) ;
w . Write ( RangeCount ) ;
w . Write ( RangesOffset ) ;
foreach ( var item in Ranges )
item . Write ( w ) ;
2021-07-17 01:24:01 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return Type + " (" + FloatUtil . ToString ( Min ) + ".." + FloatUtil . ToString ( Max ) + ") -> [" + string . Join ( "," , Ranges . AsEnumerable ( ) ) + "]" ;
2022-01-27 00:35:50 +08:00
}
2021-07-17 01:24:01 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))]
public class MrfStateOperatorRemapRange
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
public uint Unk1_Unused { get ; } // always 0, seems unused
public float Percent { get ; } // if less than or equal to ((origValue - origMin) / (origMax - origMin)), this range is selected for the remap operation
public float Length { get ; } // Length = Max - Min
public float Min { get ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateOperatorRemapRange ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
Unk1_Unused = r . ReadUInt32 ( ) ;
Percent = r . ReadSingle ( ) ;
Length = r . ReadSingle ( ) ;
Min = r . ReadSingle ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( Unk1_Unused ) ;
w . Write ( Percent ) ;
w . Write ( Length ) ;
w . Write ( Min ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return FloatUtil . ToString ( Percent ) + " - (" + FloatUtil . ToString ( Min ) + ".." + FloatUtil . ToString ( Min + Length ) + ")" ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
/// <summary>
/// Before the node updates, calculates the specified operations and stores the value in a node parameter.
/// </summary>
[TC(typeof(EXP))] public class MrfStateOperation
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeStateDef::Operation
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public ushort NodeIndex { get ; }
public /*MrfNodeParameterId*/ ushort NodeParameterId { get ; }
public ushort Unk3 { get ; } //Items.Length * 8 // TODO: verify what is this
public ushort NodeParameterExtraArg { get ; }
public MrfStateOperator [ ] Operators { get ; }
public MrfStateOperation ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
NodeIndex = r . ReadUInt16 ( ) ;
NodeParameterId = r . ReadUInt16 ( ) ;
2021-11-06 08:17:53 +08:00
Unk3 = r . ReadUInt16 ( ) ;
2022-08-09 03:54:23 +08:00
NodeParameterExtraArg = r . ReadUInt16 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
var operators = new List < MrfStateOperator > ( ) ;
2021-06-12 06:23:15 +08:00
2022-02-01 19:59:24 +08:00
while ( true )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
var startPos = r . Position ;
var opType = ( MrfOperatorType ) r . ReadUInt32 ( ) ;
r . Position = startPos ;
MrfStateOperator op ;
switch ( opType )
{
case MrfOperatorType . Finish : op = new MrfStateOperatorFinish ( r ) ; break ;
case MrfOperatorType . PushLiteral : op = new MrfStateOperatorPushLiteral ( r ) ; break ;
case MrfOperatorType . PushParameter : op = new MrfStateOperatorPushParameter ( r ) ; break ;
case MrfOperatorType . Add : op = new MrfStateOperatorAdd ( r ) ; break ;
case MrfOperatorType . Multiply : op = new MrfStateOperatorMultiply ( r ) ; break ;
case MrfOperatorType . Remap : op = new MrfStateOperatorRemap ( r ) ; break ;
default : throw new Exception ( $"Unknown operator type ({opType})" ) ;
}
operators . Add ( op ) ;
if ( opType = = MrfOperatorType . Finish )
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
break ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
Operators = operators . ToArray ( ) ;
2022-02-01 19:59:24 +08:00
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
public void Write ( DataWriter w )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( NodeIndex ) ;
w . Write ( NodeParameterId ) ;
2021-11-06 08:17:53 +08:00
w . Write ( Unk3 ) ;
2022-08-09 03:54:23 +08:00
w . Write ( NodeParameterExtraArg ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
foreach ( var op in Operators )
op . Write ( w ) ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return NodeIndex . ToString ( ) + " - " + NodeParameterId . ToString ( ) + " - " + Unk3 . ToString ( ) + " - " + NodeParameterExtraArg . ToString ( ) + " - " +
( Operators ? . Length ? ? 0 ) . ToString ( ) + " operators" ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-01-27 00:35:50 +08:00
2021-06-12 06:23:15 +08:00
#endregion
2022-01-27 00:35:50 +08:00
#region mrf node classes
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeStateMachine : MrfNodeStateBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeStateMachineClass (1)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfStateRef [ ] States { get ; set ; }
public MrfStateTransition [ ] Transitions { get ; set ; }
public MrfNodeStateMachine ( ) : base ( MrfNodeType . StateMachine ) { }
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-11-06 08:17:53 +08:00
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
if ( StateChildCount > 0 )
{
2022-08-09 03:54:23 +08:00
States = new MrfStateRef [ StateChildCount ] ;
2022-01-27 00:35:50 +08:00
for ( int i = 0 ; i < StateChildCount ; i + + )
2022-08-09 03:54:23 +08:00
States [ i ] = new MrfStateRef ( r ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( TransitionCount > 0 )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = TransitionsFileOffset )
{ } // no hits
Transitions = new MrfStateTransition [ TransitionCount ] ;
for ( int i = 0 ; i < TransitionCount ; i + + )
Transitions [ i ] = new MrfStateTransition ( r ) ;
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
StateChildCount = ( byte ) ( States ? . Length ? ? 0 ) ;
TransitionCount = ( byte ) ( Transitions ? . Length ? ? 0 ) ;
2021-11-06 08:17:53 +08:00
base . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
if ( States ! = null )
foreach ( var state in States )
state . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( Transitions ! = null )
foreach ( var transition in Transitions )
transition . Write ( w ) ;
}
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
ResolveNodeOffsetsInTransitions ( Transitions , mrf ) ;
ResolveNodeOffsetsInStates ( States , mrf ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
var offset = ( int ) ( FileOffset + 0x20 /*sizeof(MrfNodeStateBase)*/ ) ;
offset = UpdateNodeOffsetsInStates ( States , offset ) ;
offset = UpdateNodeOffsetsInTransitions ( Transitions , offset ,
offsetSetToZeroIfNoTransitions : TransitionsOffset = = 0 ) ; // MrfNodeStateMachine doesn't seem consistent on whether TransitionsOffset
// should be 0 if there are no transitions, so if it's already zero don't change it
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeTail : MrfNode
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeTail (2)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeTail ( ) : base ( MrfNodeType . Tail ) { }
2022-01-27 00:35:50 +08:00
}
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
[TC(typeof(EXP))] public class MrfNodeInlinedStateMachine : MrfNodeStateBase
{
// rage__mvNodeInlinedStateMachine (3)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public int FallbackNodeOffset { get ; set ; }
public int FallbackNodeFileOffset { get ; set ; }
public MrfStateRef [ ] States { get ; set ; }
public MrfNode FallbackNode { get ; set ; } // node used when a NodeTail is reached (maybe in some other cases too?). This node is considered a child
// of the parent NodeState, so FallbackNode and its children (including their NodeIndex) should be
// included in the parent NodeState.StateChildCount, not in this NodeInlinedStateMachine.StateChildCount
public MrfNodeInlinedStateMachine ( ) : base ( MrfNodeType . InlinedStateMachine ) { }
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
FallbackNodeOffset = r . ReadInt32 ( ) ;
FallbackNodeFileOffset = ( int ) ( r . Position + FallbackNodeOffset - 4 ) ;
2021-06-12 06:23:15 +08:00
2022-01-27 00:35:50 +08:00
if ( StateChildCount > 0 )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
States = new MrfStateRef [ StateChildCount ] ;
2022-01-27 00:35:50 +08:00
for ( int i = 0 ; i < StateChildCount ; i + + )
2022-08-09 03:54:23 +08:00
States [ i ] = new MrfStateRef ( r ) ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-08-09 03:54:23 +08:00
StateChildCount = ( byte ) ( States ? . Length ? ? 0 ) ;
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
w . Write ( FallbackNodeOffset ) ;
2021-11-06 08:17:53 +08:00
2022-01-27 00:35:50 +08:00
if ( States ! = null )
foreach ( var item in States )
item . Write ( w ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
var fallbackNode = mrf . FindNodeAtFileOffset ( FallbackNodeFileOffset ) ;
if ( fallbackNode = = null )
{ } // no hits
FallbackNode = fallbackNode ;
ResolveNodeOffsetsInStates ( States , mrf ) ;
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
var offset = FileOffset + 0x20 /*sizeof(MrfNodeStateBase)*/ ;
FallbackNodeFileOffset = FallbackNode . FileOffset ;
FallbackNodeOffset = FallbackNodeFileOffset - offset ;
offset + = 4 ;
offset = UpdateNodeOffsetsInStates ( States , offset ) ;
offset = UpdateNodeOffsetsInTransitions ( null , offset , offsetSetToZeroIfNoTransitions : true ) ;
}
2022-01-27 00:35:50 +08:00
public override string ToString ( )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
return base . ToString ( ) + " - " + FallbackNodeOffset . ToString ( ) ;
2021-06-12 06:23:15 +08:00
}
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeAnimation : MrfNodeWithFlagsBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNode* (4) not used in final game
2022-08-09 03:54:23 +08:00
// Probably not worth researching further. Seems like the introduction of NodeClip (and rage::crClip), made this node obsolete.
// Even the function pointer used to lookup the rage::crAnimation when AnimationType==Literal is null, so the only way to get animations is through a parameter.
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public uint AnimationUnkDataLength { get ; set ; }
public byte [ ] AnimationUnkData { get ; set ; }
public MetaHash AnimationName = > AnimationUnkData = = null ? 0 : BitConverter . ToUInt32 ( AnimationUnkData , 0 ) ;
public MetaHash AnimationParameterName { get ; set ; }
2022-01-27 00:35:50 +08:00
public uint Unk3 { get ; set ; }
public uint Unk4 { get ; set ; }
public uint Unk5 { get ; set ; }
public uint Unk6 { get ; set ; }
public uint Unk7 { get ; set ; }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
// flags getters and setters
public MrfValueType AnimationType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfNodeAnimation ( ) : base ( MrfNodeType . Animation ) { }
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( AnimationType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
AnimationUnkDataLength = r . ReadUInt32 ( ) ;
AnimationUnkData = r . ReadBytes ( ( int ) AnimationUnkDataLength ) ;
2021-06-12 06:23:15 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
AnimationParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
break ;
}
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 2 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk3 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 4 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk4 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 6 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk5 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 8 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk6 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 10 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk7 = r . ReadUInt32 ( ) ;
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( AnimationType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( AnimationUnkDataLength ) ;
w . Write ( AnimationUnkData ) ;
2021-11-06 08:17:53 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( AnimationParameterName ) ;
2021-11-06 08:17:53 +08:00
break ;
}
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 2 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk3 ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 4 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk4 ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 6 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk5 ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 8 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk6 ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 10 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk7 ) ;
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeBlend : MrfNodePairWeightedBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeBlend (5)
2022-08-09 03:54:23 +08:00
public MrfNodeBlend ( ) : base ( MrfNodeType . Blend ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeAddSubtract : MrfNodePairWeightedBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeAddSubtract (6)
2022-08-09 03:54:23 +08:00
public MrfNodeAddSubtract ( ) : base ( MrfNodeType . AddSubtract ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeFilter : MrfNodeWithChildAndFilterBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeFilter (7)
2022-08-09 03:54:23 +08:00
public MrfNodeFilter ( ) : base ( MrfNodeType . Filter ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeMirror : MrfNodeWithChildAndFilterBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodeMirror (8)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeMirror ( ) : base ( MrfNodeType . Mirror ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeFrame : MrfNodeWithFlagsBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeFrame (9)
2022-08-09 03:54:23 +08:00
public MetaHash FrameParameterName { get ; set ; }
public MetaHash Unk3 { get ; set ; } // unused
// flags getters and setters
public MrfValueType FrameType // only Parameter type is supported
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType Unk3Type
{
get = > ( MrfValueType ) GetFlagsSubset ( 4 , 3 ) ;
set = > SetFlagsSubset ( 4 , 3 , ( uint ) value ) ;
}
public MrfNodeFrame ( ) : base ( MrfNodeType . Frame ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( FrameType ! = MrfValueType . None )
FrameParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( Unk3Type ! = MrfValueType . None )
2021-06-12 06:23:15 +08:00
Unk3 = r . ReadUInt32 ( ) ;
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( FrameType ! = MrfValueType . None )
w . Write ( FrameParameterName ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( Unk3Type ! = MrfValueType . None )
2021-11-06 08:17:53 +08:00
w . Write ( Unk3 ) ;
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeIk : MrfNode
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodeIk (10)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeIk ( ) : base ( MrfNodeType . Ik ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeBlendN : MrfNodeNBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeBlendN (13)
2022-08-09 03:54:23 +08:00
public MrfNodeBlendN ( ) : base ( MrfNodeType . BlendN ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
public enum MrfClipContainerType : uint
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
VariableClipSet = 0 , // a fwClipSet stored in the move network by the game code (when this clipset is added to the network it enables its fwClipSet.moveNetworkFlags, when removed they are disabled)
ClipSet = 1 , // a fwClipSet
ClipDictionary = 2 , // a .ycd
Unk3 = 3 , // invalid?
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeClip : MrfNodeWithFlagsBase
{
// rage__mvNodeClip (15)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MetaHash ClipParameterName { get ; set ; }
public MrfClipContainerType ClipContainerType { get ; set ; }
public MetaHash ClipContainerName { get ; set ; }
public MetaHash ClipName { get ; set ; }
public float Phase { get ; set ; }
public MetaHash PhaseParameterName { get ; set ; }
public float Rate { get ; set ; }
public MetaHash RateParameterName { get ; set ; }
public float Delta { get ; set ; }
public MetaHash DeltaParameterName { get ; set ; }
public bool Looped { get ; set ; }
public MetaHash LoopedParameterName { get ; set ; }
// flags getters and setters
public MrfValueType ClipType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType PhaseType
{
get = > ( MrfValueType ) GetFlagsSubset ( 2 , 3 ) ;
set = > SetFlagsSubset ( 2 , 3 , ( uint ) value ) ;
}
public MrfValueType RateType
{
get = > ( MrfValueType ) GetFlagsSubset ( 4 , 3 ) ;
set = > SetFlagsSubset ( 4 , 3 , ( uint ) value ) ;
}
public MrfValueType DeltaType
{
get = > ( MrfValueType ) GetFlagsSubset ( 6 , 3 ) ;
set = > SetFlagsSubset ( 6 , 3 , ( uint ) value ) ;
}
public MrfValueType LoopedType
{
get = > ( MrfValueType ) GetFlagsSubset ( 8 , 3 ) ;
set = > SetFlagsSubset ( 8 , 3 , ( uint ) value ) ;
}
public uint UnkFlag10
{
get = > GetFlagsSubset ( 10 , 3 ) ;
set = > SetFlagsSubset ( 10 , 3 , value ) ;
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeClip ( ) : base ( MrfNodeType . Clip ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( ClipType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
ClipContainerType = ( MrfClipContainerType ) r . ReadUInt32 ( ) ;
ClipContainerName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( ClipContainerType ! = MrfClipContainerType . Unk3 )
ClipName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
ClipParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
switch ( PhaseType )
{
case MrfValueType . Literal :
Phase = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
PhaseParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( RateType )
{
case MrfValueType . Literal :
Rate = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
RateParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( DeltaType )
{
case MrfValueType . Literal :
Delta = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
DeltaParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( LoopedType )
{
case MrfValueType . Literal :
Looped = r . ReadUInt32 ( ) ! = 0 ;
break ;
case MrfValueType . Parameter :
LoopedParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( ClipType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( ( uint ) ClipContainerType ) ;
w . Write ( ClipContainerName ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( ClipContainerType ! = MrfClipContainerType . Unk3 )
2021-11-06 08:17:53 +08:00
w . Write ( ClipName ) ;
break ;
}
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( ClipParameterName ) ;
2021-11-06 08:17:53 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
switch ( PhaseType )
{
case MrfValueType . Literal :
w . Write ( Phase ) ;
break ;
case MrfValueType . Parameter :
w . Write ( PhaseParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( RateType )
{
case MrfValueType . Literal :
w . Write ( Rate ) ;
break ;
case MrfValueType . Parameter :
w . Write ( RateParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( DeltaType )
{
case MrfValueType . Literal :
w . Write ( Delta ) ;
break ;
case MrfValueType . Parameter :
w . Write ( DeltaParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( LoopedType )
{
case MrfValueType . Literal :
w . Write ( Looped ? 0x01000000 : 0 u ) ; // bool originally stored as a big-endian uint, game just checks != 0. Here we do it to output the same bytes as the input
break ;
case MrfValueType . Parameter :
w . Write ( LoopedParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodePm : MrfNodeWithFlagsBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNode* (17) not used in final game
// The backing node is rage::crmtNodePm
// Pm = Parameterized Motion
// In RDR3 renamed to rage::mvNodeMotion/rage::crmtNodeMotion?
// Seems similar to NodeClip but for rage::crpmMotion/.#pm files (added in RDR3, WIP in GTA5/MP3?)
// In GTA5 the function pointer used to lookup the rage::crpmMotion is null
public uint MotionUnkDataLength { get ; set ; }
public byte [ ] MotionUnkData { get ; set ; }
public MetaHash MotionName = > MotionUnkData = = null ? 0 : BitConverter . ToUInt32 ( MotionUnkData , 0 ) ;
public MetaHash MotionParameterName { get ; set ; }
2021-06-12 06:23:15 +08:00
public uint Unk3 { get ; set ; }
public uint Unk4 { get ; set ; }
public uint Unk5 { get ; set ; }
public uint [ ] Unk6 { get ; set ; }
2022-08-09 03:54:23 +08:00
// flags getters and setters
public MrfValueType MotionType
{
// Literal not supported, the function pointer used to lookup the motion is null
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfNodePm ( ) : base ( MrfNodeType . Pm ) { }
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( MotionType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
{
MotionUnkDataLength = r . ReadUInt32 ( ) ;
MotionUnkData = r . ReadBytes ( ( int ) MotionUnkDataLength ) ;
break ;
}
case MrfValueType . Parameter :
MotionParameterName = r . ReadUInt32 ( ) ;
break ;
2021-06-12 06:23:15 +08:00
}
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 2 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk3 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 4 ) & 3 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk4 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
if ( ( Flags > > 6 ) ! = 0 )
2021-06-12 06:23:15 +08:00
Unk5 = r . ReadUInt32 ( ) ;
2022-01-27 00:35:50 +08:00
var flags = Flags > > 19 ;
var unk6Count = ( Flags > > 10 ) & 0xF ;
2021-06-12 06:23:15 +08:00
Unk6 = new uint [ unk6Count ] ;
for ( int i = 0 ; i < unk6Count ; i + + )
{
if ( ( flags & 3 ) ! = 0 )
Unk6 [ i ] = r . ReadUInt32 ( ) ;
flags > > = 2 ;
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( MotionType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
{
w . Write ( MotionUnkDataLength ) ;
w . Write ( MotionUnkData ) ;
break ;
}
case MrfValueType . Parameter :
w . Write ( MotionParameterName ) ;
break ;
2021-11-06 08:17:53 +08:00
}
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 2 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk3 ) ;
2022-01-27 00:35:50 +08:00
if ( ( ( Flags > > 4 ) & 3 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk4 ) ;
2022-01-27 00:35:50 +08:00
if ( ( Flags > > 6 ) ! = 0 )
2021-11-06 08:17:53 +08:00
w . Write ( Unk5 ) ;
2022-01-27 00:35:50 +08:00
var unk6Count = ( Flags > > 10 ) & 0xF ;
2021-11-06 08:17:53 +08:00
if ( unk6Count > 0 )
{
foreach ( var value in Unk6 )
w . Write ( value ) ;
}
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeExtrapolate : MrfNodeWithChildBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodeExtrapolate (18)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public float Damping { get ; set ; }
public MetaHash DampingParameterName { get ; set ; }
// flags getters and setters
public MrfValueType DampingType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfNodeExtrapolate ( ) : base ( MrfNodeType . Extrapolate ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
switch ( DampingType )
{
case MrfValueType . Literal :
Damping = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
DampingParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
switch ( DampingType )
{
case MrfValueType . Literal :
w . Write ( Damping ) ;
break ;
case MrfValueType . Parameter :
w . Write ( DampingParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeExpression : MrfNodeWithChildBase
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
// rage__mvNodeExpression (19)
2022-08-09 03:54:23 +08:00
public MetaHash ExpressionDictionaryName { get ; set ; }
2021-07-17 01:24:01 +08:00
public MetaHash ExpressionName { get ; set ; }
2022-08-09 03:54:23 +08:00
public MetaHash ExpressionParameterName { get ; set ; }
public float Weight { get ; set ; }
public MetaHash WeightParameterName { get ; set ; }
public MrfNodeExpressionVariable [ ] Variables { get ; set ; }
// flags getters and setters
public MrfValueType ExpressionType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType WeightType
{
get = > ( MrfValueType ) GetFlagsSubset ( 2 , 3 ) ;
set = > SetFlagsSubset ( 2 , 3 , ( uint ) value ) ;
}
public uint VariableFlags
{
get = > GetFlagsSubset ( 4 , 0xFFFFFF ) ;
set = > SetFlagsSubset ( 4 , 0xFFFFFF , value ) ;
}
public uint VariableCount
{
get = > GetFlagsSubset ( 28 , 0xF ) ;
set = > SetFlagsSubset ( 28 , 0xF , value ) ;
}
// VariableFlags accessors by index
public MrfValueType GetVariableType ( int index )
{
return ( MrfValueType ) GetFlagsSubset ( 4 + 2 * index , 3 ) ;
}
public void SetVariableType ( int index , MrfValueType type )
{
SetFlagsSubset ( 4 + 2 * index , 3 , ( uint ) type ) ;
}
public MrfNodeExpression ( ) : base ( MrfNodeType . Expression ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
switch ( ExpressionType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
ExpressionDictionaryName = r . ReadUInt32 ( ) ;
ExpressionName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
ExpressionParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
switch ( WeightType )
{
case MrfValueType . Literal :
Weight = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
WeightParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
var varCount = VariableCount ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( varCount = = 0 )
2021-06-12 06:23:15 +08:00
return ;
2022-08-09 03:54:23 +08:00
Variables = new MrfNodeExpressionVariable [ varCount ] ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < varCount ; i + + )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
var type = GetVariableType ( i ) ;
var name = r . ReadUInt32 ( ) ;
float value = 0.0f ;
uint valueParameterName = 0 ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( type )
{
case MrfValueType . Literal :
value = r . ReadSingle ( ) ;
break ;
case MrfValueType . Parameter :
valueParameterName = r . ReadUInt32 ( ) ;
break ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
Variables [ i ] = new MrfNodeExpressionVariable ( name , value , valueParameterName ) ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
switch ( ExpressionType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
w . Write ( ExpressionDictionaryName ) ;
2021-11-06 08:17:53 +08:00
w . Write ( ExpressionName ) ;
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( ExpressionParameterName ) ;
2021-11-06 08:17:53 +08:00
break ;
}
2022-08-09 03:54:23 +08:00
switch ( WeightType )
{
case MrfValueType . Literal :
w . Write ( Weight ) ;
break ;
case MrfValueType . Parameter :
w . Write ( WeightParameterName ) ;
break ;
}
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
var varCount = VariableCount ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( varCount = = 0 )
2021-11-06 08:17:53 +08:00
return ;
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < varCount ; i + + )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
var type = GetVariableType ( i ) ;
var variable = Variables [ i ] ;
w . Write ( variable . Name ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( type )
{
case MrfValueType . Literal :
w . Write ( variable . Value ) ;
break ;
case MrfValueType . Parameter :
w . Write ( variable . ValueParameterName ) ;
break ;
}
2022-02-01 19:59:24 +08:00
}
}
2022-08-09 03:54:23 +08:00
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public struct MrfNodeExpressionVariable
{
public MetaHash Name { get ; set ; }
public float Value { get ; set ; } // used if type == Literal
public MetaHash ValueParameterName { get ; set ; } // used if type == Parameter
public MrfNodeExpressionVariable ( MetaHash name , float value , MetaHash valueParameterName )
{
Name = name ;
Value = value ;
ValueParameterName = valueParameterName ;
}
public override string ToString ( )
{
return Name . ToString ( ) + " - " + FloatUtil . ToString ( Value ) + " | " + ValueParameterName . ToString ( ) ;
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeCapture : MrfNodeWithChildBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeCaptureDef (20)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MetaHash FrameParameterName { get ; set ; }
public MetaHash Unk3 { get ; set ; } // unused
// flags getters and setters
public MrfValueType FrameType // only Parameter type is supported
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfValueType Unk3Type
{
get = > ( MrfValueType ) GetFlagsSubset ( 4 , 3 ) ;
set = > SetFlagsSubset ( 4 , 3 , ( uint ) value ) ;
}
public MrfNodeCapture ( ) : base ( MrfNodeType . Capture ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( FrameType ! = MrfValueType . None )
FrameParameterName = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( Unk3Type ! = MrfValueType . None )
2022-02-01 19:59:24 +08:00
Unk3 = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( FrameType ! = MrfValueType . None )
w . Write ( FrameParameterName ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
if ( Unk3Type ! = MrfValueType . None )
2022-02-01 19:59:24 +08:00
w . Write ( Unk3 ) ;
2021-11-06 08:17:53 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeProxy : MrfNode
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
// rage__mvNodeProxy (21)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MetaHash NodeParameterName { get ; set ; } // lookups a rage::crmtObserver parameter, then gets the observed node
public MrfNodeProxy ( ) : base ( MrfNodeType . Proxy ) { }
public override void Read ( DataReader r )
{
base . Read ( r ) ;
NodeParameterName = r . ReadUInt32 ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( NodeParameterName ) ;
}
public override string ToString ( )
{
return base . ToString ( ) + " - " + NodeParameterName . ToString ( ) ;
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeAddN : MrfNodeNBase
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
// rage__mvNodeAddN (22)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeAddN ( ) : base ( MrfNodeType . AddN ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeIdentity : MrfNode
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
// rage__mvNodeIdentity (23)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeIdentity ( ) : base ( MrfNodeType . Identity ) { }
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeMerge : MrfNodePairBase
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
// rage::mvNodeMergeDef (24)
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
public MrfSynchronizerTagFlags SynchronizerTagFlags { get ; set ; }
public MetaHash FrameFilterDictionaryName { get ; set ; }
public MetaHash FrameFilterName { get ; set ; }
public MetaHash FrameFilterParameterName { get ; set ; }
// flags getters and setters
public MrfValueType FrameFilterType
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfSynchronizerType SynchronizerType
{
get = > ( MrfSynchronizerType ) GetFlagsSubset ( 19 , 3 ) ;
set = > SetFlagsSubset ( 19 , 3 , ( uint ) value ) ;
}
public MrfNodeMerge ( ) : base ( MrfNodeType . Merge ) { }
2021-06-12 06:23:15 +08:00
2021-11-06 08:17:53 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-01-27 00:35:50 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
SynchronizerTagFlags = ( MrfSynchronizerTagFlags ) r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
FrameFilterDictionaryName = r . ReadUInt32 ( ) ;
FrameFilterName = r . ReadUInt32 ( ) ;
2022-02-01 19:59:24 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
FrameFilterParameterName = r . ReadUInt32 ( ) ;
2022-02-01 19:59:24 +08:00
break ;
2021-06-12 06:23:15 +08:00
}
}
2021-11-06 08:17:53 +08:00
public override void Write ( DataWriter w )
{
2022-01-27 00:35:50 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
if ( SynchronizerType = = MrfSynchronizerType . Tag )
w . Write ( ( uint ) SynchronizerTagFlags ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
switch ( FrameFilterType )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
case MrfValueType . Literal :
w . Write ( FrameFilterDictionaryName ) ;
w . Write ( FrameFilterName ) ;
2022-02-01 19:59:24 +08:00
break ;
2022-08-09 03:54:23 +08:00
case MrfValueType . Parameter :
w . Write ( FrameFilterParameterName ) ;
2022-02-01 19:59:24 +08:00
break ;
}
}
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodePose : MrfNodeWithFlagsBase
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodePose (25)
public uint Unk1 { get ; set ; } // unused, with type==Literal always 0x01000000, probably a bool for the Pose_IsNormalized parameter hardcoded to true in the final game
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
// flags getters and setters
public MrfValueType Unk1Type
{
get = > ( MrfValueType ) GetFlagsSubset ( 0 , 3 ) ;
set = > SetFlagsSubset ( 0 , 3 , ( uint ) value ) ;
}
public MrfNodePose ( ) : base ( MrfNodeType . Pose ) { }
2022-02-01 19:59:24 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
base . Read ( r ) ;
2022-08-09 03:54:23 +08:00
if ( Unk1Type ! = MrfValueType . None )
2022-02-01 19:59:24 +08:00
Unk1 = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
}
2022-02-01 19:59:24 +08:00
public override void Write ( DataWriter w )
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
base . Write ( w ) ;
2022-08-09 03:54:23 +08:00
if ( Unk1Type ! = MrfValueType . None )
2022-02-01 19:59:24 +08:00
w . Write ( Unk1 ) ;
2021-06-12 06:23:15 +08:00
}
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeMergeN : MrfNodeNBase
2022-02-01 19:59:24 +08:00
{
// rage__mvNodeMergeN (26)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeMergeN ( ) : base ( MrfNodeType . MergeN ) { }
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
2022-02-01 19:59:24 +08:00
[TC(typeof(EXP))] public class MrfNodeState : MrfNodeStateBase
{
// rage__mvNodeState (27)
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
public int InputParametersOffset { get ; set ; }
public int InputParametersFileOffset { get ; set ; }
public uint InputParameterCount { get ; set ; }
public int EventsOffset { get ; set ; }
public int EventsFileOffset { get ; set ; }
public uint EventCount { get ; set ; }
public int OutputParametersOffset { get ; set ; }
public int OutputParametersFileOffset { get ; set ; }
public uint OutputParameterCount { get ; set ; }
public int OperationsOffset { get ; set ; }
public int OperationsFileOffset { get ; set ; }
public uint OperationCount { get ; set ; }
public MrfStateTransition [ ] Transitions { get ; set ; }
public MrfStateInputParameter [ ] InputParameters { get ; set ; }
public MrfStateEvent [ ] Events { get ; set ; }
public MrfStateOutputParameter [ ] OutputParameters { get ; set ; }
public MrfStateOperation [ ] Operations { get ; set ; }
public MrfNodeState ( ) : base ( MrfNodeType . State ) { }
2021-06-12 06:23:15 +08:00
2022-02-01 19:59:24 +08:00
public override void Read ( DataReader r )
2021-06-12 06:23:15 +08:00
{
2022-02-01 19:59:24 +08:00
base . Read ( r ) ;
2021-11-06 08:17:53 +08:00
2022-08-09 03:54:23 +08:00
InputParametersOffset = r . ReadInt32 ( ) ;
InputParametersFileOffset = ( int ) ( r . Position + InputParametersOffset - 4 ) ;
InputParameterCount = r . ReadUInt32 ( ) ;
EventsOffset = r . ReadInt32 ( ) ;
EventsFileOffset = ( int ) ( r . Position + EventsOffset - 4 ) ;
EventCount = r . ReadUInt32 ( ) ;
OutputParametersOffset = r . ReadInt32 ( ) ;
OutputParametersFileOffset = ( int ) ( r . Position + OutputParametersOffset - 4 ) ;
OutputParameterCount = r . ReadUInt32 ( ) ;
OperationsOffset = r . ReadInt32 ( ) ;
OperationsFileOffset = ( int ) ( r . Position + OperationsOffset - 4 ) ;
OperationCount = r . ReadUInt32 ( ) ;
if ( TransitionCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = TransitionsFileOffset )
{ } // no hits
Transitions = new MrfStateTransition [ TransitionCount ] ;
for ( int i = 0 ; i < TransitionCount ; i + + )
Transitions [ i ] = new MrfStateTransition ( r ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
if ( InputParameterCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = InputParametersFileOffset )
{ } // no hits
InputParameters = new MrfStateInputParameter [ InputParameterCount ] ;
for ( int i = 0 ; i < InputParameterCount ; i + + )
InputParameters [ i ] = new MrfStateInputParameter ( r ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
if ( EventCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = EventsFileOffset )
{ } // no hits
Events = new MrfStateEvent [ EventCount ] ;
for ( int i = 0 ; i < EventCount ; i + + )
Events [ i ] = new MrfStateEvent ( r ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
if ( OutputParameterCount > 0 )
2021-11-06 08:17:53 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = OutputParametersFileOffset )
{ } // no hits
OutputParameters = new MrfStateOutputParameter [ OutputParameterCount ] ;
for ( int i = 0 ; i < OutputParameterCount ; i + + )
OutputParameters [ i ] = new MrfStateOutputParameter ( r ) ;
2021-11-06 08:17:53 +08:00
}
2022-08-09 03:54:23 +08:00
if ( OperationCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = OperationsFileOffset )
{ } // no hits
Operations = new MrfStateOperation [ OperationCount ] ;
for ( int i = 0 ; i < OperationCount ; i + + )
Operations [ i ] = new MrfStateOperation ( r ) ;
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-02-01 19:59:24 +08:00
public override void Write ( DataWriter w )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
TransitionCount = ( byte ) ( Transitions ? . Length ? ? 0 ) ;
InputParameterCount = ( uint ) ( InputParameters ? . Length ? ? 0 ) ;
EventCount = ( uint ) ( Events ? . Length ? ? 0 ) ;
OutputParameterCount = ( uint ) ( OutputParameters ? . Length ? ? 0 ) ;
OperationCount = ( uint ) ( Operations ? . Length ? ? 0 ) ;
2022-02-01 19:59:24 +08:00
base . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
w . Write ( InputParametersOffset ) ;
w . Write ( InputParameterCount ) ;
w . Write ( EventsOffset ) ;
w . Write ( EventCount ) ;
w . Write ( OutputParametersOffset ) ;
w . Write ( OutputParameterCount ) ;
w . Write ( OperationsOffset ) ;
w . Write ( OperationCount ) ;
if ( Transitions ! = null )
foreach ( var transition in Transitions )
transition . Write ( w ) ;
if ( InputParameters ! = null )
foreach ( var item in InputParameters )
2022-02-01 19:59:24 +08:00
item . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-02-01 19:59:24 +08:00
if ( Events ! = null )
foreach ( var item in Events )
item . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( OutputParameters ! = null )
foreach ( var item in OutputParameters )
2022-02-01 19:59:24 +08:00
item . Write ( w ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( Operations ! = null )
foreach ( var item in Operations )
2022-02-01 19:59:24 +08:00
item . Write ( w ) ;
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public override void ResolveRelativeOffsets ( MrfFile mrf )
{
base . ResolveRelativeOffsets ( mrf ) ;
ResolveNodeOffsetsInTransitions ( Transitions , mrf ) ;
}
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
var offset = FileOffset + 0x20 /*sizeof(MrfNodeStateBase)*/ + 8 * 4 /*all offsets/counts*/ ;
offset = UpdateNodeOffsetsInTransitions ( Transitions , offset , offsetSetToZeroIfNoTransitions : false ) ;
InputParametersFileOffset = offset ;
InputParametersOffset = InputParametersFileOffset - ( FileOffset + 0x20 + 0 ) ;
offset + = ( int ) InputParameterCount * 0xC ;
EventsFileOffset = offset ;
EventsOffset = EventsFileOffset - ( FileOffset + 0x20 + 8 ) ;
offset + = ( int ) EventCount * 0x8 ;
OutputParametersFileOffset = offset ;
OutputParametersOffset = OutputParametersFileOffset - ( FileOffset + 0x20 + 0x10 ) ;
offset + = ( int ) OutputParameterCount * 0xC ;
OperationsFileOffset = offset ;
OperationsOffset = OperationsFileOffset - ( FileOffset + 0x20 + 0x18 ) ;
}
2022-02-01 19:59:24 +08:00
public override string ToString ( )
{
2022-08-09 03:54:23 +08:00
return base . ToString ( ) ;
2022-02-01 19:59:24 +08:00
}
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeInvalid : MrfNode
2022-02-01 19:59:24 +08:00
{
// rage__mvNodeInvalid (28)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeInvalid ( ) : base ( MrfNodeType . Invalid ) { }
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeJointLimit : MrfNodeWithChildAndFilterBase
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodeJointLimit (29)
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
public MrfNodeJointLimit ( ) : base ( MrfNodeType . JointLimit ) { }
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeSubNetwork : MrfNode
2022-02-01 19:59:24 +08:00
{
// rage__mvNodeSubNetworkClass (30)
2022-08-09 03:54:23 +08:00
public MetaHash SubNetworkParameterName { get ; set ; } // parameter of type rage::mvSubNetwork to lookup
public MrfNodeSubNetwork ( ) : base ( MrfNodeType . SubNetwork ) { }
public override void Read ( DataReader r )
{
base . Read ( r ) ;
SubNetworkParameterName = r . ReadUInt32 ( ) ;
}
public override void Write ( DataWriter w )
{
base . Write ( w ) ;
w . Write ( SubNetworkParameterName ) ;
}
2022-02-01 19:59:24 +08:00
}
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public class MrfNodeReference : MrfNode
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
// rage__mvNodeReference (31)
// Unused in the final game but from testing, seems to work fine initially but when it finishes it crashes calling a pure virtual function rage::crmtNode::GetNodeTypeInfo
// Maybe some kind of double-free/use-after-free bug, not sure if a R* bug or an issue with the generated MRF file.
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
public MetaHash MoveNetworkName { get ; set ; } // .mrf file to lookup. Its RootState must be MrfNodeState, not MrfNodeStateMachine or it will crash
public int InitialParametersOffset { get ; set ; }
public int InitialParametersFileOffset { get ; set ; }
public uint InitialParameterCount { get ; set ; }
public MrfNodeReferenceInitialParameter [ ] InitialParameters { get ; set ; } // parameters added when the new network is created
public uint ImportedParameterCount { get ; set ; }
public MrfNodeReferenceImportedParameter [ ] ImportedParameters { get ; set ; } // each update these parameters are copied from the parent network to the new network
public uint MoveNetworkFlagCount { get ; set ; }
public MrfNodeReferenceMoveNetworkFlag [ ] MoveNetworkFlags { get ; set ; } // each update copies flag 'Name' state in the parent network to another bit in the *parent* network flags, the new bit position is defined by 'NewName' in the MrfFile.MoveNetworkFlags of the new network
public uint MoveNetworkTriggerCount { get ; set ; }
public MrfNodeReferenceMoveNetworkTrigger [ ] MoveNetworkTriggers { get ; set ; } // same as with the flags
public MrfNodeReference ( ) : base ( MrfNodeType . Reference ) { }
2022-02-01 19:59:24 +08:00
public override void Read ( DataReader r )
{
base . Read ( r ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
MoveNetworkName = r . ReadUInt32 ( ) ;
InitialParametersOffset = r . ReadInt32 ( ) ;
InitialParametersFileOffset = ( int ) ( r . Position + InitialParametersOffset - 4 ) ;
InitialParameterCount = r . ReadUInt32 ( ) ;
ImportedParameterCount = r . ReadUInt32 ( ) ;
MoveNetworkFlagCount = r . ReadUInt32 ( ) ;
MoveNetworkTriggerCount = r . ReadUInt32 ( ) ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( ImportedParameterCount > 0 )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
ImportedParameters = new MrfNodeReferenceImportedParameter [ ImportedParameterCount ] ;
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < ImportedParameterCount ; i + + )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var name = r . ReadUInt32 ( ) ;
var newName = r . ReadUInt32 ( ) ;
ImportedParameters [ i ] = new MrfNodeReferenceImportedParameter ( name , newName ) ;
2022-02-01 19:59:24 +08:00
}
2021-06-12 06:23:15 +08:00
}
2022-08-09 03:54:23 +08:00
if ( MoveNetworkFlagCount > 0 )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
MoveNetworkFlags = new MrfNodeReferenceMoveNetworkFlag [ MoveNetworkFlagCount ] ;
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < MoveNetworkFlagCount ; i + + )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var name = r . ReadUInt32 ( ) ;
var newName = r . ReadUInt32 ( ) ;
MoveNetworkFlags [ i ] = new MrfNodeReferenceMoveNetworkFlag ( name , newName ) ;
2022-02-01 19:59:24 +08:00
}
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( MoveNetworkTriggerCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
MoveNetworkTriggers = new MrfNodeReferenceMoveNetworkTrigger [ MoveNetworkTriggerCount ] ;
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
for ( int i = 0 ; i < MoveNetworkTriggerCount ; i + + )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var name = r . ReadUInt32 ( ) ;
var newName = r . ReadUInt32 ( ) ;
MoveNetworkTriggers [ i ] = new MrfNodeReferenceMoveNetworkTrigger ( name , newName ) ;
2022-02-01 19:59:24 +08:00
}
}
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
if ( InitialParameterCount > 0 )
2021-06-12 06:23:15 +08:00
{
2022-08-09 03:54:23 +08:00
if ( r . Position ! = InitialParametersFileOffset )
{ }
2021-06-12 06:23:15 +08:00
2022-08-09 03:54:23 +08:00
InitialParameters = new MrfNodeReferenceInitialParameter [ InitialParameterCount ] ;
for ( int i = 0 ; i < InitialParameterCount ; i + + )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
var type = r . ReadUInt32 ( ) ;
var name = r . ReadUInt32 ( ) ;
var data = r . ReadInt32 ( ) ;
InitialParameters [ i ] = new MrfNodeReferenceInitialParameter ( type , name , data ) ;
2022-02-01 19:59:24 +08:00
}
}
2021-06-12 06:23:15 +08:00
}
2022-01-27 00:35:50 +08:00
2022-02-01 19:59:24 +08:00
public override void Write ( DataWriter w )
2022-01-27 00:35:50 +08:00
{
2022-02-01 19:59:24 +08:00
base . Write ( w ) ;
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
InitialParameterCount = ( uint ) ( InitialParameters ? . Length ? ? 0 ) ;
ImportedParameterCount = ( uint ) ( ImportedParameters ? . Length ? ? 0 ) ;
MoveNetworkFlagCount = ( uint ) ( MoveNetworkFlags ? . Length ? ? 0 ) ;
MoveNetworkTriggerCount = ( uint ) ( MoveNetworkTriggers ? . Length ? ? 0 ) ;
w . Write ( MoveNetworkName ) ;
w . Write ( InitialParametersOffset ) ;
w . Write ( InitialParameterCount ) ;
w . Write ( ImportedParameterCount ) ;
w . Write ( MoveNetworkFlagCount ) ;
w . Write ( MoveNetworkTriggerCount ) ;
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
if ( ImportedParameterCount > 0 )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var entry in ImportedParameters )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Name ) ;
w . Write ( entry . NewName ) ;
2022-01-27 00:35:50 +08:00
}
2022-02-01 19:59:24 +08:00
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
if ( MoveNetworkFlagCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var entry in MoveNetworkFlags )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Name ) ;
w . Write ( entry . NewName ) ;
2022-01-27 00:35:50 +08:00
}
2022-02-01 19:59:24 +08:00
}
2022-01-27 00:35:50 +08:00
2022-08-09 03:54:23 +08:00
if ( MoveNetworkTriggerCount > 0 )
2022-02-01 19:59:24 +08:00
{
2022-08-09 03:54:23 +08:00
foreach ( var entry in MoveNetworkTriggers )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Name ) ;
w . Write ( entry . NewName ) ;
2022-01-27 00:35:50 +08:00
}
}
2022-08-09 03:54:23 +08:00
if ( InitialParameterCount > 0 )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
// FIXME: Data when used as offset is not updated and not sure where what it would point to should be written
foreach ( var entry in InitialParameters )
2022-01-27 00:35:50 +08:00
{
2022-08-09 03:54:23 +08:00
w . Write ( entry . Type ) ;
w . Write ( entry . Name ) ;
w . Write ( entry . Data ) ;
2022-01-27 00:35:50 +08:00
}
}
}
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
public override void UpdateRelativeOffsets ( )
{
base . UpdateRelativeOffsets ( ) ;
var offset = FileOffset + 0x20 ;
offset + = ( int ) ImportedParameterCount * 8 ;
offset + = ( int ) MoveNetworkFlagCount * 8 ;
offset + = ( int ) MoveNetworkTriggerCount * 8 ;
InitialParametersFileOffset = offset ;
InitialParametersOffset = InitialParametersFileOffset - ( FileOffset + 0xC ) ;
}
}
[TC(typeof(EXP))] public struct MrfNodeReferenceImportedParameter
{
public MetaHash Name { get ; set ; } // name in the parent network
public MetaHash NewName { get ; set ; } // name in the new network
public MrfNodeReferenceImportedParameter ( MetaHash name , MetaHash newName )
{
Name = name ;
NewName = newName ;
}
public override string ToString ( )
{
return $"{Name} - {NewName}" ;
}
}
[TC(typeof(EXP))] public struct MrfNodeReferenceInitialParameter
{
public uint Type { get ; set ; } // Animation = 0, Clip = 1, Expression = 2, Motion = 4, Float = 7
public MetaHash Name { get ; set ; }
public int Data { get ; set ; } // For Type==Float, this the float value. For the other types, this is the offset to the actual data needed for the lookup (e.g. dictionary/name hash pair).
public MrfNodeReferenceInitialParameter ( uint type , MetaHash name , int data )
{
Type = type ;
Name = name ;
Data = data ;
}
public override string ToString ( )
{
return $"{Type} - {Name} - {Data}" ;
}
}
[TC(typeof(EXP))] public struct MrfNodeReferenceMoveNetworkFlag
{
public MetaHash Name { get ; set ; } // name in the parent network
public MetaHash NewName { get ; set ; } // name in the new network
public MrfNodeReferenceMoveNetworkFlag ( MetaHash name , MetaHash newName )
{
Name = name ;
NewName = newName ;
}
public override string ToString ( )
{
return $"{Name} - {NewName}" ;
}
2021-06-12 06:23:15 +08:00
}
2022-02-01 19:59:24 +08:00
2022-08-09 03:54:23 +08:00
[TC(typeof(EXP))] public struct MrfNodeReferenceMoveNetworkTrigger
{
public MetaHash Name { get ; set ; } // name in the parent network
public MetaHash NewName { get ; set ; } // name in the new network
public MrfNodeReferenceMoveNetworkTrigger ( MetaHash name , MetaHash newName )
{
Name = name ;
NewName = newName ;
}
public override string ToString ( )
{
return $"{Name} - {NewName}" ;
}
}
2022-02-01 19:59:24 +08:00
#endregion
2021-06-12 06:23:15 +08:00
}