Animations XML conversion

This commit is contained in:
dexy 2019-11-14 18:58:20 +11:00
parent 918ed7fccf
commit 7e43271a67
15 changed files with 2996 additions and 737 deletions

View File

@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace CodeWalker.GameFiles
{
@ -19,6 +20,7 @@ namespace CodeWalker.GameFiles
public ClipMapEntry[] ClipMapEntries { get; set; }
public AnimationMapEntry[] AnimMapEntries { get; set; }
public string LoadException { get; set; }
public YcdFile() : base(null, GameFileType.Ycd)
{
@ -41,91 +43,58 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
ClipDictionary = rd.ReadBlock<ClipDictionary>();
ClipMap = new Dictionary<MetaHash, ClipMapEntry>();
AnimMap = new Dictionary<MetaHash, AnimationMapEntry>();
if (ClipDictionary != null)
ResourceDataReader rd = null;
try
{
if ((ClipDictionary.Clips != null) && (ClipDictionary.Clips.data_items != null))
{
foreach (var cme in ClipDictionary.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 ((ClipDictionary.Animations != null) && (ClipDictionary.Animations.Animations != null) && (ClipDictionary.Animations.Animations.data_items != null))
{
foreach (var ame in ClipDictionary.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;
}
}
}
}
rd = new ResourceDataReader(resentry, data);
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
InitDictionaries();
}
public void InitDictionaries()
{
ClipMap = ClipDictionary?.ClipMap ?? new Dictionary<MetaHash, ClipMapEntry>();
AnimMap = ClipDictionary?.AnimMap ?? new Dictionary<MetaHash, AnimationMapEntry>();
foreach (var cme in ClipMap.Values)
{
var clip = cme.Clip;
if (clip == null) continue;
clip.Ycd = this;
if (string.IsNullOrEmpty(clip.Name)) continue;
string name = clip.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);
}
clip.ShortName = name;
name = name.ToLowerInvariant();
JenkIndex.Ensure(name);
//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
//{
//}
if (cme?.Clip != null) cme.Clip.Ycd = this;
}
foreach (var ame in AnimMap.Values)
{
var anim = ame.Animation;
if (anim == null) continue;
anim.Ycd = this;
if (ame?.Animation != null) ame.Animation.Ycd = this;
}
ClipMapEntries = ClipMap.Values.ToArray();
AnimMapEntries = AnimMap.Values.ToArray();
}
public byte[] Save()
{
//if (BuildStructsOnSave)
//{
// BuildStructs();
//}
byte[] data = ResourceBuilder.Build(ClipDictionary, 46); //ycd is 46...
return data;
}
public void SaveOpenFormatsAnimation(Animation crAnim, Stream outStream)
{
var seqs = new int[(crAnim.Frames / crAnim.SequenceFrameLimit) + 1];
@ -171,7 +140,7 @@ namespace CodeWalker.GameFiles
}
else if (chList.Length == 1)
{
if (chList[0] is AnimChannelStaticSmallestThreeQuaternion)
if (chList[0] is AnimChannelStaticQuaternion)
{
isRotation = true;
}
@ -201,7 +170,7 @@ namespace CodeWalker.GameFiles
return " Static";
}
}
else if (chan is AnimChannelStaticFloat || chan is AnimChannelStaticVector3 || chan is AnimChannelStaticSmallestThreeQuaternion)
else if (chan is AnimChannelStaticFloat || chan is AnimChannelStaticVector3 || chan is AnimChannelStaticQuaternion)
{
return " Static";
}
@ -245,10 +214,10 @@ namespace CodeWalker.GameFiles
switch (chan)
{
case AnimChannelStaticFloat sf:
return $" {sf.FloatValue}\r\n";
return $" {sf.Value}\r\n";
case AnimChannelStaticVector3 v3:
return $" {v3.Value[0]} {v3.Value[1]} {v3.Value[2]}\r\n";
case AnimChannelStaticSmallestThreeQuaternion q3:
case AnimChannelStaticQuaternion q3:
return $" {q3.Value[0]} {q3.Value[1]} {q3.Value[2]} {q3.Value[3]}\r\n";
default:
{
@ -303,4 +272,61 @@ namespace CodeWalker.GameFiles
writer.Flush();
}
}
public class YcdXml : MetaXmlBase
{
public static string GetXml(YcdFile ycd)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if ((ycd != null) && (ycd.ClipDictionary != null))
{
var name = "ClipDictionary";
OpenTag(sb, 0, name);
ycd.ClipDictionary.WriteXml(sb, 1);
CloseTag(sb, 0, name);
}
return sb.ToString();
}
}
public class XmlYcd
{
public static YcdFile GetYcd(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return GetYcd(doc);
}
public static YcdFile GetYcd(XmlDocument doc)
{
YcdFile ycd = new YcdFile();
ycd.ClipDictionary = new ClipDictionary();
ycd.ClipDictionary.ReadXml(doc.DocumentElement);
ycd.InitDictionaries();
//ycd.BuildStructsOnSave = false; //structs don't need to be rebuilt here!
return ycd;
}
}
}

View File

