Coverage Report

Created: 2026-05-04 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/MDL/HalfLife/HL1MDLLoader.h
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
All rights reserved.
9
10
Redistribution and use of this software in source and binary forms,
11
with or without modification, are permitted provided that the following
12
conditions are met:
13
14
* Redistributions of source code must retain the above
15
  copyright notice, this list of conditions and the
16
  following disclaimer.
17
18
* Redistributions in binary form must reproduce the above
19
  copyright notice, this list of conditions and the
20
  following disclaimer in the documentation and/or other
21
  materials provided with the distribution.
22
23
* Neither the name of the assimp team, nor the names of its
24
  contributors may be used to endorse or promote products
25
  derived from this software without specific prior
26
  written permission of the assimp team.
27
28
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
---------------------------------------------------------------------------
40
*/
41
42
/** @file HL1MDLLoader.h
43
 *  @brief Declaration of the Half-Life 1 MDL loader.
44
 */
45
46
#ifndef AI_HL1MDLLOADER_INCLUDED
47
#define AI_HL1MDLLOADER_INCLUDED
48
49
#include "HL1FileData.h"
50
#include "HL1DataBuffer.h"
51
#include "HL1ImportSettings.h"
52
#include "UniqueNameGenerator.h"
53
54
#include <memory>
55
#include <string>
56
57
#include <assimp/types.h>
58
#include <assimp/scene.h>
59
#include <assimp/texture.h>
60
#include <assimp/IOSystem.hpp>
61
#include <assimp/DefaultIOSystem.h>
62
#include <assimp/Exceptional.h>
63
64
namespace Assimp {
65
namespace MDL {
66
namespace HalfLife {
67
68
class HL1MDLLoader {
69
public:
70
    HL1MDLLoader() = delete;
71
    HL1MDLLoader(const HL1MDLLoader &) = delete;
72
73
    /** See variables descriptions at the end for more details. */
74
    HL1MDLLoader(
75
        aiScene *scene,
76
        IOSystem *io,
77
        const unsigned char *buffer,
78
        size_t buffer_length,
79
        const std::string &file_path,
80
        const HL1ImportSettings &import_settings);
81
82
    ~HL1MDLLoader();
83
84
    void load_file();
85
86
protected:
87
    /** \brief Validate the header data structure of a Half-Life 1 MDL file.
88
     * \param[in] header Input header to be validated.
89
     * \param[in] is_texture_header Whether or not we are reading an MDL
90
     *   texture file.
91
     */
92
    void validate_header(const Header_HL1 *header, bool is_texture_header);
93
94
    void load_texture_file();
95
    void load_sequence_groups_files();
96
    void read_textures();
97
    void read_skins();
98
    void read_bones();
99
    void read_meshes();
100
    void read_animations();
101
    void read_sequence_groups_info();
102
    void read_sequence_infos();
103
    void read_sequence_transitions();
104
    void read_attachments();
105
    void read_hitboxes();
106
    void read_bone_controllers();
107
    void read_global_info();
108
109
private:
110
    void release_resources();
111
112
    /** \brief Load a file and copy it's content to a buffer.
113
     * \param file_path The path to the file to be loaded.
114
     * \param buffer A buffer to receive the data.
115
     */
116
    template <typename MDLFileHeader>
117
    void load_file_into_buffer(const std::string &file_path, HL1DataBuffer &buffer);
118
119
    /** \brief Get a pointer to the specified data type in texture buffer.
120
     * \param offset Offset in bytes.
121
     * \param elements Elements count.
122
     */
123
    template <typename DataType>
124
    const DataType *get_texture_buffer_data(int offset, int elements);
125
126
    /** \brief Get a pointer to the specified data type in animation buffer.
127
     * \param animation Animation index.
128
     * \param offset Offset in bytes.
129
     * \param elements Elements count.
130
     */
131
    template <typename DataType>
132
    const DataType *get_anim_buffer_data(int animation, int offset, int elements);
133
134
    /** \brief Get a pointer to the specified data type in MDL buffer.
135
     * \param offset Offset in bytes.
136
     * \param elements Elements count.
137
     */
138
    template <typename DataType>
139
    const DataType *get_buffer_data(int offset, int elements);
140
141
    /** \brief Read an MDL texture.
142
     * \param[in] ptexture A pointer to an MDL texture.
143
     * \param[in] data A pointer to the data from \p ptexture.
144
     * \param[in] pal A pointer to the texture palette from \p ptexture.
145
     * \param[in,out] pResult A pointer to the output resulting Assimp texture.
146
     * \param[in,out] last_palette_color The last color from the image palette.
147
     */
148
    void read_texture(const Texture_HL1 *ptexture,
149
            const uint8_t *data, const uint8_t *pal, aiTexture *pResult,
150
            aiColor3D &last_palette_color);
151
152
    /** \brief This method reads a compressed anim value.
153
    * \param[in] panimvalue A pointer to the animation data.
154
    * \param[in] frame The frame to look for.
155
    * \param[in] bone_scale The current bone scale to apply to the compressed value.
156
    * \param[in,out] value The decompressed anim value at \p frame.
157
    */
158
    void extract_anim_value(const AnimValue_HL1 *panimvalue,
159
            int frame, float bone_scale, ai_real &value);
160
161
    /**
162
     *  \brief Given the number of blend animations, determine the number of blend controllers.
163
     *
164
     * \param[in] num_blend_animations The number of blend animations.
165
     * \param[out] num_blend_controllers The number of blend controllers.
166
     * \return True if the number of blend controllers was determined. False otherwise.
167
     */
168
    static bool get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers);
169
170
    /**
171
     *  \brief Build a bone's node children hierarchy.
172
     *
173
     * \param[in] bone The bone for which we must build all children hierarchy.
174
     */
175
    struct TempBone;
176
    void build_bone_children_hierarchy(const TempBone& bone);
177
178
    /** Output scene to be filled */
179
    aiScene *scene_;
180
181
    /** Output I/O handler. Required for additional IO operations. */
182
    IOSystem *io_;
183
184
    /** Buffer from MDLLoader class. */
185
    const HL1DataBuffer buffer_;
186
187
    /** The full file path to the MDL file we are trying to load.
188
     * Used to locate other MDL files since MDL may store resources
189
     * in external MDL files. */
190
    const std::string &file_path_;
191
192
    /** Configuration for HL1 MDL */
193
    const HL1ImportSettings &import_settings_;
194
195
    /** Main MDL header. */
196
    const Header_HL1 *header_;
197
198
    /** External MDL texture header. */
199
    const Header_HL1 *texture_header_;
200
201
    /** External MDL animation headers.
202
     * One for each loaded animation file. */
203
    std::vector<const SequenceHeader_HL1*> anim_headers_;
204
205
    /** Texture file data. */
206
    HL1DataBuffer texture_buffer_;
207
208
    /** Animation files data. */
209
    std::vector<HL1DataBuffer> anim_buffers_;
210
211
    /** The number of sequence groups. */
212
    int num_sequence_groups_;
213
214
    /** The list of children to be appended to the scene's root node. */
215
    std::vector<aiNode *> rootnode_children_;
216
217
    /** A unique name generator. Used to generate names for MDL values
218
     * that may have empty/duplicate names. */
219
    UniqueNameGenerator unique_name_generator_;
220
221
    /** The list of unique sequence names. */
222
    std::vector<std::string> unique_sequence_names_;
223
224
    /** The list of unique sequence groups names. */
225
    std::vector<std::string> unique_sequence_groups_names_;
226
227
    /** Structure to store temporary bone information. */
228
    struct TempBone {
229
230
        TempBone() :
231
0
            node(nullptr),
232
0
            absolute_transform(),
233
0
            offset_matrix(),
234
0
            children() {}
235
236
        aiNode *node;
237
        aiMatrix4x4 absolute_transform;
238
        aiMatrix4x4 offset_matrix;
239
        std::vector<int> children; // Bone children
240
    };
241
242
    std::vector<TempBone> temp_bones_;
243
244
    /** The number of available bone controllers in the model. */
245
    int num_blend_controllers_;
246
247
    /** Self explanatory. */
248
    int total_models_;
249
};
250
251
// ------------------------------------------------------------------------------------------------
252
template <typename MDLFileHeader>
253
0
void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, HL1DataBuffer &buffer) {
254
0
    if (!io_->Exists(file_path))
255
0
        throw DeadlyImportError("Missing file ", DefaultIOSystem::fileName(file_path), ".");
256
257
0
    std::unique_ptr<IOStream> file(io_->Open(file_path));
258
259
0
    if (file == nullptr) {
260
0
        throw DeadlyImportError("Failed to open MDL file ", DefaultIOSystem::fileName(file_path), ".");
261
0
    }
262
263
0
    const size_t file_size = file->FileSize();
264
0
    if (file_size < sizeof(MDLFileHeader)) {
265
0
        throw DeadlyImportError("MDL file is too small.");
266
0
    }
267
268
0
    std::unique_ptr<unsigned char[]> data(new unsigned char[1 + file_size]);
269
0
    file->Read(data.get(), 1, file_size);
270
0
    data[file_size] = '\0';
271
272
0
    buffer = HL1DataBuffer::owning(std::move(data), file_size);
273
0
}
Unexecuted instantiation: void Assimp::MDL::HalfLife::HL1MDLLoader::load_file_into_buffer<Assimp::MDL::HalfLife::Header_HL1>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Assimp::MDL::HalfLife::HL1DataBuffer&)
Unexecuted instantiation: void Assimp::MDL::HalfLife::HL1MDLLoader::load_file_into_buffer<Assimp::MDL::HalfLife::SequenceHeader_HL1>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, Assimp::MDL::HalfLife::HL1DataBuffer&)
274
275
template <typename DataType>
276
0
const DataType *HL1MDLLoader::get_texture_buffer_data(int offset, int elements) {
277
0
    return texture_buffer_.get_data<DataType>(offset, elements);
278
0
}
Unexecuted instantiation: Assimp::MDL::HalfLife::Header_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_texture_buffer_data<Assimp::MDL::HalfLife::Header_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Texture_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_texture_buffer_data<Assimp::MDL::HalfLife::Texture_HL1>(int, int)
Unexecuted instantiation: unsigned char const* Assimp::MDL::HalfLife::HL1MDLLoader::get_texture_buffer_data<unsigned char>(int, int)
Unexecuted instantiation: short const* Assimp::MDL::HalfLife::HL1MDLLoader::get_texture_buffer_data<short>(int, int)
279
280
template <typename DataType>
281
0
const DataType *HL1MDLLoader::get_anim_buffer_data(int animation, int offset, int elements) {
282
0
    if (animation < 0 || animation >= num_sequence_groups_) {
283
0
        throw DeadlyImportError("MDL file contains invalid sequence group index (", animation, ")");
284
0
    }
285
286
0
    return anim_buffers_[animation].get_data<DataType>(offset, elements);
287
0
}
Unexecuted instantiation: Assimp::MDL::HalfLife::SequenceHeader_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_anim_buffer_data<Assimp::MDL::HalfLife::SequenceHeader_HL1>(int, int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::AnimValueOffset_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_anim_buffer_data<Assimp::MDL::HalfLife::AnimValueOffset_HL1>(int, int, int)
288
289
template <typename DataType>
290
0
const DataType *HL1MDLLoader::get_buffer_data(int offset, int elements) {
291
0
    return buffer_.get_data<DataType>(offset, elements);
292
0
}
Unexecuted instantiation: Assimp::MDL::HalfLife::Header_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Header_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Bone_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Bone_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Bodypart_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Bodypart_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Model_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Model_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Mesh_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Mesh_HL1>(int, int)
Unexecuted instantiation: unsigned char const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<unsigned char>(int, int)
Unexecuted instantiation: float const (*Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<float [3]>(int, int)) [3]
Unexecuted instantiation: Assimp::MDL::HalfLife::SequenceDesc_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::SequenceDesc_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::SequenceGroup_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::SequenceGroup_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::AnimValueOffset_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::AnimValueOffset_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::AnimEvent_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::AnimEvent_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Attachment_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Attachment_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::Hitbox_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::Hitbox_HL1>(int, int)
Unexecuted instantiation: Assimp::MDL::HalfLife::BoneController_HL1 const* Assimp::MDL::HalfLife::HL1MDLLoader::get_buffer_data<Assimp::MDL::HalfLife::BoneController_HL1>(int, int)
293
294
} // namespace HalfLife
295
} // namespace MDL
296
} // namespace Assimp
297
298
#endif // AI_HL1MDLLOADER_INCLUDED