2017-09-21 18:33:05 +08:00
using SharpDX ;
using System ;
using System.Collections.Generic ;
using System.ComponentModel ;
2019-10-31 11:56:40 +08:00
using System.Diagnostics ;
2019-11-14 15:58:20 +08:00
using System.IO ;
2017-09-21 18:33:05 +08:00
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
2019-11-14 15:58:20 +08:00
using System.Xml ;
2017-09-21 18:33:05 +08:00
/ *
Copyright ( c ) 2016 Neodymium
Permission is hereby granted , free of charge , to any person obtaining a copy
of this software and associated documentation files ( the "Software" ) , to deal
in the Software without restriction , including without limitation the rights
to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
copies of the Software , and to permit persons to whom the Software is
furnished to do so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE .
* /
//ruthlessly stolen
namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipDictionary : ResourceFileBase
{
public override long BlockLength
{
get { return 64 ; }
}
// structure data
public uint Unknown_10h { get ; set ; } // 0x00000000
public uint Unknown_14h { get ; set ; } // 0x00000000
public ulong AnimationsPointer { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_20h { get ; set ; } = 0x00000101 ;
2017-09-21 18:33:05 +08:00
public uint Unknown_24h { get ; set ; } // 0x00000000
public ulong ClipsPointer { get ; set ; }
public ushort ClipsMapCapacity { get ; set ; }
public ushort ClipsMapEntries { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_34h { get ; set ; } = 0x01000000 ;
2017-09-21 18:33:05 +08:00
public uint Unknown_38h { get ; set ; } // 0x00000000
public uint Unknown_3Ch { get ; set ; } // 0x00000000
// reference data
public AnimationMap Animations { get ; set ; }
public ResourcePointerArray64 < ClipMapEntry > Clips { get ; set ; }
2019-11-14 15:58:20 +08:00
//data used by CW for loading/saving
public Dictionary < MetaHash , ClipMapEntry > ClipMap { get ; set ; }
public Dictionary < MetaHash , AnimationMapEntry > AnimMap { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
this . Unknown_10h = reader . ReadUInt32 ( ) ;
this . Unknown_14h = reader . ReadUInt32 ( ) ;
this . AnimationsPointer = reader . ReadUInt64 ( ) ;
this . Unknown_20h = reader . ReadUInt32 ( ) ;
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . ClipsPointer = reader . ReadUInt64 ( ) ;
this . ClipsMapCapacity = reader . ReadUInt16 ( ) ;
this . ClipsMapEntries = reader . ReadUInt16 ( ) ;
this . Unknown_34h = reader . ReadUInt32 ( ) ;
this . Unknown_38h = reader . ReadUInt32 ( ) ;
this . Unknown_3Ch = reader . ReadUInt32 ( ) ;
// read reference data
this . Animations = reader . ReadBlockAt < AnimationMap > (
this . AnimationsPointer // offset
) ;
this . Clips = reader . ReadBlockAt < ResourcePointerArray64 < ClipMapEntry > > (
this . ClipsPointer , // offset
this . ClipsMapCapacity
) ;
2019-11-14 15:58:20 +08:00
BuildMaps ( ) ;
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// update structure data
this . AnimationsPointer = ( ulong ) ( this . Animations ! = null ? this . Animations . FilePosition : 0 ) ;
this . ClipsPointer = ( ulong ) ( this . Clips ! = null ? this . Clips . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . ClipsMapCapacity = ( ushort ) ( ( Clips ! = null ) ? Clips . Count : 0 ) ;
this . ClipsMapEntries = ( ushort ) ( ( ClipMap ! = null ) ? ClipMap . Count : 0 ) ;
2017-09-21 18:33:05 +08:00
// write structure data
writer . Write ( this . Unknown_10h ) ;
writer . Write ( this . Unknown_14h ) ;
writer . Write ( this . AnimationsPointer ) ;
writer . Write ( this . Unknown_20h ) ;
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . ClipsPointer ) ;
writer . Write ( this . ClipsMapCapacity ) ;
writer . Write ( this . ClipsMapEntries ) ;
writer . Write ( this . Unknown_34h ) ;
writer . Write ( this . Unknown_38h ) ;
writer . Write ( this . Unknown_3Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( base . GetReferences ( ) ) ;
2019-11-14 15:58:20 +08:00
if ( Animations ! = null )
{
list . Add ( Animations ) ;
Animations . AnimationsMapEntries = ( ushort ) ( AnimMap ? . Count ? ? 0 ) ;
}
2017-09-21 18:33:05 +08:00
if ( Clips ! = null ) list . Add ( Clips ) ;
return list . ToArray ( ) ;
}
2019-11-14 15:58:20 +08:00
public void BuildMaps ( )
{
ClipMap = new Dictionary < MetaHash , ClipMapEntry > ( ) ;
AnimMap = new Dictionary < MetaHash , AnimationMapEntry > ( ) ;
if ( ( Clips ! = null ) & & ( Clips . data_items ! = null ) )
{
foreach ( var cme in Clips . data_items )
{
if ( cme ! = null )
{
ClipMap [ cme . Hash ] = cme ;
var nxt = cme . Next ;
while ( nxt ! = null )
{
ClipMap [ nxt . Hash ] = nxt ;
nxt = nxt . Next ;
}
}
}
}
if ( ( Animations ! = null ) & & ( Animations . Animations ! = null ) & & ( Animations . Animations . data_items ! = null ) )
{
foreach ( var ame in Animations . Animations . data_items )
{
if ( ame ! = null )
{
AnimMap [ ame . Hash ] = ame ;
var nxt = ame . NextEntry ;
while ( nxt ! = null )
{
AnimMap [ nxt . Hash ] = nxt ;
nxt = nxt . NextEntry ;
}
}
}
}
foreach ( var cme in ClipMap . Values )
{
var clip = cme . Clip ;
if ( clip = = null ) continue ;
2019-11-20 19:51:37 +08:00
var name = clip . ShortName ; //just to make sure ShortName is generated and in JenkIndex...
2019-11-14 15:58:20 +08:00
//if (name.EndsWith("_uv_0")) //hash for these entries match string with this removed, +1
//{
//}
//if (name.EndsWith("_uv_1")) //same as above, but +2
//{
//}
}
2019-11-20 19:51:37 +08:00
//foreach (var ame in AnimMap.Values)
//{
// var anim = ame.Animation;
// if (anim == null) continue;
//}
2019-11-14 15:58:20 +08:00
}
2019-11-18 20:52:58 +08:00
public void UpdateUsageCounts ( )
{
var usages = new Dictionary < MetaHash , uint > ( ) ;
void addUsage ( MetaHash h )
{
uint u = 0 ;
usages . TryGetValue ( h , out u ) ;
u + + ;
usages [ h ] = u ;
}
if ( ( Animations ! = null ) & & ( Animations . Animations ! = null ) & & ( Animations . Animations . data_items ! = null ) )
{
foreach ( var ame in Animations . Animations . data_items )
{
if ( ame ! = null )
{
addUsage ( ame . Hash ) ;
var nxt = ame . NextEntry ;
while ( nxt ! = null )
{
addUsage ( nxt . Hash ) ;
nxt = nxt . NextEntry ;
}
}
}
}
foreach ( var cme in ClipMap . Values )
{
var ca = cme . Clip as ClipAnimation ;
var cal = cme . Clip as ClipAnimationList ;
if ( ca ? . Animation ! = null )
{
2019-11-20 19:51:37 +08:00
addUsage ( ca . Animation . Hash ) ;
2019-11-18 20:52:58 +08:00
}
if ( cal ? . Animations ! = null )
{
foreach ( var cae in cal . Animations )
{
if ( cae ? . Animation ! = null )
{
2019-11-20 19:51:37 +08:00
addUsage ( cae . Animation . Hash ) ;
2019-11-18 20:52:58 +08:00
}
}
}
}
foreach ( var ame in AnimMap . Values )
{
if ( ame . Animation ! = null )
{
uint u = 0 ;
2019-11-20 19:51:37 +08:00
if ( usages . TryGetValue ( ame . Animation . Hash , out u ) )
2019-11-18 20:52:58 +08:00
{
if ( ame . Animation . UsageCount ! = u )
{ }
ame . Animation . UsageCount = u ;
}
else
{ }
}
}
}
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
{
var clips = new List < ClipBase > ( ) ;
if ( ClipMap ! = null )
{
foreach ( var cme in ClipMap . Values )
{
if ( cme ? . Clip = = null ) continue ;
clips . Add ( cme . Clip ) ;
}
}
2019-11-20 19:51:37 +08:00
var anims = new List < Animation > ( ) ;
if ( AnimMap ! = null )
{
foreach ( var ame in AnimMap . Values )
{
if ( ame ? . Animation = = null ) continue ;
anims . Add ( ame . Animation ) ;
}
}
2019-11-14 15:58:20 +08:00
YcdXml . WriteItemArray ( sb , clips . ToArray ( ) , indent , "Clips" ) ;
YcdXml . WriteItemArray ( sb , anims . ToArray ( ) , indent , "Animations" ) ;
}
public void ReadXml ( XmlNode node )
{
2019-11-20 19:51:37 +08:00
var clipList = new List < ClipMapEntry > ( ) ;
2019-11-14 15:58:20 +08:00
var clipsNode = node . SelectSingleNode ( "Clips" ) ;
if ( clipsNode ! = null )
{
var inodes = clipsNode . SelectNodes ( "Item" ) ;
if ( inodes ? . Count > 0 )
{
foreach ( XmlNode inode in inodes )
{
var type = Xml . GetEnumValue < ClipType > ( Xml . GetChildStringAttribute ( inode , "Type" , "value" ) ) ;
var c = ClipBase . ConstructClip ( type ) ;
c . ReadXml ( inode ) ;
2019-11-20 19:51:37 +08:00
var cme = new ClipMapEntry ( ) ;
cme . Hash = c . Hash ;
cme . Clip = c ;
clipList . Add ( cme ) ;
2019-11-14 15:58:20 +08:00
}
}
}
2019-11-20 19:51:37 +08:00
var animDict = new Dictionary < MetaHash , Animation > ( ) ;
var animList = new List < AnimationMapEntry > ( ) ;
2019-11-14 15:58:20 +08:00
var anims = XmlMeta . ReadItemArrayNullable < Animation > ( node , "Animations" ) ;
if ( anims ! = null )
{
foreach ( var anim in anims )
{
2019-11-20 19:51:37 +08:00
animDict [ anim . Hash ] = anim ;
var ame = new AnimationMapEntry ( ) ;
ame . Hash = anim . Hash ;
ame . Animation = anim ;
animList . Add ( ame ) ;
}
}
foreach ( var cme in clipList )
{
var cb = cme ? . Clip ;
var clipanim = cb as ClipAnimation ;
if ( clipanim ! = null )
{
animDict . TryGetValue ( clipanim . AnimationHash , out Animation a ) ;
clipanim . Animation = a ;
}
var clipanimlist = cb as ClipAnimationList ;
if ( clipanimlist ? . Animations ? . Data ! = null )
{
foreach ( var cae in clipanimlist . Animations . Data )
{
animDict . TryGetValue ( cae . AnimationHash , out Animation a ) ;
cae . Animation = a ;
}
2019-11-14 15:58:20 +08:00
}
}
2019-11-20 19:51:37 +08:00
CreateAnimationsMap ( animList . ToArray ( ) ) ;
CreateClipsMap ( clipList . ToArray ( ) ) ;
BuildMaps ( ) ;
UpdateUsageCounts ( ) ;
}
2019-11-14 15:58:20 +08:00
2019-11-20 19:51:37 +08:00
public void CreateClipsMap ( ClipMapEntry [ ] clips )
{
var numClipBuckets = GetNumHashBuckets ( clips ? . Length ? ? 0 ) ;
var clipBuckets = new List < ClipMapEntry > [ numClipBuckets ] ;
if ( clips ! = null )
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
foreach ( var cme in clips )
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
var b = cme . Hash % numClipBuckets ;
var bucket = clipBuckets [ b ] ;
if ( bucket = = null )
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
bucket = new List < ClipMapEntry > ( ) ;
clipBuckets [ b ] = bucket ;
2019-11-14 15:58:20 +08:00
}
2019-11-20 19:51:37 +08:00
bucket . Add ( cme ) ;
2019-11-14 15:58:20 +08:00
}
}
2019-11-20 19:51:37 +08:00
var newClips = new List < ClipMapEntry > ( ) ;
foreach ( var b in clipBuckets )
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
if ( ( b ? . Count ? ? 0 ) = = 0 ) newClips . Add ( null ) ;
else
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
newClips . Add ( b [ 0 ] ) ;
var p = b [ 0 ] ;
for ( int i = 1 ; i < b . Count ; i + + )
2019-11-14 15:58:20 +08:00
{
2019-11-20 19:51:37 +08:00
var c = b [ i ] ;
c . Next = null ;
p . Next = c ;
p = c ;
}
}
}
2019-11-14 15:58:20 +08:00
2019-11-20 19:51:37 +08:00
Clips = new ResourcePointerArray64 < ClipMapEntry > ( ) ;
Clips . data_items = newClips . ToArray ( ) ;
}
public void CreateAnimationsMap ( AnimationMapEntry [ ] anims )
{
var numAnimBuckets = GetNumHashBuckets ( anims ? . Length ? ? 0 ) ;
var animBuckets = new List < AnimationMapEntry > [ numAnimBuckets ] ;
if ( anims ! = null )
{
foreach ( var ame in anims )
{
var b = ame . Hash % numAnimBuckets ;
var bucket = animBuckets [ b ] ;
if ( bucket = = null )
{
bucket = new List < AnimationMapEntry > ( ) ;
animBuckets [ b ] = bucket ;
}
bucket . Add ( ame ) ;
}
}
2019-11-14 15:58:20 +08:00
2019-11-20 19:51:37 +08:00
var newAnims = new List < AnimationMapEntry > ( ) ;
foreach ( var b in animBuckets )
{
if ( ( b ? . Count ? ? 0 ) = = 0 ) newAnims . Add ( null ) ;
else
{
newAnims . Add ( b [ 0 ] ) ;
var p = b [ 0 ] ;
for ( int i = 1 ; i < b . Count ; i + + )
{
var c = b [ i ] ;
c . NextEntry = null ;
p . NextEntry = c ;
p = c ;
2019-11-14 15:58:20 +08:00
}
}
}
2019-11-20 19:51:37 +08:00
Animations = new AnimationMap ( ) ;
Animations . Animations = new ResourcePointerArray64 < AnimationMapEntry > ( ) ;
Animations . Animations . data_items = newAnims . ToArray ( ) ;
2019-11-14 15:58:20 +08:00
2019-11-20 19:51:37 +08:00
}
public static uint GetNumHashBuckets ( int nHashes )
{
if ( nHashes < 11 ) return 11 ;
else if ( nHashes < 29 ) return 29 ;
else if ( nHashes < 59 ) return 59 ;
else if ( nHashes < 107 ) return 107 ;
else if ( nHashes < 191 ) return 191 ;
else if ( nHashes < 331 ) return 331 ;
else if ( nHashes < 563 ) return 563 ;
else if ( nHashes < 953 ) return 953 ;
else if ( nHashes < 1609 ) return 1609 ;
else if ( nHashes < 2729 ) return 2729 ;
else if ( nHashes < 4621 ) return 4621 ;
else if ( nHashes < 7841 ) return 7841 ;
else if ( nHashes < 13297 ) return 13297 ;
else if ( nHashes < 22571 ) return 22571 ;
else if ( nHashes < 38351 ) return 38351 ;
else if ( nHashes < 65167 ) return 65167 ;
else /*if (nHashes < 65521)*/ return 65521 ;
//return ((uint)nHashes / 4) * 4 + 3;
2019-11-14 15:58:20 +08:00
}
2017-09-21 18:33:05 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimationMap : ResourceSystemBlock
{
public override long BlockLength
{
get { return 48 ; }
}
// structure data
public uint VFT { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_04h { get ; set ; } = 1 ; // 0x00000001
public uint Unknown_08h { get ; set ; } = 0 ; // 0x00000000
public uint Unknown_0Ch { get ; set ; } = 0 ; // 0x00000000
public uint Unknown_10h { get ; set ; } = 0 ; // 0x00000000
public uint Unknown_14h { get ; set ; } = 0 ; // 0x00000000
2017-09-21 18:33:05 +08:00
public ulong AnimationsPointer { get ; set ; }
public ushort AnimationsMapCapacity { get ; set ; }
public ushort AnimationsMapEntries { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_24h { get ; set ; } = 16777216 ;
public uint Unknown_28h { get ; set ; } = 1 ; // 0x00000001
public uint Unknown_2Ch { get ; set ; } = 0 ; // 0x00000000
2017-09-21 18:33:05 +08:00
// reference data
public ResourcePointerArray64 < AnimationMapEntry > Animations { get ; set ; }
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . VFT = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
this . Unknown_08h = reader . ReadUInt32 ( ) ;
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_10h = reader . ReadUInt32 ( ) ;
this . Unknown_14h = reader . ReadUInt32 ( ) ;
this . AnimationsPointer = reader . ReadUInt64 ( ) ;
this . AnimationsMapCapacity = reader . ReadUInt16 ( ) ;
this . AnimationsMapEntries = reader . ReadUInt16 ( ) ;
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28h = reader . ReadUInt32 ( ) ;
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
// read reference data
this . Animations = reader . ReadBlockAt < ResourcePointerArray64 < AnimationMapEntry > > (
this . AnimationsPointer , // offset
this . AnimationsMapCapacity
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
this . AnimationsPointer = ( ulong ) ( this . Animations ! = null ? this . Animations . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . AnimationsMapCapacity = ( ushort ) ( this . Animations ! = null ? this . Animations . Count : 0 ) ;
//this.AnimationsMapEntries //this is already set by ClipDictionary
2017-09-21 18:33:05 +08:00
// write structure data
writer . Write ( this . VFT ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
writer . Write ( this . Unknown_08h ) ;
writer . Write ( this . Unknown_0Ch ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_10h ) ;
writer . Write ( this . Unknown_14h ) ;
writer . Write ( this . AnimationsPointer ) ;
writer . Write ( this . AnimationsMapCapacity ) ;
writer . Write ( this . AnimationsMapEntries ) ;
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28h ) ;
writer . Write ( this . Unknown_2Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
if ( Animations ! = null ) list . Add ( Animations ) ;
return list . ToArray ( ) ;
}
}
2019-11-20 19:51:37 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimationMapEntry : ResourceSystemBlock
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 32 ; }
}
// structure data
public MetaHash Hash { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unknown_04h { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public ulong AnimationPtr { get ; set ; }
public ulong NextEntryPtr { get ; set ; }
public uint Unknown_18h { get ; set ; } // 0x00000000
public uint Unknown_1Ch { get ; set ; } // 0x00000000
// reference data
public Animation Animation { get ; set ; }
public AnimationMapEntry NextEntry { get ; set ; }
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . Hash = new MetaHash ( reader . ReadUInt32 ( ) ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . AnimationPtr = reader . ReadUInt64 ( ) ;
this . NextEntryPtr = reader . ReadUInt64 ( ) ;
this . Unknown_18h = reader . ReadUInt32 ( ) ;
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
// read reference data
this . Animation = reader . ReadBlockAt < Animation > (
this . AnimationPtr // offset
) ;
this . NextEntry = reader . ReadBlockAt < AnimationMapEntry > (
this . NextEntryPtr // offset
) ;
2019-11-14 15:58:20 +08:00
if ( Animation ! = null )
{
2019-11-20 19:51:37 +08:00
if ( Animation . Hash ! = 0 )
2019-11-14 15:58:20 +08:00
{ }
2019-11-20 19:51:37 +08:00
Animation . Hash = Hash ;
2019-11-14 15:58:20 +08:00
}
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
this . AnimationPtr = ( ulong ) ( this . Animation ! = null ? this . Animation . FilePosition : 0 ) ;
this . NextEntryPtr = ( ulong ) ( this . NextEntry ! = null ? this . NextEntry . FilePosition : 0 ) ;
// write structure data
writer . Write ( this . Hash ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . AnimationPtr ) ;
writer . Write ( this . NextEntryPtr ) ;
writer . Write ( this . Unknown_18h ) ;
writer . Write ( this . Unknown_1Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
if ( Animation ! = null ) list . Add ( Animation ) ;
if ( NextEntry ! = null ) list . Add ( NextEntry ) ;
return list . ToArray ( ) ;
}
public override string ToString ( )
{
return Hash . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class Animation : ResourceSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 96 ; }
}
// structure data
public uint VFT { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_04h { get ; set ; } = 1 ; // 0x00000001
2019-11-18 20:52:58 +08:00
public uint Unused_08h { get ; set ; } // 0x00000000
public uint Unused_0Ch { get ; set ; } // 0x00000000
public byte Unknown_10h { get ; set ; }
public byte Unknown_11h { get ; set ; } = 1 ; // 0x01
public ushort Unused_12h { get ; set ; } // 0x0000
2019-10-31 11:56:40 +08:00
public ushort Frames { get ; set ; }
public ushort SequenceFrameLimit { get ; set ; }
public float Duration { get ; set ; }
2019-11-08 14:54:46 +08:00
public MetaHash Unknown_1Ch { get ; set ; }
2019-11-18 20:52:58 +08:00
public uint Unused_20h { get ; set ; } // 0x00000000
public uint Unused_24h { get ; set ; } // 0x00000000
public uint Unused_28h { get ; set ; } // 0x00000000
public uint Unused_2Ch { get ; set ; } // 0x00000000
public uint Unused_30h { get ; set ; } // 0x00000000
public uint Unused_34h { get ; set ; } // 0x00000000
2019-11-14 15:58:20 +08:00
public uint MaxSeqBlockLength { get ; set ; }
2019-10-31 11:56:40 +08:00
public uint UsageCount { get ; set ; }
2017-09-21 18:33:05 +08:00
public ResourcePointerList64 < Sequence > Sequences { get ; set ; }
2019-01-27 14:14:10 +08:00
public ResourceSimpleList64_s < AnimationBoneId > BoneIds { get ; set ; }
2017-09-21 18:33:05 +08:00
2017-10-04 11:35:39 +08:00
public YcdFile Ycd { get ; set ; }
2019-11-20 19:51:37 +08:00
public MetaHash Hash { get ; set ; } //updated by CW, for use when reading/writing files
2017-10-04 11:35:39 +08:00
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . VFT = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ; //1 1 1 1
2019-11-18 20:52:58 +08:00
this . Unused_08h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_0Ch = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unknown_10h = reader . ReadByte ( ) ; //1 1 1 1
this . Unknown_11h = reader . ReadByte ( ) ; //1
this . Unused_12h = reader . ReadUInt16 ( ) ; //0 0 0 0
2019-10-31 11:56:40 +08:00
this . Frames = reader . ReadUInt16 ( ) ; //221 17 151 201 frames
2019-11-18 20:52:58 +08:00
this . SequenceFrameLimit = reader . ReadUInt16 ( ) ; //223 31 159 207 sequence limit
2019-10-31 11:56:40 +08:00
this . Duration = reader . ReadSingle ( ) ; //7.34 0.53 5.0 6.66 duration
2019-11-08 14:54:46 +08:00
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
2019-11-18 20:52:58 +08:00
this . Unused_20h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_24h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_28h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_2Ch = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_30h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . Unused_34h = reader . ReadUInt32 ( ) ; //0 0 0 0
2019-11-14 15:58:20 +08:00
this . MaxSeqBlockLength = reader . ReadUInt32 ( ) ; //314 174 1238 390 maximum sequence block size
2019-11-18 20:52:58 +08:00
this . UsageCount = reader . ReadUInt32 ( ) ; //2 2 2 2
2017-09-21 18:33:05 +08:00
this . Sequences = reader . ReadBlock < ResourcePointerList64 < Sequence > > ( ) ;
2019-01-27 14:14:10 +08:00
this . BoneIds = reader . ReadBlock < ResourceSimpleList64_s < AnimationBoneId > > ( ) ;
2019-11-14 15:58:20 +08:00
AssignSequenceBoneIds ( ) ;
2019-11-17 19:14:00 +08:00
2019-11-18 20:52:58 +08:00
//bool hasUVs = false;
//if (BoneIds?.data_items != null)
//{
// foreach (var boneid in BoneIds.data_items)
// {
// if (boneid.Track == 17)//UV0
// { hasUVs = true; }
// if (boneid.Track == 18)//UV1
// { hasUVs = true; }
// }
//}
//bool hasRootMotion = false; // (Unknown_10h & 16) == hasRootMotion
//if (Sequences?.data_items != null)
//{
// foreach (var seq in Sequences.data_items)
// {
// if (seq == null) continue;
// if (seq.RootMotionRefCounts != 0) { hasRootMotion = true; }
// }
//}
//var b0 = (Unknown_1Ch) & 0xFF;
//var b1 = (Unknown_1Ch >> 8) & 0xFF;
//var b2 = (Unknown_1Ch >> 16) & 0xFF;
//var b3 = (Unknown_1Ch >> 24) & 0xFF;
//if (hasUVs)
//{
// if (Unknown_1Ch != 0x6B002400)
// { }
//}
//else
//{
//}
//switch (Unknown_10h)
//{
// case 0:
// if (hasRootMotion) { }
// break;
// case 1://is prop?
// if (hasRootMotion) { }
// break;
// case 8:
// if (hasRootMotion) { }
// break;
// case 16:
// if (!hasRootMotion) { }
// break;
// case 24:
// if (!hasRootMotion) { }
// break;
// default: break;
//}
//if (Unknown_04h != 1)
//{ }
//if (Unknown_11h != 1)
//{ }
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
2019-11-16 00:18:23 +08:00
//BuildSequencesData();
2017-09-21 18:33:05 +08:00
// write structure data
writer . Write ( this . VFT ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
2019-11-18 20:52:58 +08:00
writer . Write ( this . Unused_08h ) ;
writer . Write ( this . Unused_0Ch ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_10h ) ;
2019-11-18 20:52:58 +08:00
writer . Write ( this . Unknown_11h ) ;
writer . Write ( this . Unused_12h ) ;
2019-10-31 11:56:40 +08:00
writer . Write ( this . Frames ) ;
writer . Write ( this . SequenceFrameLimit ) ;
writer . Write ( this . Duration ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_1Ch ) ;
2019-11-18 20:52:58 +08:00
writer . Write ( this . Unused_20h ) ;
writer . Write ( this . Unused_24h ) ;
writer . Write ( this . Unused_28h ) ;
writer . Write ( this . Unused_2Ch ) ;
writer . Write ( this . Unused_30h ) ;
writer . Write ( this . Unused_34h ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . MaxSeqBlockLength ) ;
2019-10-31 11:56:40 +08:00
writer . Write ( this . UsageCount ) ;
2017-09-21 18:33:05 +08:00
writer . WriteBlock ( this . Sequences ) ;
2019-01-27 14:14:10 +08:00
writer . WriteBlock ( this . BoneIds ) ;
2017-09-21 18:33:05 +08:00
}
public override Tuple < long , IResourceBlock > [ ] GetParts ( )
{
2019-11-16 00:18:23 +08:00
BuildSequencesData ( ) ; //TODO: move this somewhere better?
2017-09-21 18:33:05 +08:00
return new Tuple < long , IResourceBlock > [ ] {
new Tuple < long , IResourceBlock > ( 0x40 , Sequences ) ,
2019-01-27 14:14:10 +08:00
new Tuple < long , IResourceBlock > ( 0x50 , BoneIds )
2017-09-21 18:33:05 +08:00
} ;
}
2019-11-14 15:58:20 +08:00
public void AssignSequenceBoneIds ( )
{
if ( Sequences ? . data_items ! = null )
{
foreach ( var seq in Sequences . data_items )
{
for ( int i = 0 ; i < seq ? . Sequences ? . Length ; i + + )
{
if ( i < BoneIds ? . data_items ? . Length )
{
seq . Sequences [ i ] . BoneId = BoneIds . data_items [ i ] ;
}
}
}
}
}
public void CalculateMaxSeqBlockLength ( )
{
if ( Sequences ? . data_items ! = null )
{
uint maxSize = 0 ;
foreach ( var seq in Sequences . data_items )
{
maxSize = Math . Max ( maxSize , ( uint ) seq . BlockLength ) ;
}
MaxSeqBlockLength = maxSize ;
}
}
2019-11-16 00:18:23 +08:00
public void BuildSequencesData ( )
{
AssignSequenceBoneIds ( ) ;
if ( Sequences ? . data_items ! = null )
{
foreach ( var seq in Sequences . data_items )
{
seq . BuildData ( ) ;
}
}
CalculateMaxSeqBlockLength ( ) ;
}
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
{
2019-11-20 19:51:37 +08:00
YcdXml . StringTag ( sb , indent , "Hash" , YcdXml . HashString ( Hash ) ) ;
2019-11-14 15:58:20 +08:00
YcdXml . ValueTag ( sb , indent , "Unknown10" , Unknown_10h . ToString ( ) ) ;
YcdXml . ValueTag ( sb , indent , "FrameCount" , Frames . ToString ( ) ) ;
YcdXml . ValueTag ( sb , indent , "SequenceFrameLimit" , SequenceFrameLimit . ToString ( ) ) ; //sequences should be transparent to this!
YcdXml . ValueTag ( sb , indent , "Duration" , FloatUtil . ToString ( Duration ) ) ;
YcdXml . StringTag ( sb , indent , "Unknown1C" , YcdXml . HashString ( Unknown_1Ch ) ) ;
YcdXml . WriteItemArray ( sb , BoneIds ? . data_items , indent , "BoneIds" ) ;
YcdXml . WriteItemArray ( sb , Sequences ? . data_items , indent , "Sequences" ) ;
}
public void ReadXml ( XmlNode node )
{
2019-11-20 19:51:37 +08:00
Hash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "Hash" ) ) ;
2019-11-18 20:52:58 +08:00
Unknown_10h = ( byte ) Xml . GetChildUIntAttribute ( node , "Unknown10" , "value" ) ;
2019-11-14 15:58:20 +08:00
Frames = ( ushort ) Xml . GetChildUIntAttribute ( node , "FrameCount" , "value" ) ;
SequenceFrameLimit = ( ushort ) Xml . GetChildUIntAttribute ( node , "SequenceFrameLimit" , "value" ) ;
Duration = Xml . GetChildFloatAttribute ( node , "Duration" , "value" ) ;
Unknown_1Ch = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "Unknown1C" ) ) ;
BoneIds = new ResourceSimpleList64_s < AnimationBoneId > ( ) ;
BoneIds . data_items = XmlMeta . ReadItemArray < AnimationBoneId > ( node , "BoneIds" ) ;
Sequences = new ResourcePointerList64 < Sequence > ( ) ;
Sequences . data_items = XmlMeta . ReadItemArrayNullable < Sequence > ( node , "Sequences" ) ;
AssignSequenceBoneIds ( ) ;
}
2019-11-25 17:44:16 +08:00
public struct FramePosition
{
public int Frame0 ;
public int Frame1 ;
public float Alpha0 ;
public float Alpha1 ;
}
public FramePosition GetFramePosition ( float t )
{
bool ignoreLastFrame = true ; //if last frame is equivalent to the first one, eg rollercoaster small light "globes" don't
FramePosition p = new FramePosition ( ) ;
var nframes = ( ignoreLastFrame ) ? ( Frames - 1 ) : Frames ;
var curPos = ( t / Duration ) * nframes ;
p . Frame0 = ( ( ushort ) curPos ) % Frames ;
p . Frame1 = ( p . Frame0 + 1 ) ; // % frames;
p . Alpha1 = ( float ) ( curPos - Math . Floor ( curPos ) ) ;
p . Alpha0 = 1.0f - p . Alpha1 ;
return p ;
}
public Vector4 EvaluateVector4 ( FramePosition frame , int boneIndex , bool interpolate )
{
var s = frame . Frame0 / SequenceFrameLimit ;
int f0 = frame . Frame0 % SequenceFrameLimit ;
int f1 = f0 + 1 ;
var seq = Sequences . data_items [ s ] ;
var aseq = seq . Sequences [ boneIndex ] ;
var v0 = aseq . EvaluateVector ( f0 ) ;
var v1 = aseq . EvaluateVector ( f1 ) ;
var v = interpolate ? ( v0 * frame . Alpha0 ) + ( v1 * frame . Alpha1 ) : v0 ;
return v ;
}
public Quaternion EvaluateQuaternion ( FramePosition frame , int boneIndex , bool interpolate )
{
var s = frame . Frame0 / SequenceFrameLimit ;
int f0 = frame . Frame0 % SequenceFrameLimit ;
int f1 = f0 + 1 ;
var seq = Sequences . data_items [ s ] ;
var aseq = seq . Sequences [ boneIndex ] ;
var q0 = aseq . EvaluateQuaternion ( f0 ) ;
var q1 = aseq . EvaluateQuaternion ( f1 ) ;
var q = interpolate ? Quaternion . Slerp ( q0 , q1 , frame . Alpha1 ) : q0 ;
return q ;
}
public int FindBoneIndex ( ushort boneTag , byte track )
{
//TODO: make this use a dict??
if ( BoneIds ? . data_items ! = null )
{
for ( int i = 0 ; i < BoneIds . data_items . Length ; i + + )
{
var b = BoneIds . data_items [ i ] ;
if ( ( b . BoneId = = boneTag ) & & ( b . Track = = track ) ) return i ;
}
}
return - 1 ;
}
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public struct AnimationBoneId : IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public ushort BoneId { get ; set ; }
public byte Unk0 { get ; set ; }
2019-10-31 11:56:40 +08:00
public byte Track { get ; set ; }
2019-11-14 15:58:20 +08:00
2017-09-21 18:33:05 +08:00
public override string ToString ( )
{
2019-10-31 11:56:40 +08:00
return BoneId . ToString ( ) + ": " + Unk0 . ToString ( ) + ", " + Track . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
YcdXml . ValueTag ( sb , indent , "BoneId" , BoneId . ToString ( ) ) ;
YcdXml . ValueTag ( sb , indent , "Track" , Track . ToString ( ) ) ;
YcdXml . ValueTag ( sb , indent , "Unk0" , Unk0 . ToString ( ) ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public void ReadXml ( XmlNode node )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
BoneId = ( ushort ) Xml . GetChildUIntAttribute ( node , "BoneId" , "value" ) ;
Track = ( byte ) Xml . GetChildUIntAttribute ( node , "Track" , "value" ) ;
Unk0 = ( byte ) Xml . GetChildUIntAttribute ( node , "Unk0" , "value" ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
}
public enum AnimChannelType : int
{
StaticQuaternion = 0 ,
StaticVector3 = 1 ,
StaticFloat = 2 ,
RawFloat = 3 ,
QuantizeFloat = 4 ,
IndirectQuantizeFloat = 5 ,
LinearFloat = 6 ,
CachedQuaternion1 = 7 ,
CachedQuaternion2 = 8 ,
}
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class AnimChannel : IMetaXmlItem
{
public AnimChannelType Type { get ; set ; }
public int Sequence { get ; set ; }
public int Index { get ; set ; }
2019-11-16 00:18:23 +08:00
public int DataOffset { get ; set ; }
public int FrameOffset { get ; set ; }
2019-11-14 15:58:20 +08:00
public abstract void Read ( AnimChannelDataReader reader ) ;
public virtual void Write ( AnimChannelDataWriter writer )
{ }
public virtual void ReadFrame ( AnimChannelDataReader reader )
{ }
public virtual void WriteFrame ( AnimChannelDataWriter writer )
{ }
2019-11-16 00:18:23 +08:00
public virtual int GetReferenceIndex ( )
{ return Index ; }
public virtual int GetFrameBits ( )
{ return 0 ; }
2019-10-31 11:56:40 +08:00
public virtual float EvaluateFloat ( int frame ) = > 0.0f ;
public void Associate ( int sequence , int index )
{
Sequence = sequence ;
Index = index ;
}
2019-11-14 15:58:20 +08:00
public virtual void WriteXml ( StringBuilder sb , int indent )
{
YcdXml . ValueTag ( sb , indent , "Type" , Type . ToString ( ) ) ;
//YcdXml.ValueTag(sb, indent, "Sequence", Sequence.ToString());
//YcdXml.ValueTag(sb, indent, "Index", Index.ToString());
}
public virtual void ReadXml ( XmlNode node )
{
//not necessary to read Type as it's already read and set in constructor
//Type = Xml.GetEnumValue<AnimChannelType>(Xml.GetChildStringAttribute(node, "Type", "value"));
//Sequence = Xml.GetChildIntAttribute(node, "Sequence", "value");
//Index = Xml.GetChildIntAttribute(node, "Index", "value");
}
public static AnimChannel ConstructChannel ( AnimChannelType type )
{
switch ( type )
{
case AnimChannelType . StaticQuaternion :
return new AnimChannelStaticQuaternion ( ) ;
case AnimChannelType . StaticVector3 :
return new AnimChannelStaticVector3 ( ) ;
case AnimChannelType . StaticFloat :
return new AnimChannelStaticFloat ( ) ;
case AnimChannelType . RawFloat :
return new AnimChannelRawFloat ( ) ;
case AnimChannelType . QuantizeFloat :
return new AnimChannelQuantizeFloat ( ) ;
case AnimChannelType . IndirectQuantizeFloat :
return new AnimChannelIndirectQuantizeFloat ( ) ;
case AnimChannelType . LinearFloat :
return new AnimChannelLinearFloat ( ) ;
case AnimChannelType . CachedQuaternion1 :
// normalized W from quaternion (evaluate first three channels, calculate W)
return new AnimChannelCachedQuaternion ( AnimChannelType . CachedQuaternion1 ) ;
case AnimChannelType . CachedQuaternion2 :
// unknown extra
// kind of the same as above but different at runtime?
return new AnimChannelCachedQuaternion ( AnimChannelType . CachedQuaternion2 ) ;
default :
return null ;
}
}
2019-11-16 00:18:23 +08:00
public override string ToString ( )
{
return Sequence . ToString ( ) + ": " + Index . ToString ( ) + ": " + Type . ToString ( ) + " DataOffset: " + DataOffset . ToString ( ) + " FrameOffset: " + FrameOffset . ToString ( ) ;
}
2019-10-31 11:56:40 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelStaticFloat : AnimChannel
{
2019-11-14 15:58:20 +08:00
public float Value { get ; set ; }
public AnimChannelStaticFloat ( )
{
Type = AnimChannelType . StaticFloat ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public override void Read ( AnimChannelDataReader reader )
{
Value = reader . ReadSingle ( ) ;
}
public override void Write ( AnimChannelDataWriter writer )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
writer . Write ( Value ) ;
2019-10-31 11:56:40 +08:00
}
public override float EvaluateFloat ( int frame )
{
2019-11-14 15:58:20 +08:00
return Value ;
}
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Value" , FloatUtil . ToString ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = Xml . GetChildFloatAttribute ( node , "Value" , "value" ) ;
2019-10-31 11:56:40 +08:00
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelStaticVector3 : AnimChannel
{
public Vector3 Value { get ; set ; }
2019-11-14 15:58:20 +08:00
public AnimChannelStaticVector3 ( )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
Type = AnimChannelType . StaticVector3 ;
}
public override void Read ( AnimChannelDataReader reader )
{
Value = reader . ReadVector3 ( ) ;
}
public override void Write ( AnimChannelDataWriter writer )
{
writer . Write ( Value ) ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . SelfClosingTag ( sb , indent , "Value " + FloatUtil . GetVector3XmlString ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
2020-01-21 23:45:27 +08:00
Value = Xml . GetChildVector3Attributes ( node , "Value" ) ;
2019-10-31 11:56:40 +08:00
}
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelStaticQuaternion : AnimChannel
2019-10-31 11:56:40 +08:00
{
public Quaternion Value { get ; set ; }
2019-11-14 15:58:20 +08:00
public AnimChannelStaticQuaternion ( )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
Type = AnimChannelType . StaticQuaternion ;
}
public override void Read ( AnimChannelDataReader reader )
{
var vec = reader . ReadVector3 ( ) ;
2019-10-31 11:56:40 +08:00
Value = new Quaternion (
vec ,
( float ) Math . Sqrt ( Math . Max ( 1.0f - vec . LengthSquared ( ) , 0.0 ) )
) ;
2019-11-14 15:58:20 +08:00
}
public override void Write ( AnimChannelDataWriter writer )
{
writer . Write ( Value . ToVector4 ( ) . XYZ ( ) ) ; //heh
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . SelfClosingTag ( sb , indent , "Value " + FloatUtil . GetVector4XmlString ( Value . ToVector4 ( ) ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
2020-01-21 23:45:27 +08:00
Value = new Quaternion ( Xml . GetChildVector4Attributes ( node , "Value" ) ) ;
2019-10-31 11:56:40 +08:00
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelIndirectQuantizeFloat : AnimChannel
{
public int FrameBits { get ; set ; }
public int ValueBits { get ; set ; }
2019-11-14 15:58:20 +08:00
public int NumInts { get ; set ; }
2019-10-31 11:56:40 +08:00
public float Quantum { get ; set ; }
public float Offset { get ; set ; }
public float [ ] Values { get ; set ; }
2019-11-17 19:14:00 +08:00
public uint [ ] ValueList { get ; set ; }
2019-10-31 11:56:40 +08:00
public uint [ ] Frames { get ; set ; }
2019-11-14 15:58:20 +08:00
public AnimChannelIndirectQuantizeFloat ( )
{
Type = AnimChannelType . IndirectQuantizeFloat ;
}
public override void Read ( AnimChannelDataReader reader )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
FrameBits = reader . ReadInt32 ( ) ;
ValueBits = reader . ReadInt32 ( ) ;
NumInts = reader . ReadInt32 ( ) ;
Quantum = reader . ReadSingle ( ) ;
Offset = reader . ReadSingle ( ) ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
Frames = new uint [ reader . NumFrames ] ;
2019-10-31 11:56:40 +08:00
2019-11-17 19:14:00 +08:00
var numValues0 = ( NumInts * 32 ) / ValueBits ;
var numValues1 = ( 1 u < < FrameBits ) - 1 ;
var numValues = Math . Min ( numValues0 , numValues1 ) ; //any better way to calculate this?
2019-11-14 15:58:20 +08:00
Values = new float [ numValues ] ;
2019-11-17 19:14:00 +08:00
ValueList = new uint [ numValues ] ;
2019-11-14 15:58:20 +08:00
reader . BitPosition = reader . Position * 8 ;
for ( int i = 0 ; i < numValues ; i + + )
{
uint bits = reader . ReadBits ( ValueBits ) ;
Values [ i ] = ( bits * Quantum ) + Offset ;
2019-11-17 19:14:00 +08:00
ValueList [ i ] = bits ;
2019-11-14 15:58:20 +08:00
}
reader . Position + = NumInts * 4 ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
//var endBit = bit + (NumInts * 32);
//var valueList = new List<float>();
//while (bit < endBit) // this actually seems to be reading too far.....
//{
// valueList.Add((reader.GetBit(bit, ValueBits) * Quantum) + Offset);
// bit += ValueBits;
//}
//Values = valueList.ToArray();
2019-11-16 00:18:23 +08:00
if ( FrameBits < 2 )
{ }
if ( ValueBits < 3 )
{ }
2019-11-14 15:58:20 +08:00
}
public override void Write ( AnimChannelDataWriter writer )
{
2019-11-17 19:14:00 +08:00
var frameBits = Math . Max ( writer . BitCount ( ( uint ) ( ( Values ? . Length ? ? 1 ) ) ) , 2 ) ; // Math.Max(writer.BitCount(Frames), 2);
//if ((frameBits != FrameBits)&&(ValueList!=null))
//{ } // ######### DEBUG TEST
2019-11-16 00:18:23 +08:00
FrameBits = frameBits ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
var valueCount = Values ? . Length ? ? 0 ;
var valueList = new uint [ valueCount ] ;
for ( int i = 0 ; i < valueCount ; i + + )
2019-10-31 11:56:40 +08:00
{
2019-11-17 19:14:00 +08:00
var bits = GetQuanta ( Values [ i ] ) ;
valueList [ i ] = bits ;
//if (ValueList != null) // ######### DEBUG TEST
//{
// var testbits = ValueList[i];
// if (bits != testbits)
// { }
//}
2019-11-14 15:58:20 +08:00
}
2019-11-16 00:18:23 +08:00
var valueBits = Math . Max ( writer . BitCount ( valueList ) , 3 ) ;
2019-11-17 19:14:00 +08:00
//if ((valueBits != ValueBits)&&(ValueList!=null))
//{ }// ######### DEBUG TEST
2019-11-16 00:18:23 +08:00
ValueBits = valueBits ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
writer . ResetBitstream ( ) ;
for ( int i = 0 ; i < valueCount ; i + + )
{
var u = valueList [ i ] ;
writer . WriteBits ( u , ValueBits ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
NumInts = writer . Bitstream . Count ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
writer . Write ( FrameBits ) ;
writer . Write ( ValueBits ) ;
writer . Write ( NumInts ) ;
writer . Write ( Quantum ) ;
writer . Write ( Offset ) ;
writer . WriteBitstream ( ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public override void ReadFrame ( AnimChannelDataReader reader )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
Frames [ reader . Frame ] = reader . ReadFrameBits ( FrameBits ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public override void WriteFrame ( AnimChannelDataWriter writer )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
writer . WriteFrameBits ( Frames [ writer . Frame ] , FrameBits ) ;
}
2019-11-16 00:18:23 +08:00
public override int GetFrameBits ( )
{
return FrameBits ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
private uint GetQuanta ( float v )
{
var q = ( v - Offset ) / Quantum ;
2019-11-17 19:14:00 +08:00
return ( uint ) ( q + 0.5f ) ;
2019-11-16 00:18:23 +08:00
//return (uint)Math.Round(q, 0);//any better way?
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
2019-10-31 11:56:40 +08:00
public override float EvaluateFloat ( int frame )
{
2019-11-08 16:53:29 +08:00
if ( Frames ? . Length > 0 ) return Values [ Frames [ frame % Frames . Length ] ] ;
2019-11-02 03:46:45 +08:00
return Offset ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Quantum" , FloatUtil . ToString ( Quantum ) ) ;
YcdXml . ValueTag ( sb , indent , "Offset" , FloatUtil . ToString ( Offset ) ) ;
YcdXml . WriteRawArray ( sb , Values , indent , "Values" , "" , FloatUtil . ToString , 10 ) ; // (Values?.Length ?? 0) + 1);
YcdXml . WriteRawArray ( sb , Frames , indent , "Frames" , "" , null , 10 ) ; // (Frames?.Length ?? 0) + 1);
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Quantum = Xml . GetChildFloatAttribute ( node , "Quantum" , "value" ) ;
Offset = Xml . GetChildFloatAttribute ( node , "Offset" , "value" ) ;
Values = Xml . GetChildRawFloatArray ( node , "Values" ) ;
Frames = Xml . GetChildRawUintArray ( node , "Frames" ) ;
}
2019-10-31 11:56:40 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelQuantizeFloat : AnimChannel
{
public int ValueBits { get ; set ; }
public float Quantum { get ; set ; }
public float Offset { get ; set ; }
public float [ ] Values { get ; set ; }
2019-11-17 19:14:00 +08:00
public uint [ ] ValueList { get ; set ; }
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public AnimChannelQuantizeFloat ( )
{
Type = AnimChannelType . QuantizeFloat ;
}
public override void Read ( AnimChannelDataReader reader )
{
ValueBits = reader . ReadInt32 ( ) ;
Quantum = reader . ReadSingle ( ) ;
Offset = reader . ReadSingle ( ) ;
Values = new float [ reader . NumFrames ] ;
2019-11-17 19:14:00 +08:00
ValueList = new uint [ reader . NumFrames ] ;
2019-11-16 00:18:23 +08:00
if ( ValueBits < 1 )
{ }
2019-11-14 15:58:20 +08:00
}
public override void Write ( AnimChannelDataWriter writer )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
var valueCount = Values ? . Length ? ? 0 ;
var valueList = new uint [ valueCount ] ;
for ( int i = 0 ; i < valueCount ; i + + )
{
2019-11-17 19:14:00 +08:00
var bits = GetQuanta ( Values [ i ] ) ;
valueList [ i ] = bits ;
//if (ValueList != null) // ######### DEBUG TEST
//{
// var testbits = ValueList[i];
// if (bits != testbits)
// { }
//}
2019-11-14 15:58:20 +08:00
}
2019-11-16 00:18:23 +08:00
var valueBits = Math . Max ( writer . BitCount ( valueList ) , 1 ) ;
2019-11-17 19:14:00 +08:00
//if ((valueBits != ValueBits)&&(ValueList!=null))
//{ } // ######### DEBUG TEST
2019-11-16 00:18:23 +08:00
ValueBits = valueBits ;
2019-11-14 15:58:20 +08:00
writer . Write ( ValueBits ) ;
writer . Write ( Quantum ) ;
writer . Write ( Offset ) ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public override void ReadFrame ( AnimChannelDataReader reader )
{
uint bits = reader . ReadFrameBits ( ValueBits ) ;
2019-11-17 19:14:00 +08:00
float val = ( bits * Quantum ) + Offset ;
Values [ reader . Frame ] = val ;
ValueList [ reader . Frame ] = bits ;
2019-11-14 15:58:20 +08:00
}
public override void WriteFrame ( AnimChannelDataWriter writer )
{
uint bits = GetQuanta ( Values [ writer . Frame ] ) ;
writer . WriteFrameBits ( bits , ValueBits ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-16 00:18:23 +08:00
public override int GetFrameBits ( )
{
return ValueBits ;
}
2019-11-14 15:58:20 +08:00
private uint GetQuanta ( float v )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
var q = ( v - Offset ) / Quantum ;
2019-11-17 19:14:00 +08:00
return ( uint ) ( q + 0.5f ) ;
2019-11-16 00:18:23 +08:00
//return (uint)Math.Round(q, 0);//any better way?
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
2019-10-31 11:56:40 +08:00
public override float EvaluateFloat ( int frame )
{
2019-11-08 16:53:29 +08:00
if ( Values ? . Length > 0 ) return Values [ frame % Values . Length ] ;
2019-11-02 03:46:45 +08:00
return Offset ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Quantum" , FloatUtil . ToString ( Quantum ) ) ;
YcdXml . ValueTag ( sb , indent , "Offset" , FloatUtil . ToString ( Offset ) ) ;
YcdXml . WriteRawArray ( sb , Values , indent , "Values" , "" , FloatUtil . ToString , 10 ) ; // (Values?.Length ?? 0) + 1);
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Quantum = Xml . GetChildFloatAttribute ( node , "Quantum" , "value" ) ;
Offset = Xml . GetChildFloatAttribute ( node , "Offset" , "value" ) ;
Values = Xml . GetChildRawFloatArray ( node , "Values" ) ;
}
2019-10-31 11:56:40 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelLinearFloat : AnimChannel
{
2019-11-14 15:58:20 +08:00
private int NumInts { get ; set ; }
private int Counts { get ; set ; }
2019-10-31 11:56:40 +08:00
public float Quantum { get ; set ; }
public float Offset { get ; set ; }
2019-11-14 15:58:20 +08:00
private int Bit { get ; set ; } //chunks start bit
private int Count1 { get ; set ; } //number of offset bits for each chunk
private int Count2 { get ; set ; } //number of value bits for each chunk
private int Count3 { get ; set ; } //number of delta bits for each frame
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public float [ ] Values { get ; set ; }
2019-11-17 19:14:00 +08:00
public int [ ] ValueList { get ; set ; }
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public AnimChannelLinearFloat ( )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
Type = AnimChannelType . LinearFloat ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public override void Read ( AnimChannelDataReader reader )
{
NumInts = reader . ReadInt32 ( ) ;
Counts = reader . ReadInt32 ( ) ;
Quantum = reader . ReadSingle ( ) ;
Offset = reader . ReadSingle ( ) ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
Bit = ( reader . Position * 8 ) ; //chunks start bit
Count1 = Counts & 0xFF ; //number of offset bits for each chunk
Count2 = ( Counts > > 8 ) & 0xFF ; //number of value bits for each chunk
Count3 = ( Counts > > 16 ) & 0xFF ; //number of delta bits for each frame
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
var streamLength = ( reader . Data ? . Length ? ? 0 ) * 8 ;
var numFrames = reader . NumFrames ;
var chunkSize = reader . ChunkSize ; //64 or 255(-1?)
2019-11-17 19:14:00 +08:00
var numChunks = ( ushort ) ( ( chunkSize + numFrames - 1 ) / chunkSize ) ;
2019-11-14 15:58:20 +08:00
var deltaOffset = Bit + ( numChunks * ( Count1 + Count2 ) ) ; //base offset to delta bits
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
reader . BitPosition = Bit ;
var chunkOffsets = new int [ numChunks ] ;
var chunkValues = new int [ numChunks ] ;
var frameValues = new float [ numFrames ] ;
2019-11-17 19:14:00 +08:00
var frameBits = new int [ numFrames ] ;
2019-11-14 15:58:20 +08:00
for ( int i = 0 ; i < numChunks ; i + + )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
chunkOffsets [ i ] = ( Count1 > 0 ) ? ( int ) reader . ReadBits ( Count1 ) : 0 ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
for ( int i = 0 ; i < numChunks ; i + + )
{
chunkValues [ i ] = ( Count2 > 0 ) ? ( int ) reader . ReadBits ( Count2 ) : 0 ;
}
for ( int i = 0 ; i < numChunks ; i + + )
{
var doffs = chunkOffsets [ i ] + deltaOffset ; //bit offset for chunk deltas
var value = chunkValues [ i ] ; //chunk start frame value
var cframe = ( i * chunkSize ) ; //chunk start frame
////if ((reader.BitPosition != doffs))
////{ }
reader . BitPosition = doffs ;
var inc = 0 ;
for ( int j = 0 ; j < chunkSize ; j + + )
{
int frame = cframe + j ;
if ( frame > = numFrames ) break ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
frameValues [ frame ] = ( value * Quantum ) + Offset ;
2019-11-17 19:14:00 +08:00
frameBits [ frame ] = value ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
if ( ( j + 1 ) > = chunkSize ) break ; //that's the last frame in the chunk, don't go further
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
var delta = ( Count3 ! = 0 ) ? ( int ) reader . ReadBits ( Count3 ) : 0 ;
var so = reader . BitPosition ;
2019-11-16 00:18:23 +08:00
var maxso = streamLength ; //Math.Min(so + 32 - Count3, streamLength); //
2019-11-14 15:58:20 +08:00
uint b = 0 ;
while ( b = = 0 ) // scan for a '1' bit
{
b = reader . ReadBits ( 1 ) ;
if ( reader . BitPosition > = maxso )
{ break ; } //trying to read more than 32 bits, or end of data... don't get into an infinite loop..!
}
delta | = ( ( reader . BitPosition - so - 1 ) < < Count3 ) ; //add the found bit onto the delta. (position-so-1) is index of found bit
if ( delta ! = 0 )
{
var sign = reader . ReadBits ( 1 ) ;
if ( sign = = 1 )
{
delta = - delta ;
}
}
inc + = delta ;
value + = inc ;
}
}
Values = frameValues ;
2019-11-17 19:14:00 +08:00
ValueList = frameBits ;
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
//for (int i = 1; i < numChunks; i++)
//{
// if ((chunkOffsets[i] <= chunkOffsets[i - 1]) && (i != numChunks - 1))
// { break; }//what's going on here? chunks not in order..? only seems to affect the final chunks?
//}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
reader . Position - = 16 ; //TODO: fix this?
reader . Position + = NumInts * 4 ;
}
public override void Write ( AnimChannelDataWriter writer )
{
2019-11-16 00:18:23 +08:00
//TODO: fix this!
2019-11-14 15:58:20 +08:00
var numFrames = writer . NumFrames ;
2019-11-17 19:14:00 +08:00
var numChunks = ( ushort ) ( ( 64 + numFrames - 1 ) / 64 ) ; //default value, if chunks used, chunkSize is always 64!
2019-11-14 15:58:20 +08:00
byte chunkSize = 64 ; //seems to always be 64 for this
if ( writer . ChunkSize ! = chunkSize )
{ writer . ChunkSize = chunkSize ; }
var valueCount = Values ? . Length ? ? 0 ;
2019-11-17 19:14:00 +08:00
var valueList = new int [ valueCount ] ;
2019-11-14 15:58:20 +08:00
for ( int i = 0 ; i < valueCount ; i + + )
2019-10-31 11:56:40 +08:00
{
2019-11-17 19:14:00 +08:00
var bits = GetQuanta ( Values [ i ] ) ;
valueList [ i ] = bits ;
//if (ValueList != null) // ######### DEBUG TEST
//{
// var testbits = ValueList[i];
// if (bits != testbits)
// { }
//}
2019-11-14 15:58:20 +08:00
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
var chunkOffsets = new uint [ numChunks ] ;
var chunkValues = new uint [ numChunks ] ;
var chunkDeltas = new int [ numChunks ] [ ] ;
var chunkDeltaBits = new uint [ numFrames ] ;
for ( int i = 0 ; i < numChunks ; i + + )
{
var cframe = ( i * chunkSize ) ; //chunk start frame
2019-11-17 19:14:00 +08:00
var cvalue = ( cframe < valueCount ) ? valueList [ cframe ] : valueList [ 0 ] ;
2019-11-14 15:58:20 +08:00
var cdeltas = new int [ chunkSize ] ;
var cinc = 0 ;
2019-11-17 19:14:00 +08:00
chunkValues [ i ] = ( uint ) cvalue ;
2019-11-14 15:58:20 +08:00
chunkDeltas [ i ] = cdeltas ;
for ( int j = 1 ; j < chunkSize ; j + + )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
int frame = cframe + j ;
if ( frame > = numFrames ) break ;
var value = valueList [ frame ] ;
2019-11-17 19:14:00 +08:00
var inc = value - cvalue ;
2019-11-14 15:58:20 +08:00
var delta = inc - cinc ;
var deltaa = ( uint ) Math . Abs ( delta ) ;
cinc = inc ;
cvalue = value ;
cdeltas [ j ] = delta ;
chunkDeltaBits [ frame ] = deltaa ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
}
Count3 = writer . BitCount ( chunkDeltaBits ) ; //number of delta bits for each frame
uint coffset = 0 ;
for ( int i = 0 ; i < numChunks ; i + + )
{
chunkOffsets [ i ] = coffset ;
var cdeltas = chunkDeltas [ i ] ;
for ( int j = 1 ; j < chunkSize ; j + + )
{
var delta = cdeltas [ j ] ;
2019-11-16 00:18:23 +08:00
coffset + = ( uint ) Count3 + ( ( delta < 0 ) ? 2 u : 1 u ) ;
2019-11-14 15:58:20 +08:00
}
}
Count1 = writer . BitCount ( chunkOffsets ) ; //number of offset bits for each chunk
Count2 = writer . BitCount ( chunkValues ) ; //number of value bits for each chunk
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
writer . ResetBitstream ( ) ;
if ( Count1 > 0 ) ////write chunk delta offsets
{
for ( int i = 0 ; i < numChunks ; i + + )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
writer . WriteBits ( chunkOffsets [ i ] , Count1 ) ;
}
}
if ( Count2 > 0 ) ////write chunk start values
{
for ( int i = 0 ; i < numChunks ; i + + )
{
writer . WriteBits ( chunkValues [ i ] , Count2 ) ;
}
}
for ( int i = 0 ; i < numChunks ; i + + ) ////write chunk frame deltas
{
var cdeltas = chunkDeltas [ i ] ;
for ( int j = 1 ; j < chunkSize ; j + + )
{
var delta = cdeltas [ j ] ;
var deltaa = ( uint ) Math . Abs ( delta ) ;
writer . WriteBits ( deltaa , Count3 ) ;
writer . WriteBits ( 1 , 1 ) ; //"stop" bit
if ( delta < 0 )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
writer . WriteBits ( 1 , 1 ) ; //sign bit
2019-10-31 11:56:40 +08:00
}
}
}
2019-11-14 15:58:20 +08:00
Counts = Count1 & 0xFF ;
Counts + = ( Count2 & 0xFF ) < < 8 ;
Counts + = ( Count3 & 0xFF ) < < 16 ;
NumInts = 4 + writer . Bitstream . Count ;
writer . Write ( NumInts ) ;
writer . Write ( Counts ) ;
writer . Write ( Quantum ) ;
writer . Write ( Offset ) ;
writer . WriteBitstream ( ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
2019-10-31 11:56:40 +08:00
public override float EvaluateFloat ( int frame )
{
2019-11-08 16:53:29 +08:00
if ( Values ? . Length > 0 ) return Values [ frame % Values . Length ] ;
2019-11-02 03:46:45 +08:00
return Offset ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
2019-11-17 19:14:00 +08:00
private int GetQuanta ( float v )
2019-11-14 15:58:20 +08:00
{
var q = ( v - Offset ) / Quantum ;
2019-11-17 19:14:00 +08:00
return ( int ) ( q + 0.5f ) ;
//return (uint)Math.Round(Math.Max(q, 0));//any better way?
2019-11-14 15:58:20 +08:00
}
public override void WriteXml ( StringBuilder sb , int indent )
{
2019-11-16 00:18:23 +08:00
//base.WriteXml(sb, indent);
Type = AnimChannelType . QuantizeFloat ; //TODO - FIX! temporary: just export this as a quantize float to avoid import issues..
2019-11-14 15:58:20 +08:00
base . WriteXml ( sb , indent ) ;
Type = AnimChannelType . LinearFloat ;
float minVal = float . MaxValue ;
for ( int i = 0 ; i < Values . Length ; i + + )
{
minVal = Math . Min ( minVal , Values [ i ] ) ;
}
if ( minVal ! = Offset )
{
Offset = minVal ;
}
YcdXml . ValueTag ( sb , indent , "Quantum" , FloatUtil . ToString ( Quantum ) ) ;
YcdXml . ValueTag ( sb , indent , "Offset" , FloatUtil . ToString ( Offset ) ) ;
YcdXml . WriteRawArray ( sb , Values , indent , "Values" , "" , FloatUtil . ToString , 10 ) ; // (Values?.Length ?? 0) + 1);
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Quantum = Xml . GetChildFloatAttribute ( node , "Quantum" , "value" ) ;
Offset = Xml . GetChildFloatAttribute ( node , "Offset" , "value" ) ;
Values = Xml . GetChildRawFloatArray ( node , "Values" ) ;
}
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelRawFloat : AnimChannel
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
public float [ ] Values { get ; set ; }
public AnimChannelRawFloat ( )
{
Type = AnimChannelType . RawFloat ;
}
public override void Read ( AnimChannelDataReader reader )
{
Values = new float [ reader . NumFrames ] ;
}
public override void Write ( AnimChannelDataWriter writer )
{
//nothing to do here
}
public override void ReadFrame ( AnimChannelDataReader reader )
{
uint bits = reader . ReadFrameBits ( 32 ) ;
float v = MetaTypes . ConvertData < float > ( MetaTypes . ConvertToBytes ( bits ) ) ;
Values [ reader . Frame ] = v ;
}
public override void WriteFrame ( AnimChannelDataWriter writer )
{
float v = Values [ writer . Frame ] ;
var b = BitConverter . GetBytes ( v ) ;
2019-11-17 19:14:00 +08:00
var bits = BitConverter . ToUInt32 ( b , 0 ) ;
2019-11-14 15:58:20 +08:00
writer . WriteFrameBits ( bits , 32 ) ;
}
2019-11-17 19:14:00 +08:00
public override int GetFrameBits ( )
{
return 32 ;
}
2019-11-14 15:58:20 +08:00
public override float EvaluateFloat ( int frame )
{
if ( Values ? . Length > 0 ) return Values [ frame % Values . Length ] ;
return base . EvaluateFloat ( frame ) ;
}
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . WriteRawArray ( sb , Values , indent , "Values" , "" , FloatUtil . ToString , 10 ) ; // (Values?.Length ?? 0) + 1);
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Values = Xml . GetChildRawFloatArray ( node , "Values" ) ;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimChannelCachedQuaternion : AnimChannel
{
private AnimChannelDataReader blockStream ;
2019-10-31 11:56:40 +08:00
private float [ ] valueCache ;
public float [ ] Values
{
get
{
if ( valueCache ! = null )
{
return valueCache ;
}
valueCache = new float [ blockStream . NumFrames ] ;
var channels = new AnimChannel [ 3 ] ;
var ch = 0 ;
for ( int i = 0 ; i < 4 ; i + + )
{
2019-11-16 00:18:23 +08:00
if ( i ! = 3 )
2019-10-31 11:56:40 +08:00
{
channels [ ch ] = blockStream . Sequences [ Sequence ] . Channels [ i ] ;
ch + + ;
}
}
for ( int i = 0 ; i < valueCache . Length ; i + + )
{
var vec = new Vector3 (
channels [ 0 ] . EvaluateFloat ( i ) ,
channels [ 1 ] . EvaluateFloat ( i ) ,
channels [ 2 ] . EvaluateFloat ( i )
) ;
valueCache [ i ] = ( float ) Math . Sqrt ( Math . Max ( 1.0f - vec . LengthSquared ( ) , 0.0 ) ) ;
}
return valueCache ;
}
}
2019-11-20 19:51:37 +08:00
public int QuatIndex { get ; set ; }
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public AnimChannelCachedQuaternion ( AnimChannelType type )
{
Type = type ;
}
public override void Read ( AnimChannelDataReader reader )
{
this . blockStream = reader ;
}
2019-11-16 00:18:23 +08:00
public override int GetReferenceIndex ( )
{
return QuatIndex ;
}
2019-10-31 11:56:40 +08:00
public override float EvaluateFloat ( int frame )
{
2019-11-08 16:53:29 +08:00
if ( Values ? . Length > 0 ) return Values [ frame % Values . Length ] ;
2019-11-02 15:14:36 +08:00
return 0.0f ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "QuatIndex" , QuatIndex . ToString ( ) ) ;
//data is already written in other channels...
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
QuatIndex = Xml . GetChildIntAttribute ( node , "QuatIndex" , "value" ) ;
//data was already read in other channels...
2019-10-31 11:56:40 +08:00
}
}
2019-11-14 15:58:20 +08:00
public class AnimChannelDataReader
{
public byte [ ] Data { get ; set ; }
public ushort NumFrames { get ; set ; }
public byte ChunkSize { get ; set ; } //stride of channel frame items (in frames)
public int Position { get ; set ; } //current byte that the main data reader is on
public int Frame { get ; set ; } //current frame that the reader is on
public uint FrameOffset { get ; set ; } //offset to frame data items / bytes
public ushort FrameLength { get ; set ; } //stride of frame data item
public int ChannelListOffset { get ; set ; } //offset to channel counts
public int ChannelDataOffset { get ; set ; } //offset to channel data/info ushorts
public int ChannelFrameOffset { get ; set ; } //offset to channel current frame data (in bits!)
public AnimSequence [ ] Sequences { get ; set ; } //used by AnimChannelCachedQuaternion when accessing values (when evaluating)
public int BitPosition { get ; set ; } //for use with ReadBits()
public AnimChannelDataReader ( byte [ ] data , ushort numFrames , byte chunkSize , uint frameOffset , ushort frameLength )
{
Data = data ;
NumFrames = numFrames ;
ChunkSize = chunkSize ;
Position = 0 ;
Frame = 0 ;
FrameOffset = frameOffset ;
FrameLength = frameLength ;
ChannelListOffset = ( int ) FrameOffset + ( FrameLength * NumFrames ) ;
ChannelDataOffset = ChannelListOffset + ( 9 * 2 ) ;
ChannelFrameOffset = 0 ;
}
public int ReadInt32 ( )
{
int i = BitConverter . ToInt32 ( Data , Position ) ;
Position + = 4 ;
return i ;
}
public float ReadSingle ( )
{
float f = BitConverter . ToSingle ( Data , Position ) ;
Position + = 4 ;
return f ;
}
public Vector3 ReadVector3 ( )
{
var v = new Vector3 ( ) ;
v . X = BitConverter . ToSingle ( Data , Position ) ;
v . Y = BitConverter . ToSingle ( Data , Position + 4 ) ;
v . Z = BitConverter . ToSingle ( Data , Position + 8 ) ;
Position + = 12 ;
return v ;
}
public uint GetBits ( int startBit , int length )
{
//dexyfex version that won't read too many bytes - probably won't perform as well
if ( startBit < 0 )
{ return 0 ; } //something must have went wrong reading other data... happening in fos_ep_1_p6-35.ycd
int startByte = startBit / 8 ;
int bitOffset = startBit % 8 ;
uint result = 0 ;
int shift = - bitOffset ;
int curByte = startByte ;
int bitsRemaining = length ;
while ( bitsRemaining > 0 )
{
var b = ( curByte < Data . Length ) ? ( uint ) Data [ curByte + + ] : 0 ;
var sb = ( shift < 0 ) ? ( b > > - shift ) : ( b < < shift ) ;
var bm = ( ( 1 u < < Math . Min ( bitsRemaining , 8 ) ) - 1 u ) < < ( Math . Max ( shift , 0 ) ) ;
var mb = ( sb & bm ) ;
result + = mb ;
bitsRemaining - = ( 8 + Math . Min ( shift , 0 ) ) ;
shift + = 8 ;
}
return result ;
////original calcium version - has issues near the end of the data from trying to read too many bytes.//
//var mask = MaskTable[length];
//var lowByte = BitConverter.ToUInt32(Data, (startBit / 32) * 4);
//var highByte = BitConverter.ToUInt32(Data, ((startBit / 32) + 1) * 4);
//var pair = ((ulong)highByte << 32) | lowByte;
//var res = (uint)((pair >> (startBit % 32)) & mask);
//if (result != res)//dexyfex sanity check
//{ }
//return res;
//private static uint[] MaskTable = new uint[]
//{
// 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF,
// 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF,
// 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF,
// 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
// 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
//};
}
public uint ReadBits ( int length )
{
uint bits = GetBits ( BitPosition , length ) ;
BitPosition + = length ;
return bits ;
}
public ushort ReadChannelCount ( )
{
ushort channelCount = BitConverter . ToUInt16 ( Data , ChannelListOffset ) ;
ChannelListOffset + = 2 ;
return channelCount ;
}
public ushort ReadChannelDataBits ( )
{
ushort channelDataBit = BitConverter . ToUInt16 ( Data , ChannelDataOffset ) ;
ChannelDataOffset + = 2 ;
return channelDataBit ;
}
2019-11-16 00:18:23 +08:00
public byte [ ] ReadChannelDataBytes ( int n )
{
var r = new byte [ n ] ;
Buffer . BlockCopy ( Data , ChannelDataOffset , r , 0 , n ) ;
ChannelDataOffset + = n ;
return r ;
}
2019-11-14 15:58:20 +08:00
public void AlignChannelDataOffset ( int channelCount )
{
int remainder = channelCount % 4 ;
if ( remainder > 0 )
{
int addamt = ( 4 - remainder ) * 2 ;
ChannelDataOffset + = addamt ;
}
}
public void BeginFrame ( int f )
{
Frame = f ;
ChannelFrameOffset = ( int ) ( ( FrameOffset + ( FrameLength * f ) ) * 8 ) ;
}
public uint ReadFrameBits ( int n )
{
uint b = GetBits ( ChannelFrameOffset , n ) ;
ChannelFrameOffset + = n ;
return b ;
}
}
public class AnimChannelDataWriter
{
public int ChannelListOffset { get ; set ; } //offset to channel counts
public int ChannelItemOffset { get ; set ; } //offset to channel data/info ushorts
public int ChannelFrameOffset { get ; set ; } //offset to channel current frame data (in bits!)
public int Position { get ; set ; } //current byte that the main data reader is on
public int Frame { get ; set ; } //current frame that the reader is on
public ushort NumFrames { get ; set ; }
public byte ChunkSize { get ; set ; } //stride of channel frame items - starts at 0 and will be set to 64 if need be
public ushort FrameLength { get ; set ; } = 0 ; //stride of frame data item, calculated when ending frames
MemoryStream ChannelListStream = new MemoryStream ( ) ;
MemoryStream ChannelItemStream = new MemoryStream ( ) ;
MemoryStream MainStream = new MemoryStream ( ) ;
BinaryWriter ChannelListWriter = null ;
BinaryWriter ChannelItemWriter = null ;
BinaryWriter MainWriter = null ;
public List < uint > ChannelFrameStream { get ; private set ; } = new List < uint > ( ) ; //frame bits stream.
public List < uint [ ] > ChannelFrames { get ; private set ; } = new List < uint [ ] > ( ) ; //bitstreams for each frame
public List < uint > Bitstream { get ; private set ; } = new List < uint > ( ) ; //temporary bitstream, used from WriteBits()
public int BitstreamPos { get ; set ; } = 0 ;
public AnimChannelDataWriter ( ushort numFrames )
{
Position = 0 ;
Frame = 0 ;
NumFrames = numFrames ;
ChunkSize = 0 ; //default 0 value means chunks not used
ChannelListWriter = new BinaryWriter ( ChannelListStream ) ;
ChannelItemWriter = new BinaryWriter ( ChannelItemStream ) ;
MainWriter = new BinaryWriter ( MainStream ) ;
}
public void WriteChannelListData ( ushort c )
{
ChannelListWriter . Write ( c ) ;
ChannelListOffset + = 2 ;
}
public void WriteChannelItemData ( ushort c )
{
ChannelItemWriter . Write ( c ) ;
ChannelItemOffset + = 2 ;
}
2019-11-16 00:18:23 +08:00
public void AlignChannelItemData ( int channelCount , int sequenceCount )
2019-11-14 15:58:20 +08:00
{
int remainder = channelCount % 4 ;
if ( remainder > 0 )
{
2019-11-16 00:18:23 +08:00
ushort writeval = ( ushort ) ( sequenceCount < < 2 ) ;
2019-11-14 15:58:20 +08:00
int addamt = ( 4 - remainder ) ;
for ( int i = 0 ; i < addamt ; i + + )
{
2019-11-16 00:18:23 +08:00
WriteChannelItemData ( writeval ) ;
2019-11-14 15:58:20 +08:00
}
}
}
2019-11-16 00:18:23 +08:00
public void WriteChannelItemDataBytes ( byte [ ] data )
{
if ( data ? . Length > 0 )
{
ChannelItemWriter . Write ( data ) ;
ChannelItemOffset + = data . Length ;
}
}
2019-11-14 15:58:20 +08:00
public void Write ( int i )
{
MainWriter . Write ( i ) ;
Position + = 4 ;
}
public void Write ( float f )
{
MainWriter . Write ( f ) ;
Position + = 4 ;
}
public void Write ( Vector3 v )
{
MainWriter . Write ( v . X ) ;
MainWriter . Write ( v . Y ) ;
MainWriter . Write ( v . Z ) ;
Position + = 12 ;
}
public void BeginFrame ( int f )
{
Frame = f ;
ChannelFrameStream . Clear ( ) ;
ChannelFrameOffset = 0 ;
}
public void WriteFrameBits ( uint bits , int n )
{
WriteToBitstream ( ChannelFrameStream , ChannelFrameOffset , bits , n ) ;
ChannelFrameOffset + = n ;
}
public void EndFrame ( )
{
FrameLength = Math . Max ( FrameLength , ( ushort ) ( ChannelFrameStream . Count * 4 ) ) ;
ChannelFrames . Add ( ChannelFrameStream . ToArray ( ) ) ;
ChannelFrameStream . Clear ( ) ;
}
public int BitCount ( uint bits ) //could be static, but not for convenience
{
int bc = 0 ;
for ( int i = 0 ; i < 32 ; i + + )
{
uint mask = 1 u < < i ;
if ( ( bits & mask ) > 0 ) bc = ( i + 1 ) ;
}
return bc ;
}
public int BitCount ( uint [ ] values )
{
uint maxValue = 0 ;
for ( int i = 0 ; i < values ? . Length ; i + + )
{
maxValue = Math . Max ( maxValue , values [ i ] ) ;
}
return BitCount ( maxValue ) ;
}
public void ResetBitstream ( )
{
Bitstream . Clear ( ) ;
BitstreamPos = 0 ;
}
public void WriteBits ( uint bits , int n ) //write n bits to the bitstream.
{
WriteToBitstream ( Bitstream , BitstreamPos , bits , n ) ;
BitstreamPos + = n ;
}
public void WriteBitstream ( ) //write the contents of the bitstream (as uints) to the main writer
{
for ( int i = 0 ; i < Bitstream . Count ; i + + )
{
MainWriter . Write ( Bitstream [ i ] ) ;
}
Position + = ( Bitstream . Count * 4 ) ;
}
private void WriteToBitstream ( List < uint > stream , int offset , uint bits , int n )
{
if ( stream = = null ) return ;
uint mask = ( uint ) ( ( 1L < < n ) - 1 ) ;
uint masked = bits & mask ;
if ( bits ! = masked )
{ }
int soffset = offset % 32 ;
int sindex = offset / 32 ;
while ( sindex > = stream . Count ) stream . Add ( 0 ) ; //pad beginning of the stream
uint sval = stream [ sindex ] ;
uint sbits = bits < < soffset ;
stream [ sindex ] = sval + sbits ;
int endbit = ( soffset + n ) - 32 ;
if ( endbit > 0 )
{
int eindex = sindex + 1 ;
while ( eindex > = stream . Count ) stream . Add ( 0 ) ; //pad end of stream
int eoffset = 32 - soffset ;
uint eval = stream [ eindex ] ;
uint ebits = bits > > eoffset ;
stream [ eindex ] = eval + ebits ;
}
}
public byte [ ] GetStreamData ( MemoryStream ms )
{
var length = ( int ) ms . Length ;
var data = new byte [ length ] ;
ms . Flush ( ) ;
ms . Position = 0 ;
ms . Read ( data , 0 , length ) ;
return data ;
}
public byte [ ] GetChannelListDataBytes ( )
{
return GetStreamData ( ChannelListStream ) ;
}
public byte [ ] GetChannelItemDataBytes ( )
{
return GetStreamData ( ChannelItemStream ) ;
}
public byte [ ] GetMainDataBytes ( )
{
return GetStreamData ( MainStream ) ;
}
public byte [ ] GetFrameDataBytes ( )
{
var ms = new MemoryStream ( ) ;
var bw = new BinaryWriter ( ms ) ;
var frameUintCount = FrameLength / 4 ;
for ( int i = 0 ; i < ChannelFrames . Count ; i + + )
{
var frameData = ChannelFrames [ i ] ;
for ( int f = 0 ; f < frameUintCount ; f + + )
{
bw . Write ( ( f < frameData . Length ) ? frameData [ f ] : 0 ) ;
}
}
return GetStreamData ( ms ) ;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class AnimSequence : IMetaXmlItem
2019-10-31 11:56:40 +08:00
{
public AnimChannel [ ] Channels { get ; set ; }
public bool IsType7Quat { get ; internal set ; }
2019-11-14 15:58:20 +08:00
public AnimationBoneId BoneId { get ; set ; } //for convenience
2019-11-17 19:14:00 +08:00
public Quaternion EvaluateQuaternionType7 ( int frame )
2019-10-31 11:56:40 +08:00
{
if ( ! IsType7Quat )
{
return new Quaternion (
Channels [ 0 ] . EvaluateFloat ( frame ) ,
Channels [ 1 ] . EvaluateFloat ( frame ) ,
Channels [ 2 ] . EvaluateFloat ( frame ) ,
Channels [ 3 ] . EvaluateFloat ( frame )
) ;
}
2019-11-16 00:18:23 +08:00
var t7 = Channels [ 3 ] as AnimChannelCachedQuaternion ; //type 1
if ( t7 = = null ) t7 = Channels [ 4 ] as AnimChannelCachedQuaternion ; //type 2
2019-10-31 11:56:40 +08:00
var x = Channels [ 0 ] . EvaluateFloat ( frame ) ;
var y = Channels [ 1 ] . EvaluateFloat ( frame ) ;
var z = Channels [ 2 ] . EvaluateFloat ( frame ) ;
var normalized = t7 . EvaluateFloat ( frame ) ;
switch ( t7 . QuatIndex )
{
case 0 :
return new Quaternion ( normalized , x , y , z ) ;
case 1 :
return new Quaternion ( x , normalized , y , z ) ;
case 2 :
return new Quaternion ( x , y , normalized , z ) ;
case 3 :
return new Quaternion ( x , y , z , normalized ) ;
default :
return Quaternion . Identity ;
}
2017-09-21 18:33:05 +08:00
}
2019-11-02 03:46:45 +08:00
2019-11-17 19:14:00 +08:00
public Quaternion EvaluateQuaternion ( int frame )
{
if ( IsType7Quat ) return EvaluateQuaternionType7 ( frame ) ;
return EvaluateVector ( frame ) . ToQuaternion ( ) ;
}
2019-11-02 03:46:45 +08:00
public Vector4 EvaluateVector ( int frame )
{
if ( Channels = = null ) return Vector4 . Zero ;
2019-11-17 19:14:00 +08:00
if ( IsType7Quat ) return Quaternion . Normalize ( EvaluateQuaternionType7 ( frame ) ) . ToVector4 ( ) ; //normalization shouldn't be necessary, but saves explosions in case of incorrectness
2019-11-02 03:46:45 +08:00
var v = Vector4 . Zero ;
int c = 0 ;
for ( int i = 0 ; i < Channels . Length ; i + + )
{
if ( c > = 4 ) break ;
var channel = Channels [ i ] ;
var sv3c = channel as AnimChannelStaticVector3 ;
2019-11-14 15:58:20 +08:00
var ssqc = channel as AnimChannelStaticQuaternion ;
2019-11-02 03:46:45 +08:00
if ( sv3c ! = null )
{
for ( int n = 0 ; n < 3 ; n + + )
{
if ( ( c + n ) > = 4 ) break ;
v [ c + n ] = sv3c . Value [ n ] ;
}
c + = 3 ;
}
else if ( ssqc ! = null )
{
for ( int n = 0 ; n < 4 ; n + + )
{
if ( ( c + n ) > = 4 ) break ;
2019-11-02 15:14:36 +08:00
v [ c + n ] = ssqc . Value [ n ] ;
2019-11-02 03:46:45 +08:00
}
c + = 4 ;
}
else
{
v [ c ] = channel . EvaluateFloat ( frame ) ;
c + + ;
}
}
return v ;
}
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
{
//YcdXml.ValueTag(sb, indent, "BoneId", BoneId.BoneId.ToString()); //just for convenience really.....
YcdXml . WriteItemArray ( sb , Channels , indent , "Channels" ) ;
}
public void ReadXml ( XmlNode node )
{
//AnimationBoneId b = new AnimationBoneId();
//b.BoneId = (ushort)Xml.GetChildUIntAttribute(node, "BoneId", "value");
//BoneId = b;
//Channels = XmlMeta.ReadItemArrayNullable<AnimChannel>(node, "Channels");
var chansNode = node . SelectSingleNode ( "Channels" ) ;
if ( chansNode ! = null )
{
var inodes = chansNode . SelectNodes ( "Item" ) ;
if ( inodes ? . Count > 0 )
{
var clist = new List < AnimChannel > ( ) ;
foreach ( XmlNode inode in inodes )
{
var type = Xml . GetEnumValue < AnimChannelType > ( Xml . GetChildStringAttribute ( inode , "Type" , "value" ) ) ;
var c = AnimChannel . ConstructChannel ( type ) ;
c . ReadXml ( inode ) ;
clist . Add ( c ) ;
}
Channels = clist . ToArray ( ) ;
}
}
}
2019-11-16 00:18:23 +08:00
public override string ToString ( )
{
return "AnimSequence: " + ( Channels ? . Length ? ? 0 ) . ToString ( ) + " channels" ;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class SequenceRootChannelRef : IMetaXmlItem
{
public byte [ ] Bytes { get ; set ; }
public byte ChannelType { get { return Bytes [ 0 ] ; } set { Bytes [ 0 ] = value ; } }
public byte ChannelIndex { get { return Bytes [ 1 ] ; } set { Bytes [ 1 ] = value ; } }
public ushort DataIntOffset
{
get { return ( ushort ) ( Bytes [ 2 ] + ( Bytes [ 3 ] < < 8 ) ) ; }
set
{
Bytes [ 2 ] = ( byte ) ( value & 0xFF ) ;
Bytes [ 3 ] = ( byte ) ( ( value > > 8 ) & 0xFF ) ;
}
}
public ushort FrameBitOffset
{
get { return ( ushort ) ( Bytes [ 4 ] + ( Bytes [ 5 ] < < 8 ) ) ; }
set
{
Bytes [ 4 ] = ( byte ) ( value & 0xFF ) ;
Bytes [ 5 ] = ( byte ) ( ( value > > 8 ) & 0xFF ) ;
}
}
public SequenceRootChannelRef ( )
{
Bytes = new byte [ 6 ] ;
}
public SequenceRootChannelRef ( AnimChannelType type , int channelIndex )
{
Bytes = new byte [ 6 ] ;
ChannelType = ( byte ) type ;
ChannelIndex = ( byte ) channelIndex ;
}
public SequenceRootChannelRef ( byte [ ] bytes )
{
Bytes = bytes ;
}
public override string ToString ( )
{
if ( Bytes ? . Length > = 6 )
{
return ChannelType . ToString ( ) + ", " + ChannelIndex . ToString ( ) + ", " + DataIntOffset . ToString ( ) + ", " + FrameBitOffset . ToString ( ) ;
}
return "(empty)" ;
}
public void WriteXml ( StringBuilder sb , int indent )
{
YcdXml . WriteRawArray ( sb , Bytes , indent , "Bytes" , "" , YcdXml . FormatHexByte , 6 ) ;
}
public void ReadXml ( XmlNode node )
{
Bytes = Xml . GetChildRawByteArray ( node , "Bytes" ) ;
}
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class Sequence : ResourceSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
2019-11-16 00:18:23 +08:00
get { return 32 + ( Data ? . Length ? ? 0 ) ; }
2017-09-21 18:33:05 +08:00
}
// structure data
2017-10-03 17:24:33 +08:00
public MetaHash Unknown_00h { get ; set ; } //identifier / name?
2017-09-21 18:33:05 +08:00
public uint DataLength { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unused_08h { get ; set ; } // 0x00000000
2019-11-14 15:58:20 +08:00
public uint FrameOffset { get ; set ; } //offset to frame data items / bytes
2019-11-18 20:52:58 +08:00
public uint RootMotionRefsOffset { get ; set ; } //offset to root motion items (relative to start of the chunk, -32), ==BlockLength when no root motion
2017-10-03 17:24:33 +08:00
public ushort Unused_14h { get ; set ; } //0x0000
2019-11-14 15:58:20 +08:00
public ushort NumFrames { get ; set ; } // count of frame data items
public ushort FrameLength { get ; set ; } //stride of frame data item
public ushort IndirectQuantizeFloatNumInts { get ; set ; } //total number of ints that the indirect quantize float channels take (not the frames data though!)
public ushort QuantizeFloatValueBits { get ; set ; } //total number of quantize float value bits per frame?
2019-10-31 11:56:40 +08:00
public byte ChunkSize { get ; set ; } //64|255 0x40|0xFF
2019-11-16 00:18:23 +08:00
public byte RootMotionRefCounts { get ; set ; } //0|17|20|21|49|52|53 0x11|0x14|0x15|0x31|0x34|0x35
2017-09-21 18:33:05 +08:00
public byte [ ] Data { get ; set ; }
2017-10-03 17:24:33 +08:00
2019-10-31 11:56:40 +08:00
// parsed data
public AnimSequence [ ] Sequences { get ; set ; }
2017-10-03 17:24:33 +08:00
2019-11-16 00:18:23 +08:00
public SequenceRootChannelRef [ ] RootPositionRefs { get ; set ; }
public SequenceRootChannelRef [ ] RootRotationRefs { get ; set ; }
public int RootPositionRefCount
{
get { return ( RootMotionRefCounts > > 4 ) & 0xF ; }
set
{
var rrc = RootMotionRefCounts & 0xF ;
RootMotionRefCounts = ( byte ) ( rrc + ( ( value & 0xF ) < < 4 ) ) ;
}
}
public int RootRotationRefCount
{
get { return RootMotionRefCounts & 0xF ; }
set
{
var rpc = ( RootMotionRefCounts > > 4 ) & 0xF ;
RootMotionRefCounts = ( byte ) ( rpc + ( value & 0xF ) ) ;
}
}
2017-10-03 17:24:33 +08:00
2019-10-31 11:56:40 +08:00
class AnimChannelListItem
{
public int Sequence ;
public int Index ;
public AnimChannel Channel ;
public AnimChannelListItem ( int seq , int ind , AnimChannel channel )
{
Sequence = seq ;
Index = ind ;
Channel = channel ;
}
}
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
2019-11-14 15:58:20 +08:00
this . Unknown_00h = reader . ReadUInt32 ( ) ; //2965995365 2837183178
this . DataLength = reader . ReadUInt32 ( ) ; //282 142 1206 358
this . Unused_08h = reader . ReadUInt32 ( ) ; //0 0 0 0
this . FrameOffset = reader . ReadUInt32 ( ) ; //224 (E0) 32 (20) 536 (218) 300
2019-11-18 20:52:58 +08:00
this . RootMotionRefsOffset = reader . ReadUInt32 ( ) ; //314 174 1238 390 (=Length)
2019-11-14 15:58:20 +08:00
this . Unused_14h = reader . ReadUInt16 ( ) ; //0 0 0 0
this . NumFrames = reader . ReadUInt16 ( ) ; //221 (DD) 17 (11) 151 (97) 201
this . FrameLength = reader . ReadUInt16 ( ) ; //0 4 4 0
this . IndirectQuantizeFloatNumInts = reader . ReadUInt16 ( ) ; //0 0 106 0
this . QuantizeFloatValueBits = reader . ReadUInt16 ( ) ; //0 17 0 0
this . ChunkSize = reader . ReadByte ( ) ; //64 255 255 64
2019-11-16 00:18:23 +08:00
this . RootMotionRefCounts = reader . ReadByte ( ) ; //0 0 0 0
2017-09-21 18:33:05 +08:00
this . Data = reader . ReadBytes ( ( int ) DataLength ) ;
2019-11-14 15:58:20 +08:00
ParseData ( ) ;
2019-11-02 15:54:43 +08:00
2019-11-14 15:58:20 +08:00
}
2019-11-02 15:54:43 +08:00
2019-11-14 15:58:20 +08:00
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
2019-11-16 00:18:23 +08:00
//BuildData should be called before this
2019-11-14 15:58:20 +08:00
// write structure data
writer . Write ( this . Unknown_00h ) ;
writer . Write ( this . DataLength ) ;
writer . Write ( this . Unused_08h ) ;
writer . Write ( this . FrameOffset ) ;
2019-11-18 20:52:58 +08:00
writer . Write ( this . RootMotionRefsOffset ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . Unused_14h ) ;
writer . Write ( this . NumFrames ) ;
writer . Write ( this . FrameLength ) ;
writer . Write ( this . IndirectQuantizeFloatNumInts ) ;
writer . Write ( this . QuantizeFloatValueBits ) ;
writer . Write ( this . ChunkSize ) ;
2019-11-16 00:18:23 +08:00
writer . Write ( this . RootMotionRefCounts ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . Data ) ;
}
2019-11-02 15:54:43 +08:00
2019-11-14 15:58:20 +08:00
public override string ToString ( )
{
return Unknown_00h . ToString ( ) + ": " + DataLength . ToString ( ) ;
}
2017-10-01 14:29:31 +08:00
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public void ParseData ( )
{
var reader = new AnimChannelDataReader ( Data , NumFrames , ChunkSize , FrameOffset , FrameLength ) ;
var channelList = new List < AnimChannelListItem > ( ) ;
2019-10-31 11:56:40 +08:00
var channelLists = new AnimChannel [ 9 ] [ ] ;
2019-11-17 19:14:00 +08:00
var frameOffset = 0 ;
2019-11-14 15:58:20 +08:00
for ( int i = 0 ; i < 9 ; i + + ) //iterate through anim channel types
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
var ctype = ( AnimChannelType ) i ;
int channelCount = reader . ReadChannelCount ( ) ;
2019-10-31 11:56:40 +08:00
var channels = new AnimChannel [ channelCount ] ;
2019-11-14 15:58:20 +08:00
for ( int c = 0 ; c < channelCount ; c + + ) //construct and read channels
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
var channel = AnimChannel . ConstructChannel ( ctype ) ;
var channelDataBit = reader . ReadChannelDataBits ( ) ;
if ( channel ! = null ) //read channel sequences and indexes
2019-10-31 11:56:40 +08:00
{
2019-11-16 00:18:23 +08:00
channel . DataOffset = reader . Position / 4 ;
channel . Read ( reader ) ;
channels [ c ] = channel ;
2019-11-14 15:58:20 +08:00
var sequence = channelDataBit > > 2 ;
var index = channelDataBit & 3 ;
if ( channel is AnimChannelCachedQuaternion t7 )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
t7 . QuatIndex = index ;
2019-11-16 00:18:23 +08:00
index = ( channel . Type = = AnimChannelType . CachedQuaternion1 ) ? 3 : 4 ;
2019-10-31 11:56:40 +08:00
}
channel . Associate ( sequence , index ) ;
2019-11-14 15:58:20 +08:00
channelList . Add ( new AnimChannelListItem ( sequence , index , channel ) ) ;
2019-11-17 19:14:00 +08:00
channel . FrameOffset = frameOffset ;
frameOffset + = channel . GetFrameBits ( ) ;
2019-10-31 11:56:40 +08:00
}
}
2019-11-14 15:58:20 +08:00
reader . AlignChannelDataOffset ( channelCount ) ;
2019-10-31 11:56:40 +08:00
channelLists [ i ] = channels ;
}
2019-11-14 15:58:20 +08:00
for ( int f = 0 ; f < NumFrames ; f + + ) //read channel frame data
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
reader . BeginFrame ( f ) ;
for ( int i = 0 ; i < 9 ; i + + )
2019-10-31 11:56:40 +08:00
{
var channels = channelLists [ i ] ;
for ( int c = 0 ; c < channels . Length ; c + + )
{
var channel = channels [ c ] ;
2019-11-14 15:58:20 +08:00
channel ? . ReadFrame ( reader ) ;
2019-10-31 11:56:40 +08:00
}
}
}
2019-11-14 15:58:20 +08:00
Sequences = new AnimSequence [ channelList . Max ( a = > a . Sequence ) + 1 ] ;
for ( int i = 0 ; i < Sequences . Length ; i + + ) //assign channels to sequences according to read indices
2019-10-31 11:56:40 +08:00
{
2019-11-02 15:37:21 +08:00
Sequences [ i ] = new AnimSequence ( ) ;
2019-11-14 15:58:20 +08:00
var thisSeq = channelList . Where ( a = > a . Sequence = = i ) ;
2019-11-02 15:37:21 +08:00
if ( thisSeq . Count ( ) = = 0 )
{ continue ; }
2019-10-31 11:56:40 +08:00
Sequences [ i ] . Channels = new AnimChannel [ thisSeq . Max ( a = > a . Index ) + 1 ] ;
2019-11-16 00:18:23 +08:00
2019-10-31 11:56:40 +08:00
for ( int j = 0 ; j < Sequences [ i ] . Channels . Length ; j + + )
{
2019-11-14 15:58:20 +08:00
Sequences [ i ] . Channels [ j ] = thisSeq . FirstOrDefault ( a = > a . Index = = j ) ? . Channel ;
2019-10-31 11:56:40 +08:00
2019-11-16 00:18:23 +08:00
if ( Sequences [ i ] . Channels [ j ] . Type = = AnimChannelType . CachedQuaternion1 ) // is AnimChannelCachedQuaternion)
2019-10-31 11:56:40 +08:00
{
Sequences [ i ] . IsType7Quat = true ;
}
}
}
2019-11-14 15:58:20 +08:00
reader . Sequences = Sequences ;
2019-11-16 00:18:23 +08:00
int numPosRefs = RootPositionRefCount ;
int numRotRefs = RootRotationRefCount ;
if ( numPosRefs > 0 )
2019-11-14 15:58:20 +08:00
{
2019-11-16 00:18:23 +08:00
RootPositionRefs = new SequenceRootChannelRef [ numPosRefs ] ;
for ( int i = 0 ; i < numPosRefs ; i + + )
{
var pref = new SequenceRootChannelRef ( reader . ReadChannelDataBytes ( 6 ) ) ;
RootPositionRefs [ i ] = pref ;
}
}
if ( numRotRefs > 0 )
{
RootRotationRefs = new SequenceRootChannelRef [ numRotRefs ] ;
for ( int i = 0 ; i < numRotRefs ; i + + )
{
var rref = new SequenceRootChannelRef ( reader . ReadChannelDataBytes ( 6 ) ) ;
RootRotationRefs [ i ] = rref ;
}
2019-11-14 15:58:20 +08:00
}
2019-11-16 00:18:23 +08:00
if ( reader . ChannelDataOffset ! = Data . Length )
{
var brem = Data . Length - reader . ChannelDataOffset ;
}
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
public void BuildData ( )
2017-09-21 18:33:05 +08:00
{
2019-11-14 15:58:20 +08:00
// convert parsed sequences into Data byte array............
if ( Sequences = = null ) return ; //this shouldn't happen...
var writer = new AnimChannelDataWriter ( NumFrames ) ;
var channelLists = new List < AnimChannel > [ 9 ] ;
for ( int s = 0 ; s < Sequences . Length ; s + + )
{
var seq = Sequences [ s ] ;
if ( seq ? . Channels = = null ) continue ;
for ( int c = 0 ; c < seq . Channels . Length ; c + + )
{
var chan = seq . Channels [ c ] ;
if ( chan = = null ) continue ;
int typeid = ( int ) chan . Type ;
if ( ( typeid < 0 ) | | ( typeid > = 9 ) )
{ continue ; }
var chanList = channelLists [ typeid ] ;
if ( chanList = = null )
{
chanList = new List < AnimChannel > ( ) ;
channelLists [ typeid ] = chanList ;
}
if ( chan is AnimChannelCachedQuaternion accq )
{
chan . Index = accq . QuatIndex ; //seems to have QuatIndex stored in there (for channelDataBit below)
}
chanList . Add ( chan ) ;
}
}
for ( int i = 0 ; i < 9 ; i + + )
{
var channelList = channelLists [ i ] ;
var channelCount = ( ushort ) ( channelList ? . Count ? ? 0 ) ;
writer . WriteChannelListData ( channelCount ) ;
for ( int c = 0 ; c < channelCount ; c + + )
{
var channel = channelList [ c ] ;
var channelDataBit = ( ushort ) ( ( channel ? . Index ? ? 0 ) + ( ( channel ? . Sequence ? ? 0 ) < < 2 ) ) ;
writer . WriteChannelItemData ( channelDataBit ) ;
2019-11-16 00:18:23 +08:00
if ( channel ! = null )
{
channel . DataOffset = writer . Position / 4 ;
channel ? . Write ( writer ) ;
}
2019-11-14 15:58:20 +08:00
}
2019-11-16 00:18:23 +08:00
writer . AlignChannelItemData ( channelCount , Sequences . Length ) ;
2019-11-14 15:58:20 +08:00
}
for ( int f = 0 ; f < NumFrames ; f + + ) //write channel frame data
{
writer . BeginFrame ( f ) ;
for ( int i = 0 ; i < 9 ; i + + )
{
var channelList = channelLists [ i ] ;
var channelCount = ( ushort ) ( channelList ? . Count ? ? 0 ) ;
for ( int c = 0 ; c < channelCount ; c + + )
{
var channel = channelList [ c ] ;
channel ? . WriteFrame ( writer ) ;
}
}
writer . EndFrame ( ) ;
}
2019-11-16 00:18:23 +08:00
var frameOffset = 0 ;
for ( int i = 0 ; i < channelLists . Length ; i + + )
{
var chanList = channelLists [ i ] ;
if ( chanList = = null ) continue ;
for ( int c = 0 ; c < chanList . Count ; c + + )
{
var chan = chanList [ c ] ;
if ( chan = = null ) continue ;
chan . FrameOffset = frameOffset ;
frameOffset + = chan . GetFrameBits ( ) ;
}
}
UpdateRootMotionRefs ( ) ;
if ( RootPositionRefs ! = null )
{
for ( int i = 0 ; i < RootPositionRefs . Length ; i + + )
{
writer . WriteChannelItemDataBytes ( RootPositionRefs [ i ] . Bytes ) ;
}
}
if ( RootRotationRefs ! = null )
{
for ( int i = 0 ; i < RootRotationRefs . Length ; i + + )
{
writer . WriteChannelItemDataBytes ( RootRotationRefs [ i ] . Bytes ) ;
}
}
2019-11-14 15:58:20 +08:00
var mainData = writer . GetMainDataBytes ( ) ;
var frameData = writer . GetFrameDataBytes ( ) ;
var channelListData = writer . GetChannelListDataBytes ( ) ;
var channelItemData = writer . GetChannelItemDataBytes ( ) ;
var dataLen = mainData . Length + frameData . Length + channelListData . Length + channelItemData . Length ;
var data = new byte [ dataLen ] ;
var curpos = 0 ;
Buffer . BlockCopy ( mainData , 0 , data , 0 , mainData . Length ) ; curpos + = mainData . Length ;
Buffer . BlockCopy ( frameData , 0 , data , curpos , frameData . Length ) ; curpos + = frameData . Length ;
Buffer . BlockCopy ( channelListData , 0 , data , curpos , channelListData . Length ) ; curpos + = channelListData . Length ;
Buffer . BlockCopy ( channelItemData , 0 , data , curpos , channelItemData . Length ) ;
2019-11-16 00:18:23 +08:00
//if (FrameLength != writer.FrameLength)
//{ }
//else if (Data != null)
//{
// if (Data.Length != data.Length)
// {
// if ((channelLists[6]?.Count ?? 0) == 0)
// { }
// }
// else
// {
// for (int b = 0; b < Data.Length; b++)
// {
// if (Data[b] != data[b])
// {
// if ((channelLists[6]?.Count ?? 0) == 0)
// { }
// break;
// }
// }
// }
//}
2019-11-14 15:58:20 +08:00
Data = data ;
DataLength = ( uint ) data . Length ;
FrameOffset = ( uint ) mainData . Length ;
FrameLength = writer . FrameLength ;
ChunkSize = ( writer . ChunkSize > 0 ) ? writer . ChunkSize : ( byte ) 255 ;
QuantizeFloatValueBits = GetQuantizeFloatValueBits ( ) ;
IndirectQuantizeFloatNumInts = GetIndirectQuantizeFloatNumInts ( ) ;
2019-11-16 00:18:23 +08:00
RootMotionRefCounts = ( byte ) ( ( ( ( uint ) ( RootPositionRefs ? . Length ? ? 0 ) ) < < 4 ) | ( ( uint ) ( RootRotationRefs ? . Length ? ? 0 ) ) ) ;
2019-11-18 20:52:58 +08:00
RootMotionRefsOffset = ( uint ) ( BlockLength - ( ( RootPositionRefCount + RootRotationRefCount ) * 6 ) ) ;
2017-09-21 18:33:05 +08:00
}
2017-09-29 20:23:37 +08:00
2019-11-14 15:58:20 +08:00
public void AssociateSequenceChannels ( ) //assigns Sequence and Index to all channels
2017-09-29 20:23:37 +08:00
{
2019-11-14 15:58:20 +08:00
if ( Sequences = = null ) return ; //this shouldn't happen...
for ( int s = 0 ; s < Sequences . Length ; s + + )
{
var seq = Sequences [ s ] ;
if ( seq ? . Channels = = null ) continue ;
for ( int c = 0 ; c < seq . Channels . Length ; c + + )
{
var chan = seq . Channels [ c ] ;
chan ? . Associate ( s , c ) ;
}
}
2017-09-29 20:23:37 +08:00
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public ushort GetQuantizeFloatValueBits ( )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
int b = 0 ;
foreach ( var seq in Sequences )
{
foreach ( var chan in seq . Channels )
{
if ( chan . Type = = AnimChannelType . QuantizeFloat )
{
var acqf = chan as AnimChannelQuantizeFloat ;
b + = acqf . ValueBits ;
}
}
}
return ( ushort ) b ;
}
public ushort GetIndirectQuantizeFloatNumInts ( )
{
int b = 0 ;
foreach ( var seq in Sequences )
{
foreach ( var chan in seq . Channels )
{
if ( chan . Type = = AnimChannelType . IndirectQuantizeFloat )
{
var acif = chan as AnimChannelIndirectQuantizeFloat ;
b + = acif . NumInts + 5 ;
}
}
}
return ( ushort ) b ;
}
2019-10-31 11:56:40 +08:00
2019-11-16 00:18:23 +08:00
public void UpdateRootMotionRefs ( )
{
// OFFSETS - [ChannelType], [Index], [DataIntOffset 0xFFFF], [FrameBitOffset 0xFFFF]
var newPosRefs = new List < SequenceRootChannelRef > ( ) ;
var newRotRefs = new List < SequenceRootChannelRef > ( ) ;
for ( int i = 0 ; i < Sequences . Length ; i + + )
{
var seq = Sequences [ i ] ;
if ( seq = = null ) continue ;
if ( seq . BoneId . Track = = 5 ) //root position
{
for ( var c = 0 ; c < seq . Channels ? . Length ; c + + )
{
var chan = seq . Channels [ c ] ;
var newPosRef = new SequenceRootChannelRef ( chan . Type , chan . GetReferenceIndex ( ) ) ;
newPosRef . DataIntOffset = ( ushort ) chan . DataOffset ;
newPosRef . FrameBitOffset = ( ushort ) chan . FrameOffset ;
newPosRefs . Add ( newPosRef ) ;
}
}
if ( seq . BoneId . Track = = 6 ) //root rotation
{
for ( var c = 0 ; c < seq . Channels ? . Length ; c + + )
{
var chan = seq . Channels [ c ] ;
var newRotRef = new SequenceRootChannelRef ( chan . Type , chan . GetReferenceIndex ( ) ) ;
newRotRef . DataIntOffset = ( ushort ) chan . DataOffset ;
newRotRef . FrameBitOffset = ( ushort ) chan . FrameOffset ;
newRotRefs . Add ( newRotRef ) ;
}
}
}
int compare ( SequenceRootChannelRef a , SequenceRootChannelRef b )
{
var v1 = a . ChannelType . CompareTo ( b . ChannelType ) ;
if ( v1 ! = 0 ) return v1 ;
var v2 = a . ChannelIndex . CompareTo ( b . ChannelIndex ) ;
if ( v2 ! = 0 ) return v2 ;
return 0 ;
}
newPosRefs . Sort ( ( a , b ) = > { return compare ( a , b ) ; } ) ;
newRotRefs . Sort ( ( a , b ) = > { return compare ( a , b ) ; } ) ;
2019-11-17 19:14:00 +08:00
//if (RootPositionRefs != null)
//{
// if (RootPositionRefs.Length != newPosRefs.Count)
// { }
// else
// {
// for (int i = 0; i < RootPositionRefs.Length; i++)
// {
// var oldRef = RootPositionRefs[i];
// var newRef = newPosRefs[i];
// for (int b = 0; b < 6; b++)
// {
// if (oldRef.Bytes[b] != newRef.Bytes[b])
// { }
// }
// }
// }
//}
//if (RootRotationRefs != null)
//{
// if (RootRotationRefs.Length != newRotRefs.Count)
// { }
// else
// {
// for (int i = 0; i < RootRotationRefs.Length; i++)
// {
// var oldRef = RootRotationRefs[i];
// var newRef = newRotRefs[i];
// for (int b = 0; b < 6; b++)
// {
// if (oldRef.Bytes[b] != newRef.Bytes[b])
// { }
// }
// }
// }
//}
2019-11-16 00:18:23 +08:00
RootPositionRefs = ( newPosRefs . Count > 0 ) ? newPosRefs . ToArray ( ) : null ;
RootRotationRefs = ( newRotRefs . Count > 0 ) ? newRotRefs . ToArray ( ) : null ;
}
2019-10-31 11:56:40 +08:00
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
{
YcdXml . StringTag ( sb , indent , "Hash" , YcdXml . HashString ( Unknown_00h ) ) ;
YcdXml . ValueTag ( sb , indent , "FrameCount" , NumFrames . ToString ( ) ) ;
YcdXml . WriteItemArray ( sb , Sequences , indent , "SequenceData" ) ;
2019-10-31 11:56:40 +08:00
}
2019-11-14 15:58:20 +08:00
public void ReadXml ( XmlNode node )
2019-10-31 11:56:40 +08:00
{
2019-11-14 15:58:20 +08:00
Unknown_00h = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "Hash" ) ) ;
NumFrames = ( ushort ) Xml . GetChildUIntAttribute ( node , "FrameCount" , "value" ) ;
Sequences = XmlMeta . ReadItemArray < AnimSequence > ( node , "SequenceData" ) ;
AssociateSequenceChannels ( ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-10-03 17:24:33 +08:00
2019-11-20 19:51:37 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipMapEntry : ResourceSystemBlock
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 32 ; }
}
// structure data
public MetaHash Hash { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unknown_04h { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public ulong ClipPointer { get ; set ; }
public ulong NextPointer { get ; set ; }
public uint Unknown_18h { get ; set ; } // 0x00000000
public uint Unknown_1Ch { get ; set ; } // 0x00000000
// reference data
public ClipBase Clip { get ; set ; }
public ClipMapEntry Next { get ; set ; }
2019-11-14 15:58:20 +08:00
public bool EnableRootMotion { get ; set ; } = false ; //used by CW to toggle whether or not to include root motion when playing animations
2019-11-25 22:26:28 +08:00
public bool OverridePlayTime { get ; set ; } = false ; //used by CW to manually override the animation playback time
public float PlayTime { get ; set ; } = 0.0f ;
2019-11-14 15:58:20 +08:00
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . Hash = new MetaHash ( reader . ReadUInt32 ( ) ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . ClipPointer = reader . ReadUInt64 ( ) ;
this . NextPointer = reader . ReadUInt64 ( ) ;
this . Unknown_18h = reader . ReadUInt32 ( ) ;
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
// read reference data
this . Clip = reader . ReadBlockAt < ClipBase > (
this . ClipPointer // offset
) ;
this . Next = reader . ReadBlockAt < ClipMapEntry > (
this . NextPointer // offset
) ;
2019-11-20 19:51:37 +08:00
if ( Clip ! = null )
{
Clip . Hash = Hash ;
}
else
{ }
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
this . ClipPointer = ( ulong ) ( this . Clip ! = null ? this . Clip . FilePosition : 0 ) ;
this . NextPointer = ( ulong ) ( this . Next ! = null ? this . Next . FilePosition : 0 ) ;
// write structure data
writer . Write ( this . Hash ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . ClipPointer ) ;
writer . Write ( this . NextPointer ) ;
writer . Write ( this . Unknown_18h ) ;
writer . Write ( this . Unknown_1Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
if ( Clip ! = null ) list . Add ( Clip ) ;
if ( Next ! = null ) list . Add ( Next ) ;
return list . ToArray ( ) ;
}
public override string ToString ( )
{
2017-09-29 20:23:37 +08:00
return Clip ? . Name ? ? Hash . ToString ( ) ;
2017-09-21 18:33:05 +08:00
}
2019-11-08 14:54:46 +08:00
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipBase : ResourceSystemBlock , IResourceXXSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 112 ; }
}
// structure data
public uint VFT { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_04h { get ; set ; } = 1 ; // 0x00000001
2017-10-03 17:24:33 +08:00
public uint Unknown_08h { get ; set ; } // 0x00000000
public uint Unknown_0Ch { get ; set ; } // 0x00000000
2019-11-14 15:58:20 +08:00
public ClipType Type { get ; set ; } // 1, 2
2017-09-21 18:33:05 +08:00
public uint Unknown_14h { get ; set ; } // 0x00000000
public ulong NamePointer { get ; set ; }
2017-09-29 20:23:37 +08:00
public ushort NameLength { get ; set ; } // short, name length
public ushort NameCapacity { get ; set ; } // short, name length +1
2017-09-21 18:33:05 +08:00
public uint Unknown_24h { get ; set ; } // 0x00000000
2019-11-14 15:58:20 +08:00
public ulong Unknown_28hPtr { get ; set ; } = 0x50000000 ; // 0x50000000
public uint Unknown_30h { get ; set ; } // 0, 1
2017-09-21 18:33:05 +08:00
public uint Unknown_34h { get ; set ; } // 0x00000000
2017-09-24 18:14:41 +08:00
public ulong TagsPointer { get ; set ; }
public ulong PropertiesPointer { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_48h { get ; set ; } = 1 ; // 0x00000001
2017-09-21 18:33:05 +08:00
public uint Unknown_4Ch { get ; set ; } // 0x00000000
// reference data
public string Name { get ; set ; }
2017-09-24 18:14:41 +08:00
public ClipTagList Tags { get ; set ; }
public ClipPropertyMap Properties { get ; set ; }
2017-09-21 18:33:05 +08:00
2019-11-14 15:58:20 +08:00
private string_r NameBlock = null ;
2017-10-04 11:35:39 +08:00
public YcdFile Ycd { get ; set ; }
2019-11-20 19:51:37 +08:00
public string ShortName
{
get
{
if ( ! string . IsNullOrEmpty ( _ShortName ) ) return _ShortName ;
if ( string . IsNullOrEmpty ( Name ) ) return null ;
string name = Name . Replace ( '\\' , '/' ) ;
var slidx = name . LastIndexOf ( '/' ) ;
if ( ( slidx > = 0 ) & & ( slidx < name . Length - 1 ) )
{
name = name . Substring ( slidx + 1 ) ;
}
var didx = name . LastIndexOf ( '.' ) ;
if ( ( didx > 0 ) & & ( didx < name . Length ) )
{
name = name . Substring ( 0 , didx ) ;
}
_ShortName = name . ToLowerInvariant ( ) ;
JenkIndex . Ensure ( _ShortName ) ;
return _ShortName ;
}
}
private string _ShortName ;
public MetaHash Hash { get ; set ; } //used by CW when reading/writing
2017-10-04 11:35:39 +08:00
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . VFT = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
this . Unknown_08h = reader . ReadUInt32 ( ) ;
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2019-11-14 15:58:20 +08:00
this . Type = ( ClipType ) reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_14h = reader . ReadUInt32 ( ) ;
this . NamePointer = reader . ReadUInt64 ( ) ;
2017-09-29 20:23:37 +08:00
this . NameLength = reader . ReadUInt16 ( ) ;
this . NameCapacity = reader . ReadUInt16 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28hPtr = reader . ReadUInt64 ( ) ;
this . Unknown_30h = reader . ReadUInt32 ( ) ;
this . Unknown_34h = reader . ReadUInt32 ( ) ;
2017-09-24 18:14:41 +08:00
this . TagsPointer = reader . ReadUInt64 ( ) ;
this . PropertiesPointer = reader . ReadUInt64 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_48h = reader . ReadUInt32 ( ) ;
this . Unknown_4Ch = reader . ReadUInt32 ( ) ;
2017-09-24 18:14:41 +08:00
2017-09-21 18:33:05 +08:00
this . Name = reader . ReadStringAt ( this . NamePointer ) ;
2017-09-24 18:14:41 +08:00
this . Tags = reader . ReadBlockAt < ClipTagList > (
this . TagsPointer // offset
2017-09-21 18:33:05 +08:00
) ;
2017-09-24 18:14:41 +08:00
this . Properties = reader . ReadBlockAt < ClipPropertyMap > (
this . PropertiesPointer // offset
2017-09-21 18:33:05 +08:00
) ;
2019-11-14 15:58:20 +08:00
if ( Unknown_28hPtr ! = 0x50000000 )
{ }
switch ( VFT ) //some examples
{
case 1079664808 :
case 1079656584 :
case 1079607128 :
break ;
default :
break ;
}
switch ( Unknown_30h )
2017-09-21 18:33:05 +08:00
{
2019-11-14 15:58:20 +08:00
case 0 :
case 1 :
break ;
default :
break ;
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
if ( Tags ? . Tags ? . data_items = = null )
{ }
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
2019-11-14 15:58:20 +08:00
this . NamePointer = ( ulong ) ( this . NameBlock ! = null ? this . NameBlock . FilePosition : 0 ) ;
this . NameLength = ( ushort ) ( Name ? . Length ? ? 0 ) ;
this . NameCapacity = ( ushort ) ( ( Name ! = null ) ? Name . Length + 1 : 0 ) ;
2017-09-24 18:14:41 +08:00
this . TagsPointer = ( ulong ) ( this . Tags ! = null ? this . Tags . FilePosition : 0 ) ;
this . PropertiesPointer = ( ulong ) ( this . Properties ! = null ? this . Properties . FilePosition : 0 ) ;
2017-09-21 18:33:05 +08:00
2019-11-14 15:58:20 +08:00
2017-09-21 18:33:05 +08:00
// write structure data
writer . Write ( this . VFT ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
writer . Write ( this . Unknown_08h ) ;
writer . Write ( this . Unknown_0Ch ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( ( uint ) this . Type ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_14h ) ;
writer . Write ( this . NamePointer ) ;
2017-09-29 20:23:37 +08:00
writer . Write ( this . NameLength ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . NameCapacity ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28hPtr ) ;
writer . Write ( this . Unknown_30h ) ;
writer . Write ( this . Unknown_34h ) ;
2017-09-24 18:14:41 +08:00
writer . Write ( this . TagsPointer ) ;
writer . Write ( this . PropertiesPointer ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_48h ) ;
writer . Write ( this . Unknown_4Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
2019-11-14 15:58:20 +08:00
if ( Name ! = null )
{
NameBlock = ( string_r ) Name ;
list . Add ( NameBlock ) ;
}
2017-09-24 18:14:41 +08:00
if ( Tags ! = null ) list . Add ( Tags ) ;
if ( Properties ! = null ) list . Add ( Properties ) ;
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
public IResourceSystemBlock GetType ( ResourceDataReader reader , params object [ ] parameters )
{
reader . Position + = 16 ;
var type = reader . ReadByte ( ) ;
reader . Position - = 17 ;
2019-11-14 15:58:20 +08:00
return ConstructClip ( ( ClipType ) type ) ;
}
public static ClipBase ConstructClip ( ClipType type )
{
2017-09-21 18:33:05 +08:00
switch ( type )
{
2019-11-14 15:58:20 +08:00
case ClipType . Animation : return new ClipAnimation ( ) ;
case ClipType . AnimationList : return new ClipAnimationList ( ) ;
2017-09-21 18:33:05 +08:00
default : return null ; // throw new Exception("Unknown type");
}
}
public override string ToString ( )
{
return Name ;
}
2019-11-14 15:58:20 +08:00
public virtual void WriteXml ( StringBuilder sb , int indent )
{
2019-11-20 19:51:37 +08:00
YcdXml . StringTag ( sb , indent , "Hash" , YcdXml . HashString ( Hash ) ) ;
2019-11-14 15:58:20 +08:00
YcdXml . StringTag ( sb , indent , "Name" , MetaXml . XmlEscape ( Name ) ) ;
YcdXml . ValueTag ( sb , indent , "Type" , Type . ToString ( ) ) ;
YcdXml . ValueTag ( sb , indent , "Unknown30" , Unknown_30h . ToString ( ) ) ;
YcdXml . WriteItemArray ( sb , Tags ? . Tags ? . data_items , indent , "Tags" ) ;
2019-11-20 19:51:37 +08:00
YcdXml . WriteItemArray ( sb , Properties ? . AllProperties , indent , "Properties" ) ;
2019-11-14 15:58:20 +08:00
}
public virtual void ReadXml ( XmlNode node )
{
2019-11-20 19:51:37 +08:00
Hash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "Hash" ) ) ;
2019-11-14 15:58:20 +08:00
Name = Xml . GetChildInnerText ( node , "Name" ) ;
Unknown_30h = Xml . GetChildUIntAttribute ( node , "Unknown30" , "value" ) ;
var tags = XmlMeta . ReadItemArrayNullable < ClipTag > ( node , "Tags" ) ;
Tags = new ClipTagList ( ) ;
if ( tags ! = null )
{
Tags . Tags = new ResourcePointerArray64 < ClipTag > ( ) ;
Tags . Tags . data_items = tags ;
Tags . BuildAllTags ( ) ;
Tags . AssignTagOwners ( ) ;
}
2019-11-20 19:51:37 +08:00
var props = XmlMeta . ReadItemArrayNullable < ClipProperty > ( node , "Properties" ) ;
2019-11-14 15:58:20 +08:00
Properties = new ClipPropertyMap ( ) ;
2019-11-20 19:51:37 +08:00
Properties . CreatePropertyMap ( props ) ;
2019-11-14 15:58:20 +08:00
}
2017-09-21 18:33:05 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipAnimation : ClipBase
{
public override long BlockLength
{
get { return 112 ; }
}
// structure data
public ulong AnimationPointer { get ; set ; }
2019-11-07 23:00:16 +08:00
public float StartTime { get ; set ; } //start time
public float EndTime { get ; set ; } //end time
public float Rate { get ; set ; } //1.0 rate..?
2017-09-21 18:33:05 +08:00
public uint Unknown_64h { get ; set ; } // 0x00000000
public uint Unknown_68h { get ; set ; } // 0x00000000
public uint Unknown_6Ch { get ; set ; } // 0x00000000
// reference data
public Animation Animation { get ; set ; }
2019-11-14 15:58:20 +08:00
public MetaHash AnimationHash { get ; set ; } //used when reading XML.
public ClipAnimation ( )
{
Type = ClipType . Animation ;
}
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
this . AnimationPointer = reader . ReadUInt64 ( ) ;
2019-11-07 23:00:16 +08:00
this . StartTime = reader . ReadSingle ( ) ;
this . EndTime = reader . ReadSingle ( ) ;
this . Rate = reader . ReadSingle ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_64h = reader . ReadUInt32 ( ) ;
this . Unknown_68h = reader . ReadUInt32 ( ) ;
this . Unknown_6Ch = reader . ReadUInt32 ( ) ;
this . Animation = reader . ReadBlockAt < Animation > (
this . AnimationPointer // offset
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
this . AnimationPointer = ( ulong ) ( this . Animation ! = null ? this . Animation . FilePosition : 0 ) ;
writer . Write ( this . AnimationPointer ) ;
2019-11-07 23:00:16 +08:00
writer . Write ( this . StartTime ) ;
writer . Write ( this . EndTime ) ;
writer . Write ( this . Rate ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_64h ) ;
writer . Write ( this . Unknown_68h ) ;
writer . Write ( this . Unknown_6Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
list . AddRange ( base . GetReferences ( ) ) ;
if ( Animation ! = null ) list . Add ( Animation ) ;
return list . ToArray ( ) ;
}
2019-11-07 23:00:16 +08:00
public float GetPlaybackTime ( double currentTime )
{
double scaledTime = currentTime * Rate ;
double duration = EndTime - StartTime ;
double curpos = scaledTime % duration ;
return StartTime + ( float ) curpos ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
2019-11-20 19:51:37 +08:00
YcdXml . StringTag ( sb , indent , "AnimationHash" , YcdXml . HashString ( Animation ? . Hash ? ? 0 ) ) ;
2019-11-14 15:58:20 +08:00
YcdXml . ValueTag ( sb , indent , "StartTime" , FloatUtil . ToString ( StartTime ) ) ;
YcdXml . ValueTag ( sb , indent , "EndTime" , FloatUtil . ToString ( EndTime ) ) ;
YcdXml . ValueTag ( sb , indent , "Rate" , FloatUtil . ToString ( Rate ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
AnimationHash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "AnimationHash" ) ) ;
StartTime = Xml . GetChildFloatAttribute ( node , "StartTime" , "value" ) ;
EndTime = Xml . GetChildFloatAttribute ( node , "EndTime" , "value" ) ;
Rate = Xml . GetChildFloatAttribute ( node , "Rate" , "value" ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipAnimationList : ClipBase
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 112 ; }
}
// structure data
public ulong AnimationsPointer { get ; set ; }
public ushort AnimationsCount1 { get ; set ; }
public ushort AnimationsCount2 { get ; set ; }
public uint Unknown_5Ch { get ; set ; } // 0x00000000
2019-10-31 11:56:40 +08:00
public float Duration { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_64h { get ; set ; } = 1 ; // 0x00000001
2017-09-21 18:33:05 +08:00
public uint Unknown_68h { get ; set ; } // 0x00000000
public uint Unknown_6Ch { get ; set ; } // 0x00000000
// reference data
public ResourceSimpleArray < ClipAnimationsEntry > Animations { get ; set ; }
2019-11-14 15:58:20 +08:00
public ClipAnimationList ( )
{
Type = ClipType . AnimationList ;
}
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
this . AnimationsPointer = reader . ReadUInt64 ( ) ;
this . AnimationsCount1 = reader . ReadUInt16 ( ) ;
this . AnimationsCount2 = reader . ReadUInt16 ( ) ;
this . Unknown_5Ch = reader . ReadUInt32 ( ) ;
2019-10-31 11:56:40 +08:00
this . Duration = reader . ReadSingle ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_64h = reader . ReadUInt32 ( ) ;
this . Unknown_68h = reader . ReadUInt32 ( ) ;
this . Unknown_6Ch = reader . ReadUInt32 ( ) ;
this . Animations = reader . ReadBlockAt < ResourceSimpleArray < ClipAnimationsEntry > > (
this . AnimationsPointer , // offset
this . AnimationsCount1
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
this . AnimationsPointer = ( ulong ) ( this . Animations ! = null ? this . Animations . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . AnimationsCount1 = ( ushort ) ( this . Animations ! = null ? this . Animations . Count : 0 ) ;
this . AnimationsCount2 = this . AnimationsCount1 ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . AnimationsPointer ) ;
writer . Write ( this . AnimationsCount1 ) ;
writer . Write ( this . AnimationsCount2 ) ;
writer . Write ( this . Unknown_5Ch ) ;
2019-10-31 11:56:40 +08:00
writer . Write ( this . Duration ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_64h ) ;
writer . Write ( this . Unknown_68h ) ;
writer . Write ( this . Unknown_6Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
list . AddRange ( base . GetReferences ( ) ) ;
if ( Animations ! = null ) list . Add ( Animations ) ;
return list . ToArray ( ) ;
}
2019-11-08 14:54:46 +08:00
public float GetPlaybackTime ( double currentTime )
{
double scaledTime = currentTime ; // * Rate;
double duration = Duration ; // EndTime - StartTime;
double curpos = scaledTime % duration ;
return /*StartTime +*/ ( float ) curpos ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Duration" , FloatUtil . ToString ( Duration ) ) ;
YcdXml . WriteItemArray ( sb , Animations ? . Data . ToArray ( ) , indent , "Animations" ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Duration = Xml . GetChildFloatAttribute ( node , "Duration" , "value" ) ;
Animations = new ResourceSimpleArray < ClipAnimationsEntry > ( ) ;
Animations . Data = new List < ClipAnimationsEntry > ( ) ;
var anims = XmlMeta . ReadItemArrayNullable < ClipAnimationsEntry > ( node , "Animations" ) ;
if ( anims ! = null ) Animations . Data . AddRange ( anims ) ;
}
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipAnimationsEntry : ResourceSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 24 ; }
}
// structure data
2019-10-31 11:56:40 +08:00
public float StartTime { get ; set ; }
public float EndTime { get ; set ; }
public float Rate { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unknown_0Ch { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public ulong AnimationPointer { get ; set ; }
// reference data
public Animation Animation { get ; set ; }
2019-11-14 15:58:20 +08:00
public MetaHash AnimationHash { get ; set ; } //used when reading XML.
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
2019-10-31 11:56:40 +08:00
this . StartTime = reader . ReadSingle ( ) ;
this . EndTime = reader . ReadSingle ( ) ;
this . Rate = reader . ReadSingle ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . AnimationPointer = reader . ReadUInt64 ( ) ;
// read reference data
this . Animation = reader . ReadBlockAt < Animation > (
this . AnimationPointer // offset
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
this . AnimationPointer = ( ulong ) ( this . Animation ! = null ? this . Animation . FilePosition : 0 ) ;
// write structure data
2019-10-31 11:56:40 +08:00
writer . Write ( this . StartTime ) ;
writer . Write ( this . EndTime ) ;
writer . Write ( this . Rate ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_0Ch ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . AnimationPointer ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
if ( Animation ! = null ) list . Add ( Animation ) ;
return list . ToArray ( ) ;
}
2019-11-07 23:00:16 +08:00
public float GetPlaybackTime ( double currentTime )
{
double scaledTime = currentTime * Rate ;
double duration = EndTime - StartTime ;
double curpos = scaledTime % duration ;
return StartTime + ( float ) curpos ;
}
2019-11-14 15:58:20 +08:00
public void WriteXml ( StringBuilder sb , int indent )
{
2019-11-20 19:51:37 +08:00
YcdXml . StringTag ( sb , indent , "AnimationHash" , YcdXml . HashString ( Animation ? . Hash ? ? 0 ) ) ;
2019-11-14 15:58:20 +08:00
YcdXml . ValueTag ( sb , indent , "StartTime" , FloatUtil . ToString ( StartTime ) ) ;
YcdXml . ValueTag ( sb , indent , "EndTime" , FloatUtil . ToString ( EndTime ) ) ;
YcdXml . ValueTag ( sb , indent , "Rate" , FloatUtil . ToString ( Rate ) ) ;
}
public void ReadXml ( XmlNode node )
{
AnimationHash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "AnimationHash" ) ) ;
StartTime = Xml . GetChildFloatAttribute ( node , "StartTime" , "value" ) ;
EndTime = Xml . GetChildFloatAttribute ( node , "EndTime" , "value" ) ;
Rate = Xml . GetChildFloatAttribute ( node , "Rate" , "value" ) ;
}
}
public enum ClipType : uint
{
Animation = 1 ,
AnimationList = 2 ,
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyMap : ResourceSystemBlock
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 16 ; }
}
// structure data
2017-09-24 18:14:41 +08:00
public ulong PropertyEntriesPointer { get ; set ; }
public ushort PropertyEntriesCapacity { get ; set ; }
2019-11-14 15:58:20 +08:00
public ushort PropertyEntriesCount { get ; set ; }
public uint Unknown_0Ch { get ; set ; } = 0x01000000 ; // 0x01000000
2017-09-21 18:33:05 +08:00
// reference data
2017-09-24 18:14:41 +08:00
public ResourcePointerArray64 < ClipPropertyMapEntry > Properties { get ; set ; }
2017-09-21 18:33:05 +08:00
2017-10-03 17:24:33 +08:00
public ClipProperty [ ] AllProperties { get ; set ; }
public Dictionary < MetaHash , ClipProperty > PropertyMap { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
2017-09-24 18:14:41 +08:00
this . PropertyEntriesPointer = reader . ReadUInt64 ( ) ;
this . PropertyEntriesCapacity = reader . ReadUInt16 ( ) ;
2019-11-14 15:58:20 +08:00
this . PropertyEntriesCount = reader . ReadUInt16 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
// read reference data
2017-09-24 18:14:41 +08:00
this . Properties = reader . ReadBlockAt < ResourcePointerArray64 < ClipPropertyMapEntry > > (
this . PropertyEntriesPointer , // offset
2019-11-14 15:58:20 +08:00
this . PropertyEntriesCapacity
2017-09-21 18:33:05 +08:00
) ;
2017-10-03 17:24:33 +08:00
2019-11-14 15:58:20 +08:00
BuildPropertyMap ( ) ;
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
2017-09-24 18:14:41 +08:00
this . PropertyEntriesPointer = ( ulong ) ( this . Properties ! = null ? this . Properties . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . PropertyEntriesCapacity = ( ushort ) ( this . Properties ! = null ? this . Properties . Count : 0 ) ;
this . PropertyEntriesCount = ( ushort ) ( this . AllProperties ? . Length ? ? 0 ) ;
2017-09-21 18:33:05 +08:00
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . PropertyEntriesPointer ) ;
writer . Write ( this . PropertyEntriesCapacity ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . PropertyEntriesCount ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_0Ch ) ;
2017-09-21 18:33:05 +08:00
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
2017-09-24 18:14:41 +08:00
if ( Properties ! = null ) list . Add ( Properties ) ;
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
2017-10-04 11:35:39 +08:00
return "Count: " + ( AllProperties ? . Length ? ? 0 ) . ToString ( ) ;
2017-10-03 17:24:33 +08:00
}
2019-11-14 15:58:20 +08:00
public void BuildPropertyMap ( )
{
if ( Properties ? . data_items ! = null )
{
List < ClipProperty > pl = new List < ClipProperty > ( ) ;
foreach ( var pme in Properties . data_items )
{
ClipPropertyMapEntry cpme = pme ;
while ( cpme ? . Data ! = null )
{
pl . Add ( cpme . Data ) ;
cpme = cpme . Next ;
}
}
AllProperties = pl . ToArray ( ) ;
PropertyMap = new Dictionary < MetaHash , ClipProperty > ( ) ;
foreach ( var cp in AllProperties )
{
PropertyMap [ cp . NameHash ] = cp ;
}
}
2019-11-20 19:51:37 +08:00
}
public void CreatePropertyMap ( ClipProperty [ ] properties )
{
var numBuckets = ClipDictionary . GetNumHashBuckets ( properties ? . Length ? ? 0 ) ;
var buckets = new List < ClipPropertyMapEntry > [ numBuckets ] ;
if ( properties ! = null )
{
foreach ( var prop in properties )
{
var b = prop . NameHash % numBuckets ;
var bucket = buckets [ b ] ;
if ( bucket = = null )
{
bucket = new List < ClipPropertyMapEntry > ( ) ;
buckets [ b ] = bucket ;
}
var pme = new ClipPropertyMapEntry ( ) ;
pme . PropertyNameHash = prop . NameHash ;
pme . Data = prop ;
bucket . Add ( pme ) ;
}
}
2019-11-14 15:58:20 +08:00
2019-11-20 19:51:37 +08:00
var newProperties = new List < ClipPropertyMapEntry > ( ) ;
foreach ( var b in buckets )
{
if ( ( b ? . Count ? ? 0 ) = = 0 ) newProperties . Add ( null ) ;
else
{
newProperties . Add ( b [ 0 ] ) ;
var p = b [ 0 ] ;
for ( int i = 1 ; i < b . Count ; i + + )
{
var c = b [ i ] ;
c . Next = null ;
p . Next = c ;
p = c ;
}
}
}
Properties = new ResourcePointerArray64 < ClipPropertyMapEntry > ( ) ;
Properties . data_items = newProperties . ToArray ( ) ;
AllProperties = properties ;
2019-11-14 15:58:20 +08:00
}
2017-09-21 18:33:05 +08:00
}
2019-11-20 19:51:37 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyMapEntry : ResourceSystemBlock
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 32 ; }
}
// structure data
2017-10-03 17:24:33 +08:00
public MetaHash PropertyNameHash { get ; set ; }
public uint Unknown_04h { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public ulong DataPointer { get ; set ; }
public ulong NextPointer { get ; set ; }
public uint Unknown_18h { get ; set ; } // 0x00000000
public uint Unknown_1Ch { get ; set ; } // 0x00000000
// reference data
2017-09-24 18:14:41 +08:00
public ClipProperty Data { get ; set ; }
public ClipPropertyMapEntry Next { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
2017-10-03 17:24:33 +08:00
this . PropertyNameHash = reader . ReadUInt32 ( ) ;
this . Unknown_04h = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . DataPointer = reader . ReadUInt64 ( ) ;
this . NextPointer = reader . ReadUInt64 ( ) ;
this . Unknown_18h = reader . ReadUInt32 ( ) ;
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
// read reference data
2017-09-24 18:14:41 +08:00
this . Data = reader . ReadBlockAt < ClipProperty > (
2017-09-21 18:33:05 +08:00
this . DataPointer // offset
) ;
2017-09-24 18:14:41 +08:00
this . Next = reader . ReadBlockAt < ClipPropertyMapEntry > (
2017-09-21 18:33:05 +08:00
this . NextPointer // offset
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
this . DataPointer = ( ulong ) ( this . Data ! = null ? this . Data . FilePosition : 0 ) ;
this . NextPointer = ( ulong ) ( this . Next ! = null ? this . Next . FilePosition : 0 ) ;
// write structure data
2017-10-03 17:24:33 +08:00
writer . Write ( this . PropertyNameHash ) ;
writer . Write ( this . Unknown_04h ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . DataPointer ) ;
writer . Write ( this . NextPointer ) ;
writer . Write ( this . Unknown_18h ) ;
writer . Write ( this . Unknown_1Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
if ( Data ! = null ) list . Add ( Data ) ;
if ( Next ! = null ) list . Add ( Next ) ;
return list . ToArray ( ) ;
}
2019-11-14 15:58:20 +08:00
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipProperty : ResourceSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 64 ; }
}
// structure data
public uint VFT { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_04h { get ; set ; } = 1 ; // 0x00000001
2017-10-03 17:24:33 +08:00
public uint Unknown_08h { get ; set ; } // 0x00000000
public uint Unknown_0Ch { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public uint Unknown_10h { get ; set ; } // 0x00000000
public uint Unknown_14h { get ; set ; } // 0x00000000
2017-10-03 17:24:33 +08:00
public MetaHash NameHash { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_1Ch { get ; set ; } // 0x00000000
2017-09-24 18:14:41 +08:00
public ulong AttributesPointer { get ; set ; }
public ushort AttributesCount { get ; set ; }
public ushort AttributesCapacity { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_2Ch { get ; set ; } // 0x00000000
public uint Unknown_30h { get ; set ; } // 0x00000000
public uint Unknown_34h { get ; set ; } // 0x00000000
2017-10-03 17:24:33 +08:00
public MetaHash UnkHash { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_3Ch { get ; set ; } // 0x00000000
// reference data
2017-09-24 18:14:41 +08:00
public ResourcePointerArray64 < ClipPropertyAttribute > Attributes { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
this . VFT = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
this . Unknown_08h = reader . ReadUInt32 ( ) ;
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_10h = reader . ReadUInt32 ( ) ;
this . Unknown_14h = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . NameHash = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
2017-09-24 18:14:41 +08:00
this . AttributesPointer = reader . ReadUInt64 ( ) ;
this . AttributesCount = reader . ReadUInt16 ( ) ;
this . AttributesCapacity = reader . ReadUInt16 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
this . Unknown_30h = reader . ReadUInt32 ( ) ;
this . Unknown_34h = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . UnkHash = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_3Ch = reader . ReadUInt32 ( ) ;
// read reference data
2017-09-24 18:14:41 +08:00
this . Attributes = reader . ReadBlockAt < ResourcePointerArray64 < ClipPropertyAttribute > > (
this . AttributesPointer , // offset
this . AttributesCount
2017-09-21 18:33:05 +08:00
) ;
2019-11-14 15:58:20 +08:00
switch ( VFT ) //some examples
{
case 1080111464 :
case 1080103160 :
case 1080119200 :
case 1080069168 :
case 1080053176 :
break ;
default :
break ;
}
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
2017-09-24 18:14:41 +08:00
this . AttributesPointer = ( ulong ) ( this . Attributes ! = null ? this . Attributes . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . AttributesCount = ( ushort ) ( this . Attributes ! = null ? this . Attributes . Count : 0 ) ;
this . AttributesCapacity = this . AttributesCount ;
2017-09-21 18:33:05 +08:00
// write structure data
writer . Write ( this . VFT ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
writer . Write ( this . Unknown_08h ) ;
writer . Write ( this . Unknown_0Ch ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_10h ) ;
writer . Write ( this . Unknown_14h ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . NameHash ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_1Ch ) ;
2017-09-24 18:14:41 +08:00
writer . Write ( this . AttributesPointer ) ;
writer . Write ( this . AttributesCount ) ;
writer . Write ( this . AttributesCapacity ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_2Ch ) ;
writer . Write ( this . Unknown_30h ) ;
writer . Write ( this . Unknown_34h ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . UnkHash ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_3Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
2017-09-24 18:14:41 +08:00
if ( Attributes ! = null ) list . Add ( Attributes ) ;
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
StringBuilder sb = new StringBuilder ( ) ;
if ( ( Attributes ! = null ) & & ( Attributes . data_items ! = null ) )
{
foreach ( var item in Attributes . data_items )
{
if ( sb . Length > 0 ) sb . Append ( ", " ) ;
sb . Append ( item . ToString ( ) ) ;
}
}
return NameHash . ToString ( ) + ": " + UnkHash . ToString ( ) + ": " + sb . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public virtual void WriteXml ( StringBuilder sb , int indent )
{
YcdXml . StringTag ( sb , indent , "NameHash" , YcdXml . HashString ( NameHash ) ) ;
YcdXml . StringTag ( sb , indent , "UnkHash" , YcdXml . HashString ( UnkHash ) ) ;
YcdXml . WriteItemArray ( sb , Attributes ? . data_items , indent , "Attributes" ) ;
}
public virtual void ReadXml ( XmlNode node )
{
NameHash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "NameHash" ) ) ;
UnkHash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "UnkHash" ) ) ;
var attrsNode = node . SelectSingleNode ( "Attributes" ) ;
if ( attrsNode ! = null )
{
var inodes = attrsNode . SelectNodes ( "Item" ) ;
if ( inodes ? . Count > 0 )
{
var alist = new List < ClipPropertyAttribute > ( ) ;
foreach ( XmlNode inode in inodes )
{
var item = new ClipPropertyAttribute ( ) ;
item . ReadXml ( inode ) ;
var v = ClipPropertyAttribute . ConstructItem ( item . Type ) ;
v . ReadXml ( inode ) ; //slightly wasteful but meh
alist . Add ( v ) ;
}
Attributes = new ResourcePointerArray64 < ClipPropertyAttribute > ( ) ;
Attributes . data_items = alist . ToArray ( ) ;
}
}
}
2017-09-21 18:33:05 +08:00
}
2019-11-14 15:58:20 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttribute : ResourceSystemBlock , IResourceXXSystemBlock , IMetaXmlItem
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 16 ; }
}
public uint VFT { get ; set ; }
2019-11-14 15:58:20 +08:00
public uint Unknown_04h { get ; set ; } = 1 ; // 0x00000001
public ClipPropertyAttributeType Type { get ; set ; }
2017-10-03 17:24:33 +08:00
public byte Unknown_09h { get ; set ; } // 0x00
2017-09-24 18:14:41 +08:00
public ushort Unknown_Ah { get ; set ; } // 0x0000
public uint Unknown_Ch { get ; set ; } // 0x00000000
public uint Unknown_10h { get ; set ; } // 0x00000000
public uint Unknown_14h { get ; set ; } // 0x00000000
public MetaHash NameHash { get ; set ; }
public uint Unknown_1Ch { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
this . VFT = reader . ReadUInt32 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_04h = reader . ReadUInt32 ( ) ;
2019-11-14 15:58:20 +08:00
this . Type = ( ClipPropertyAttributeType ) reader . ReadByte ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_09h = reader . ReadByte ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_Ah = reader . ReadUInt16 ( ) ;
this . Unknown_Ch = reader . ReadUInt32 ( ) ;
2017-09-24 18:14:41 +08:00
this . Unknown_10h = reader . ReadUInt32 ( ) ;
this . Unknown_14h = reader . ReadUInt32 ( ) ;
this . NameHash = reader . ReadUInt32 ( ) ;
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
2019-11-14 15:58:20 +08:00
switch ( VFT ) //some examples
{
case 1080119416 : //type 1
case 1080119528 : //type 2
case 1080119640 : //type 3
case 1080119752 : //type 4
case 1080119832 : //type 6
case 1080120088 : //type 8
case 1080120472 : //type 12
case 1080069384 : //type 1
case 1080069496 : //type 2
case 1080127976 :
case 1080069800 : //type 6
break ;
default :
break ;
}
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
writer . Write ( this . VFT ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_04h ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( ( byte ) this . Type ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_09h ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_Ah ) ;
writer . Write ( this . Unknown_Ch ) ;
2017-09-24 18:14:41 +08:00
writer . Write ( this . Unknown_10h ) ;
writer . Write ( this . Unknown_14h ) ;
writer . Write ( this . NameHash ) ;
writer . Write ( this . Unknown_1Ch ) ;
2017-09-21 18:33:05 +08:00
}
public IResourceSystemBlock GetType ( ResourceDataReader reader , params object [ ] parameters )
{
reader . Position + = 8 ;
2019-11-14 15:58:20 +08:00
var type = ( ClipPropertyAttributeType ) reader . ReadByte ( ) ;
2017-09-21 18:33:05 +08:00
reader . Position - = 9 ;
2019-11-14 15:58:20 +08:00
return ConstructItem ( type ) ;
}
2017-09-21 18:33:05 +08:00
2019-11-14 15:58:20 +08:00
public static ClipPropertyAttribute ConstructItem ( ClipPropertyAttributeType type )
{
2017-09-21 18:33:05 +08:00
switch ( type )
{
2019-11-14 15:58:20 +08:00
case ClipPropertyAttributeType . Float : return new ClipPropertyAttributeFloat ( ) ;
case ClipPropertyAttributeType . Int : return new ClipPropertyAttributeInt ( ) ;
case ClipPropertyAttributeType . Bool : return new ClipPropertyAttributeBool ( ) ;
case ClipPropertyAttributeType . String : return new ClipPropertyAttributeString ( ) ;
case ClipPropertyAttributeType . Vector3 : return new ClipPropertyAttributeVector3 ( ) ;
case ClipPropertyAttributeType . Vector4 : return new ClipPropertyAttributeVector4 ( ) ;
case ClipPropertyAttributeType . HashString : return new ClipPropertyAttributeHashString ( ) ;
2017-09-21 18:33:05 +08:00
default : return null ; // throw new Exception("Unknown type");
}
}
2019-11-14 15:58:20 +08:00
public virtual void WriteXml ( StringBuilder sb , int indent )
{
YcdXml . StringTag ( sb , indent , "NameHash" , YcdXml . HashString ( NameHash ) ) ;
YcdXml . ValueTag ( sb , indent , "Type" , Type . ToString ( ) ) ;
}
public virtual void ReadXml ( XmlNode node )
{
NameHash = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "NameHash" ) ) ;
Type = Xml . GetEnumValue < ClipPropertyAttributeType > ( Xml . GetChildStringAttribute ( node , "Type" , "value" ) ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeFloat : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2017-09-24 18:14:41 +08:00
public float Value { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_24h { get ; set ; } // 0x00000000
public uint Unknown_28h { get ; set ; } // 0x00000000
public uint Unknown_2Ch { get ; set ; } // 0x00000000
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2017-09-24 18:14:41 +08:00
this . Value = reader . ReadSingle ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28h = reader . ReadUInt32 ( ) ;
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . Value ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28h ) ;
writer . Write ( this . Unknown_2Ch ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return "Float:" + FloatUtil . ToString ( Value ) ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Value" , FloatUtil . ToString ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = Xml . GetChildFloatAttribute ( node , "Value" , "value" ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeInt : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2017-09-24 18:14:41 +08:00
public int Value { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_24h { get ; set ; } // 0x00000000
public uint Unknown_28h { get ; set ; } // 0x00000000
public uint Unknown_2Ch { get ; set ; } // 0x00000000
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2017-09-24 18:14:41 +08:00
this . Value = reader . ReadInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28h = reader . ReadUInt32 ( ) ;
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . Value ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28h ) ;
writer . Write ( this . Unknown_2Ch ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return "Int:" + Value . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Value" , Value . ToString ( ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = Xml . GetChildIntAttribute ( node , "Value" , "value" ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeBool : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2017-09-24 18:14:41 +08:00
public uint Value { get ; set ; }
2017-09-21 18:33:05 +08:00
public uint Unknown_24h { get ; set ; } // 0x00000000
public uint Unknown_28h { get ; set ; } // 0x00000000
public uint Unknown_2Ch { get ; set ; } // 0x00000000
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2017-09-24 18:14:41 +08:00
this . Value = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28h = reader . ReadUInt32 ( ) ;
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . Value ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28h ) ;
writer . Write ( this . Unknown_2Ch ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return "Uint:" + Value . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Value" , Value . ToString ( ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = Xml . GetChildUIntAttribute ( node , "Value" , "value" ) ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeString : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2017-09-24 18:14:41 +08:00
public ulong ValuePointer { get ; set ; }
2019-11-14 15:58:20 +08:00
public ushort ValueLength { get ; set ; }
public ushort ValueCapacity { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unknown_02Ch { get ; set ; } // 0x00000000
2017-09-21 18:33:05 +08:00
2017-09-24 18:14:41 +08:00
public string Value ;
2019-11-14 15:58:20 +08:00
private string_r ValueBlock = null ;
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2017-09-24 18:14:41 +08:00
this . ValuePointer = reader . ReadUInt64 ( ) ;
2019-11-14 15:58:20 +08:00
this . ValueLength = reader . ReadUInt16 ( ) ;
this . ValueCapacity = reader . ReadUInt16 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_02Ch = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
2017-09-24 18:14:41 +08:00
//// read reference data
Value = reader . ReadStringAt ( ValuePointer ) ;
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// update structure data
2019-11-14 15:58:20 +08:00
this . ValuePointer = ( ulong ) ( this . ValueBlock ! = null ? this . ValueBlock . FilePosition : 0 ) ;
this . ValueLength = ( ushort ) ( Value ? . Length ? ? 0 ) ;
this . ValueCapacity = ( ushort ) ( ( Value ! = null ) ? Value . Length + 1 : 0 ) ;
2017-09-21 18:33:05 +08:00
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . ValuePointer ) ;
2019-11-14 15:58:20 +08:00
writer . Write ( this . ValueLength ) ;
writer . Write ( this . ValueCapacity ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_02Ch ) ;
2017-09-21 18:33:05 +08:00
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( base . GetReferences ( ) ) ;
2019-11-14 15:58:20 +08:00
if ( Value ! = null )
{
ValueBlock = ( string_r ) Value ;
list . Add ( ValueBlock ) ;
}
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return "String:" + Value ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . StringTag ( sb , indent , "Value" , MetaXml . XmlEscape ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = Xml . GetChildInnerText ( node , "Value" ) ;
ValueLength = ( ushort ) ( Value ? . Length ? ? 0 ) ;
ValueCapacity = ValueLength ;
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeVector3 : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2019-11-14 15:58:20 +08:00
public Vector3 Value { get ; set ; }
2017-10-03 17:24:33 +08:00
public float Unknown_02Ch { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2019-11-14 15:58:20 +08:00
Value = reader . ReadVector3 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_02Ch = reader . ReadSingle ( ) ;
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
2017-09-24 18:14:41 +08:00
// write structure data
2019-11-14 15:58:20 +08:00
writer . Write ( this . Value ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_02Ch ) ;
}
public override string ToString ( )
{
2019-11-14 15:58:20 +08:00
return "Vector3:" + FloatUtil . GetVector3String ( Value ) ;
}
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . SelfClosingTag ( sb , indent , "Value " + FloatUtil . GetVector3XmlString ( Value ) ) ;
YcdXml . ValueTag ( sb , indent , "Unknown2C" , FloatUtil . ToString ( Unknown_02Ch ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
2020-01-21 23:45:27 +08:00
Value = Xml . GetChildVector3Attributes ( node , "Value" ) ;
2019-11-14 15:58:20 +08:00
Unknown_02Ch = Xml . GetChildFloatAttribute ( node , "Unknown2C" , "value" ) ;
2017-09-21 18:33:05 +08:00
}
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeVector4 : ClipPropertyAttribute
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 48 ; }
}
2019-11-14 15:58:20 +08:00
public Vector4 Value { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
2019-11-14 15:58:20 +08:00
this . Value = reader . ReadVector4 ( ) ;
2017-09-24 18:14:41 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// write structure data
2019-11-14 15:58:20 +08:00
writer . Write ( this . Value ) ;
2017-09-24 18:14:41 +08:00
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
2019-11-14 15:58:20 +08:00
return "Vector4:" + FloatUtil . GetVector4String ( Value ) ;
}
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . SelfClosingTag ( sb , indent , "Value " + FloatUtil . GetVector4XmlString ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
2020-01-21 23:45:27 +08:00
Value = Xml . GetChildVector4Attributes ( node , "Value" ) ;
2017-10-03 17:24:33 +08:00
}
2017-09-24 18:14:41 +08:00
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipPropertyAttributeHashString : ClipPropertyAttribute
{
public override long BlockLength = > 0x30 ;
public MetaHash Value { get ; set ; }
public uint Unknown_24h { get ; set ; } // 0x00000000
public uint Unknown_28h { get ; set ; } // 0x00000000
public uint Unknown_2Ch { get ; set ; } // 0x00000000
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
base . Read ( reader , parameters ) ;
// read structure data
this . Value = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_24h = reader . ReadUInt32 ( ) ;
this . Unknown_28h = reader . ReadUInt32 ( ) ;
this . Unknown_2Ch = reader . ReadUInt32 ( ) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
base . Write ( writer , parameters ) ;
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . Value ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_24h ) ;
writer . Write ( this . Unknown_28h ) ;
writer . Write ( this . Unknown_2Ch ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return "Hash:" + Value . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . StringTag ( sb , indent , "Value" , YcdXml . HashString ( Value ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Value = XmlMeta . GetHash ( Xml . GetChildInnerText ( node , "Value" ) ) ;
}
}
public enum ClipPropertyAttributeType : byte
{
Float = 1 ,
Int = 2 ,
Bool = 3 ,
String = 4 ,
Vector3 = 6 ,
Vector4 = 8 ,
HashString = 12 ,
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipTagList : ResourceSystemBlock
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 32 ; }
}
// structure data
2017-09-24 18:14:41 +08:00
public ulong TagsPointer { get ; set ; }
public ushort TagCount1 { get ; set ; }
public ushort TagCount2 { get ; set ; }
2017-10-03 17:24:33 +08:00
public uint Unknown_0Ch { get ; set ; } // 0x00000000
2019-11-18 02:10:55 +08:00
public uint HasBlockTag { get ; set ; } // 0, 1
2017-09-21 18:33:05 +08:00
public uint Unknown_14h { get ; set ; } // 0x00000000
public uint Unknown_18h { get ; set ; } // 0x00000000
public uint Unknown_1Ch { get ; set ; } // 0x00000000
// reference data
2017-09-24 18:14:41 +08:00
public ResourcePointerArray64 < ClipTag > Tags { get ; set ; }
2017-09-21 18:33:05 +08:00
2017-10-03 17:24:33 +08:00
public ClipTag [ ] AllTags { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
// read structure data
2017-09-24 18:14:41 +08:00
this . TagsPointer = reader . ReadUInt64 ( ) ;
this . TagCount1 = reader . ReadUInt16 ( ) ;
this . TagCount2 = reader . ReadUInt16 ( ) ;
2017-10-03 17:24:33 +08:00
this . Unknown_0Ch = reader . ReadUInt32 ( ) ;
2019-11-18 02:10:55 +08:00
this . HasBlockTag = reader . ReadUInt32 ( ) ;
2017-09-21 18:33:05 +08:00
this . Unknown_14h = reader . ReadUInt32 ( ) ;
this . Unknown_18h = reader . ReadUInt32 ( ) ;
this . Unknown_1Ch = reader . ReadUInt32 ( ) ;
// read reference data
2017-09-24 18:14:41 +08:00
this . Tags = reader . ReadBlockAt < ResourcePointerArray64 < ClipTag > > (
this . TagsPointer , // offset
this . TagCount1
2017-09-21 18:33:05 +08:00
) ;
2017-10-03 17:24:33 +08:00
2019-11-14 15:58:20 +08:00
BuildAllTags ( ) ;
if ( TagCount1 ! = TagCount2 )
{ }
2017-09-21 18:33:05 +08:00
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
// update structure data
2017-09-24 18:14:41 +08:00
this . TagsPointer = ( ulong ) ( this . Tags ! = null ? this . Tags . FilePosition : 0 ) ;
2019-11-14 15:58:20 +08:00
this . TagCount1 = ( ushort ) ( this . Tags ! = null ? this . Tags . Count : 0 ) ;
this . TagCount2 = this . TagCount1 ;
2017-09-21 18:33:05 +08:00
2019-11-18 02:10:55 +08:00
BuildAllTags ( ) ; //just in case? updates HasBlockTag
2017-09-21 18:33:05 +08:00
// write structure data
2017-09-24 18:14:41 +08:00
writer . Write ( this . TagsPointer ) ;
writer . Write ( this . TagCount1 ) ;
writer . Write ( this . TagCount2 ) ;
2017-10-03 17:24:33 +08:00
writer . Write ( this . Unknown_0Ch ) ;
2019-11-18 02:10:55 +08:00
writer . Write ( this . HasBlockTag ) ;
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_14h ) ;
writer . Write ( this . Unknown_18h ) ;
writer . Write ( this . Unknown_1Ch ) ;
}
public override IResourceBlock [ ] GetReferences ( )
{
var list = new List < IResourceBlock > ( ) ;
2017-09-24 18:14:41 +08:00
if ( Tags ! = null ) list . Add ( Tags ) ;
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
2017-10-04 11:35:39 +08:00
return "Count: " + ( AllTags ? . Length ? ? 0 ) . ToString ( ) ;
2017-10-03 17:24:33 +08:00
}
2019-11-14 15:58:20 +08:00
public void BuildAllTags ( )
{
if ( ( Tags ! = null ) & & ( Tags . data_items ! = null ) )
{
List < ClipTag > tl = new List < ClipTag > ( ) ;
foreach ( var te in Tags . data_items )
{
if ( te . Tags ! = this )
{ }
if ( te ! = null )
{
tl . Add ( te ) ;
}
}
AllTags = tl . ToArray ( ) ;
}
2019-11-18 02:10:55 +08:00
uint hasBlock = 0 ;
if ( AllTags ! = null )
{
foreach ( var tag in AllTags )
{
if ( tag . NameHash = = ( uint ) MetaName . block )
{ hasBlock = 1 ; break ; }
}
}
if ( HasBlockTag ! = hasBlock )
{ }
HasBlockTag = hasBlock ;
2019-11-14 15:58:20 +08:00
}
public void AssignTagOwners ( )
{
if ( Tags ? . data_items = = null ) return ;
foreach ( var tag in Tags . data_items )
{
tag . Tags = this ;
}
}
2017-09-21 18:33:05 +08:00
}
2017-09-24 18:14:41 +08:00
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClipTag : ClipProperty
2017-09-21 18:33:05 +08:00
{
public override long BlockLength
{
get { return 80 ; }
}
2019-11-14 15:58:20 +08:00
public float Unknown_40h { get ; set ; }
public float Unknown_44h { get ; set ; }
2017-09-24 18:14:41 +08:00
public ulong TagsPointer { get ; set ; }
2017-09-21 18:33:05 +08:00
// reference data
2017-09-24 18:14:41 +08:00
public ClipTagList Tags { get ; set ; }
2017-09-21 18:33:05 +08:00
public override void Read ( ResourceDataReader reader , params object [ ] parameters )
{
2017-09-24 18:14:41 +08:00
base . Read ( reader , parameters ) ;
2017-09-21 18:33:05 +08:00
// read structure data
2019-11-14 15:58:20 +08:00
this . Unknown_40h = reader . ReadSingle ( ) ;
this . Unknown_44h = reader . ReadSingle ( ) ;
2017-09-24 18:14:41 +08:00
this . TagsPointer = reader . ReadUInt64 ( ) ;
2017-09-21 18:33:05 +08:00
// read reference data
2017-09-24 18:14:41 +08:00
this . Tags = reader . ReadBlockAt < ClipTagList > (
this . TagsPointer // offset
2017-09-21 18:33:05 +08:00
) ;
}
public override void Write ( ResourceDataWriter writer , params object [ ] parameters )
{
2017-09-24 18:14:41 +08:00
base . Write ( writer , parameters ) ;
2017-09-21 18:33:05 +08:00
// update structure data
2017-09-24 18:14:41 +08:00
this . TagsPointer = ( ulong ) ( this . Tags ! = null ? this . Tags . FilePosition : 0 ) ;
2017-09-21 18:33:05 +08:00
2017-09-24 18:14:41 +08:00
// write structure data
2017-09-21 18:33:05 +08:00
writer . Write ( this . Unknown_40h ) ;
writer . Write ( this . Unknown_44h ) ;
2017-09-24 18:14:41 +08:00
writer . Write ( this . TagsPointer ) ;
2017-09-21 18:33:05 +08:00
}
public override IResourceBlock [ ] GetReferences ( )
{
2017-09-24 18:14:41 +08:00
var list = new List < IResourceBlock > ( base . GetReferences ( ) ) ;
if ( Tags ! = null ) list . Add ( Tags ) ;
2017-09-21 18:33:05 +08:00
return list . ToArray ( ) ;
}
2017-10-03 17:24:33 +08:00
public override string ToString ( )
{
return base . ToString ( ) + ": " + Unknown_40h . ToString ( ) + ", " + Unknown_44h . ToString ( ) ;
}
2019-11-14 15:58:20 +08:00
public override void WriteXml ( StringBuilder sb , int indent )
{
base . WriteXml ( sb , indent ) ;
YcdXml . ValueTag ( sb , indent , "Unknown40" , FloatUtil . ToString ( Unknown_40h ) ) ;
YcdXml . ValueTag ( sb , indent , "Unknown44" , FloatUtil . ToString ( Unknown_44h ) ) ;
}
public override void ReadXml ( XmlNode node )
{
base . ReadXml ( node ) ;
Unknown_40h = Xml . GetChildFloatAttribute ( node , "Unknown40" , "value" ) ;
Unknown_44h = Xml . GetChildFloatAttribute ( node , "Unknown44" , "value" ) ;
}
2017-09-21 18:33:05 +08:00
}
2019-10-31 11:56:40 +08:00
}