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

MeltyPlayer / MeltyTool / 23082132179

14 Mar 2026 06:14AM UTC coverage: 41.449% (-0.01%) from 41.461%
23082132179

push

github

MeltyPlayer
Got Labyrinth Plus model loader kind of working.

7010 of 18991 branches covered (36.91%)

Branch coverage included in aggregate %.

0 of 26 new or added lines in 2 files covered. (0.0%)

29943 of 70161 relevant lines covered (42.68%)

62225.99 hits per line

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

0.0
/FinModelUtility/Games/TheLabyrinthPlusEdition/TheLabyrinthPlusEdition/src/api/ScbModelImporter.cs
1
using System.Numerics;
2

3
using fin.data.dictionaries;
4
using fin.io;
5
using fin.model;
6
using fin.model.impl;
7
using fin.model.io;
8
using fin.model.io.importers;
9
using fin.model.util;
10
using fin.util.sets;
11
using fin.util.strings;
12

13
using schema.text.reader;
14

15
using tlpe.scb;
16

17
namespace tlpe.api;
18

19
public sealed record ScbModelFileBundle(
×
20
    IReadOnlyTreeFile ScbFile,
×
21
    IReadOnlyTreeFile BallsFile,
×
22
    IReadOnlyTreeDirectory TexturesDir)
×
23
    : IModelFileBundle {
24
  public IReadOnlyTreeFile MainFile => this.ScbFile;
×
25
}
26