@ -2964,38 +2964,274 @@ namespace CodeWalker.GameFiles
}
public void TestYcds()
{
var errorfiles = new List<YcdFile>();
var errorentries = new List<RpfEntry>();
foreach (RpfFile file in AllRpfs)
{
foreach (RpfEntry entry in file.AllEntries)
{
try
//try
//{
if (entry.NameLower.EndsWith(".ycd"))
{
if (entry.NameLower.EndsWith(".ycd"))
UpdateStatus(string.Format(entry.Path));
YcdFile ycd1 = RpfMan.GetFile<YcdFile>(entry);
if (ycd1 == null)
{
UpdateStatus(string.Format(entry.Path));
YcdFile ycdfile = RpfMan.GetFile<YcdFile>(entry);
if ((ycdfile != null))// && (ycdfile.Meta != null))
{ }
errorentries.Add(entry);
}
else if (ycd1?.LoadException != null)
{
errorfiles.Add(ycd1);//these ones have file corruption issues and won't load as resource...
}
else
{
if (ycd1.ClipDictionary == null)
{ continue; }
var t = true;
if (t)//just here to test loading only
{ continue; }
var xml = YcdXml.GetXml(ycd1);
var ycdX = XmlYcd.GetYcd(xml);
var data = ycdX.Save();
var ycd2 = new YcdFile();
RpfFile.LoadResourceFile(ycd2, data, 46);//full roundtrip
if (ycd2 == null)
{ continue; }
if (ycd2.ClipDictionary == null)
{ continue; }
var c1 = ycd1.ClipDictionary.Clips?.data_items;
var c2 = ycd2.ClipDictionary.Clips?.data_items;
if ((c1 == null) || (c2 == null))
{ continue; }
if (c1.Length != c2.Length)
{ continue; }
var a1 = ycd1.ClipDictionary.Animations?.Animations?.data_items;
var a2 = ycd2.ClipDictionary.Animations?.Animations?.data_items;
if ((a1 == null) || (a2 == null))
{ continue; }
if (a1.Length != a2.Length)
{ continue; }
var m1 = ycd1.AnimMap;
var m2 = ycd2.AnimMap;
if ((m1 == null) || (m2 == null))
{ continue; }
if (m1.Count != m2.Count)
{ continue; }
foreach (var kvp1 in m1)
{
var an1 = kvp1.Value;
var an2 = an1;
if(!m2.TryGetValue(kvp1.Key, out an2))
{ continue; }
var sa1 = an1?.Animation?.Sequences?.data_items;
var sa2 = an2?.Animation?.Sequences?.data_items;
if ((sa1 == null) || (sa2 == null))
{ continue; }
if (sa1.Length != sa2.Length)
{ continue; }
for (int s = 0; s < sa1.Length; s++)
{
var s1 = sa1[s];
var s2 = sa2[s];
if ((s1?.Sequences == null) || (s2?.Sequences == null))
{ continue; }
if (s1.NumFrames != s2.NumFrames)
{ }
if (s1.ChunkSize != s2.ChunkSize)
{ }
if (s1.FrameOffset != s2.FrameOffset)
{ }
if (s1.DataLength != s2.DataLength)
{ }
else
{
//for (int b = 0; b < s1.DataLength; b++)
//{
// var b1 = s1.Data[b];
// var b2 = s2.Data[b];
// if (b1 != b2)
// { }
//}
}
for (int ss = 0; ss < s1.Sequences.Length; ss++)
{
var ss1 = s1.Sequences[ss];
var ss2 = s2.Sequences[ss];
if ((ss1?.Channels == null) || (ss2?.Channels == null))
{ continue; }
if (ss1.Channels.Length != ss2.Channels.Length)
{ continue; }
for (int c = 0; c < ss1.Channels.Length; c++)
{
var sc1 = ss1.Channels[c];
var sc2 = ss2.Channels[c];
if ((sc1 == null) || (sc2 == null))
{ continue; }
if (sc1.Type == AnimChannelType.LinearFloat)
{ continue; }
if (sc1.Type != sc2.Type)
{ continue; }
if (sc1.Index != sc2.Index)
{ continue; }
if (sc1.Type == AnimChannelType.StaticQuaternion)
{
var acsq1 = sc1 as AnimChannelStaticQuaternion;
var acsq2 = sc2 as AnimChannelStaticQuaternion;
var vdiff = acsq1.Value - acsq2.Value;
var len = vdiff.Length();
var v1len = Math.Max(acsq1.Value.Length(), 1);
if (len > 1e-2f * v1len)
{ continue; }
}
else if (sc1.Type == AnimChannelType.StaticVector3)
{
var acsv1 = sc1 as AnimChannelStaticVector3;
var acsv2 = sc2 as AnimChannelStaticVector3;
var vdiff = acsv1.Value - acsv2.Value;
var len = vdiff.Length();
var v1len = Math.Max(acsv1.Value.Length(), 1);
if (len > 1e-2f * v1len)
{ continue; }
}
else if (sc1.Type == AnimChannelType.StaticFloat)
{
var acsf1 = sc1 as AnimChannelStaticFloat;
var acsf2 = sc2 as AnimChannelStaticFloat;
var vdiff = Math.Abs(acsf1.Value - acsf2.Value);
var v1len = Math.Max(Math.Abs(acsf1.Value), 1);
if (vdiff > 1e-2f * v1len)
{ continue; }
}
else if (sc1.Type == AnimChannelType.RawFloat)
{
var acrf1 = sc1 as AnimChannelRawFloat;
var acrf2 = sc2 as AnimChannelRawFloat;
for (int v = 0; v < acrf1.Values.Length; v++)
{
var v1 = acrf1.Values[v];
var v2 = acrf2.Values[v];
var vdiff = Math.Abs(v1 - v2);
var v1len = Math.Max(Math.Abs(v1), 1);
if (vdiff > 1e-2f * v1len)
{ break; }
}
}
else if (sc1.Type == AnimChannelType.QuantizeFloat)
{
var acqf1 = sc1 as AnimChannelQuantizeFloat;
var acqf2 = sc2 as AnimChannelQuantizeFloat;
if (acqf1.ValueBits != acqf2.ValueBits)
{ continue; }
if (Math.Abs(acqf1.Offset - acqf2.Offset) > (0.001f * Math.Abs(acqf1.Offset)))
{ continue; }
if (Math.Abs(acqf1.Quantum - acqf2.Quantum) > 0.00001f)
{ continue; }
for (int v = 0; v < acqf1.Values.Length; v++)
{
var v1 = acqf1.Values[v];
var v2 = acqf2.Values[v];
var vdiff = Math.Abs(v1 - v2);
var v1len = Math.Max(Math.Abs(v1), 1);
if (vdiff > 1e-2f * v1len)
{ break; }
}
}
else if (sc1.Type == AnimChannelType.IndirectQuantizeFloat)
{
var aciqf1 = sc1 as AnimChannelIndirectQuantizeFloat;
var aciqf2 = sc2 as AnimChannelIndirectQuantizeFloat;
if (aciqf1.FrameBits != aciqf2.FrameBits)
{ continue; }
if (aciqf1.ValueBits != aciqf2.ValueBits)
{ continue; }
if (Math.Abs(aciqf1.Offset - aciqf2.Offset) > (0.001f * Math.Abs(aciqf1.Offset)))
{ continue; }
if (Math.Abs(aciqf1.Quantum - aciqf2.Quantum) > 0.00001f)
{ continue; }
for (int f = 0; f < aciqf1.Frames.Length; f++)
{
if (aciqf1.Frames[f] != aciqf2.Frames[f])
{ break; }
}
for (int v = 0; v < aciqf1.Values.Length; v++)
{
var v1 = aciqf1.Values[v];
var v2 = aciqf2.Values[v];
var vdiff = Math.Abs(v1 - v2);
var v1len = Math.Max(Math.Abs(v1), 1);
if (vdiff > 1e-2f * v1len)
{ break; }
}
}
else if ((sc1.Type == AnimChannelType.CachedQuaternion1)||(sc1.Type == AnimChannelType.CachedQuaternion2))
{
var acrf1 = sc1 as AnimChannelCachedQuaternion;
var acrf2 = sc2 as AnimChannelCachedQuaternion;
if (acrf1.QuatIndex != acrf2.QuatIndex)
{ continue; }
}
}
//for (int f = 0; f < s1.NumFrames; f++)
//{
// var v1 = ss1.EvaluateVector(f);
// var v2 = ss2.EvaluateVector(f);
// var vdiff = v1 - v2;
// var len = vdiff.Length();
// var v1len = Math.Max(v1.Length(), 1);
// if (len > 1e-2f*v1len)
// { }
//}
}
}
}
}
//if (entry.NameLower.EndsWith(".awc")) //awcs can also contain clip dicts..
//{
// UpdateStatus(string.Format(entry.Path));
// AwcFile awcfile = RpfMan.GetFile<AwcFile>(entry);
// if ((awcfile != null))
// { }
//}
}
catch (Exception ex)
{
UpdateStatus("Error! " + ex.ToString());
}
//if (entry.NameLower.EndsWith(".awc")) //awcs can also contain clip dicts..
//{
// UpdateStatus(string.Format(entry.Path));
// AwcFile awcfile = RpfMan.GetFile<AwcFile>(entry);
// if ((awcfile != null))
// { }
//}
//}
//catch (Exception ex)
//{
// UpdateStatus("Error! " + ex.ToString());
//}
}
}
//var sd = Sequence.SeqDict;
//if (sd != null)
//{
//}
if (errorfiles.Count > 0)
{ }
}
public void TestYtds()
{

View File

@ -1384,18 +1384,6 @@ namespace CodeWalker.GameFiles
}
public static string XmlEscape(string unescaped)
{
if (unescaped == null) return null;
XmlDocument doc = new XmlDocument();
XmlNode node = doc.CreateElement("root");
node.InnerText = unescaped;
var escaped = node.InnerXml;
if (escaped != unescaped)
{ }
return node.InnerXml;
}
public class PsoCont
{
@ -1742,9 +1730,16 @@ namespace CodeWalker.GameFiles
var cind2 = ind + 2;
for (int i = 0; i < itemCount; i++)
{
OpenTag(sb, cind, "Item");
arr[i].WriteXml(sb, cind2);
CloseTag(sb, cind, "Item");
if (arr[i] != null)
{
OpenTag(sb, cind, "Item");
arr[i].WriteXml(sb, cind2);
CloseTag(sb, cind, "Item");
}
else
{
SelfClosingTag(sb, cind, "Item");
}
}
CloseTag(sb, ind, name);
}
@ -1891,6 +1886,22 @@ namespace CodeWalker.GameFiles
public static string XmlEscape(string unescaped)
{
if (unescaped == null) return null;
XmlDocument doc = new XmlDocument();
XmlNode node = doc.CreateElement("root");
node.InnerText = unescaped;
var escaped = node.InnerXml;
if (escaped != unescaped)
{ }
return node.InnerXml;
}
public enum XmlTagMode
{
None = 0,

View File

@ -813,6 +813,78 @@ namespace CodeWalker.GameFiles
return 0;
}
public static T[] ReadItemArray<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
{
var vnode2 = node.SelectSingleNode(name);
if (vnode2 != null)
{
var inodes = vnode2.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<T>();
foreach (XmlNode inode in inodes)
{
var v = new T();
v.ReadXml(inode);
vlist.Add(v);
}
return vlist.ToArray();
}
}
return null;
}
public static T[] ReadItemArrayNullable<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
{
var vnode2 = node.SelectSingleNode(name);
if (vnode2 != null)
{
var inodes = vnode2.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<T>();
foreach (XmlNode inode in inodes)
{
if (inode.HasChildNodes)
{
var v = new T();
v.ReadXml(inode);
vlist.Add(v);
}
else
{
vlist.Add(default(T));
}
}
return vlist.ToArray();
}
}
return null;
}
public static MetaHash[] ReadHashItemArray(XmlNode node, string name)
{
var vnode = node.SelectSingleNode(name);
if (vnode != null)
{
var inodes = vnode.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<MetaHash>();
foreach (XmlNode inode in inodes)
{
vlist.Add(GetHash(inode.InnerText));
}
return vlist.ToArray();
}
}
return null;
}
}
struct ArrayResults

File diff suppressed because it is too large Load Diff

View File

@ -748,7 +748,7 @@ namespace CodeWalker.GameFiles
public ushort EntriesCapacity { get; private set; }
// reference data
public T[] data_items { get; private set; }
public T[] data_items { get; set; }
private ResourceSystemStructBlock<T> data_block;//used for saving.

View File

@ -218,11 +218,35 @@ namespace CodeWalker
return GetRawByteArray(cnode);
}
public static uint[] GetRawUintArray(XmlNode node)
{
if (node == null) return new uint[0];
var data = new List<uint>();
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
for (int i = 0; i < split.Length; i++)
{
if (!string.IsNullOrEmpty(split[i]))
{
var str = split[i];
if (string.IsNullOrEmpty(str)) continue;
var val = 0u;
uint.TryParse(str, out val);
data.Add(val);
}
}
return data.ToArray();
}
public static uint[] GetChildRawUintArray(XmlNode node, string name)
{
var cnode = node.SelectSingleNode(name);
return GetRawUintArray(cnode);
}
public static float[] GetRawFloatArray(XmlNode node)
{
if (node == null) return new float[0];
var items = new List<float>();
var split = node.InnerText.Split('\n');// Regex.Split(node.InnerText, @"[\s\r\n\t]");
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");//node.InnerText.Split('\n');//
for (int i = 0; i < split.Length; i++)
{
var s = split[i]?.Trim();

View File

@ -28,6 +28,7 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Windows.Forms.ListViewGroup listViewGroup1 = new System.Windows.Forms.ListViewGroup("Clips", System.Windows.Forms.HorizontalAlignment.Left);
System.Windows.Forms.ListViewGroup listViewGroup2 = new System.Windows.Forms.ListViewGroup("Animations", System.Windows.Forms.HorizontalAlignment.Left);
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(YcdForm));
@ -35,10 +36,18 @@
this.MainListView = new System.Windows.Forms.ListView();
this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.MainPropertyGrid = new CodeWalker.WinForms.ReadOnlyPropertyGrid();
this.MainTabControl = new System.Windows.Forms.TabControl();
this.DetailsTabPage = new System.Windows.Forms.TabPage();
this.XmlTabPage = new System.Windows.Forms.TabPage();
this.XmlTextBox = new FastColoredTextBoxNS.FastColoredTextBox();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.MainTabControl.SuspendLayout();
this.DetailsTabPage.SuspendLayout();
this.XmlTabPage.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.XmlTextBox)).BeginInit();
this.SuspendLayout();
//
// splitContainer1
@ -55,7 +64,7 @@
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.MainPropertyGrid);
this.splitContainer1.Size = new System.Drawing.Size(763, 474);
this.splitContainer1.Size = new System.Drawing.Size(751, 442);
this.splitContainer1.SplitterDistance = 254;
this.splitContainer1.TabIndex = 1;
//
@ -78,7 +87,7 @@
this.MainListView.Location = new System.Drawing.Point(3, 3);
this.MainListView.MultiSelect = false;
this.MainListView.Name = "MainListView";
this.MainListView.Size = new System.Drawing.Size(248, 468);
this.MainListView.Size = new System.Drawing.Size(248, 436);
this.MainListView.TabIndex = 0;
this.MainListView.UseCompatibleStateImageBehavior = false;
this.MainListView.View = System.Windows.Forms.View.Details;
@ -98,15 +107,92 @@
this.MainPropertyGrid.Location = new System.Drawing.Point(3, 3);
this.MainPropertyGrid.Name = "MainPropertyGrid";
this.MainPropertyGrid.ReadOnly = false;
this.MainPropertyGrid.Size = new System.Drawing.Size(499, 468);
this.MainPropertyGrid.Size = new System.Drawing.Size(487, 436);
this.MainPropertyGrid.TabIndex = 0;
//
// MainTabControl
//
this.MainTabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.MainTabControl.Controls.Add(this.DetailsTabPage);
this.MainTabControl.Controls.Add(this.XmlTabPage);
this.MainTabControl.Location = new System.Drawing.Point(2, 3);
this.MainTabControl.Name = "MainTabControl";
this.MainTabControl.SelectedIndex = 0;
this.MainTabControl.Size = new System.Drawing.Size(759, 468);
this.MainTabControl.TabIndex = 2;
this.MainTabControl.SelectedIndexChanged += new System.EventHandler(this.MainTabControl_SelectedIndexChanged);
//
// DetailsTabPage
//
this.DetailsTabPage.Controls.Add(this.splitContainer1);
this.DetailsTabPage.Location = new System.Drawing.Point(4, 22);
this.DetailsTabPage.Name = "DetailsTabPage";
this.DetailsTabPage.Size = new System.Drawing.Size(751, 442);
this.DetailsTabPage.TabIndex = 0;
this.DetailsTabPage.Text = "Details";
this.DetailsTabPage.UseVisualStyleBackColor = true;
//
// XmlTabPage
//
this.XmlTabPage.Controls.Add(this.XmlTextBox);
this.XmlTabPage.Location = new System.Drawing.Point(4, 22);
this.XmlTabPage.Name = "XmlTabPage";
this.XmlTabPage.Size = new System.Drawing.Size(751, 442);
this.XmlTabPage.TabIndex = 1;
this.XmlTabPage.Text = "XML";
this.XmlTabPage.UseVisualStyleBackColor = true;
//
// XmlTextBox
//
this.XmlTextBox.AutoCompleteBracketsList = new char[] {
'(',
')',
'{',
'}',
'[',
']',
'\"',
'\"',
'\'',
'\''};
this.XmlTextBox.AutoIndentChars = false;
this.XmlTextBox.AutoIndentCharsPatterns = "";
this.XmlTextBox.AutoIndentExistingLines = false;
this.XmlTextBox.AutoScrollMinSize = new System.Drawing.Size(27, 14);
this.XmlTextBox.BackBrush = null;
this.XmlTextBox.CharHeight = 14;
this.XmlTextBox.CharWidth = 8;
this.XmlTextBox.CommentPrefix = null;
this.XmlTextBox.Cursor = System.Windows.Forms.Cursors.IBeam;
this.XmlTextBox.DelayedEventsInterval = 1;
this.XmlTextBox.DisabledColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))));
this.XmlTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.XmlTextBox.Font = new System.Drawing.Font("Courier New", 9.75F);
this.XmlTextBox.IsReplaceMode = false;
this.XmlTextBox.Language = FastColoredTextBoxNS.Language.XML;
this.XmlTextBox.LeftBracket = '<';
this.XmlTextBox.LeftBracket2 = '(';
this.XmlTextBox.Location = new System.Drawing.Point(0, 0);
this.XmlTextBox.Name = "XmlTextBox";
this.XmlTextBox.Paddings = new System.Windows.Forms.Padding(0);
this.XmlTextBox.RightBracket = '>';
this.XmlTextBox.RightBracket2 = ')';
this.XmlTextBox.SelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(255)))));
this.XmlTextBox.ServiceColors = ((FastColoredTextBoxNS.ServiceColors)(resources.GetObject("XmlTextBox.ServiceColors")));
this.XmlTextBox.Size = new System.Drawing.Size(751, 442);
this.XmlTextBox.TabIndex = 1;
this.XmlTextBox.Zoom = 100;
this.XmlTextBox.TextChanged += new System.EventHandler<FastColoredTextBoxNS.TextChangedEventArgs>(this.XmlTextBox_TextChanged);
this.XmlTextBox.VisibleRangeChangedDelayed += new System.EventHandler(this.XmlTextBox_VisibleRangeChangedDelayed);
//
// YcdForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(763, 474);
this.Controls.Add(this.splitContainer1);
this.Controls.Add(this.MainTabControl);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "YcdForm";
this.Text = "Clip Dictionary Inspector - CodeWalker by dexyfex";
@ -114,6 +200,10 @@
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.MainTabControl.ResumeLayout(false);
this.DetailsTabPage.ResumeLayout(false);
this.XmlTabPage.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.XmlTextBox)).EndInit();
this.ResumeLayout(false);
}
@ -123,5 +213,9 @@
private System.Windows.Forms.ListView MainListView;
private System.Windows.Forms.ColumnHeader columnHeader1;
private WinForms.ReadOnlyPropertyGrid MainPropertyGrid;
private System.Windows.Forms.TabControl MainTabControl;
private System.Windows.Forms.TabPage DetailsTabPage;
private System.Windows.Forms.TabPage XmlTabPage;
private FastColoredTextBoxNS.FastColoredTextBox XmlTextBox;
}
}

