From 4e92d037f3760699b8c3b25d2c802042bb5264b5 Mon Sep 17 00:00:00 2001 From: Jens Finkhaeuser Date: Sun, 1 Feb 2026 10:33:11 +0100 Subject: [PATCH 1/4] Make sure LDR_RESTRICT doesn't mess with GCC/Clang --- src/ldrawloader.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ldrawloader.h b/src/ldrawloader.h index 815ad24..063d741 100644 --- a/src/ldrawloader.h +++ b/src/ldrawloader.h @@ -93,7 +93,16 @@ extern "C" { #define LDR_INVALID_IDX uint32_t(~0) #define LDR_INVALID_SHAPETYPE 0 +#if defined(WINDOWS) || defined(WIN32) #define LDR_RESTRICT __restrict +#else +// XXX: This isn't the perfect soluton. Looks like GCC/Clang are strict about +// qv-qualifiers producing incompatible types, which messes with function +// resultion when values are marked LDR_RESTRICT and then passed to +// functions where the parameters are not marked. +// So to keep things simple for now, disable this. +#define LDR_RESTRICT +#endif typedef enum LdrResult : int32_t { From 0a74765683e49fd796a5076a41962adb1f113657 Mon Sep 17 00:00:00 2001 From: Jens Finkhaeuser Date: Sun, 1 Feb 2026 10:35:39 +0100 Subject: [PATCH 2/4] Need stddef.h for size_t --- src/ldrawloader.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ldrawloader.h b/src/ldrawloader.h index 063d741..8d9157a 100644 --- a/src/ldrawloader.h +++ b/src/ldrawloader.h @@ -28,6 +28,7 @@ #pragma once #include +#include // size_t // LDR_CFG_THREADSAFE // Enabled: you can load parts/models from multiple threads From 7032bde264fc69b779971adf87183da039eeb870 Mon Sep 17 00:00:00 2001 From: Jens Finkhaeuser Date: Sun, 1 Feb 2026 10:41:55 +0100 Subject: [PATCH 3/4] - Add for std::memset - Add for FLT_* constants It could've been and , but if this is C++, it might as well be the C++ headers. --- src/ldrawloader.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ldrawloader.hpp b/src/ldrawloader.hpp index 25ced07..15c2f61 100644 --- a/src/ldrawloader.hpp +++ b/src/ldrawloader.hpp @@ -35,6 +35,9 @@ #include #endif +#include +#include + #include #include #include @@ -329,7 +332,7 @@ struct Loader BitArray() {} BitArray(uint32_t num, bool value) { resize(num, value); } - void clear() { memset(data, 0, sizeof(uint64_t) * (num_allocated / 64)); } + void clear() { std::memset(data, 0, sizeof(uint64_t) * (num_allocated / 64)); } uint32_t size() const { return num; } @@ -344,7 +347,7 @@ struct Loader uint32_t idx = num_old / 64; uint32_t num_delta = numNew - num_old; - memset(data + idx, value ? 0xFFFFFFFF : 0, sizeof(uint64_t) * (num_delta / 64)); + std::memset(data + idx, value ? 0xFFFFFFFF : 0, sizeof(uint64_t) * (num_delta / 64)); } } From acf22f55fa0e15eac7e0846a334e80ccd681aabb Mon Sep 17 00:00:00 2001 From: Jens Finkhaeuser Date: Sun, 1 Feb 2026 10:44:54 +0100 Subject: [PATCH 4/4] - intrin.h is not portable, apparently/supposedly immintrin.h is - A few headers were missing, or the C headers were used. - Add a few std:: prefixes to functions from C++/C compatibility headers - When trying to resolve parts, ensure / directory separators are used when necessary. Without that last change, loading a model won't find sub-parts in the "s" subdirectory of the library. This does nothing on Windows, and on POSIX somewhat simplistically assumes that whenever a backslash is found in a "file name", it should really be a slash. --- src/ldrawloader.cpp | 50 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/ldrawloader.cpp b/src/ldrawloader.cpp index e942128..7e1fb74 100644 --- a/src/ldrawloader.cpp +++ b/src/ldrawloader.cpp @@ -25,10 +25,15 @@ #include "ldrawloader.hpp" +#include + +#include +#include +#include + #include -#include -#include -#include + +#include #define LDR_DEBUG_FLAG_FILELOAD 1 #define LDR_DEBUG_FLAG 0 @@ -526,7 +531,7 @@ class Mesh sides[i] = vec_normalize(vec_sub(positions[triangles[t * 3 + i]], positions[triangles[t * 3 + (i + 1) % 3]])); } for(uint32_t i = 0; i < 3; i++) { - float angle = fabsf(vec_dot(vec_neg(sides[i]), sides[(i + 1) % 3])); + float angle = std::fabs(vec_dot(vec_neg(sides[i]), sides[(i + 1) % 3])); if(angle < minAngle) { minAngle = angle; corner = i; @@ -2094,7 +2099,7 @@ class MeshUtils } // just in case things go beyond south - if(isnan(chamferModifier) || isinf(chamferModifier) || (edgeA.isOpen() && edgeB.isOpen())) { + if(std::isnan(chamferModifier) || std::isinf(chamferModifier) || (edgeA.isOpen() && edgeB.isOpen())) { chamferModifier = 0; } @@ -2500,7 +2505,7 @@ class MeshUtils i = i; } - memset(localOutIdx.data(), LDR_INVALID_IDX, maxOutCount * sizeof(uint32_t)); + std::memset(localOutIdx.data(), LDR_INVALID_IDX, maxOutCount * sizeof(uint32_t)); for(uint32_t o = 0; o < outInfo.count; o++) { uint32_t rvtx = builder.vtxOutIndices[outInfo.begin + o]; @@ -2960,10 +2965,31 @@ LdrResult Loader::registerInternalPart(const char* filename, const std::string& return result; } +std::filesystem::path normalizePath(const std::string& messyPath) +{ + std::vector path_copy{messyPath.begin(), messyPath.end()}; +#if defined(WINDOWS) || defined(WIN32) + // Do nothing here. +#else + for (size_t i = 0 ; i < path_copy.size() ; ++i) { + if (path_copy[i] == '\\') { + path_copy[i] = '/'; + } + } +#endif + std::filesystem::path path{path_copy.begin(), path_copy.end()}; + std::filesystem::path canonicalPath = std::filesystem::weakly_canonical(path); + return canonicalPath.make_preferred(); +} + bool Loader::findLibraryFile(const char* filename, std::string& foundname, bool allowPrimitives, bool& isPrimitive) { + auto fname{normalizePath(filename)}; + for(uint32_t i = allowPrimitives ? (m_config.partHiResPrimitives ? 0 : 1) : PRIMITIVE_PATHS; i < SEARCH_PATHS; i++) { - foundname = m_searchPaths[i] + filename; + std::filesystem::path p{m_searchPaths[i]}; + p /= fname; + foundname = p.lexically_normal().native(); FILE* f = fopen(foundname.c_str(), "rb"); bool found = f != nullptr; @@ -3531,7 +3557,7 @@ LdrResult Loader::loadData(LdrPart& part, LdrRenderPart& renderPart, const char* float distC = vec_length(vec_sub(vecA, vecC)); float dist = std::min(std::min(distA, distB), distC); - float dotA = fabsf(vec_dot(vec_normalize(vec_sub(vecB, vecA)), vec_normalize(vec_sub(vecC, vecA)))); + float dotA = std::fabs(vec_dot(vec_normalize(vec_sub(vecB, vecA)), vec_normalize(vec_sub(vecC, vecA)))); if(dotA <= Loader::NO_AREA_TRIANGLE_DOT && dist > Loader::MIN_MERGE_EPSILON) { uint32_t vidx = (uint32_t)builder.positions.size(); uint32_t tidx = (uint32_t)builder.triangles.size() / 3; @@ -3581,8 +3607,8 @@ LdrResult Loader::loadData(LdrPart& part, LdrRenderPart& renderPart, const char* material = fixupMaterialID(material); - float dotA = fabsf(vec_dot(vec_normalize(vec_sub(vecB, vecA)), vec_normalize(vec_sub(vecC, vecA)))); - float dotD = fabsf(vec_dot(vec_normalize(vec_sub(vecA, vecD)), vec_normalize(vec_sub(vecC, vecD)))); + float dotA = std::fabs(vec_dot(vec_normalize(vec_sub(vecB, vecA)), vec_normalize(vec_sub(vecC, vecA)))); + float dotD = std::fabs(vec_dot(vec_normalize(vec_sub(vecA, vecD)), vec_normalize(vec_sub(vecC, vecD)))); if(dotA <= Loader::NO_AREA_TRIANGLE_DOT || dotD <= Loader::NO_AREA_TRIANGLE_DOT) { uint32_t vidx = (uint32_t)builder.positions.size(); uint32_t tidx = (uint32_t)builder.triangles.size() / 3; @@ -4404,7 +4430,7 @@ void Loader::fixPart(LdrPartID partid) fillBuilderPart(builder, partid); deinitPart(part); - memset(&part, 0, sizeof(LdrPart)); + std::memset(&part, 0, sizeof(LdrPart)); MeshUtils::fixBuilderPart(builder, m_config); @@ -4653,7 +4679,7 @@ void Loader::BuilderPart::getCanonicalQuad(uint32_t t, uint32_t quad[4]) const LDR_API void ldrGetDefaultCreateInfo(LdrLoaderCreateInfo* info) { - memset(info, 0, sizeof(LdrLoaderCreateInfo)); + std::memset(info, 0, sizeof(LdrLoaderCreateInfo)); info->partHiResPrimitives = LDR_FALSE; info->partFixMode = LDR_PART_FIX_NONE; info->partFixTjunctions = LDR_TRUE;