27
public sealed class ScbModelImporter : IModelImporter<ScbModelFileBundle> {
28
  public IModel Import(ScbModelFileBundle fileBundle) {
×
29
    var scb = fileBundle.ScbFile.ReadNew<Scb>();
×
30

31
    var files = fileBundle.ScbFile.AsFileSet();
×
32
    var finModel = new ModelImpl {
×
33
        FileBundle = fileBundle,
×
34
        Files = files
×
35
    };
×
36

37
    var allBallAttributes = ReadAllBallAttributes_(fileBundle.BallsFile);
×
38
    var ballAttributesByGeometry
×
39
        = allBallAttributes
×
40
          .Where(a => a.Geometry != null)
×
41
          .ToListDictionary(a => a.Geometry!.ToLower());
×
42

43
    var textureFiles = new List<IReadOnlyTreeFile>();
×
44
    if (ballAttributesByGeometry.TryGetList(
×
45
            fileBundle.ScbFile.Name.ToString().ToLower(),
×
46
            out var matchingBallAttributes)) {
×
47
      foreach (var ballAttributes in matchingBallAttributes) {
×
48
        if (ballAttributes.Texture != null) {
×
49
          var textureFile = fileBundle.TexturesDir.AssertGetExistingFile(
×
50
              ballAttributes.Texture);
×
51
          textureFiles.Add(textureFile);
×
52
        }
×
53
      }
×
54
    }
×
55

NEW
56
    foreach (var scbSection in scb.Sections) {
×
NEW
57
      if (scbSection is Section6 section6) {
×
NEW
58
        var textureName = section6.Names.Name1;
×
NEW
59
        if (textureName.Length > 0) {
×
NEW
60
          var textureFile = fileBundle.ScbFile.AssertGetParent()
×
NEW
61
                                      .AssertGetExistingFile(textureName);
×
NEW
62
          textureFiles.Add(textureFile);
×
NEW
63
        }
×
NEW
64
      }
×
NEW
65
    }
×
66

67
    IMaterial[] textureMaterials
×
68
        = textureFiles
×
69
          .Select(textureFile => {
×
70
            (var finMaterial, _)
×
71
                = finModel.MaterialManager.AddSimpleTextureMaterialFromFile(
×
72
                    textureFile);
×
73
            return finMaterial;
×
74
          })
×
75
          .ToArray();
×
76

77
    var finMaterial = textureMaterials.Length > 0
×
78
        ? textureMaterials[0]
×
79
        : finModel.MaterialManager.AddNullMaterial();
×
80

81
    var finSkin = finModel.Skin;
×
82

83
    foreach (var scbSection in scb.Sections) {
×
84
      switch (scbSection) {
×
85
        case MeshSection meshSection: {
×
86
          var finMesh = finSkin.AddMesh();
×
87

NEW
88
          var scbVertices = meshSection.Vertices;
×
NEW
89
          var finVertices = new IReadOnlyVertex[scbVertices.Length];
×
NEW
90
          for (var i = 0; i < scbVertices.Length; ++i) {
×
NEW
91
            var scbVertex = scbVertices[i];
×
92

NEW
93
            var finVertex = finSkin.AddVertex(AdjustVector3_(scbVertex.Position));
×
NEW
94
            finVertex.SetLocalNormal(AdjustVector3_(scbVertex.Normal));
×
NEW
95
            finVertex.SetUv(0, scbVertex.Uv0);
×
NEW
96
            finVertex.SetUv(1, scbVertex.Uv1);
×
97

NEW
98
            finVertices[scbVertices.Length - 1 - i] = finVertex;
×
NEW
99
          }
×
100

101
          var triangles
×
102
              = meshSection
×
103
                .Faces.Select(f => {
×
NEW
104
                  var v0 = finVertices[f.Vertex0];
×
NEW
105
                  var v1 = finVertices[f.Vertex1];
×
NEW
106
                  var v2 = finVertices[f.Vertex2];
×
107

×
108
                  return (v0, v1, v2);
×
109
                })
×
110
                .ToArray();
×
111

112
          var finPrimitive = finMesh.AddTriangles(triangles);
×
113
          finPrimitive.SetMaterial(finMaterial);
×
114

115
          break;
×
116
        }
117
      }
118
    }
×
119

120
    return finModel;
×
121
  }
×
122

123
  private class BallAttributes {
124
    public string Id { get; set; }
×
125
    public string? Geometry { get; set; }
×
126
    public string? Texture { get; set; }
×
127
    public bool MapFromFile { get; set; }
×
128
    public bool NoEnvMap { get; set; }
×
129
  }
130

131
  private static Vector3 AdjustVector3_(Vector3 input)
NEW
132
    => new(input.X, input.Z, input.Y);
×
133

134
  private static IList<BallAttributes> ReadAllBallAttributes_(
135
      IReadOnlyTreeFile file) {
×
136
    using var tr = new SchemaTextReader(file.OpenRead());
×
137

138
    var allAttributes = new List<BallAttributes>();
×
139

140
    BallAttributes currentAttributes = default!;
×
141
    while (!tr.Eof) {
×
142
      var line = tr.ReadLine().Trim();
×
143

144
      if (line.StartsWith('[')) {
×
145
        var id = line[1..].SubstringUpTo(']');
×
146
        currentAttributes = new BallAttributes { Id = id };
×
147
        allAttributes.Add(currentAttributes);
×
148
      } else {
×
149
        var equalsParts = line.Split('=');
×
150
        if (equalsParts.Length > 1) {
×
151
          var before = equalsParts[0].Trim();
×
152
          var after = equalsParts[1].Trim();
×
153

154
          switch (before) {
×
155
            case "Geometry": {
×
156
              currentAttributes.Geometry = after;
×
157
              break;
×
158
            }
159
            case "MapFromFile": {
×
160
              currentAttributes.MapFromFile = after == "1";
×
161
              break;
×
162
            }
163
            case "NoEnvMap": {
×
164
              currentAttributes.NoEnvMap = after == "1";
×
165
              break;
×
166
            }
167
            case "Texture": {
×
168
              currentAttributes.Texture = after;
×
169
              break;
×
170
            }
171
          }
172
        }
×
173
      }
×
174
    }
×
175

176
    return allAttributes;
×
177
  }
×
178
}
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