View File

@ -1,4 +1,5 @@
using CodeWalker.GameFiles;
using FastColoredTextBoxNS;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -29,6 +30,8 @@ namespace CodeWalker.Forms
}
public string FilePath { get; set; }
private bool LoadingXml = false;
private bool DelayHighlight = false;
public YcdForm()
@ -43,30 +46,7 @@ namespace CodeWalker.Forms
private void ExportOnim_Click(object sender, EventArgs e)
{
if (MainListView.SelectedItems[0].Tag is Animation anim)
{
var saveFileDialog = new SaveFileDialog();
string newfn = $"{Path.GetFileNameWithoutExtension(Ycd.Name)}_{MainListView.SelectedItems[0].Text}.onim";
saveFileDialog.FileName = newfn;
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
string path = saveFileDialog.FileName;
try
{
using (var file = File.OpenWrite(path))
{
Ycd.SaveOpenFormatsAnimation(anim, file);
}
}
catch (Exception ex)
{
MessageBox.Show("Error saving file " + path + ":\n" + ex.ToString());
}
}
}
}
private void UpdateFormTitle()
@ -74,6 +54,49 @@ namespace CodeWalker.Forms
Text = fileName + " - Clip Dictionary Inspector - CodeWalker by dexyfex";
}
private void UpdateXmlTextBox(string xml)
{
LoadingXml = true;
XmlTextBox.Text = "";
XmlTextBox.Language = Language.XML;
DelayHighlight = false;
if (string.IsNullOrEmpty(xml))
{
LoadingXml = false;
return;
}
//if (xml.Length > (1048576 * 5))
//{
// XmlTextBox.Language = Language.Custom;
// XmlTextBox.Text = "[XML size > 10MB - Not shown due to performance limitations - Please use an external viewer for this file.]";
// return;
//}
//else
if (xml.Length > (1024 * 512))
{
XmlTextBox.Language = Language.Custom;
DelayHighlight = true;
}
//else
//{
// XmlTextBox.Language = Language.XML;
//}
Cursor = Cursors.WaitCursor;
XmlTextBox.Text = xml;
//XmlTextBox.IsChanged = false;
XmlTextBox.ClearUndo();
Cursor = Cursors.Default;
LoadingXml = false;
}
public void LoadYcd(YcdFile ycd)
{
@ -120,8 +143,60 @@ namespace CodeWalker.Forms
}
public void LoadXml()
{
if (Ycd != null)
{
var xml = YcdXml.GetXml(Ycd);
UpdateXmlTextBox(xml);
}
}
private void HTMLSyntaxHighlight(Range range)
{
try
{
Style BlueStyle = new TextStyle(Brushes.Blue, null, FontStyle.Regular);
Style RedStyle = new TextStyle(Brushes.Red, null, FontStyle.Regular);
Style MaroonStyle = new TextStyle(Brushes.Maroon, null, FontStyle.Regular);
//clear style of changed range
range.ClearStyle(BlueStyle, MaroonStyle, RedStyle);
//tag brackets highlighting
range.SetStyle(BlueStyle, @"<|/>|</|>");
//tag name
range.SetStyle(MaroonStyle, @"<(?<range>[!\w]+)");
//end of tag
range.SetStyle(MaroonStyle, @"</(?<range>\w+)>");
//attributes
range.SetStyle(RedStyle, @"(?<range>\S+?)='[^']*'|(?<range>\S+)=""[^""]*""|(?<range>\S+)=\S+");
//attribute values
range.SetStyle(BlueStyle, @"\S+?=(?<range>'[^']*')|\S+=(?<range>""[^""]*"")|\S+=(?<range>\S+)");
}
catch
{ }
}
private void XmlTextBox_VisibleRangeChangedDelayed(object sender, EventArgs e)
{
//this approach is much faster to load, but no outlining is available
//highlight only visible area of text
if (DelayHighlight)
{
HTMLSyntaxHighlight(XmlTextBox.VisibleRange);
}
}
private void XmlTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!LoadingXml)
{
}
}
private void MainListView_SelectedIndexChanged(object sender, EventArgs e)
{
if (MainListView.SelectedItems.Count == 1)
@ -143,5 +218,16 @@ namespace CodeWalker.Forms
//MainPropertyGrid.SelectedObject = null;
}
}
private void MainTabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (MainTabControl.SelectedTab == XmlTabPage)
{
if (string.IsNullOrEmpty(XmlTextBox.Text))
{
LoadXml();
}
}
}
}
}

