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

MeltyPlayer / MeltyTool / 21328384721

25 Jan 2026 06:39AM UTC coverage: 42.223% (-0.007%) from 42.23%
21328384721

push

github

MeltyPlayer
Set up a super basic implementation for texture swaps, so it should just work when I set up an importer for it.

6863 of 18404 branches covered (37.29%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 1 file covered. (0.0%)

55 existing lines in 2 files now uncovered.

29404 of 67490 relevant lines covered (43.57%)

64416.38 hits per line

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

62.96
/FinModelUtility/Libraries/Level5/Level5/src/schema/Xi.cs
1
using System.Drawing;
2

3
using FastBitmapLib;
4

5
using fin.image;
6
using fin.image.formats;
7
using fin.image.io;
8
using fin.image.io.tile;
9
using fin.io;
10

11
using level5.decompression;
12

13
using schema.binary;
14

15
using SixLabors.ImageSharp.PixelFormats;
16

17
namespace level5.schema;
18

19
/// <summary>
20
///   Image
21
/// </summary>
22
public sealed class Xi {
23
  public int Width { get; set; }
133,124✔
24
  public int Height { get; set; }
68✔
25

26
  List<int> Tiles { get; set; } = [];
6,148✔
27

28
  public byte ImageFormat { get; set; }
4✔
29

30
  public byte[] ImageData { get; set; }
4✔
31

32
  private bool SwitchFile { get; set; } = false;
2,050✔
33

34
  public void Open(IReadOnlyGenericFile xiFile) {
2✔
35
    using var r = xiFile.OpenReadAsBinary(Endianness.LittleEndian);
2✔
36
    r.Position = 0x10;
2✔
37
    this.Width = r.ReadInt16();
2✔
38
    this.Height = r.ReadInt16();
2✔
39

40
    r.Position = 0xA;
2✔
41
    int type = r.ReadByte();
2✔
42

43
    r.Position = 0x1C;
2✔
44
    int someTable = r.ReadInt16();
2✔
45

46
    r.Position = 0x38;
2✔
47
    int someTableSize = r.ReadInt32();
2✔
48

49
    int imageDataOffset = someTable + someTableSize;
2✔
50

51
    var level5Decompressor = new Level5Decompressor();
2✔
52
    byte[] tileBytes =
2✔
53
        level5Decompressor.Decompress(
2✔
54
            r.SubreadAt((uint) someTable, () => r.ReadBytes(someTableSize)));
4✔
55

56
    if (tileBytes.Length > 2 && tileBytes[0] == 0x53 &&
2!
57
        tileBytes[1] == 0x04)
2✔
UNCOV
58
      this.SwitchFile = true;
×
59

60
    using (var tileData =
2✔
61
           new SchemaBinaryReader(tileBytes, Endianness.LittleEndian)) {
4✔
62
      int tileCount = 0;
2✔
63
      while (tileData.Position + 2 <= tileData.Length) {
4,098✔
64
        int i = this.SwitchFile ? tileData.ReadInt32() : tileData.ReadInt16();
2,048!
65
        if (i > tileCount) tileCount = i;
3,293✔
66
        this.Tiles.Add(i);
2,048✔
67
      }
2,048✔
68
    }
2✔
69

70
    switch (type) {
2!
71
      case 0x1:
72
        type = 0x4;
×
73
        break;
×
74
      case 0x3:
75
        type = 0x1;
×
76
        break;
×
77
      case 0x4:
UNCOV
78
        type = 0x3;
×
UNCOV
79
        break;
×
80
      case 0x1B:
81
        type = 0xC;
2✔
82
        break;
2✔
83
      case 0x1C:
UNCOV
84
        type = 0xD;
×
85
        break;
×
86
      case 0x1D:
87
      case 0x1F:
88
        break;
×
89
      default:
90
        //File.WriteAllBytes("texture.bin", Decompress.Level5Decom(r.GetSection((uint)imageDataOffset, (int)(r.BaseStream.Length - imageDataOffset))));
UNCOV
91
        throw new Exception("Unknown Texture Type " + type.ToString("x"));
×
92
      //break;
93
    }
94

95
    this.ImageFormat = (byte) type;
2✔
96

97
    var len = r.Length;
2✔
98
    this.ImageData = level5Decompressor.Decompress(
2✔
99
        r.SubreadAt((uint) imageDataOffset,
2✔
100
                    () => r.ReadBytes((int) (len - imageDataOffset))));
4✔
101
  }
4✔
102
    
103
  /// <summary>
104
  /// 
105
  /// </summary>
106
  /// <returns></returns>
107
  public unsafe IImage ToBitmap() {
2✔
108
      Bitmap tileSheet;
109

110
      var imageFormat = (_3dsImageTools.TexFormat) this.ImageFormat;
2✔
111
      if (imageFormat is _3dsImageTools.TexFormat.ETC1
2!
112
                         or _3dsImageTools.TexFormat.ETC1a4) {
4✔
113
        tileSheet = TiledImageReader.New(this.Tiles.Count * 8,
2✔
114
                                         8,
2✔
115
                                         new Etc1TileReader(
2✔
116
                                             imageFormat is _3dsImageTools.TexFormat.ETC1a4))
2✔
117
                                    .ReadImage(this.ImageData)
2✔
118
                                    .AsBitmap();
2✔
119
      } else {
2✔
120
        tileSheet = _3dsImageTools.DecodeImage(this.ImageData,
×
121
                                               this.Tiles.Count * 8,
×
UNCOV
122
            8,
×
UNCOV
123
            imageFormat);
×
124
      }
×
125

126
      var pixelFormat = imageFormat switch {
2!
127
          _3dsImageTools.TexFormat.RGBA8    => PixelFormat.RGBA8888,
×
128
          _3dsImageTools.TexFormat.RGB8     => PixelFormat.RGB888,
×
129
          _3dsImageTools.TexFormat.RGBA5551 => PixelFormat.RGBA5551,
×
130
          _3dsImageTools.TexFormat.RGB565   => PixelFormat.RGB565,
×
131
          _3dsImageTools.TexFormat.RGBA4444 => PixelFormat.RGBA4444,
×
132
          _3dsImageTools.TexFormat.LA8      => PixelFormat.LA88,
×
133
          _3dsImageTools.TexFormat.HILO8    => PixelFormat.HILO88,
×
134
          _3dsImageTools.TexFormat.L8       => PixelFormat.L8,
×
135
          _3dsImageTools.TexFormat.A8       => PixelFormat.A8,
×
UNCOV
136
          _3dsImageTools.TexFormat.LA4      => PixelFormat.LA44,
×
137
          _3dsImageTools.TexFormat.L4       => PixelFormat.L4,
×
138
          _3dsImageTools.TexFormat.A4       => PixelFormat.A4,
×
139
          _3dsImageTools.TexFormat.ETC1     => PixelFormat.ETC1,
2✔
UNCOV
140
          _3dsImageTools.TexFormat.ETC1a4   => PixelFormat.ETC1A,
×
UNCOV
141
          _                                 => throw new ArgumentOutOfRangeException()
×
142
      };
2✔
143

144
      var tileSheetWidth = tileSheet.Width;
2✔
145

146
      var img = new Rgba32Image(pixelFormat, this.Width, this.Height);
2✔
147

148
      using var inputBmpData = tileSheet.FastLock();
2✔
149
      var inputPtr = (byte*) inputBmpData.Scan0;
2✔
150

151
      using var dstImgLock = img.Lock();
2✔
152
      var dstPtr = dstImgLock.Pixels;
2✔
153

154
      int y = 0;
2✔
155
      int x = 0;
2✔
156
      for (int i = 0; i < this.Tiles.Count; i++) {
6,144✔
157
        int code = this.Tiles[i];
2,048✔
158

159
        if (code != -1) {
4,096✔
160
          for (int h = 0; h < 8; h++) {
53,248✔
161
            for (int w = 0; w < 8; w++) {
425,984✔
162
              var inputIndex = 4 * ((code * 8 + w) + (h) * tileSheetWidth);
131,072✔
163
              var b = inputPtr[inputIndex];
131,072✔
164
              var g = inputPtr[inputIndex + 1];
131,072✔
165
              var r = inputPtr[inputIndex + 2];
131,072✔
166
              var a = inputPtr[inputIndex + 3];
131,072✔
167

168
              dstPtr[(x + w) * this.Width + y + h] = new Rgba32(r, g, b, a);
131,072✔
169
            }
131,072✔
170
          }
16,384✔
171
        }
2,048✔
172

173
        if (code == -1 && (this.ImageFormat == 0xC || this.ImageFormat == 0xD)) {
2,048!
174
          for (int h = 0; h < 8; h++) {
×
175
            for (int w = 0; w < 8; w++) {
×
176
              dstPtr[(x + w) * this.Width + y + h] = new Rgba32(0, 0, 0, 0);
×
UNCOV
177
            }
×
UNCOV
178
          }
×
UNCOV
179
        }
×
180

181
        y += 8;
2,048✔
182

183
        if (y >= this.Width) {
2,112✔
184
          y = 0;
64✔
185
          x += 8;
64✔
186

187
          // TODO: This skips early, may not use all of the tiles. Is this right?
188
          if (x >= this.Height) {
66✔
189
            break;
2✔
190
          }
191
        }
62✔
192
      }
2,046✔
193

194
      return img;
2✔
195
    }
2✔
196
}
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