Compare commits

...

4 Commits

Author SHA1 Message Date
peng
cf7b652190 MD3: Fix MD3Importer surface header bounds checks to prevent heap overflow (#6441)
Improve bounds checks in MD3Importer::ValidateSurfaceHeaderOffsets to prevent pcSurf from accessing data outside the MD3 buffer (fixes #6070, CVE-2025-3549).

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-01-20 00:29:27 +01:00
tyler92
129c1333e6 MDL/HL1: bounds-checked buffers and safer parsing (#6445) 2026-01-19 21:25:09 +01:00
peng
d1e6bcff6b MDC: Fix MDCImporter surface header bounds and endianness checks (#6440)
- Validate ulOffsetEnd in MDCImporter::ValidateSurfaceHeader to
  prevent pcSurface2 from moving past the MDC buffer(fixes #6167, CVE-2025-5165).
- Apply AI_SWAP4 to ulOffsetShaders before using it in bounds checks.

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-01-15 13:23:54 +01:00
Vitaly Ovchinnikov
d8a9074cd0 OBJ: avoid extra scanning while reading faces in ObjFileParser.cpp (#6281)
* obj: avoid extra scanning while reading faces

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-01-14 16:43:36 +01:00
8 changed files with 316 additions and 108 deletions

View File

@@ -390,13 +390,21 @@ void MD3Importer::ValidateHeaderOffsets() {
void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface *pcSurf) {
// Calculate the relative offset of the surface
const int32_t ofs = int32_t((const unsigned char *)pcSurf - this->mBuffer);
if (ofs + sizeof(MD3::Surface) > fileSize) {
throw DeadlyImportError("Surface header is outside file bounds");
}
auto inRange = [this, ofs](uint32_t rel_offset, uint32_t count, size_t elem_size) -> bool {
size_t abs_offset = ofs + rel_offset;
if (count > 0 && elem_size > 0 && count > SIZE_MAX / elem_size) return false;
size_t total = abs_offset + size_t(count) * elem_size;
return abs_offset <= fileSize && total <= fileSize;
};
// Check whether all data chunks are inside the valid range
if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize ||
pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) {
if (!inRange(pcSurf->OFS_TRIANGLES, pcSurf->NUM_TRIANGLES, sizeof(MD3::Triangle)) ||
!inRange(pcSurf->OFS_SHADERS, pcSurf->NUM_SHADER, sizeof(MD3::Shader)) ||
!inRange(pcSurf->OFS_ST, pcSurf->NUM_VERTICES, sizeof(MD3::TexCoord)) ||
!inRange(pcSurf->OFS_XYZNORMAL, pcSurf->NUM_VERTICES, sizeof(MD3::Vertex))) {
throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
}

View File

@@ -160,6 +160,7 @@ void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface *pcSurf) {
AI_SWAP4(pcSurf->ulOffsetTexCoords);
AI_SWAP4(pcSurf->ulOffsetBaseVerts);
AI_SWAP4(pcSurf->ulOffsetCompVerts);
AI_SWAP4(pcSurf->ulOffsetShaders);
AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames);
AI_SWAP4(pcSurf->ulOffsetFrameCompFrames);
AI_SWAP4(pcSurf->ulOffsetEnd);
@@ -172,7 +173,8 @@ void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface *pcSurf) {
pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax ||
pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax ||
pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax ||
(pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax)) {
(pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax) ||
pcSurf->ulOffsetEnd > iMax) {
throw DeadlyImportError("Some of the offset values in the MDC surface header "
"are invalid and point somewhere behind the file.");
}

View File

@@ -0,0 +1,182 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2026, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file HL1DataBuffer.h
* @brief Declaration of the Half-Life 1 data buffer.
*/
#ifndef AI_HL1DATABUFFER_INCLUDED
#define AI_HL1DATABUFFER_INCLUDED
#include <assimp/Exceptional.h>
#include <assimp/defs.h>
#include <cstddef>
#include <memory>
namespace Assimp {
namespace MDL {
namespace HalfLife {
/**
* \brief Lightweight byte buffer wrapper for HL1 binary parsing.
*
* Acts as either:
* - a non-owning view into external memory, or
* - an owning buffer backed by a unique_ptr.
*
* Copy is disabled to avoid accidental double-ownership; move is supported.
*/
class HL1DataBuffer {
public:
/** \brief Construct an empty buffer (null view). */
HL1DataBuffer() AI_NO_EXCEPT : data_(nullptr),
length_(0),
owner_(nullptr) {}
/** \brief Non-copyable (buffer may own memory). */
HL1DataBuffer(const HL1DataBuffer &) = delete;
/** \brief Non-copyable (buffer may own memory). */
HL1DataBuffer &operator=(const HL1DataBuffer &) = delete;
/** \brief Move-construct, transferring ownership/view state. */
HL1DataBuffer(HL1DataBuffer &&other) AI_NO_EXCEPT : data_(other.data_),
length_(other.length_),
owner_(std::move(other.owner_)) {
other.data_ = nullptr;
other.length_ = 0;
other.owner_ = nullptr;
}
/** \brief Move-assign, transferring ownership/view state. */
HL1DataBuffer &operator=(HL1DataBuffer &&other) AI_NO_EXCEPT {
if (this != &other) {
data_ = other.data_;
length_ = other.length_;
owner_ = std::move(other.owner_);
other.data_ = nullptr;
other.length_ = 0;
other.owner_ = nullptr;
}
return *this;
}
/**
* \brief Create a non-owning view into external bytes.
*
* \param[in] data Pointer to raw bytes (must outlive the view).
* \param[in] length Size in bytes.
*/
static HL1DataBuffer view(const unsigned char *data, size_t length) {
HL1DataBuffer b;
b.data_ = data;
b.length_ = length;
b.owner_ = nullptr;
return b;
}
/**
* \brief Create a non-owning view of another buffer.
*
* \param[in] other Source buffer.
*/
static HL1DataBuffer view(const HL1DataBuffer &other) {
HL1DataBuffer b;
b.data_ = other.data_;
b.length_ = other.length_;
b.owner_ = nullptr;
return b;
}
/**
* \brief Create an owning buffer by taking ownership of allocated storage.
*
* \param[in] buffer Unique buffer storage.
* \param[in] length Size in bytes.
*/
static HL1DataBuffer owning(std::unique_ptr<unsigned char[]> buffer, size_t length) {
HL1DataBuffer b;
b.data_ = buffer.get();
b.length_ = length;
b.owner_ = std::move(buffer);
return b;
}
/**
* \brief Return a typed pointer into the buffer with bounds checks.
*
* \param[in] offset Byte offset from the start of the buffer.
* \param[in] elements Number of elements to access.
* \return Pointer to the requested typed data inside the buffer.
* \throws DeadlyImportError if the request is out of range.
*/
template <typename DataType>
const DataType *get_data(int offset, int elements) const {
if (offset < 0 || elements < 0) {
throw DeadlyImportError("MDL file contains invalid data");
}
const size_t uoffset = static_cast<size_t>(offset);
const size_t uelements = static_cast<size_t>(elements);
if (uoffset > length_ || uelements > (length_ - uoffset) / sizeof(DataType)) {
throw DeadlyImportError("MDL file contains invalid data");
}
return reinterpret_cast<const DataType *>(data_ + uoffset);
}
private:
/** Raw byte pointer (points to owner_.get() when owning, otherwise external). */
const unsigned char *data_;
/** Buffer length in bytes. */
size_t length_;
/** Owning storage (null for views). */
std::unique_ptr<unsigned char[]> owner_;
};
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_HL1DATABUFFER_INCLUDED

View File

@@ -78,18 +78,19 @@ HL1MDLLoader::HL1MDLLoader(
aiScene *scene,
IOSystem *io,
const unsigned char *buffer,
size_t buffer_length,
const std::string &file_path,
const HL1ImportSettings &import_settings) :
scene_(scene),
io_(io),
buffer_(buffer),
buffer_(HL1DataBuffer::view(buffer, buffer_length)),
file_path_(file_path),
import_settings_(import_settings),
header_(nullptr),
texture_header_(nullptr),
anim_headers_(nullptr),
texture_buffer_(nullptr),
anim_buffers_(nullptr),
anim_headers_(),
texture_buffer_(),
anim_buffers_(),
num_sequence_groups_(0),
rootnode_children_(),
unique_name_generator_(),
@@ -108,28 +109,6 @@ HL1MDLLoader::~HL1MDLLoader() {
// ------------------------------------------------------------------------------------------------
void HL1MDLLoader::release_resources() {
if (buffer_ != texture_buffer_) {
delete[] texture_buffer_;
texture_buffer_ = nullptr;
}
if (num_sequence_groups_ && anim_buffers_) {
for (int i = 1; i < num_sequence_groups_; ++i) {
if (anim_buffers_[i]) {
delete[] anim_buffers_[i];
anim_buffers_[i] = nullptr;
}
}
delete[] anim_buffers_;
anim_buffers_ = nullptr;
}
if (anim_headers_) {
delete[] anim_headers_;
anim_headers_ = nullptr;
}
// Root has some children nodes. so let's proceed them
if (!rootnode_children_.empty()) {
// Here, it means that the nodes were not added to the
@@ -147,7 +126,7 @@ void HL1MDLLoader::release_resources() {
// ------------------------------------------------------------------------------------------------
void HL1MDLLoader::load_file() {
try {
header_ = (const Header_HL1 *)buffer_;
header_ = get_buffer_data<Header_HL1>(0, 1);
validate_header(header_, false);
// Create the root scene node.
@@ -286,10 +265,10 @@ void HL1MDLLoader::load_texture_file() {
load_file_into_buffer<Header_HL1>(texture_file_path, texture_buffer_);
} else {
// Model has no external texture file. This means the texture is stored inside the main MDL file.
texture_buffer_ = const_cast<unsigned char *>(buffer_);
texture_buffer_ = HL1DataBuffer::view(buffer_);
}
texture_header_ = (const Header_HL1 *)texture_buffer_;
texture_header_ = get_texture_buffer_data<Header_HL1>(0, 1);
// Validate texture header.
validate_header(texture_header_, true);
@@ -318,12 +297,8 @@ void HL1MDLLoader::load_sequence_groups_files() {
num_sequence_groups_ = header_->numseqgroups;
anim_buffers_ = new unsigned char *[num_sequence_groups_];
anim_headers_ = new SequenceHeader_HL1 *[num_sequence_groups_];
for (int i = 0; i < num_sequence_groups_; ++i) {
anim_buffers_[i] = nullptr;
anim_headers_[i] = nullptr;
}
anim_buffers_.resize(num_sequence_groups_);
anim_headers_.resize(num_sequence_groups_, nullptr);
std::string file_path_without_extension =
DefaultIOSystem::absolutePath(file_path_) +
@@ -340,14 +315,14 @@ void HL1MDLLoader::load_sequence_groups_files() {
load_file_into_buffer<SequenceHeader_HL1>(sequence_file_path, anim_buffers_[i]);
anim_headers_[i] = (SequenceHeader_HL1 *)anim_buffers_[i];
anim_headers_[i] = get_anim_buffer_data<SequenceHeader_HL1>(i, 0, 1);
}
}
// ------------------------------------------------------------------------------------------------
// Read an MDL texture.
void HL1MDLLoader::read_texture(const Texture_HL1 *ptexture,
uint8_t *data, uint8_t *pal, aiTexture *pResult,
const uint8_t *data, const uint8_t *pal, aiTexture *pResult,
aiColor3D &last_palette_color) {
pResult->mFilename = ptexture->name;
pResult->mWidth = static_cast<unsigned int>(ptexture->width);
@@ -381,24 +356,24 @@ void HL1MDLLoader::read_texture(const Texture_HL1 *ptexture,
// ------------------------------------------------------------------------------------------------
void HL1MDLLoader::read_textures() {
const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
unsigned char *pin = texture_buffer_;
scene_->mTextures = new aiTexture *[texture_header_->numtextures];
scene_->mMaterials = new aiMaterial *[texture_header_->numtextures];
scene_->mNumTextures = scene_->mNumMaterials = texture_header_->numtextures;
scene_->mTextures = new aiTexture *[scene_->mNumTextures];
scene_->mMaterials = new aiMaterial *[scene_->mNumMaterials];
const Texture_HL1 *ptexture = get_texture_buffer_data<Texture_HL1>(texture_header_->textureindex, texture_header_->numtextures);
for (int i = 0; i < texture_header_->numtextures; ++i) {
scene_->mTextures[i] = new aiTexture();
++scene_->mNumTextures;
const uint8_t *data = get_texture_buffer_data<uint8_t>(ptexture[i].index, ptexture[i].width * ptexture[i].height);
const uint8_t *pal = get_texture_buffer_data<uint8_t>(ptexture[i].index + ptexture[i].width * ptexture[i].height, 256 * 3);
aiColor3D last_palette_color;
read_texture(&ptexture[i],
pin + ptexture[i].index,
pin + ptexture[i].width * ptexture[i].height + ptexture[i].index,
scene_->mTextures[i],
last_palette_color);
read_texture(&ptexture[i], data, pal, scene_->mTextures[i], last_palette_color);
aiMaterial *scene_material = scene_->mMaterials[i] = new aiMaterial();
aiMaterial *scene_material = new aiMaterial();
scene_->mMaterials[i] = scene_material;
++scene_->mNumMaterials;
const aiTextureType texture_type = aiTextureType_DIFFUSE;
aiString texture_name(ptexture[i].name);
@@ -435,10 +410,14 @@ void HL1MDLLoader::read_skins() {
}
// Pointer to base texture index.
short *default_skin_ptr = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
const short *default_skin_ptr = get_texture_buffer_data<short>(
texture_header_->skinindex,
texture_header_->numskinref);
// Start at first replacement skin.
short *replacement_skin_ptr = default_skin_ptr + texture_header_->numskinref;
const short *replacement_skin_ptr = get_texture_buffer_data<short>(
texture_header_->skinindex + texture_header_->numskinref * sizeof(short),
(texture_header_->numskinfamilies - 1) * texture_header_->numskinref);
for (int i = 1; i < texture_header_->numskinfamilies; ++i, replacement_skin_ptr += texture_header_->numskinref) {
for (int j = 0; j < texture_header_->numskinref; ++j) {
@@ -457,7 +436,7 @@ void HL1MDLLoader::read_bones() {
return;
}
const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
const Bone_HL1 *pbone = get_buffer_data<Bone_HL1>(header_->boneindex, header_->numbones);
std::vector<std::string> unique_bones_names(header_->numbones);
for (int i = 0; i < header_->numbones; ++i) {
@@ -589,12 +568,12 @@ void HL1MDLLoader::read_meshes() {
int total_triangles = 0;
total_models_ = 0;
const Bodypart_HL1 *pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
const Bodypart_HL1 *pbodypart = get_buffer_data<Bodypart_HL1>(header_->bodypartindex, header_->numbodyparts);
const Model_HL1 *pmodel = nullptr;
const Mesh_HL1 *pmesh = nullptr;
const Texture_HL1 *ptexture = (const Texture_HL1 *)((uint8_t *)texture_header_ + texture_header_->textureindex);
short *pskinref = (short *)((uint8_t *)texture_header_ + texture_header_->skinindex);
const Texture_HL1 *ptexture = get_texture_buffer_data<Texture_HL1>(texture_header_->textureindex, texture_header_->numtextures);
const short *pskinref = get_texture_buffer_data<short>(texture_header_->skinindex, texture_header_->numskinref);
scene_->mNumMeshes = 0;
@@ -606,7 +585,7 @@ void HL1MDLLoader::read_meshes() {
for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
unique_bodyparts_names[i] = pbodypart->name;
pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
pmodel = get_buffer_data<Model_HL1>(pbodypart->modelindex, pbodypart->nummodels);
for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel) {
scene_->mNumMeshes += pmodel->nummesh;
total_verts += pmodel->numverts;
@@ -633,7 +612,7 @@ void HL1MDLLoader::read_meshes() {
unique_name_generator_.make_unique(unique_bodyparts_names);
// Now do the same for each model.
pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
pbodypart = get_buffer_data<Bodypart_HL1>(header_->bodypartindex, header_->numbodyparts);
// Prepare template name for bodypart models.
std::vector<std::string> unique_models_names;
@@ -642,7 +621,7 @@ void HL1MDLLoader::read_meshes() {
unsigned int model_index = 0;
for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart) {
pmodel = (Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
pmodel = get_buffer_data<Model_HL1>(pbodypart->modelindex, pbodypart->nummodels);
for (int j = 0; j < pbodypart->nummodels; ++j, ++pmodel, ++model_index)
unique_models_names[model_index] = pmodel->name;
}
@@ -654,7 +633,7 @@ void HL1MDLLoader::read_meshes() {
scene_->mMeshes = new aiMesh *[scene_->mNumMeshes];
pbodypart = (const Bodypart_HL1 *)((uint8_t *)header_ + header_->bodypartindex);
pbodypart = get_buffer_data<Bodypart_HL1>(header_->bodypartindex, header_->numbodyparts);
/* Create a node that will represent the mesh hierarchy.
@@ -738,7 +717,7 @@ void HL1MDLLoader::read_meshes() {
model_index = 0;
for (int i = 0; i < header_->numbodyparts; ++i, ++pbodypart, ++bodyparts_node_ptr) {
pmodel = (const Model_HL1 *)((uint8_t *)header_ + pbodypart->modelindex);
pmodel = get_buffer_data<Model_HL1>(pbodypart->modelindex, pbodypart->nummodels);
// Create bodypart node for the mesh tree hierarchy.
aiNode *bodypart_node = (*bodyparts_node_ptr) = new aiNode(unique_bodyparts_names[i]);
@@ -753,12 +732,12 @@ void HL1MDLLoader::read_meshes() {
for (int j = 0; j < pbodypart->nummodels;
++j, ++pmodel, ++bodypart_models_ptr, ++model_index) {
pmesh = (const Mesh_HL1 *)((uint8_t *)header_ + pmodel->meshindex);
pmesh = get_buffer_data<Mesh_HL1>(pmodel->meshindex, pmodel->nummesh);
uint8_t *pvertbone = ((uint8_t *)header_ + pmodel->vertinfoindex);
uint8_t *pnormbone = ((uint8_t *)header_ + pmodel->norminfoindex);
vec3_t *pstudioverts = (vec3_t *)((uint8_t *)header_ + pmodel->vertindex);
vec3_t *pstudionorms = (vec3_t *)((uint8_t *)header_ + pmodel->normindex);
const uint8_t *pvertbone = get_buffer_data<uint8_t>(pmodel->vertinfoindex, pmodel->numverts);
const uint8_t *pnormbone = get_buffer_data<uint8_t>(pmodel->norminfoindex, pmodel->numnorms);
const vec3_t *pstudioverts = get_buffer_data<vec3_t>(pmodel->vertindex, pmodel->numverts);
const vec3_t *pstudionorms = get_buffer_data<vec3_t>(pmodel->normindex, pmodel->numnorms);
// Each vertex and normal is in local space, so transform
// each of them to bring them in bind pose.
@@ -964,7 +943,7 @@ void HL1MDLLoader::read_animations() {
return;
}
const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
const SequenceDesc_HL1 *pseqdesc = get_buffer_data<SequenceDesc_HL1>(header_->seqindex, header_->numseq);
const SequenceGroup_HL1 *pseqgroup = nullptr;
const AnimValueOffset_HL1 *panim = nullptr;
const AnimValue_HL1 *panimvalue = nullptr;
@@ -990,22 +969,22 @@ void HL1MDLLoader::read_animations() {
// Get the number of available blend controllers for global info.
get_num_blend_controllers(highest_num_blend_animations, num_blend_controllers_);
pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
pseqdesc = get_buffer_data<SequenceDesc_HL1>(header_->seqindex, header_->numseq);
aiAnimation **scene_animations_ptr = scene_->mAnimations = new aiAnimation *[scene_->mNumAnimations];
for (int sequence = 0; sequence < header_->numseq; ++sequence, ++pseqdesc) {
pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex) + pseqdesc->seqgroup;
pseqgroup = get_buffer_data<SequenceGroup_HL1>(header_->seqgroupindex + pseqdesc->seqgroup * sizeof(SequenceGroup_HL1), 1);
if (pseqdesc->seqgroup == 0) {
panim = (const AnimValueOffset_HL1 *)((uint8_t *)header_ + pseqgroup->unused2 + pseqdesc->animindex);
panim = get_buffer_data<AnimValueOffset_HL1>(pseqgroup->unused2 + pseqdesc->animindex, pseqdesc->numblends * header_->numbones);
} else {
panim = (const AnimValueOffset_HL1 *)((uint8_t *)anim_headers_[pseqdesc->seqgroup] + pseqdesc->animindex);
panim = get_anim_buffer_data<AnimValueOffset_HL1>(pseqdesc->seqgroup, pseqdesc->animindex, pseqdesc->numblends * header_->numbones);
}
for (int blend = 0; blend < pseqdesc->numblends; ++blend, ++scene_animations_ptr) {
const Bone_HL1 *pbone = (const Bone_HL1 *)((uint8_t *)header_ + header_->boneindex);
const Bone_HL1 *pbone = get_buffer_data<Bone_HL1>(header_->boneindex, header_->numbones);
aiAnimation *scene_animation = (*scene_animations_ptr) = new aiAnimation();
@@ -1074,7 +1053,7 @@ void HL1MDLLoader::read_sequence_groups_info() {
sequence_groups_node->mNumChildren = static_cast<unsigned int>(header_->numseqgroups);
sequence_groups_node->mChildren = new aiNode *[sequence_groups_node->mNumChildren];
const SequenceGroup_HL1 *pseqgroup = (const SequenceGroup_HL1 *)((uint8_t *)header_ + header_->seqgroupindex);
const SequenceGroup_HL1 *pseqgroup = get_buffer_data<SequenceGroup_HL1>(header_->seqgroupindex, header_->numseqgroups);
unique_sequence_groups_names_.resize(header_->numseqgroups);
for (int i = 0; i < header_->numseqgroups; ++i) {
@@ -1106,7 +1085,7 @@ void HL1MDLLoader::read_sequence_infos() {
return;
}
const SequenceDesc_HL1 *pseqdesc = (const SequenceDesc_HL1 *)((uint8_t *)header_ + header_->seqindex);
const SequenceDesc_HL1 *pseqdesc = get_buffer_data<SequenceDesc_HL1>(header_->seqindex, header_->numseq);
aiNode *sequence_infos_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS);
rootnode_children_.push_back(sequence_infos_node);
@@ -1178,7 +1157,7 @@ void HL1MDLLoader::read_sequence_infos() {
pseqdesc->numevents, "animation events");
}
const AnimEvent_HL1 *pevent = (const AnimEvent_HL1 *)((uint8_t *)header_ + pseqdesc->eventindex);
const AnimEvent_HL1 *pevent = get_buffer_data<AnimEvent_HL1>(pseqdesc->eventindex, pseqdesc->numevents);
aiNode *pEventsNode = new aiNode(AI_MDL_HL1_NODE_ANIMATION_EVENTS);
sequence_info_node_children.push_back(pEventsNode);
@@ -1215,7 +1194,7 @@ void HL1MDLLoader::read_sequence_transitions() {
aiNode *transition_graph_node = new aiNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH);
rootnode_children_.push_back(transition_graph_node);
uint8_t *ptransitions = ((uint8_t *)header_ + header_->transitionindex);
const uint8_t *ptransitions = get_buffer_data<uint8_t>(header_->transitionindex, header_->numtransitions * header_->numtransitions);
aiMetadata *md = transition_graph_node->mMetaData = aiMetadata::Alloc(header_->numtransitions * header_->numtransitions);
for (unsigned int i = 0; i < md->mNumProperties; ++i)
md->Set(i, std::to_string(i), static_cast<int>(ptransitions[i]));
@@ -1226,7 +1205,7 @@ void HL1MDLLoader::read_attachments() {
return;
}
const Attachment_HL1 *pattach = (const Attachment_HL1 *)((uint8_t *)header_ + header_->attachmentindex);
const Attachment_HL1 *pattach = get_buffer_data<Attachment_HL1>(header_->attachmentindex, header_->numattachments);
aiNode *attachments_node = new aiNode(AI_MDL_HL1_NODE_ATTACHMENTS);
rootnode_children_.push_back(attachments_node);
@@ -1250,7 +1229,7 @@ void HL1MDLLoader::read_hitboxes() {
return;
}
const Hitbox_HL1 *phitbox = (const Hitbox_HL1 *)((uint8_t *)header_ + header_->hitboxindex);
const Hitbox_HL1 *phitbox = get_buffer_data<Hitbox_HL1>(header_->hitboxindex, header_->numhitboxes);
aiNode *hitboxes_node = new aiNode(AI_MDL_HL1_NODE_HITBOXES);
rootnode_children_.push_back(hitboxes_node);
@@ -1277,7 +1256,9 @@ void HL1MDLLoader::read_bone_controllers() {
return;
}
const BoneController_HL1 *pbonecontroller = (const BoneController_HL1 *)((uint8_t *)header_ + header_->bonecontrollerindex);
const BoneController_HL1 *pbonecontroller = get_buffer_data<BoneController_HL1>(
header_->bonecontrollerindex,
header_->numbonecontrollers);
aiNode *bones_controller_node = new aiNode(AI_MDL_HL1_NODE_BONE_CONTROLLERS);
rootnode_children_.push_back(bones_controller_node);

View File

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_HL1MDLLOADER_INCLUDED
#include "HL1FileData.h"
#include "HL1DataBuffer.h"
#include "HL1ImportSettings.h"
#include "UniqueNameGenerator.h"
@@ -74,6 +75,7 @@ public:
aiScene *scene,
IOSystem *io,
const unsigned char *buffer,
size_t buffer_length,
const std::string &file_path,
const HL1ImportSettings &import_settings);
@@ -109,10 +111,32 @@ private:
/** \brief Load a file and copy it's content to a buffer.
* \param file_path The path to the file to be loaded.
* \param buffer A pointer to a buffer to receive the data.
* \param buffer A buffer to receive the data.
*/
template <typename MDLFileHeader>
void load_file_into_buffer(const std::string &file_path, unsigned char *&buffer);
void load_file_into_buffer(const std::string &file_path, HL1DataBuffer &buffer);
/** \brief Get a pointer to the specified data type in texture buffer.
* \param offset Offset in bytes.
* \param elements Elements count.
*/
template <typename DataType>
const DataType *get_texture_buffer_data(int offset, int elements);
/** \brief Get a pointer to the specified data type in animation buffer.
* \param animation Animation index.
* \param offset Offset in bytes.
* \param elements Elements count.
*/
template <typename DataType>
const DataType *get_anim_buffer_data(int animation, int offset, int elements);
/** \brief Get a pointer to the specified data type in MDL buffer.
* \param offset Offset in bytes.
* \param elements Elements count.
*/
template <typename DataType>
const DataType *get_buffer_data(int offset, int elements);
/** \brief Read an MDL texture.
* \param[in] ptexture A pointer to an MDL texture.
@@ -122,7 +146,7 @@ private:
* \param[in,out] last_palette_color The last color from the image palette.
*/
void read_texture(const Texture_HL1 *ptexture,
uint8_t *data, uint8_t *pal, aiTexture *pResult,
const uint8_t *data, const uint8_t *pal, aiTexture *pResult,
aiColor3D &last_palette_color);
/** \brief This method reads a compressed anim value.
@@ -158,7 +182,7 @@ private:
IOSystem *io_;
/** Buffer from MDLLoader class. */
const unsigned char *buffer_;
const HL1DataBuffer buffer_;
/** The full file path to the MDL file we are trying to load.
* Used to locate other MDL files since MDL may store resources
@@ -176,13 +200,13 @@ private:
/** External MDL animation headers.
* One for each loaded animation file. */
SequenceHeader_HL1 **anim_headers_;
std::vector<const SequenceHeader_HL1*> anim_headers_;
/** Texture file data. */
unsigned char *texture_buffer_;
HL1DataBuffer texture_buffer_;
/** Animation files data. */
unsigned char **anim_buffers_;
std::vector<HL1DataBuffer> anim_buffers_;
/** The number of sequence groups. */
int num_sequence_groups_;
@@ -226,7 +250,7 @@ private:
// ------------------------------------------------------------------------------------------------
template <typename MDLFileHeader>
void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, HL1DataBuffer &buffer) {
if (!io_->Exists(file_path))
throw DeadlyImportError("Missing file ", DefaultIOSystem::fileName(file_path), ".");
@@ -241,9 +265,30 @@ void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned
throw DeadlyImportError("MDL file is too small.");
}
buffer = new unsigned char[1 + file_size];
file->Read((void *)buffer, 1, file_size);
buffer[file_size] = '\0';
std::unique_ptr<unsigned char[]> data(new unsigned char[1 + file_size]);
file->Read(data.get(), 1, file_size);
data[file_size] = '\0';
buffer = HL1DataBuffer::owning(std::move(data), file_size);
}
template <typename DataType>
const DataType *HL1MDLLoader::get_texture_buffer_data(int offset, int elements) {
return texture_buffer_.get_data<DataType>(offset, elements);
}
template <typename DataType>
const DataType *HL1MDLLoader::get_anim_buffer_data(int animation, int offset, int elements) {
if (animation < 0 || animation >= num_sequence_groups_) {
throw DeadlyImportError("MDL file contains invalid sequence group index (", animation, ")");
}
return anim_buffers_[animation].get_data<DataType>(offset, elements);
}
template <typename DataType>
const DataType *HL1MDLLoader::get_buffer_data(int offset, int elements) {
return buffer_.get_data<DataType>(offset, elements);
}
} // namespace HalfLife

View File

@@ -2001,6 +2001,7 @@ void MDLImporter::InternReadFile_HL1(const std::string &pFile, const uint32_t iM
pScene,
mIOHandler,
mBuffer,
iFileSize,
pFile,
mHL1ImportSettings);
}

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@@ -471,19 +471,7 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
iPos = 0;
} else {
//OBJ USES 1 Base ARRAYS!!!!
int iVal;
auto end = m_DataIt;
// find either the buffer end or the '\0'
while (end < m_DataItEnd && *end != '\0')
++end;
// avoid temporary string allocation if there is a zero
if (end != m_DataItEnd) {
iVal = ::atoi(&(*m_DataIt));
} else {
// otherwise make a zero terminated copy, which is safe to pass to atoi
std::string number(&(*m_DataIt), m_DataItEnd - m_DataIt);
iVal = ::atoi(number.c_str());
}
const int iVal = ::atoi(&(*m_DataIt));
// increment iStep position based off of the sign and # of digits
int tmp = iVal;

View File

@@ -457,6 +457,7 @@ ADD_ASSIMP_IMPORTER( MDL
AssetLib/MDL/MDLLoader.h
AssetLib/MDL/MDLMaterialLoader.cpp
AssetLib/MDL/HalfLife/HalfLifeMDLBaseHeader.h
AssetLib/MDL/HalfLife/HL1DataBuffer.h
AssetLib/MDL/HalfLife/HL1FileData.h
AssetLib/MDL/HalfLife/HL1MDLLoader.cpp
AssetLib/MDL/HalfLife/HL1MDLLoader.h