View File

@ -117,6 +117,24 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="XmlTextBox.ServiceColors" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdGYXN0Q29sb3JlZFRleHRCb3gsIFZlcnNpb249Mi4xNi4yNC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWZiOGFhMTJiOTk0ZWY2MWIMAwAAAFFTeXN0
ZW0uRHJhd2luZywgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2Vu
PWIwM2Y1ZjdmMTFkNTBhM2EFAQAAACJGYXN0Q29sb3JlZFRleHRCb3hOUy5TZXJ2aWNlQ29sb3JzBgAA
ACg8Q29sbGFwc2VNYXJrZXJGb3JlQ29sb3I+a19fQmFja2luZ0ZpZWxkKDxDb2xsYXBzZU1hcmtlckJh
Y2tDb2xvcj5rX19CYWNraW5nRmllbGQqPENvbGxhcHNlTWFya2VyQm9yZGVyQ29sb3I+a19fQmFja2lu
Z0ZpZWxkJjxFeHBhbmRNYXJrZXJGb3JlQ29sb3I+a19fQmFja2luZ0ZpZWxkJjxFeHBhbmRNYXJrZXJC
YWNrQ29sb3I+a19fQmFja2luZ0ZpZWxkKDxFeHBhbmRNYXJrZXJCb3JkZXJDb2xvcj5rX19CYWNraW5n
RmllbGQEBAQEBAQUU3lzdGVtLkRyYXdpbmcuQ29sb3IDAAAAFFN5c3RlbS5EcmF3aW5nLkNvbG9yAwAA
ABRTeXN0ZW0uRHJhd2luZy5Db2xvcgMAAAAUU3lzdGVtLkRyYXdpbmcuQ29sb3IDAAAAFFN5c3RlbS5E
cmF3aW5nLkNvbG9yAwAAABRTeXN0ZW0uRHJhd2luZy5Db2xvcgMAAAACAAAABfz///8UU3lzdGVtLkRy
YXdpbmcuQ29sb3IEAAAABG5hbWUFdmFsdWUKa25vd25Db2xvcgVzdGF0ZQEAAAAJBwcDAAAACgAAAAAA
AAAAlgABAAH7/////P///woAAAAAAAAAAKQAAQAB+v////z///8KAAAAAAAAAACWAAEAAfn////8////
CgAAAAAAAAAATgABAAH4/////P///woAAAAAAAAAAKQAAQAB9/////z///8KAAAAAAAAAACWAAEACw==
</value>
</data>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@ -108,6 +108,7 @@
this.label4 = new System.Windows.Forms.Label();
this.ToolsTabControl = new System.Windows.Forms.TabControl();
this.ToolsPanel = new System.Windows.Forms.Panel();
this.EnableRootMotionCheckBox = new System.Windows.Forms.CheckBox();
this.ConsolePanel.SuspendLayout();
this.ToolsOptionsTabPage.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.TimeOfDayTrackBar)).BeginInit();
@ -656,6 +657,7 @@
//
// ToolsPedTabPage
//
this.ToolsPedTabPage.Controls.Add(this.EnableRootMotionCheckBox);
this.ToolsPedTabPage.Controls.Add(this.label23);
this.ToolsPedTabPage.Controls.Add(this.label22);
this.ToolsPedTabPage.Controls.Add(this.ClipComboBox);
@ -1064,6 +1066,17 @@
this.ToolsPanel.TabIndex = 7;
this.ToolsPanel.Visible = false;
//
// EnableRootMotionCheckBox
//
this.EnableRootMotionCheckBox.AutoSize = true;
this.EnableRootMotionCheckBox.Location = new System.Drawing.Point(54, 503);
this.EnableRootMotionCheckBox.Name = "EnableRootMotionCheckBox";
this.EnableRootMotionCheckBox.Size = new System.Drawing.Size(114, 17);
this.EnableRootMotionCheckBox.TabIndex = 32;
this.EnableRootMotionCheckBox.Text = "Enable root motion";
this.EnableRootMotionCheckBox.UseVisualStyleBackColor = true;
this.EnableRootMotionCheckBox.CheckedChanged += new System.EventHandler(this.EnableRootMotionCheckBox_CheckedChanged);
//
// PedsForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1184,5 +1197,6 @@
private System.Windows.Forms.Label label21;
private System.Windows.Forms.ComboBox ClipDictComboBox;
private System.Windows.Forms.Label label23;
private System.Windows.Forms.CheckBox EnableRootMotionCheckBox;
}
}

