• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MeltyPlayer / MeltyTool / 21237956361

22 Jan 2026 06:03AM UTC coverage: 42.256% (+0.003%) from 42.253%
21237956361

push

github

MeltyPlayer
Used Shift-JIS when reading PLvPW names.

6867 of 18394 branches covered (37.33%)

Branch coverage included in aggregate %.

0 of 10 new or added lines in 3 files covered. (0.0%)

1 existing line in 1 file now uncovered.

29415 of 67468 relevant lines covered (43.6%)

64437.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/FinModelUtility/Libraries/Level5/Level5/src/schema/Mtn2.cs
1
using System.Numerics;
2

3
using CommunityToolkit.Diagnostics;
4

5
using fin.data.dictionaries;
6
using fin.io;
7
using fin.math.rotations;
8
using fin.schema;
9
using fin.util.asserts;
10

11
using level5.decompression;
12

13
using schema.binary;
14
using schema.binary.attributes;
15

16

17
namespace level5.schema;
18

19
public sealed class Mtn2 {
20
  public GenericAnimation Anim { get; } = new GenericAnimation();
×
21

22
  public ListDictionary<uint, (short, short)> Somethings { get; } = new();
×
23

24
  public sealed class AnimTrack {
25
    public int Type { get; set; }
×
26
    public int DataType { get; set; }
×
27

28
    [Unknown]
29
    public int Unk { get; set; }
×
30

31
    public int DataCount { get; set; }
×
32
    public int Start { get; set; }
×
33
    public int End { get; set; }
×
34
  }
35

36
  public void Open(IReadOnlyGenericFile mtn2File) {
×
37
    var endianness = Endianness.LittleEndian;
×
38
    using var r = mtn2File.OpenReadAsBinary(endianness);
×
39
    r.Position = 0x08;
×
40
    var decomSize = r.ReadInt32();
×
41
    var nameOffset = r.ReadUInt32();
×
42
    var compDataOffset = r.ReadUInt32();
×
43
    var positionCount = r.ReadInt32();
×
44
    var rotationCount = r.ReadInt32();
×
45
    var scaleCount = r.ReadInt32();
×
46

47
    // This corresponds to PRMs that may be toggled on/off in the animation.
48
    var toggledPrmCount = r.ReadInt32();
×
49
    // This corresponds to bones that will be moved in the animation.
50
    var boneCount = r.ReadInt32();
×
51

52
    r.Position = 0x54;
×
53
    this.Anim.FrameCount = r.ReadInt32();
×
54

55
    r.Position = nameOffset;
×
56
    this.Anim.NameHash = r.ReadUInt32();
×
NEW
57
    this.Anim.Name = r.ReadStringNT(StringEncodingType.SHIFT_JIS);
×
58

59
    var data = new Level5Decompressor().Decompress(
×
60
        r.SubreadAt(compDataOffset,
×
61
                    () => r.ReadBytes(
×
62
                        (int) (r.Length - compDataOffset))));
×
63

64
    using (var d =
×
65
           new SchemaBinaryReader(new MemoryStream(data), endianness)) {
×
66
      // Header
67
      var hashTableOffset = d.ReadUInt32();
×
68
      var trackInfoOffset = d.ReadUInt32();
×
69
      var dataOffset = d.ReadUInt32();
×
70

71
      // Bone Hashes
72
      List<uint> hashes = [];
×
73
      d.Position = (hashTableOffset);
×
74
      while (d.Position < trackInfoOffset) {
×
75
        hashes.Add(d.ReadUInt32());
×
76
      }
×
77
      Asserts.Equal(toggledPrmCount + boneCount, hashes.Count);
×
78

79
      // Track Information
80
      List<AnimTrack> tracks = [];
×
81
      for (int i = 0; i < 4; i++) {
×
82
        d.Position = ((uint) (trackInfoOffset + 2 * i));
×
83
        d.Position = (d.ReadUInt16());
×
84

85
        tracks.Add(new AnimTrack() {
×
86
            Type = d.ReadByte(),
×
87
            DataType = d.ReadByte(),
×
88
            Unk = d.ReadByte(),
×
89
            DataCount = d.ReadByte(),
×
90
            Start = d.ReadUInt16(),
×
91
            End = d.ReadUInt16()
×
92
        });
×
93
      }
×
94

95
      // Data
96

97
      foreach (var v in hashes) {
×
98
        var node = new GenericAnimationTransform();
×
99
        node.Hash = v;
×
100
        node.HashType = AnimNodeHashType.CRC32C;
×
101
        this.Anim.TransformNodes.Add(node);
×
102
      }
×
103

104
      var offset = 0;
×
105
      this.ReadFrameData_(d,
×
106
                          offset,
×
107
                          positionCount,
×
108
                          dataOffset,
×
109
                          boneCount,
×
110
                          tracks[0],
×
111
                          hashes);
×
112
      offset += positionCount;
×
113
      this.ReadFrameData_(d,
×
114
                          offset,
×
115
                          rotationCount,
×
116
                          dataOffset,
×
117
                          boneCount,
×
118
                          tracks[1],
×
119
                          hashes);
×
120
      offset += rotationCount;
×
121
      this.ReadFrameData_(d,
×
122
                          offset,
×
123
                          scaleCount,
×
124
                          dataOffset,
×
125
                          boneCount,
×
126
                          tracks[2],
×
127
                          hashes);
×
128
      offset += scaleCount;
×
129
      this.ReadFrameData_(d,
×
130
                          offset,
×
131
                          toggledPrmCount,
×
132
                          dataOffset,
×
133
                          boneCount,
×
134
                          tracks[3],
×
135
                          hashes);
×
136
    }
×
137
  }
×
138

139
  private void ReadFrameData_(IBinaryReader br,
140
                              int offset,
141
                              int count,
142
                              uint dataOffset,
143
                              int boneCount,
144
                              AnimTrack track,
145
                              IReadOnlyList<uint> hashes) {
×
146
    for (int i = offset; i < offset + count; i++) {
×
147
      br.Position = ((uint) (dataOffset + 4 * 4 * i));
×
148
      var flagOffset = br.ReadUInt32();
×
149
      var keyFrameOffset = br.ReadUInt32();
×
150
      var keyDataOffset = br.ReadUInt32();
×
151
      br.AssertUInt32(0);
×
152

153
      br.Position = (flagOffset);
×
154
      var boneIndex = br.ReadInt16();
×
155
      var keyFrameCount = br.ReadByte();
×
156
      var flag = br.ReadByte();
×
157

158
      var nodeIndex = boneIndex + (flag == 0 ? boneCount : 0);
×
159

160
      var node = this.Anim.TransformNodes[nodeIndex];
×
161

162
      br.Position = (keyDataOffset);
×
163
      for (int k = 0; k < keyFrameCount; k++) {
×
164
        var temp = br.Position;
×
165
        br.Position = ((uint) (keyFrameOffset + k * 2));
×
166
        var frame = br.ReadInt16();
×
167
        br.Position = (temp);
×
168

169
        if (br.Eof) {
×
170
          break;
×
171
        }
172

173
        float[] animdata = new float[track.DataCount];
×
174
        ulong boneVisibilityData = 0;
×
175
        for (int j = 0; j < track.DataCount; j++)
×
176
          switch (track.DataType) {
×
177
            case 1:
178
              animdata[j] = br.ReadInt16() / (float) short.MaxValue;
×
179
              break;
×
180
            case 2:
181
              animdata[j] = br.ReadSingle();
×
182
              break;
×
183
            case 4:
184
              switch (flag) {
×
185
                case 0: {
×
186
                  animdata[j] = br.ReadByte();
×
187
                  break;
×
188
                }
189
                case 0x20: {
×
190
                  boneVisibilityData = br.ReadUInt64();
×
191
                  break;
×
192
                }
193
                default: {
×
194
                  throw new NotImplementedException(
×
195
                      $"Flag with value: {flag.ToHexString()}");
×
196
                }
197
              }
198
              break;
×
199
            default:
200
              throw new NotImplementedException(
×
201
                  "Data Type " + track.DataType + " not implemented");
×
202
          }
203

204
        switch (track.Type) {
×
205
          case 1:
206
            node.AddKey(frame,
×
207
                        animdata[0],
×
208
                        AnimationTrackFormat.TranslateX);
×
209
            node.AddKey(frame,
×
210
                        animdata[1],
×
211
                        AnimationTrackFormat.TranslateY);
×
212
            node.AddKey(frame,
×
213
                        animdata[2],
×
214
                        AnimationTrackFormat.TranslateZ);
×
215
            break;
×
216
          case 2:
217
            // TODO: Invert?
218
            var e = QuaternionUtil.ToEulerRadians(
×
219
                new Quaternion(animdata[0],
×
220
                               animdata[1],
×
221
                               animdata[2],
×
222
                               animdata[3]));
×
223
            node.AddKey(frame, e.X, AnimationTrackFormat.RotateX);
×
224
            node.AddKey(frame, e.Y, AnimationTrackFormat.RotateY);
×
225
            node.AddKey(frame, e.Z, AnimationTrackFormat.RotateZ);
×
226
            break;
×
227
          case 3:
228
            node.AddKey(frame, animdata[0], AnimationTrackFormat.ScaleX);
×
229
            node.AddKey(frame, animdata[1], AnimationTrackFormat.ScaleY);
×
230
            node.AddKey(frame, animdata[2], AnimationTrackFormat.ScaleZ);
×
231
            break;
×
232
          case 9: {
×
233
            switch (flag) {
×
234
              case 0x00: {
×
235
                this.Somethings.Add(node.Hash,
×
236
                                    (frame, (short) Math.Round(animdata[0])));
×
237
                break;
×
238
              }
239
              case 0x20: {
×
240
                for (var b = 0; b < hashes.Count; ++b) {
×
241
                  var isActive = (boneVisibilityData >> b) & 1;
×
242
                  this.Somethings.Add(hashes[boneIndex + b],
×
243
                                      (frame, (short) isActive));
×
244
                }
×
245
                break;
×
246
              }
247
            }
248
            break;
×
249
          }
250
          default:
251
            throw new NotImplementedException(
×
252
                "Track Type " + track.Type + " not implemented");
×
253
        }
254
      }
×
255
    }
×
256
  }
×
257
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc