Compare commits

...

17 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
tyler92
1ce99b9cf7 MD5: Fix uninitialized pointer dereference for file with invalid vertex index (#6439)
A segmentation fault occurred while parsing an MD5 file that contains an invalid vertex index.
The issue was caused by mScene->mMaterials not being kept in sync with mScene->mNumMaterials.
As a result, the aiScene destructor could call delete on uninitialized pointers. This patch
ensures that mScene->mNumMaterials always matches the actual contents of the mScene->mMaterials
array. That way, if an exception is thrown during file import, delete is only called for
properly allocated aiMaterial objects.
2026-01-12 15:08:31 +01:00
LP
522c703bb9 fuzz: Fix memory leak in ForceFormat helper (#6435)
The ForceFormat function unregisters importers from the Importer
but doesn't delete them, causing memory leaks detected by ASan
during OSS-Fuzz check_build.

When UnregisterLoader is called, the importer is removed from the
internal list but the memory is not freed. Since the Importer
originally allocated these objects and we're removing them from
its management, we must delete them explicitly.

Also include BaseImporter.h to ensure complete type information
is available for proper deletion.

This fixes OSS-Fuzz check_build failures for all format-specific
fuzzers (obj, gltf, glb, fbx, collada, stl).
2026-01-09 11:02:21 +01:00
peng
17318b02cf MDC: Fix heap OOB read by validating vertex buffer boundaries (#6168) (#6438)
- Add explicit boundary checks for pcVerts and pcCVerts arrays in MDCImporter.
- Prevents heap out-of-bounds reads with malformed or truncated files.
- Fixes CVE-2025-5166.

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
2026-01-09 08:59:22 +01:00
peng
ac8eac60a5 MDL: Fix synctype validation in MDLImporter to prevent OOB (#6437)
* MDL: Validate synctype > 0 in MDL 3/4/5 loader to prevent OOB access (#6170)

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2026-01-07 10:50:12 +01:00
Kim Kulling
c234aa28c6 Add CHANGES.md file (#6436) 2026-01-03 22:32:33 +01:00
LP
ae6f477604 Fuzzing: Add OSS-Fuzz integration and multiple format-specific fuzzers (#6429)
* fuzz: Add OSS-Fuzz integration and multiple format-specific fuzzers

* fuzz: Add GLB fuzzer and fix null pointer issue

- Add null check for mFileExtensions in ForceFormat() to prevent
  undefined behavior when the field is null
- Fix misleading comment in gltf fuzzer (only covers text format)
- Add separate GLB fuzzer for binary glTF format coverage
- Update OSS-Fuzz build script to include GLB fuzzer with corpus

* Add copyright and licensing information

Added licensing information and copyright notice to fuzzer_common.h

---------

Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2025-12-23 19:31:27 +01:00
hankarun
0fb7c4a439 Feature: Add EXT_texture_webp extension to glb importer (#6431)
* Add EXT_texture_webp extension texture read for glb file format.
2025-12-22 12:44:18 +01:00
ljgdsq
8c0ab23470 Fix UTF-8 flag check in CMakeLists.txt (#6432) 2025-12-21 18:19:10 +01:00
Kim Kulling
f4980c455c Test against possible nullptr dereferencing (#6430)
Co-authored-by: Kim Kulling <kim.kulling@draeger.com>
2025-12-18 11:26:13 +01:00
dependabot[bot]
c58496185d Bump actions/cache from 4 to 5 (#6425)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2025-12-16 12:56:08 +01:00
dependabot[bot]
a438bbb271 Bump actions/upload-artifact from 5 to 6 (#6426)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kim Kulling <kimkulling@users.noreply.github.com>
2025-12-16 11:43:03 +01:00
dependabot[bot]
0373bbede8 Bump actions/download-artifact from 6 to 7 (#6427)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 11:22:14 +01:00
Kim Kulling
c0b8cfaf41 Add closing brace to CMakePresets.json (#6424)
* Add closing braces to CMakePresets.json
2025-12-14 10:38:31 +01:00
30 changed files with 5029 additions and 788 deletions

View File

@@ -65,7 +65,7 @@ jobs:
- name: Cache DX SDK
id: dxcache
if: contains(matrix.name, 'windows')
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: '${{ github.workspace }}/DX_SDK'
key: ${{ runner.os }}-DX_SDK
@@ -109,7 +109,7 @@ jobs:
run: cd build/bin && ./unit ${{ steps.hunter_extra_test_args.outputs.args }}
shell: bash
- uses: actions/upload-artifact@v5
- uses: actions/upload-artifact@v6
if: matrix.name == 'windows-msvc'
with:
name: 'assimp-bins-${{ matrix.name }}'
@@ -141,7 +141,7 @@ jobs:
prerelease: true
- run: |
echo '${{steps.create-release.outputs.upload_url}}' > release_upload_url.txt
- uses: actions/upload-artifact@v5
- uses: actions/upload-artifact@v6
with:
name: create-release
path: release_upload_url.txt
@@ -188,7 +188,7 @@ jobs:
- id: upload-url
run: |
echo "url=$(cat create-release/release_upload_url.txt)" >> $GITHUB_OUTPUT
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v7
with:
name: 'assimp-bins-${{ matrix.name }}-${{ github.sha }}'
- uses: actions/upload-release-asset@v1

View File

@@ -19,7 +19,7 @@ jobs:
dry-run: false
language: c++
- name: Upload Crash
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts

607
CHANGES
View File

@@ -1,607 +0,0 @@
----------------------------------------------------------------------
CHANGELOG
----------------------------------------------------------------------
4.1.0 (2017-12):
- FEATURES:
- Export 3MF ( experimental )
- Import / Export glTF 2
- Introduce new zib-lib to eb able to export zip-archives
- FIXES/HOUSEKEEPING:
- Added missing include to stdlib.h and remove load library call
- Fix install for builds with MSVC compiler and NMake.
- Update list of supported file formats.
- Add TriLib to the official list of supported ports.
- Re-enabling PACK_STRUCT for MDL files.
- Use std.::unique_ptr
- Update D3MFExporter.h
- Update MD3Loader.cpp, using index
- Fix all warnings on MSVC14
- Copy assimp dll to unit folder on windows
- Update jvm port supported formats
- Add support for building Mac OS X Framework bundles
- Check for nullptr dereferencing before copying scene data
- Update ValidateDataStructure.h, typo
- Enable data structure validation in cases where it doesn't cause failures
- Remove some dead assignments
- fast_atof: Silence some uninitialized variable warnings
- Check for area test if the face is a triangle.
- Set mNumUVComponents to 0 when deleting texture coordinate sets
- Only scale the root node because this will rescale all children nodes as well.
- Issue 1514: Fix frame pointer arithmetic
- Prevent failing stringstream to crash the export process
- powf -> pow
- add Defines.h to include folder for install.
- Android:
- Fix android build
- Fix assimp for cross compile for android
- Use define for D_FILE_OFFSET_BITS only for not-android systems.
- FBX:
- Fix handling with embedded textures
- FBX 7500 Binary reading
- Remove dead assignment
- Fix export of deleted meshes; Add LazyDict::Remove method
- Log an error instead of letting the fbx-importer crash. ( issue 213 )
- Replace bad pointer casting with memcpy
- Remove useless const qualifier from return value
- Add explicit instantiation of log_prefix so other FBX source files can see it
- add missing inversion of postrotation matrix for fbx.
- FIReader: Silence uninitialized variable warning
- Update version check in FBX reader to check for version >= 7500
- Use actual min/max of anim keys when start/stop time is missing
- GLTF1:
- Fix output of glTF 1 version string
- Fix delete / delete[] mismatch in glTFAsset
- Dont ignore rgba(1,1,1,1) color properties
- glTF2 primitives fixes
- Dont ignore rgba(1,1,1,1) color properties
- Fix delete / delete[] mismatch in glTFAsset
- Remove KHR_binary_glTF code
- glTF nodes can only hold one mesh. this simply assigns to and checks a Nodes Mesh
- version in glb header is stored as uint32_t
- GLTF2:
- node name conflict fix
- Fix transform matrices multiplication order
- Preserve node names when importing
- Add support for tangents in import
- Fix typo on gltf2 camera parameters
- Moved byteStride from accessor to bufferView
- Implemented reading binary glTF2 (glb) files
- Fix signed/unsigned warning
- Add postprocess step for scaling
- Fix shininess to roughness conversion
- Prefer “BLEND” over “MASK” as an alphaMode default
- Approximate specularity / glossiness in metallicRoughness materials
- Diffuse color and diffuse texture import and export improvements
- Addressed some mismatched news/deletes caused by the new glTF2 sources.
- Fix delete / delete[] mismatches in glTF2 importer
- use correct name of exporter to gltf2
- Fix possible infinite loop when exporting to gltf2
- Fix glTF2::Asset::FindUniqueID() when the input string is >= 256 chars
- Fix glTF2 alphaMode storage and reading
- Fix glTF 2.0 multi-primitive support
- Load gltf .bin files from correct directory
- Add support for importing both glTF and glTF2 files
- ampler improvements; Add new LazyDict method
- Changes to GLTF2 materials
- Remove Light, Technique references
- Start removing materials common, and adding pbrSpecularGlossiness
- Use !ObjectEmpty() vs. MemberCount() > 0
- Working read, import, export, and write of gltf2 (pbr) material
- Check in gltf2 models to test directory
- Remove un-needed test models
- Start managing and importing gltf2 pbr materials
- Update glTF2 Asset to use indexes
- Duplicate gltfImporter as gltf2Importer; Include glTF2 importer in CMake List
- glTF2: Fix animation export
- use opacity for diffuse alpha + alphaMode
- STL:
- Restore import of multi mesh binary STLs
- Blender:
- Silence warning about uninitialized member
- MDLImporter:
- Don't take address of packed struct member
- assimp_cmd:
- Fix strict-aliasing warnings
- Open3DGC:
- Fix strict-aliasing warnings
- Add assertions to silence static analyzer warnings
- Remove redundant const qualifiers from return types
- Fix some uninitialized variable warnings
- Remove OPEN3DGC and compression references
- unzip:
- Remove dead assignment
- Bail on bad compression method
- Fix possibly uninitialized variables
- clipper:
- Add assertion to silence a static analyzer warning
- OpenDDLExport:
- Reduce scope of a variable
- Remove dead variable
- Remove dead assignment
- Fix another potential memory leak
- X3DImporter:
- Add assertions to silence static analyzer warnings
- Add missing unittest
- Workaround for buggy Android NDK (issue #1361)
- TerragenLoader:
- Remove unused variable
- SIBImporter:
- Add assertions to silence static analyzer warnings
- IFC:
- Remove dead code
- Add explicit instantiation of log_prefix so IFCMaterial.cpp can see it
- PLY:
- Remove dead assignment and reduce scope of a variable
- fix vertex attribute lookup.
- OpenGEX:
- Add assertion to silence a static analyzer warning
- Fix for TextureFile with number in file name
- Return early when element is TextureFile
- NFF:
- Add assertions to silence static analyzer warnings
- Split up some complicated assignments
- Raw: Fix misleading indentation warning
- Reduce scope of a variable
- LWO
- Reduce scope of a variable
- IRRLoader:
- Fix confusing boolean casting
- AssbinExporter:
- Add assertion to silence a static analyzer warning
- ASE:
- Add assertion to silence a static analyzer warning
- AMFImporter:
- Add assertion to silence a static analyzer warning
- Add a block
- OptimizeGraph:
- Fix possible null pointer dereference
- RemoveRedundantMaterials:
- Add assertion to silence a static analyzer warning
- ImproveCacheLocality:
- Add assertion to silence a static analyzer warning
- RemoveRedundantMaterials:
- Set pointer to nullptr after deleting it
- Travis:
- Disable unit tests in scan-build config
- Move slower builds earlier to improve parallelization
- Add static analysis to build
- Remove unused branch rule for travis.
- Add Clang UBSan build configuration
- Treat warnings as errors, without typos this time
- Unittests:
- Add VS-based source groups for the unittests.
- Collada:
- export <library_animations> tag
- Update ColladaExporter.cpp
- Silence uninitialized variable warning
- Add support for line strip primitives
- Obj Wavefront:
- check in exporting against out-of-bounds-access .
- Issue 1351: use correct name for obj-meshname export for groups.
- fix mem-lead: face will be not released in case of an error.
- Anatoscope obj exporter nomtl
- Raise exception when obj file contains invalid face indices
- Added alternative displacement texture token in OBJ MTL material.
- Obj: rename attribute from exporter.
- Fix OBJ discarding all material names if the material library is missing
- Step:
- use correct lookup for utf32
- MD2:
- Fix MD2 frames containing garbage
- STL
- add missing const.
- Fix memory-alignment bug.
- Fix issue 104: deal with more solids in one STL file.
- CMake
- Fix issue 213: use correct include folder for assimp
- Doxygen
- Fix issue 1513: put irrXML onto exclucde list for doxygen run
- PyAssimp:
- Search for libassimp.so in LD_LIBRARY_PATH if available.
- Fix operator precedence issue in header check
- Split setup.py into multiple lines
- Detect if Anaconda and fixed 3d_viewer for Python 3
- created a python3 version of the 3dviewer and fixed the / = float in py3
- Blender:
- Fix invalid access to mesh array when the array is empty.
- Fix short overflow.
- Silence warning about inline function which is declared but not defined
- JAssimp
- Changed license header for IHMC contributions from Apache 2.0 to BSD
- Add Node metadata to the Jassmip Java API
- Added supported for custom IO Systems in Java. Implemented ClassLoader IO System
- Added a link to pure jvm assimp port
- Clang sanitizer:
- Undefined Behavior sanitizer
- Fixed a divide by zero error in IFCBoolean that was latent, but nevertheless a bug
- B3DImporter:
- Replace bad pointer casting with memcpy
- AppVeyor:
- Cleanup and Addition of VS 2017 and running Tests
- Fixed File Size reported as 0 in tests that use temporary files
- x86 isn't a valid VS platform. Win32 it is, then.
- Replaced the worker image name, which doesn't work as generator name, with a manually created generator name.
- Cleaned up appveyor setup, added VS 2017 to the build matrix and attempted to add running of tests.
- Treat warnings as errors on Appveyor
- Disable warning 4351 on MSVC 2013
- OpenGEXImporter:
- Copy materials to scene
- Store RefInfo in unique_ptr so they get automatically cleaned up
- Fix IOStream leak
- Store ChildInfo in unique_ptr so they get automatically cleaned up
- improve logging to be able to detect error-prone situations.
- AMFImporter:
- Fix memory leak
- UnrealLoader:
- Fix IOStream leak
- Upgrade RapidJSON to get rid of a clang warning
- zlib:
- Update zlib contribution
- Removed unnecessary files from zlib contribution
- Replaced unsigned long for the crc table to z_crc_t, to match what is returned by get-crc_table
- MakeVerboseFormat:
- Fix delete / delete[] mismatches in MakeVerboseFormat
- MaterialSystem:
- Fix out-of-bounds read in MaterialSystem unit test
- SIB:
- Added support for SIB models from Silo 2.5
- AssbinExporter:
- Fix strict aliasing violation
- Add Write specialization for aiColor3D
- DefaultLogger:
- Whitespace cleanup to fix GCC misleading indentation warning
- MDP:
- Fix encoding issues.
- PreTransformVertices:
- fix name lost in mesh and nodes when load with flag
- C4D:
- Fixes for C4D importer
- Unzip:
- Latest greatest.
4.0.1 (2017-07-28)
- FIXES/HOUSEKEEPING:
- fix version test.
- Not compiling when using ASSIMP_DOUBLE_PRECISION
- Added support for python3
- Check if cmake is installed with brew
- Low performance in OptimizeMeshesProcess::ProcessNode with huge numbers of meshes
- Elapsed seconds not shown correctly
- StreamReader: fix out-of-range exception
- PPdPmdParser: fix compilation for clang
4.0.0 (2017-07-18)
FEATURES:
- Double precision support provided ( available via cmake option )
- QT-Widget based assimp-viewer ( works for windows, linux, osx )
- Open3DGC codec supported by glFT-importer
- glTF: Read and write transparency values
- Add Triangulate post-processing step to glTF exporters
- Update rapidjson to v1.0.2
- Added method to append new metadata to structure
- Unittests: intoduce a prototype model differ
- X3D support
- AMF support
- Lugdunum3D support
- Obj-Importer: obj-homogeneous_coords support
- Obj-Importer: new streaming handling
- Added support for 64 bit version header introduced in FbxSdk2016
- Travis: enable coverall support.
- PyAssimp: New version of the pyASSIMP 3D viewer, with much improved 3D controls
- Morph animation support for collada
- Added support for parameters Ni and Tf in OBJ/MTL file format
- aiScene: add method to add children
- Added new option to IFC importer to control tessellation angle + removed unused IFC option
- aiMetaData: introduce aiMetaData::Dealloc
- Samples: add a DX11 example
- travis ci: test on OXS ( XCode 6.3 ) as well
- travis ci: enable sudo support.
- openddlparser: integrate release v0.4.0
- aiMetaData: Added support for metadata in assbin format
FIXES/HOUSEKEEPING:
- Introduce usage of #pragma statement
- Put cmake-scripts into their own folder
- Fix install pathes ( issue 938 )
- Fix object_compare in blender importer( issue 946 )
- Fix OSX compilation error
- Fix unzip path when no other version was found ( issue 967 )
- Set _FILE_OFFSET_BITS=64 for 32-bit linux ( issue 975 )
- Fix constructor for radjson on OSX
- Use Assimp namespace to fix build for big-endian architectures
- Add -fPIC to C Flags for 64bit linux Shared Object builds
- MDLLoader: fix resource leak.
- MakeVerboseFormat: fix invalid delete statement
- IFC: fix possible use after free access bug
- ComputeUVMappingprocess: add missing initialization for scalar value
- Fix invalid release of mat + mesh
- IrrImporter: Fix release functions
- Split mesh before exporting gltf ( issue 995 )
- 3MFImporter: add source group for visual studio
- IFC: Switch generated file to 2 files to fix issue related to <mingw4.9 ( Thanks Qt! )
- ObjImporter: fix test for vertices import
- export scene combiner ( issues177 )
- FBX: make lookup test less strict ( issues 994 )
- OpenGEX-Importer: add import of vertex colors ( issue 954 )
- fix bug when exporting mRotationKeys data
- fix mingw build (mingw supports stat64 nowadays)
- cfileio: fix leaks by not closing files in the destructor
- Fix OBJ parser mtllib statement parsing bug.
- Q3BSP-Importer: remove dead code
- Fix BlenderDNA for clang cross compiler.
- ScenePreprocessor: fix invalid index counter.
- Fix compiler warnings ( issue 957 )
- Fix obj .mtl file loading
- Fixed a compile error on MSVC14 x64 caused by the /bigobj flag failing to be set for the 1 and 2-suffixed versions introduced in commit 0a25b076b8968b7ea2aa96d7d1b4381be2d72ce6
- Fixed build warnings on MSVC14 x64
- Remove scaling of specular exponent in OBJFileImporter.cpp
- use ai_assert instead of assert ( issue 1076 )
- Added a preprocessor definition for MSVC to silence safety warnings regarding C library functions. This addresses all warnings for MSVC x86 and x64 when building zlib, tools and viewer as a static lib
- fix parsing of texture name ( issue 899 )
- add warning when detecting invalid mat definition ( issue 1111 )
- copy aiTexture type declaration instead of using decltype for declaration to fix iOS build( issue 1101 )
- FBX: Add additional material properties
- FBX: Correct camera position and clip planes
- FBX: Add correct light locations and falloff values
- fix typo ( issue 1141 )
- Fix collada export. Don't duplicate TEXCOORD/NORMALS/COLORS in <vertices> and <polylist> ( issue 1084 )
- OBJParser: set material index when changing current material
- OBJ: check for null mesh before updating material index
- add vertex color export support ( issue 809 )
- Fix memory leak in Collada importer ( issue 1169 )
- add stp to the list of supported extensions for step-files ( issue 1183 )
- fix clang build ( Issue-1169 )
- fix for FreeBSD
- Import FindPkgMacros to main CMake Configuration
- Extended support for tessellation parameter to more IFC shapes
- defensice handling of utf-8 decode issues ( issue 1211 )
- Fixed compiler error on clang 4.0 running on OSX
- use test extension for exported test files ( issue 1228 )
- Set UVW index material properties for OBJ files
- Fixed no member named 'atop' in global namespace issue for Android NDK compilation
- Apply mechanism to decide use for IrrXML external or internal
- Fix static init ordering bug in OpenGEX importer
- GLTF exporter: ensure animation accessors have same count
- GLTF exporter: convert animation time from ticks to seconds
- Add support for reading texture coordinates from PLY meshes with properties named 'texture_u' and 'texture_v'
- Added TokensForSearch in BlenderLoader to allow CanRead return true for in-memory files.
- fix wrong delete ( issue 1266 )
- OpenGEX: fix invalid handling with color4 token ( issue 1262 )
- LWOLoader: fix link in loader description
- Fix error when custom CMAKE_C_FLAGS is specified
- Fast-atof: log overflow errors
- Obj-Importer: do not break when detecting an overflow ( issue 1244 )
- Obj-Importer: fix parsing of multible line data definitions
- Fixed bug where IFC models with multiple IFCSite only loaded 1 site instead of the complete model
- PLYImporter: - optimize memory and speed on ply importer / change parser to use a file stream - manage texture path in ply
import - manage texture coords on faces in ply import - correction on point cloud faces generation
- Utf8: integrate new lib ( issue 1158 )
- fixed CMAKE_MODULE_PATH overwriting previous values
- OpenGEX: Fixed bug in material color processing ( issue 1271 )
- SceneCombiner: move header for scenecombiner to public folder.
- GLTF exporter: ensure buffer view byte offsets are correctly aligned
- X3D importer: Added EXPORT and IMPORT to the list of ignored XML tags
- X3D Exporter: fixed missing attributes
- X3D importer: Fixed import of normals for the single index / normal per vertex case
- X3D importer: Fixed handling of inlined files
- X3D importer: fixed whitespace handling (issue 1202)
- X3D importer: Fixed iterator on MSVC 2015
- X3D importer: Fixed problems with auto, override and regex on older compilers
- X3D importer: Fixed missing header file
- X3D importer: Fixed path handling
- X3D importer: Implemented support for binary X3D files
- fix build without 3DS ( issue 1319 )
- pyassimp: Fixed indices for IndexedTriangleFanSet, IndexedTriangleSet and IndexedTriangleStripSet
- Fixes parameters to pyassimp.load
- Obj-Importe: Fixed texture bug due simultaneously using 'usemtl' and 'usemap' attributes
- check if all exporters are disabled ( issue 1320 )
- Remove std functions deprecated by C++11.
- X-Importer: make it deal with lines
- use correct path for compilers ( issue 1335 )
- Collada: add workaround to deal with polygon with holes
- update python readme
- Use unique node names when loading Collada files
- Fixed many FBX bugs
API COMPATIBILITY:
- Changed ABI-compatibility to v3.3.1, please rebuild your precompiled libraries ( see issue 1182 )
- VS2010 outdated
3.3.1 (2016-07-08)
FIXES/HOUSEKEEPING:
- Setup of default precision for 17 exporters
- Fix xcode project files
- Fix BlenderTesselator: offsetof operator
- Invalid version in cmake file
- Update pstdint.h to latest greatest
3.3.0 (2016-07-05)
FEATURES:
- C++11 support enabled
- New regression-test-UI
- Experimental glTF-importer support
- OpenGEX: add support for cameras and lights
- C4D: update to latest Melange-SDK
- Add a gitter channel
- Coverity check enabled
- Switch to <...> include brackets for public headers
- Enable export by pyAssimp
- CI: check windows build
- Add functionality to perform a singlepost-processing step
- many more, just check the history
FIXES/HOUSEKEEPING:
- Fix of many resource leaks in unittests and main lib
- Fix iOS-buildfor X64
- Choosing zlib manually for cmake
- many more, just check the history
3.2.1 (2016-010-10)
FEATURES:
- Updated glTF exporter to meet 1.0 specification.
FIXES/HOUSEKEEPING:
- Fixed glTF Validator errors for exported glTF format.
ISSUES:
- Hard coded sampler setting for
- magFilter
- minFilter
- void* in ExportData for accessor max and min.
3.2.0 (2015-11-03)
FEATURES:
- OpenDDL-Parser is part of contrib-source.
- Experimental OpenGEX-support
- CI-check for linux and windows
- Coverity check added
- New regression testsuite.
FIXES/HOUSEKEEPING:
- Hundreds of bugfixes in all parts of the library
- Unified line endings
API COMPATIBILITY:
- Removed precompiled header to increase build speed for linux
3.1.1 (2014-06-15)
FEATURES:
- Support for FBX 2013 and newer, binary and ASCII (this is partly
work from Google Summer of Code 2012)
- Support for OGRE binary mesh and skeleton format
- Updated BLEND support for newer Blender versions
- Support for arbitrary meta data, used to hold FBX and DAE metadata
- OBJ Export now produces smaller files
- Meshes can now have names, this is supported by the major importers
- Improved IFC geometry generation
- M3 support has been removed
FIXES/HOUSEKEEPING:
- Hundreds of bugfixes in all parts of the library
- CMake is now the primary build system
API COMPATIBILITY:
- 3.1.1 is not binary compatible to 3.0 due to aiNode::mMetaData
and aiMesh::mName
- Export interface has been cleaned up and unified
- Other than that no relevant changes
3.0 (2012-07-07)
FEATURES:
- new export interface similar to the import API.
- Supported export formats: Collada, OBJ, PLY and STL
- added new import formats: XGL/ZGL, M3 (experimental)
- new postprocessing steps: Debone
- vastly improved IFC (Industry Foundation Classes) support
- introduced API to query importer meta information (such as supported
format versions, full name, maintainer info).
- reworked Ogre XML import
- C-API now supports per-import properties
FIXES/HOUSEKEEPING:
- hundreds of bugfixes in all parts of the library
- unified naming and cleanup of public headers
- improved CMake build system
- templatized math library
- reduce dependency on boost.thread, only remaining spot
is synchronization for the C logging API
API COMPATIBILITY:
- renamed headers, export interface, C API properties and meta data
prevent compatibility with code written for 2.0, but in
most cases these can be easily resolved
- Note: 3.0 is not binary compatible with 2.0
2.0 (2010-11-21)
FEATURES:
- Add support for static Blender (*.blend) scenes
- Add support for Q3BSP scenes
- Add a windows-based OpenGL sample featuring texturing & basic materials
- Add an experimental progress feedback interface.
- Vastly improved performance (up to 500%, depending on mesh size and
spatial structure) in some expensive postprocessing steps
- AssimpView now uses a reworked layout which leaves more space
to the scene hierarchy window
- Add C# bindings ('Assimp.NET')
- Keep BSD-licensed and otherwise free test files in separate
folders (./test/models and ./test/models-nonbsd).
FIXES:
- Many Collada bugfixes, improve fault tolerance
- Fix possible crashes in the Obj loader
- Improve the Ogre XML loader
- OpenGL-sample now works with MinGW
- Fix Importer::FindLoader failing on uppercase file extensions
- Fix flawed path handling when locating external files
- Limit the maximum number of vertices, faces, face indices and
weights that Assimp is able to handle. This is to avoid
crashes due to overflowing counters.
- Updated XCode project files
- Further CMAKE build improvements
API CHANGES:
- Add data structures for vertex-based animations (These are not
currently used, however ...)
- Some Assimp::Importer methods are const now.
1.1 (2010-04-17)
This is the list of relevant changes from the 1.0 (r412) release to 1.1 (r700).
FEATURES:
- Vastly improved Collada support
- Add MS3D (Milkshape 3D) support
- Add support for Ogre XML static meshes
- Add experimental COB (TrueSpace) support
- Automatic test suite to quickly locate regressions
- D bindings (`dAssimp`)
- Python 2.n bindings (`PyAssimp`)
- Add basic support for Unicode input files (utf8, utf16 and utf32)
- Add further utilities to the `assimp` tool (xml/binary dumps, quick file stats)
- Switch to a CMAKE-based build system including an install target for unix'es
- Automatic evaluation of subdivision surfaces for some formats.
- Add `Importer::ReadFileFromMemory` and the corresponding C-API `aiReadFileFromMemory`
- Expose further math utilities via the C-API (i.e. `aiMultiplyMatrix4`)
- Move noboost files away from the public include directory
- Many, many bugfixes and improvements in existing loaders and postprocessing steps
- Documentation improved and clarified in many places.
- Add a sample on using Assimp in conjunction with OpenGL
- Distribution/packaging: comfortable SDK installer for Windows
- Distribution/packaging: improved release packages for other architectures
CRITICAL FIXES:
- Resolve problems with clashing heap managers, STL ABIs and runtime libraries (win32)
- Fix automatic detection of file type if no file extension is given
- Improved exception safety and robustness, prevent leaking of exceptions through the C interface
- Fix possible heap corruption due to material properties pulled in incorrectly
- Avoid leaking in certain error scenarios
- Fix 64 bit compatibility problems in some loaders (i.e. MDL)
BREAKING API CHANGES:
- None -
MINOR API BEHAVIOUR CHANGES:
- Change quaternion orientation to suit to the more common convention (-w).
- aiString is utf8 now. Not yet consistent, however.

3961
CHANGES.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -362,7 +362,7 @@ ELSEIF(MSVC)
elseif((GENERATOR_IS_MULTI_CONFIG) OR (CMAKE_BUILD_TYPE MATCHES Release))
message("-- MSVC PDB generation disabled. Release binary will not be debuggable.")
endif()
if(NOT /utf-8 IN_LIST CMAKE_CXX_FLAGS)
if(NOT CMAKE_CXX_FLAGS MATCHES "/utf-8")
# Source code is encoded in UTF-8
ADD_COMPILE_OPTIONS(/source-charset:utf-8)
endif()

View File

@@ -49,3 +49,5 @@
"ASSIMP_BUILD_DOCS": "ON"
}
}
]
}

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

@@ -361,19 +361,19 @@ void MD5Importer::LoadMD5MeshFile() {
#else
// FIX: MD5 files exported from Blender can have empty meshes
unsigned int numMaterials = 0;
for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(), end = meshParser.mMeshes.end(); it != end; ++it) {
if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) {
++mScene->mNumMaterials;
++numMaterials;
}
}
// generate all meshes
mScene->mNumMeshes = mScene->mNumMaterials;
mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
mScene->mMaterials = new aiMaterial *[mScene->mNumMeshes];
mScene->mMeshes = new aiMesh *[numMaterials];
mScene->mMaterials = new aiMaterial *[numMaterials];
// storage for node mesh indices
pcNode->mNumMeshes = mScene->mNumMeshes;
pcNode->mNumMeshes = numMaterials;
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
for (unsigned int m = 0; m < pcNode->mNumMeshes; ++m) {
pcNode->mMeshes[m] = m;
@@ -386,7 +386,10 @@ void MD5Importer::LoadMD5MeshFile() {
continue;
}
aiMesh *mesh = mScene->mMeshes[n] = new aiMesh();
aiMesh* mesh = new aiMesh();
mScene->mMeshes[n] = mesh;
++mScene->mNumMeshes;
mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
// generate unique vertices in our internal verbose format
@@ -508,6 +511,7 @@ void MD5Importer::LoadMD5MeshFile() {
// generate a material for the mesh
aiMaterial *mat = new aiMaterial();
mScene->mMaterials[n] = mat;
++mScene->mNumMaterials;
// insert the typical doom3 textures:
// nnn_local.tga - normal map

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.");
}
@@ -324,6 +326,15 @@ void MDCImporter::InternReadFile(
#endif
// boundary check for pcVerts
auto surfStart = reinterpret_cast<const uint8_t*>(pcSurface);
const uint8_t* surfEnd = surfStart + pcSurface->ulOffsetEnd;
auto vertBufStart = reinterpret_cast<const uint8_t*>(pcVerts);
const size_t needVertBytes = sizeof(MDC::BaseVertex) * pcSurface->ulNumVertices;
if (vertBufStart < surfStart || vertBufStart + needVertBytes > surfEnd) {
throw DeadlyImportError("MDCImporter: pcVerts points outside of surface block.");
}
const MDC::CompressedVertex *pcCVerts = nullptr;
int16_t *mdcCompVert = nullptr;
@@ -335,6 +346,12 @@ void MDCImporter::InternReadFile(
pcCVerts = (const MDC::CompressedVertex *)((int8_t *)pcSurface +
pcSurface->ulOffsetCompVerts) +
*mdcCompVert * pcSurface->ulNumVertices;
auto cvertBufStart = reinterpret_cast<const uint8_t*>(pcCVerts);
const size_t needCompVertBytes = sizeof(MDC::CompressedVertex) * pcSurface->ulNumVertices;
if (cvertBufStart < surfStart || cvertBufStart > surfEnd ||
needCompVertBytes > static_cast<size_t>(surfEnd - cvertBufStart)) {
throw DeadlyImportError("MDCImporter: pcCVerts points outside of surface block.");
}
} else
mdcCompVert = nullptr;
}

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

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@@ -602,6 +602,9 @@ void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1() {
// Read a MDL 3,4,5 file
void MDLImporter::InternReadFile_3DGS_MDL345() {
ai_assert(nullptr != pScene);
if (pScene == nullptr) {
throw DeadlyImportError("INvalid scene pointer detected.");
}
// the header of MDL 3/4/5 is nearly identical to the original Quake1 header
BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header *)this->mBuffer;
@@ -610,6 +613,10 @@ void MDLImporter::InternReadFile_3DGS_MDL345() {
#endif
ValidateHeader_Quake1(pcHeader);
if (pcHeader->synctype < 0) {
throw DeadlyImportError("Invalid synctype value in MDL header; possible corrupt file.");
}
// current cursor position in the file
const unsigned char *szCurrent = (const unsigned char *)(pcHeader + 1);
const unsigned char *szEnd = mBuffer + iFileSize;
@@ -619,8 +626,7 @@ void MDLImporter::InternReadFile_3DGS_MDL345() {
if (szCurrent + sizeof(uint32_t) > szEnd) {
throw DeadlyImportError("Texture data past end of file.");
}
BE_NCONST MDL::Skin *pcSkin;
pcSkin = (BE_NCONST MDL::Skin *)szCurrent;
BE_NCONST MDL::Skin *pcSkin = (BE_NCONST MDL::Skin *)szCurrent;
AI_SWAP4(pcSkin->group);
// create one output image
unsigned int iSkip = i ? UINT_MAX : 0;
@@ -1838,6 +1844,10 @@ void MDLImporter::GenerateOutputMeshes_3DGS_MDL7(
const unsigned int iNumOutBones = pcHeader->bones_num;
for (std::vector<aiMaterial *>::size_type i = 0; i < shared.pcMats.size(); ++i) {
if (splitGroupData.aiSplit == nullptr) {
continue;
}
if (!splitGroupData.aiSplit[i]->empty()) {
// allocate the output mesh
@@ -1991,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

@@ -364,11 +364,8 @@ struct CustomExtension {
}
CustomExtension() = default;
~CustomExtension() = default;
CustomExtension(const CustomExtension &other) = default;
CustomExtension& operator=(const CustomExtension&) = default;
};
@@ -423,7 +420,6 @@ public:
Type_text
};
/// \struct SEncodedRegion
/// Descriptor of encoded region in "bufferView".
struct SEncodedRegion {
const size_t Offset; ///< Offset from begin of "bufferView" to encoded region, in bytes.
@@ -432,7 +428,6 @@ public:
const size_t DecodedData_Length; ///< Size of decoded region, in bytes.
const std::string ID; ///< ID of the region.
/// \fn SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID)
/// Constructor.
/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
@@ -446,7 +441,6 @@ public:
DecodedData_Length(pDecodedData_Length),
ID(pID) {}
/// \fn ~SEncodedRegion()
/// Destructor.
~SEncodedRegion() { delete[] DecodedData; }
};
@@ -460,7 +454,6 @@ public:
Type type;
/// \var EncodedRegion_Current
/// Pointer to currently active encoded region.
/// Why not decoding all regions at once and not to set one buffer with decoded data?
/// Yes, why not? Even "accessor" point to decoded data. I mean that fields "byteOffset", "byteStride" and "count" has values which describes decoded
@@ -500,7 +493,6 @@ public:
bool LoadFromStream(IOStream &stream, size_t length = 0, size_t baseOffset = 0);
/// \fn void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
/// Mark region of "bufferView" as encoded. When data is request from such region then "bufferView" use decoded data.
/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
@@ -509,12 +501,10 @@ public:
/// \param [in] pID - ID of the region.
void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID);
/// \fn void EncodedRegion_SetCurrent(const std::string& pID)
/// Select current encoded region by ID. \sa EncodedRegion_Current.
/// \param [in] pID - ID of the region.
void EncodedRegion_SetCurrent(const std::string &pID);
/// \fn bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
/// Replace part of buffer data. Pay attention that function work with original array of data (\ref mData) not with encoded regions.
/// \param [in] pBufferData_Offset - index of first element in buffer from which new data will be placed.
/// \param [in] pBufferData_Count - count of bytes in buffer which will be replaced.
@@ -1002,27 +992,24 @@ struct Texture : public Object {
struct Animation : public Object {
struct Sampler {
Sampler() :
interpolation(Interpolation_LINEAR) {}
Sampler() = default;
Ref<Accessor> input; //!< Accessor reference to the buffer storing the key-frame times.
Ref<Accessor> output; //!< Accessor reference to the buffer storing the key-frame values.
Interpolation interpolation; //!< Type of interpolation algorithm to use between key-frames.
Interpolation interpolation{Interpolation_LINEAR}; //!< Type of interpolation algorithm to use between key-frames.
};
struct Target {
Target() :
path(AnimationPath_TRANSLATION) {}
Target() = default;
Ref<Node> node; //!< The node to animate.
AnimationPath path; //!< The property of the node to animate.
AnimationPath path{AnimationPath_TRANSLATION}; //!< The property of the node to animate.
};
struct Channel {
Channel() :
sampler(-1) {}
Channel() = default;
int sampler; //!< The sampler index containing the animation data.
int sampler{-1}; //!< The sampler index containing the animation data.
Target target; //!< The node and property to animate.
};
@@ -1135,50 +1122,34 @@ class Asset {
public:
//! Keeps info about the enabled extensions
struct Extensions {
bool KHR_materials_pbrSpecularGlossiness;
bool KHR_materials_specular;
bool KHR_materials_unlit;
bool KHR_lights_punctual;
bool KHR_texture_transform;
bool KHR_materials_sheen;
bool KHR_materials_clearcoat;
bool KHR_materials_transmission;
bool KHR_materials_volume;
bool KHR_materials_ior;
bool KHR_materials_emissive_strength;
bool KHR_materials_anisotropy;
bool KHR_draco_mesh_compression;
bool FB_ngon_encoding;
bool KHR_texture_basisu;
bool KHR_materials_pbrSpecularGlossiness{false};
bool KHR_materials_specular{false};
bool KHR_materials_unlit{false};
bool KHR_lights_punctual{false};
bool KHR_texture_transform{false};
bool KHR_materials_sheen{false};
bool KHR_materials_clearcoat{false};
bool KHR_materials_transmission{false};
bool KHR_materials_volume{false};
bool KHR_materials_ior{false};
bool KHR_materials_emissive_strength{false};
bool KHR_materials_anisotropy{false};
bool KHR_draco_mesh_compression{false};
bool FB_ngon_encoding{false};
bool KHR_texture_basisu{false};
bool EXT_texture_webp{false};
Extensions() :
KHR_materials_pbrSpecularGlossiness(false),
KHR_materials_specular(false),
KHR_materials_unlit(false),
KHR_lights_punctual(false),
KHR_texture_transform(false),
KHR_materials_sheen(false),
KHR_materials_clearcoat(false),
KHR_materials_transmission(false),
KHR_materials_volume(false),
KHR_materials_ior(false),
KHR_materials_emissive_strength(false),
KHR_materials_anisotropy(false),
KHR_draco_mesh_compression(false),
FB_ngon_encoding(false),
KHR_texture_basisu(false) {
// empty
}
Extensions() = default;
~Extensions() = default;
} extensionsUsed;
//! Keeps info about the required extensions
struct RequiredExtensions {
bool KHR_draco_mesh_compression;
bool KHR_texture_basisu;
bool KHR_draco_mesh_compression{false};
bool KHR_texture_basisu{false};
bool EXT_texture_webp{false};
RequiredExtensions() : KHR_draco_mesh_compression(false), KHR_texture_basisu(false) {
// empty
}
RequiredExtensions() = default;
} extensionsRequired;
AssetMetadata asset;

View File

@@ -1217,6 +1217,13 @@ inline void Texture::Read(Value &obj, Asset &r) {
if (r.extensionsUsed.KHR_texture_basisu) {
if (Value *curBasisU = FindObject(*extensions, "KHR_texture_basisu")) {
if (Value *sourceVal = FindUInt(*curBasisU, "source")) {
source = r.images.Retrieve(sourceVal->GetUint());
}
}
} else if(r.extensionsUsed.EXT_texture_webp) {
if (Value *curBasisU = FindObject(*extensions, "EXT_texture_webp")) {
if (Value *sourceVal = FindUInt(*curBasisU, "source")) {
source = r.images.Retrieve(sourceVal->GetUint());
}
@@ -2149,6 +2156,7 @@ inline void Asset::ReadExtensionsRequired(Document &doc) {
CHECK_REQUIRED_EXT(KHR_draco_mesh_compression);
CHECK_REQUIRED_EXT(KHR_texture_basisu);
CHECK_REQUIRED_EXT(EXT_texture_webp);
#undef CHECK_REQUIRED_EXT
}
@@ -2179,6 +2187,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
CHECK_EXT(KHR_materials_anisotropy);
CHECK_EXT(KHR_draco_mesh_compression);
CHECK_EXT(KHR_texture_basisu);
CHECK_EXT(EXT_texture_webp);
#undef CHECK_EXT
}

View File

@@ -1663,7 +1663,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
ext = "kx2";
} else if (strcmp(ext, "basis") == 0) { // basisu
ext = "bu";
}
} // webp requires no transformation
size_t len = strlen(ext);
if (len > 3) len = 3;

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

View File

@@ -47,6 +47,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
// Limit input size to 1MB to prevent OOMs and timeouts
if (dataSize > 1024 * 1024) {
return 0;
}
#ifdef _DEBUG
aiLogStream stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT, nullptr);
aiAttachLogStream(&stream);

40
fuzz/assimp_fuzzer.dict Normal file
View File

@@ -0,0 +1,40 @@
# OBJ
"v "
"vn "
"vt "
"f "
"mtllib "
"usemtl "
# GLTF (JSON)
"asset"
"scene"
"scenes"
"nodes"
"meshes"
"accessors"
"bufferViews"
"buffers"
# PLY
"ply"
"format"
"element"
"property"
"vertex"
"face"
# STL
"solid"
"facet"
"normal"
"outer loop"
"vertex"
"endloop"
"endfacet"
"endsolid"
# Collada (XML)
"COLLADA"
"library_geometries"
"library_visual_scenes"

View File

@@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force Collada format (dae)
if (!AssimpFuzz::ForceFormat(importer, "dae")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "dae");
return 0;
}

63
fuzz/assimp_fuzzer_fbx.cc Normal file
View File

@@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force FBX format
if (!AssimpFuzz::ForceFormat(importer, "fbx")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "fbx");
return 0;
}

63
fuzz/assimp_fuzzer_glb.cc Normal file
View File

@@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force GLB binary format only (see assimp_fuzzer_gltf.cc for text glTF)
if (!AssimpFuzz::ForceFormat(importer, "glb")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "glb");
return 0;
}

View File

@@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force glTF text format only (see assimp_fuzzer_glb.cc for binary GLB)
if (!AssimpFuzz::ForceFormat(importer, "gltf")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "gltf");
return 0;
}

65
fuzz/assimp_fuzzer_obj.cc Normal file
View File

@@ -0,0 +1,65 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
// Limit input size to 1MB
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force OBJ format
if (!AssimpFuzz::ForceFormat(importer, "obj")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
// We pass "obj" hint as well, though only OBJ loader is registered now.
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "obj");
return 0;
}

63
fuzz/assimp_fuzzer_stl.cc Normal file
View File

@@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#include "fuzzer_common.h"
#include <assimp/cimport.h>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
using namespace Assimp;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) {
if (dataSize > 1024 * 1024) {
return 0;
}
Importer importer;
// Force STL format
if (!AssimpFuzz::ForceFormat(importer, "stl")) {
return 0;
}
unsigned int flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_ValidateDataStructure;
const aiScene *sc = importer.ReadFileFromMemory(data, dataSize, flags, "stl");
return 0;
}

107
fuzz/fuzzer_common.h Normal file
View File

@@ -0,0 +1,107 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, 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.
---------------------------------------------------------------------------
*/
#pragma once
#include <assimp/Importer.hpp>
#include <assimp/BaseImporter.h>
#include <assimp/importerdesc.h>
#include <cstring>
#include <vector>
namespace AssimpFuzz {
// Unregisters all loaders except the ones matching the given extension.
// Returns true if at least one loader was kept.
inline bool ForceFormat(Assimp::Importer& importer, const char* targetExtension) {
size_t count = importer.GetImporterCount();
std::vector<Assimp::BaseImporter*> toRemove;
bool found = false;
for (size_t i = 0; i < count; ++i) {
const aiImporterDesc* desc = importer.GetImporterInfo(i);
Assimp::BaseImporter* imp = importer.GetImporter(i);
if (!desc || !imp) continue;
// Check if the importer supports the target extension
// mFileExtensions is a space-separated list (e.g., "obj mod")
// We wrap target in spaces or check bounds to be precise,
// but for fuzzing, a simple strstr is usually sufficient
// if the target string is unique enough (e.g. "gltf", "obj").
// A more robust check:
bool isTarget = false;
const char* extList = desc->mFileExtensions;
if (!extList) {
toRemove.push_back(imp);
continue;
}
const size_t targetLen = strlen(targetExtension);
const char* p = extList;
while ((p = strstr(p, targetExtension)) != nullptr) {
// Check boundaries
const char prev = (p == extList) ? ' ' : *(p - 1);
const char next = *(p + targetLen);
if (prev == ' ' && (next == ' ' || next == '\0')) {
isTarget = true;
break;
}
p++;
}
if (isTarget) {
found = true;
} else {
toRemove.push_back(imp);
}
}
for (auto* imp : toRemove) {
importer.UnregisterLoader(imp);
delete imp; // Free the unregistered importer to prevent memory leaks
}
return found;
}
}

22
fuzz/ossfuzz/Dockerfile Normal file
View File

@@ -0,0 +1,22 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
FROM gcr.io/oss-fuzz-base/base-builder
RUN apt-get update && apt-get install -y make autoconf automake libtool ninja-build cmake zip
RUN git clone --depth 1 https://github.com/assimp/assimp.git assimp
WORKDIR assimp
COPY build.sh $SRC/

100
fuzz/ossfuzz/build.sh Executable file
View File

@@ -0,0 +1,100 @@
#!/bin/bash -eu
# Build directory
mkdir -p build
cd build
# Configure
cmake .. \
-G Ninja \
-DCMAKE_C_COMPILER="${CC}" \
-DCMAKE_CXX_COMPILER="${CXX}" \
-DCMAKE_C_FLAGS="${CFLAGS}" \
-DCMAKE_CXX_FLAGS="${CXXFLAGS}" \
-DASSIMP_BUILD_ZLIB=ON \
-DASSIMP_BUILD_TESTS=OFF \
-DASSIMP_BUILD_ASSIMP_TOOLS=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=ON \
-DASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT=ON
# Build the library
ninja
# Helper function to build fuzzers
build_fuzzer() {
local fuzzer_name=$1
local source_file=$2
echo "Building $fuzzer_name..."
$CXX $CXXFLAGS -I../include -I../build/include -c "$source_file" -o "${fuzzer_name}.o"
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE "${fuzzer_name}.o" -o "$OUT/${fuzzer_name}" \
./lib/libassimp.a \
./contrib/zlib/libzlibstatic.a \
-lpthread -ldl
}
# 1. Generic Fuzzer
build_fuzzer "assimp_fuzzer" "../fuzz/assimp_fuzzer.cc"
# Corpus for generic fuzzer (all models)
(cd ../test/models && zip -q -r $OUT/assimp_fuzzer_seed_corpus.zip .)
# Dictionary
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer.dict || true
# 2. OBJ Fuzzer
build_fuzzer "assimp_fuzzer_obj" "../fuzz/assimp_fuzzer_obj.cc"
if [ -d "../test/models/OBJ" ]; then
(cd ../test/models/OBJ && zip -q -r $OUT/assimp_fuzzer_obj_seed_corpus.zip .)
fi
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_obj.dict || true
# 3. GLTF Fuzzer (text format only, glTF and glTF2 versions)
build_fuzzer "assimp_fuzzer_gltf" "../fuzz/assimp_fuzzer_gltf.cc"
mkdir -p gltf_corpus
[ -d "../test/models/glTF" ] && cp -r ../test/models/glTF/* gltf_corpus/
[ -d "../test/models/glTF2" ] && cp -r ../test/models/glTF2/* gltf_corpus/
if [ -d "gltf_corpus" ] && [ "$(ls -A gltf_corpus)" ]; then
(cd gltf_corpus && zip -q -r $OUT/assimp_fuzzer_gltf_seed_corpus.zip .)
fi
rm -rf gltf_corpus
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_gltf.dict || true
# 4. GLB Fuzzer (binary glTF format)
build_fuzzer "assimp_fuzzer_glb" "../fuzz/assimp_fuzzer_glb.cc"
mkdir -p glb_corpus
# GLB files can be found in glTF and glTF2 directories
[ -d "../test/models/glTF" ] && find ../test/models/glTF -name "*.glb" -exec cp {} glb_corpus/ \; 2>/dev/null || true
[ -d "../test/models/glTF2" ] && find ../test/models/glTF2 -name "*.glb" -exec cp {} glb_corpus/ \; 2>/dev/null || true
if [ -d "glb_corpus" ] && [ "$(ls -A glb_corpus)" ]; then
(cd glb_corpus && zip -q -r $OUT/assimp_fuzzer_glb_seed_corpus.zip .)
fi
rm -rf glb_corpus
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_glb.dict || true
# 5. FBX Fuzzer
build_fuzzer "assimp_fuzzer_fbx" "../fuzz/assimp_fuzzer_fbx.cc"
if [ -d "../test/models/FBX" ]; then
(cd ../test/models/FBX && zip -q -r $OUT/assimp_fuzzer_fbx_seed_corpus.zip .)
fi
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_fbx.dict || true
# 6. Collada Fuzzer
build_fuzzer "assimp_fuzzer_collada" "../fuzz/assimp_fuzzer_collada.cc"
if [ -d "../test/models/Collada" ]; then
(cd ../test/models/Collada && zip -q -r $OUT/assimp_fuzzer_collada_seed_corpus.zip .)
fi
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_collada.dict || true
# 7. STL Fuzzer
build_fuzzer "assimp_fuzzer_stl" "../fuzz/assimp_fuzzer_stl.cc"
if [ -d "../test/models/STL" ]; then
(cd ../test/models/STL && zip -q -r $OUT/assimp_fuzzer_stl_seed_corpus.zip .)
fi
cp ../fuzz/assimp_fuzzer.dict $OUT/assimp_fuzzer_stl.dict || true

14
fuzz/ossfuzz/project.yaml Normal file
View File

@@ -0,0 +1,14 @@
homepage: "https://github.com/assimp/assimp"
main_repo: "https://github.com/assimp/assimp"
language: c++
primary_contact: "kim.kulling@assimp.org"
auto_ccs:
- "kim.kulling@googlemail.com"
sanitizers:
- address
- undefined
- memory
fuzzing_engines:
- libfuzzer
- afl
- honggfuzz