View File

@ -92,6 +92,7 @@ namespace CodeWalker.Peds
public ClipMapEntry AnimClip { get; set; } = null;
public Drawable[] Drawables { get; set; } = new Drawable[12];
public Texture[] Textures { get; set; } = new Texture[12];
public bool EnableRootMotion { get; set; } = false; //used to toggle whether or not to include root motion when playing animations
}
PedSelection SelectedPed = new PedSelection();
@ -971,6 +972,80 @@ namespace CodeWalker.Peds
private void LoadClipDict(string name)
{
var ycdhash = JenkHash.GenHash(name.ToLowerInvariant());
var ycd = GameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded))
{
Thread.Sleep(20);//kinda hacky
ycd = GameFileCache.GetYcd(ycdhash);
}
//if (ycd != null)
//{
// ////// TESTING XML CONVERSIONS
// var xml = YcdXml.GetXml(ycd);
// var ycd2 = XmlYcd.GetYcd(xml);
// var data = ycd2.Save();
// var ycd3 = new YcdFile();
// RpfFile.LoadResourceFile(ycd3, data, 46);
// //var xml2 = YcdXml.GetXml(ycd3);
// //if (xml != xml2)
// //{ }
// ycd = ycd3;
//}
SelectedPed.Ycd = ycd;
ClipComboBox.Items.Clear();
ClipComboBox.Items.Add("");
if (ycd?.ClipMapEntries == null)
{
ClipComboBox.SelectedIndex = 0;
SelectedPed.AnimClip = null;
return;
}
List<string> items = new List<string>();
foreach (var cme in ycd.ClipMapEntries)
{
var animclip = cme.Clip as ClipAnimation;
if (animclip != null)
{
items.Add(animclip.ShortName);
continue;
}
var animcliplist = cme.Clip as ClipAnimationList;
if (animcliplist?.Animations?.Data != null)
{
items.Add(animcliplist.ShortName);
continue;
}
}
items.Sort();
foreach (var item in items)
{
ClipComboBox.Items.Add(item);
}
}
private void SelectClip(string name)
{
MetaHash cliphash = JenkHash.GenHash(name);
ClipMapEntry cme = null;
SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
SelectedPed.AnimClip = cme;
}
@ -1193,6 +1268,10 @@ namespace CodeWalker.Peds
var td = SelectedPed.Ytd?.TextureDict;
var ac = SelectedPed.AnimClip;
if (ac != null)
{
ac.EnableRootMotion = SelectedPed.EnableRootMotion;
}
var skel = SelectedPed.Yft?.Fragment?.Drawable?.Skeleton;
if (skel != null)
@ -1788,69 +1867,22 @@ namespace CodeWalker.Peds
private void ClipDictComboBox_TextChanged(object sender, EventArgs e)
{
var ycdhash = JenkHash.GenHash(ClipDictComboBox.Text.ToLowerInvariant());
var ycd = GameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded))
{
Thread.Sleep(20);//kinda hacky
ycd = GameFileCache.GetYcd(ycdhash);
}
SelectedPed.Ycd = ycd;
ClipComboBox.Items.Clear();
ClipComboBox.Items.Add("");
if (ycd?.ClipMapEntries == null)
{
ClipComboBox.SelectedIndex = 0;
SelectedPed.AnimClip = null;
return;
}
List<string> items = new List<string>();
foreach (var cme in ycd.ClipMapEntries)
{
var animclip = cme.Clip as ClipAnimation;
if (animclip != null)
{
items.Add(animclip.ShortName);
continue;
}
var animcliplist = cme.Clip as ClipAnimationList;
if (animcliplist?.Animations?.Data != null)
{
items.Add(animcliplist.ShortName);
continue;
}
}
items.Sort();
foreach (var item in items)
{
ClipComboBox.Items.Add(item);
}
LoadClipDict(ClipDictComboBox.Text);
}
private void ClipComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
var name = ClipComboBox.Text;
MetaHash cliphash = JenkHash.GenHash(name);
ClipMapEntry cme = null;
SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
SelectedPed.AnimClip = cme;
SelectClip(ClipComboBox.Text);
}
private void ClipComboBox_TextChanged(object sender, EventArgs e)
{
ClipComboBox_SelectedIndexChanged(sender, e);
SelectClip(ClipComboBox.Text);
}
private void EnableRootMotionCheckBox_CheckedChanged(object sender, EventArgs e)
{
SelectedPed.EnableRootMotion = EnableRootMotionCheckBox.Checked;
}
}
}

