|
1 | | -using System; |
| 1 | +using CATHODE.Animations; |
| 2 | +using CATHODE.Scripting; |
| 3 | +using CathodeLib; |
| 4 | +using System; |
2 | 5 | using System.Collections.Generic; |
3 | 6 | using System.IO; |
4 | 7 | using System.Linq; |
5 | 8 | using System.Numerics; |
6 | 9 | using System.Runtime.InteropServices; |
7 | 10 | using System.Runtime.InteropServices.ComTypes; |
8 | 11 | using System.Text; |
9 | | -using CATHODE.Scripting; |
10 | | -using CathodeLib; |
| 12 | +using static System.Collections.Specialized.BitVector32; |
11 | 13 |
|
12 | 14 | namespace CATHODE |
13 | 15 | { |
14 | | - /* DATA/GLOBAL/ANIMATION.PAK -> ANIM_CLIP_DB.BIN */ |
| 16 | + /// <summary> |
| 17 | + /// DATA/GLOBAL/ANIMATION.PAK -> x_ANIM_CLIP_DB.BIN |
| 18 | + /// </summary> |
15 | 19 | public class AnimClipDB : CathodeFile |
16 | 20 | { |
17 | | - public Dictionary<uint, string> Entries = new Dictionary<uint, string>(); |
18 | | - public static new Implementation Implementation = Implementation.LOAD; |
| 21 | + public List<AnimClip> Animations = new List<AnimClip>(); |
| 22 | + public List<Tuple<string, string>> BlendSets = new List<Tuple<string, string>>(); |
| 23 | + public List<Context> Contexts = new List<Context>(); |
| 24 | + |
| 25 | + public string Character { get; set; } |
| 26 | + |
| 27 | + public static new Implementation Implementation = Implementation.LOAD | Implementation.CREATE; |
19 | 28 |
|
20 | | - public AnimClipDB(string path) : base(path) { } |
21 | | - public AnimClipDB(MemoryStream stream, string path = "") : base(stream, path) { } |
22 | | - public AnimClipDB(byte[] data, string path = "") : base(data, path) { } |
| 29 | + public AnimClipDB(string path, AnimationStrings strings) : base(path) |
| 30 | + { |
| 31 | + _strings = strings; |
| 32 | + _loaded = Load(); |
| 33 | + } |
| 34 | + public AnimClipDB(MemoryStream stream, AnimationStrings strings, string path) : base(stream, path) |
| 35 | + { |
| 36 | + _strings = strings; |
| 37 | + _loaded = Load(stream); |
| 38 | + } |
| 39 | + public AnimClipDB(byte[] data, AnimationStrings strings, string path) : base(data, path) |
| 40 | + { |
| 41 | + _strings = strings; |
| 42 | + using (MemoryStream stream = new MemoryStream(data)) |
| 43 | + { |
| 44 | + _loaded = Load(stream); |
| 45 | + } |
| 46 | + } |
| 47 | + |
| 48 | + private AnimationStrings _strings; |
23 | 49 |
|
24 | 50 | #region FILE_IO |
25 | 51 | override protected bool LoadInternal(MemoryStream stream) |
26 | 52 | { |
| 53 | + if (_strings == null || _filepath == null || _filepath == "") |
| 54 | + return false; |
| 55 | + |
27 | 56 | using (BinaryReader reader = new BinaryReader(stream)) |
28 | 57 | { |
29 | | - int EntryCount1 = reader.ReadInt32(); |
30 | | - int EntryCount2 = reader.ReadInt32(); |
31 | | - IndexPair[] Entries1 = Utilities.ConsumeArray<IndexPair>(reader, EntryCount1); |
32 | | - IndexPair[] Entries2 = Utilities.ConsumeArray<IndexPair>(reader, EntryCount2); |
33 | | - |
34 | | - int Count0 = reader.ReadInt32(); |
35 | | - int Count1 = reader.ReadInt32(); |
36 | | - IndexPair[] Stuff0 = Utilities.ConsumeArray<IndexPair>(reader, Count0); |
37 | | - OffsetPair[] Stuff1 = Utilities.ConsumeArray<OffsetPair>(reader, Count1); |
| 58 | + reader.BaseStream.Position += 8; |
| 59 | + Character = _strings.GetString(reader.ReadUInt32()); //note - the name of the file is this hashed string, followed by _ANIM_CLIP_DB.BIN |
| 60 | + reader.BaseStream.Position += 4; |
38 | 61 |
|
39 | | - int Count2 = reader.ReadInt32(); |
40 | | - int[] Stuff2 = Utilities.ConsumeArray<int>(reader, Count2); |
| 62 | + //anim_db.cpp line 4482 |
41 | 63 |
|
42 | | - int Count4 = reader.ReadInt32(); |
43 | | - int Count5 = reader.ReadInt32(); |
44 | | - int Count6 = reader.ReadInt32(); |
45 | | - IndexPair[] Stuff5 = Utilities.ConsumeArray<IndexPair>(reader, Count5); |
46 | | - int[] Stuff6 = Utilities.ConsumeArray<int>(reader, Count6); |
| 64 | + Animations = ReadAnimationHashTable(reader); |
47 | 65 |
|
48 | | - int Count7 = reader.ReadInt32(); |
49 | | - int[] Stuff7 = Utilities.ConsumeArray<int>(reader, Count7); |
| 66 | + BlendSets = HashTable.Read(reader, (r, n) => |
| 67 | + new Tuple<string, string>(n, _strings.GetString(r.ReadUInt32())) |
| 68 | + , _strings); |
50 | 69 |
|
51 | | - byte[] HeaderCounts0 = Utilities.ConsumeArray<byte>(reader, 5); |
52 | | - float[] HeaderFloats0 = Utilities.ConsumeArray<float>(reader, 6); // TODO: Is this HKX min/max floats for compression? |
53 | | - int[] HeaderStuff0 = Utilities.ConsumeArray<int>(reader, 4); |
| 70 | + List<Tuple<string, uint>> contextNames = ReadStringHashTable(reader); |
| 71 | + for (int i = 0; i < contextNames.Count; i++) |
| 72 | + { |
| 73 | + Context context = new Context(); |
| 74 | + context.Name = contextNames.FirstOrDefault(o => o.Item2 == i)?.Item1; //sometimes this is null - when it's null, the index of the string seems to be a hash? |
| 75 | + context.Animations = ReadAnimationHashTable(reader); |
| 76 | + context.BlendSets = HashTable.Read(reader, (r, n) => |
| 77 | + new Tuple<string, string>(n, _strings.GetString(r.ReadUInt32())) |
| 78 | + , _strings); |
| 79 | + Contexts.Add(context); |
| 80 | + } |
54 | 81 |
|
55 | | - int[] ContentStuff0 = Utilities.ConsumeArray<int>(reader, HeaderCounts0[1] * 4); |
56 | | - Vector2[] ContentStuff1 = Utilities.ConsumeArray<Vector2>(reader, HeaderCounts0[2]); |
| 82 | + reader.BaseStream.Position += 4; |
| 83 | + return true; |
| 84 | + } |
| 85 | + } |
57 | 86 |
|
58 | | - bone_entry[] BoneEntries = Utilities.ConsumeArray<bone_entry>(reader, HeaderCounts0[3]); |
| 87 | + private List<AnimClip> ReadAnimationHashTable(BinaryReader reader) |
| 88 | + { |
| 89 | + return HashTable.Read(reader, (r, n) => new AnimClip |
| 90 | + { |
| 91 | + Name = n, |
| 92 | + Path = _strings.GetString(r.ReadUInt32()), |
| 93 | + MetadataInstance = r.ReadInt32() //what does this map to |
| 94 | + }, _strings); |
| 95 | + } |
59 | 96 |
|
60 | | - // NOTE: Following content seems to be 4 unknown u8s followed by 4 u8s of which the 0th is ff and 1, 2 and 3 seem to |
61 | | - // sum to 255. I would guess those are bone weights? Bone weights tend to sum to 1. |
62 | | - } |
63 | | - return true; |
| 97 | + private List<Tuple<string, uint>> ReadStringHashTable(BinaryReader reader) |
| 98 | + { |
| 99 | + return HashTable.Read(reader, (r, n) => |
| 100 | + new Tuple<string, uint>(n, r.ReadUInt32()) |
| 101 | + , _strings); |
64 | 102 | } |
65 | 103 |
|
66 | 104 | override protected bool SaveInternal() |
67 | 105 | { |
| 106 | + return false; |
| 107 | + |
| 108 | + /* |
68 | 109 | using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(_filepath))) |
69 | 110 | { |
70 | 111 | writer.BaseStream.SetLength(0); |
| 112 | +
|
| 113 | + writer.Write(new byte[8]); |
| 114 | + writer.Write(_strings.GetID(Character)); |
| 115 | + writer.Write(0); |
| 116 | +
|
| 117 | + WriteHashTable(writer, Animations, (w, item) => { |
| 118 | + w.Write(_strings.GetID(item.PathLabel)); |
| 119 | + w.Write(item.MetadataInstance); |
| 120 | + }); |
| 121 | + |
| 122 | + WriteHashTable(writer, BlendSets, (w, item) => { |
| 123 | + w.Write(_strings.GetID(item)); |
| 124 | + }); |
| 125 | + |
| 126 | + WriteHashTable(writer, PatchNames, (w, item) => { |
| 127 | + w.Write(_strings.GetID(item)); |
| 128 | + }); |
| 129 | + |
| 130 | + foreach (var context in Contexts) |
| 131 | + { |
| 132 | + WriteHashTable(writer, context.Animations, (w, item) => { |
| 133 | + w.Write(_strings.GetID(item.Path)); |
| 134 | + w.Write(item.MetadataInstance); |
| 135 | + }); |
| 136 | + WriteHashTable(writer, context.BlendSets, (w, item) => { |
| 137 | + w.Write(_strings.GetID(item)); |
| 138 | + }); |
| 139 | + } |
| 140 | +
|
| 141 | + // Write termination |
| 142 | + writer.Write(0); |
| 143 | +
|
| 144 | + return true; |
| 145 | + } |
| 146 | + */ |
| 147 | + } |
| 148 | + |
| 149 | + private void WriteHashTable<T>(BinaryWriter writer, List<T> data, Action<BinaryWriter, T> itemWriter) |
| 150 | + { |
| 151 | + writer.Write(data.Count); |
| 152 | + writer.Write(data.Count); |
| 153 | + |
| 154 | + // Write hash table entries |
| 155 | + for (int i = 0; i < data.Count; i++) |
| 156 | + { |
| 157 | + writer.Write(Utilities.AnimationHashedString($"item_{i}")); // Generate hash |
| 158 | + writer.Write(i); |
| 159 | + } |
| 160 | + |
| 161 | + // Write data entries |
| 162 | + foreach (var item in data) |
| 163 | + { |
| 164 | + itemWriter(writer, item); |
71 | 165 | } |
72 | | - return true; |
73 | 166 | } |
74 | 167 | #endregion |
75 | 168 |
|
76 | 169 | #region STRUCTURES |
77 | | - [StructLayout(LayoutKind.Sequential, Pack = 1)] |
78 | | - private struct IndexPair |
| 170 | + public struct AnimClip |
79 | 171 | { |
80 | | - public uint id; |
81 | | - public int index; |
| 172 | + public string Name; |
| 173 | + public string Path; |
| 174 | + public int MetadataInstance; |
82 | 175 | } |
83 | | - [StructLayout(LayoutKind.Sequential, Pack = 1)] |
84 | | - private struct bone_entry |
| 176 | + |
| 177 | + public class Context |
85 | 178 | { |
86 | | - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] |
87 | | - byte[] Joints; |
88 | | - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] |
89 | | - byte[] Weights; |
90 | | - }; |
| 179 | + public string Name; |
| 180 | + public List<AnimClip> Animations = new List<AnimClip>(); |
| 181 | + public List<Tuple<string, string>> BlendSets = new List<Tuple<string, string>>(); |
| 182 | + } |
91 | 183 | #endregion |
92 | 184 | } |
93 | 185 | } |
0 commit comments