View File

@ -79,6 +79,8 @@ namespace CodeWalker.Rendering
public Matrix3_s[] BoneTransforms;
public List<Bone> Bones;
public bool EnableRootMotion = false; //used to toggle whether or not to include root motion when playing animations
public override void Init(DrawableBase drawable)
{
@ -385,39 +387,39 @@ namespace CodeWalker.Rendering
BoneTransforms[i] = bt;
}
//var drawbl = Key;
//if (AllModels == null) return;
//for (int i = 0; i < AllModels.Length; i++)
//{
// var model = AllModels[i];
// if (model?.Geometries == null) continue;
// for (int g = 0; g < model.Geometries.Length; g++)
// {
// var geom = model.Geometries[g];
// var boneids = geom?.DrawableGeom?.BoneIds;
// if (boneids == null) continue;
// if (boneids.Length != Bones.Count)
// {
// var idc = boneids.Length;
// if (geom.BoneTransforms == null)
// {
// geom.BoneTransforms = new Matrix3_s[idc];
// }
// for (int b = 0; b < idc; b++)
// {
// var id = boneids[b];
// if (id < BoneTransforms.Length)
// {
// geom.BoneTransforms[b] = BoneTransforms[id];
// if (id != b)
// { }
// }
// else
// { }
// }
// }
// }
//}
var drawbl = Key;
if (AllModels == null) return;
for (int i = 0; i < AllModels.Length; i++)
{
var model = AllModels[i];
if (model?.Geometries == null) continue;
for (int g = 0; g < model.Geometries.Length; g++)
{
var geom = model.Geometries[g];
var boneids = geom?.DrawableGeom?.BoneIds;
if (boneids == null) continue;
if (boneids.Length != Bones.Count)
{
var idc = boneids.Length;
if (geom.BoneTransforms == null)
{
geom.BoneTransforms = new Matrix3_s[idc];
}
for (int b = 0; b < idc; b++)
{
var id = boneids[b];
if (id < BoneTransforms.Length)
{
geom.BoneTransforms[b] = BoneTransforms[id];
if (id != b)
{ }
}
else
{ }
}
}
}
}
}
@ -428,6 +430,8 @@ namespace CodeWalker.Rendering
if (CurrentAnimTime == realTime) return;//already updated this!
CurrentAnimTime = realTime;
EnableRootMotion = ClipMapEntry?.EnableRootMotion ?? false;
if (ClipMapEntry != null)
{
UpdateAnim(ClipMapEntry); //animate skeleton/models
@ -451,6 +455,7 @@ namespace CodeWalker.Rendering
}
private void UpdateAnim(ClipMapEntry cme)
{
if (cme.Next != null)
{
UpdateAnim(cme.Next);
@ -545,10 +550,13 @@ namespace CodeWalker.Rendering
case 5://root motion vector
if (bone.Tag != 0)
{ }
//v0 = aseq.EvaluateVector(f0);
//v1 = aseq.EvaluateVector(f1);
//v = interpolate ? (v0 * ialpha) + (v1 * falpha) : v0;
//bone.AnimTranslation += v.XYZ();
if (EnableRootMotion)
{
v0 = aseq.EvaluateVector(f0);
v1 = aseq.EvaluateVector(f1);
v = interpolate ? (v0 * ialpha) + (v1 * falpha) : v0;
bone.AnimTranslation += v.XYZ();
}
break;
case 6://quaternion... root rotation?
if (bone.Tag != 0)
@ -574,6 +582,8 @@ namespace CodeWalker.Rendering
{ }
break;
default:
if (bone.Tag != 0)
{ }
break;
}
}
@ -832,7 +842,7 @@ namespace CodeWalker.Rendering
public bool isHair = false;
public bool disableRendering = false;
//public Matrix3_s[] BoneTransforms = null;
public Matrix3_s[] BoneTransforms = null;
public static ShaderParamNames[] GetTextureSamplerList()
{

View File

@ -801,11 +801,11 @@ namespace CodeWalker.Rendering
}
//if (geom.BoneTransforms != null)
//{
// SetBoneMatrices(context, geom.BoneTransforms);
// defaultBoneMatricesBound = false;
//}
if (geom.BoneTransforms != null)
{
SetBoneMatrices(context, geom.BoneTransforms);
defaultBoneMatricesBound = false;
}
}

View File

@ -358,11 +358,11 @@ namespace CodeWalker.Rendering
}
//if (geom.BoneTransforms != null)
//{
// SetBoneMatrices(context, geom.BoneTransforms);
// defaultBoneMatricesBound = false;
//}
if (geom.BoneTransforms != null)
{
SetBoneMatrices(context, geom.BoneTransforms);
defaultBoneMatricesBound = false;
}
}