Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcacheutils.cpp
Go to the documentation of this file.
1// Qt-Security score:critical reason:data-parser
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5// Security note: This file reads user provided cooked mesh files which are then
6// fed to PhysX which creates meshes from these. This file also reads and writes
7// mesh cache files but that is assumed to be safe.
8
10
11#include <QFile>
12#include <QFileInfo>
13#include <QtQml/QQmlFile>
14#include <extensions/PxExtensionsAPI.h>
16
18namespace QCacheUtils {
19
21
22static QString MESH_CACHE_PATH = qEnvironmentVariable("QT_PHYSICS_CACHE_PATH");
23
24static QString getCachedFilename(const QString &filePath, CacheGeometry geom)
25{
26 const char *extension = "unknown_physx";
27 switch (geom) {
29 extension = "triangle_physx";
30 break;
32 extension = "convex_physx";
33 break;
35 extension = "heightfield_physx";
36 break;
37 }
38
39 return QString::fromUtf8("%1/%2.%3")
40 .arg(MESH_CACHE_PATH, QFileInfo(filePath).fileName(), QLatin1StringView(extension));
41}
42
43static void readCachedMesh(const QString &meshFilename, physx::PxPhysics &physics,
44 physx::PxTriangleMesh *&triangleMesh, physx::PxConvexMesh *&convexMesh,
45 physx::PxHeightField *&heightField, CacheGeometry geom)
46{
47 if (MESH_CACHE_PATH.isEmpty())
48 return;
49
50 QString cacheFilename = getCachedFilename(meshFilename, geom);
51 QFile cacheFile(cacheFilename);
52 QFile meshFile(meshFilename);
53 uchar *cacheData = nullptr;
54 uchar *meshData = nullptr;
55
56 auto cleanup = qScopeGuard([&] {
57 if (cacheData)
58 cacheFile.unmap(cacheData);
59 if (meshData)
60 meshFile.unmap(meshData);
61 if (cacheFile.isOpen())
62 cacheFile.close();
63 if (meshFile.isOpen())
64 meshFile.close();
65 });
66
67 if (!cacheFile.open(QIODevice::ReadOnly)) {
68 return;
69 }
70 if (!meshFile.open(QIODevice::ReadOnly)) {
71 qWarning() << "Could not open" << meshFilename;
72 return;
73 }
74
75 // first uint64 (8 bytes) is hash of input file
76 if (cacheFile.size() <= qint64(sizeof(uint64_t))) {
77 qWarning() << "Invalid convex mesh from file" << cacheFilename;
78 return;
79 }
80
81 cacheData = cacheFile.map(0, cacheFile.size());
82 if (!cacheData) {
83 qWarning() << "Could not map" << cacheFilename;
84 return;
85 }
86 uint64_t cacheHash = *reinterpret_cast<uint64_t *>(cacheData);
87
88 meshData = meshFile.map(0, meshFile.size());
89 if (!meshData) {
90 qWarning() << "Could not map" << meshFilename;
91 return;
92 }
93 uint64_t meshHash = qHash(QByteArrayView(meshData, meshFile.size()));
94
95 if (cacheHash != meshHash)
96 return; // hash is different, need to re-cook
97
98 physx::PxDefaultMemoryInputData input(cacheData + sizeof(uint64_t),
99 physx::PxU32(cacheFile.size() - sizeof(uint64_t)));
100
101 switch (geom) {
103 triangleMesh = physics.createTriangleMesh(input);
104 qCDebug(lcQuick3dPhysics) << "Read triangle mesh" << triangleMesh << "from file"
105 << cacheFilename;
106 break;
107 }
109 convexMesh = physics.createConvexMesh(input);
110 qCDebug(lcQuick3dPhysics) << "Read convex mesh" << convexMesh << "from file"
111 << cacheFilename;
112 break;
113 }
114 case CacheGeometry::HeightField:
115 heightField = physics.createHeightField(input);
116 qCDebug(lcQuick3dPhysics) << "Read height field" << heightField << "from file"
117 << cacheFilename;
118 break;
119 }
120}
121
122static void writeCachedMesh(const QString &meshFilename, physx::PxDefaultMemoryOutputStream &buf,
123 CacheGeometry geom)
124{
125 if (MESH_CACHE_PATH.isEmpty())
126 return;
127
128 QString cacheFilename = getCachedFilename(meshFilename, geom);
129 QFile cacheFile(cacheFilename);
130 QFile meshFile(meshFilename);
131 uchar *cacheData = nullptr;
132 uchar *meshData = nullptr;
133
134 auto cleanup = qScopeGuard([&] {
135 if (cacheData)
136 cacheFile.unmap(cacheData);
137 if (meshData)
138 meshFile.unmap(meshData);
139 if (cacheFile.isOpen())
140 cacheFile.close();
141 if (meshFile.isOpen())
142 meshFile.close();
143 });
144
145 if (!cacheFile.open(QIODevice::WriteOnly)) {
146 qCWarning(lcQuick3dPhysics) << "Could not open" << cacheFile.fileName() << "for writing.";
147 return;
148 }
149 if (!meshFile.open(QIODevice::ReadOnly)) {
150 qWarning() << "Could not open" << meshFilename;
151 return;
152 }
153
154 meshData = meshFile.map(0, meshFile.size());
155 if (!meshData) {
156 qWarning() << "Could not map" << meshFilename;
157 return;
158 }
159 uint64_t meshHash = qHash(QByteArrayView(meshData, meshFile.size()));
160
161 cacheFile.write(reinterpret_cast<char *>(&meshHash), sizeof(uint64_t));
162 cacheFile.write(reinterpret_cast<char *>(buf.getData()), buf.getSize());
163 cacheFile.close();
164
165 qCDebug(lcQuick3dPhysics) << "Wrote" << cacheFile.size() << "bytes to" << cacheFile.fileName();
166}
167
168void writeCachedTriangleMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
169{
170 writeCachedMesh(filePath, buf, CacheGeometry::TriangleMesh);
171}
172
173void writeCachedConvexMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
174{
175 writeCachedMesh(filePath, buf, CacheGeometry::ConvexMesh);
176}
177
178void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
179{
180 writeCachedMesh(filePath, buf, CacheGeometry::HeightField);
181}
182
183static void readCookedMesh(const QString &meshFilename, physx::PxPhysics &physics,
184 physx::PxTriangleMesh *&triangleMesh, physx::PxConvexMesh *&convexMesh,
185 physx::PxHeightField *&heightField, CacheGeometry geom)
186{
187 QFile file(meshFilename);
188 uchar *data = nullptr;
189
190 auto cleanup = qScopeGuard([&] {
191 if (data)
192 file.unmap(data);
193 if (file.isOpen())
194 file.close();
195 });
196
197 if (!file.open(QIODevice::ReadOnly)) {
198 qWarning() << "Could not open" << meshFilename;
199 return;
200 }
201
202 data = file.map(0, file.size());
203 if (!data) {
204 qWarning() << "Could not map" << meshFilename;
205 return;
206 }
207
208 physx::PxDefaultMemoryInputData input(data, physx::PxU32(file.size()));
209
210 switch (geom) {
212 triangleMesh = physics.createTriangleMesh(input);
213 break;
214 }
216 convexMesh = physics.createConvexMesh(input);
217 break;
218 }
219 case CacheGeometry::HeightField:
220 heightField = physics.createHeightField(input);
221 break;
222 }
223}
224
225physx::PxTriangleMesh *readCachedTriangleMesh(const QString &filePath, physx::PxPhysics &physics)
226{
227 physx::PxTriangleMesh *triangleMesh = nullptr;
228 physx::PxConvexMesh *convexMesh = nullptr;
229 physx::PxHeightField *heightField = nullptr;
230 readCachedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
232 return triangleMesh;
233}
234
235physx::PxConvexMesh *readCachedConvexMesh(const QString &filePath, physx::PxPhysics &physics)
236{
237 physx::PxTriangleMesh *triangleMesh = nullptr;
238 physx::PxConvexMesh *convexMesh = nullptr;
239 physx::PxHeightField *heightField = nullptr;
240 readCachedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
242 return convexMesh;
243}
244
245physx::PxHeightField *readCachedHeightField(const QString &filePath, physx::PxPhysics &physics)
246{
247 physx::PxTriangleMesh *triangleMesh = nullptr;
248 physx::PxConvexMesh *convexMesh = nullptr;
249 physx::PxHeightField *heightField = nullptr;
250 readCachedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
252 return heightField;
253}
254
255physx::PxTriangleMesh *readCookedTriangleMesh(const QString &filePath, physx::PxPhysics &physics)
256{
257 physx::PxTriangleMesh *triangleMesh = nullptr;
258 physx::PxConvexMesh *convexMesh = nullptr;
259 physx::PxHeightField *heightField = nullptr;
260 readCookedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
262 return triangleMesh;
263}
264
265physx::PxConvexMesh *readCookedConvexMesh(const QString &filePath, physx::PxPhysics &physics)
266{
267 physx::PxTriangleMesh *triangleMesh = nullptr;
268 physx::PxConvexMesh *convexMesh = nullptr;
269 physx::PxHeightField *heightField = nullptr;
270 readCookedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
272 return convexMesh;
273}
274
275physx::PxHeightField *readCookedHeightField(const QString &filePath, physx::PxPhysics &physics)
276{
277 physx::PxTriangleMesh *triangleMesh = nullptr;
278 physx::PxConvexMesh *convexMesh = nullptr;
279 physx::PxHeightField *heightField = nullptr;
280 readCookedMesh(filePath, physics, triangleMesh, convexMesh, heightField,
282 return heightField;
283}
284
285}
286QT_END_NAMESPACE
static void readCookedMesh(const QString &meshFilename, physx::PxPhysics &physics, physx::PxTriangleMesh *&triangleMesh, physx::PxConvexMesh *&convexMesh, physx::PxHeightField *&heightField, CacheGeometry geom)
void writeCachedConvexMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
void writeCachedTriangleMesh(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
static void readCachedMesh(const QString &meshFilename, physx::PxPhysics &physics, physx::PxTriangleMesh *&triangleMesh, physx::PxConvexMesh *&convexMesh, physx::PxHeightField *&heightField, CacheGeometry geom)
physx::PxTriangleMesh * readCachedTriangleMesh(const QString &filePath, physx::PxPhysics &physics)
static QString MESH_CACHE_PATH
void writeCachedHeightField(const QString &filePath, physx::PxDefaultMemoryOutputStream &buf)
physx::PxConvexMesh * readCookedConvexMesh(const QString &filePath, physx::PxPhysics &physics)
physx::PxTriangleMesh * readCookedTriangleMesh(const QString &filePath, physx::PxPhysics &physics)
physx::PxHeightField * readCookedHeightField(const QString &filePath, physx::PxPhysics &physics)
static void writeCachedMesh(const QString &meshFilename, physx::PxDefaultMemoryOutputStream &buf, CacheGeometry geom)
static QString getCachedFilename(const QString &filePath, CacheGeometry geom)
physx::PxConvexMesh * readCachedConvexMesh(const QString &filePath, physx::PxPhysics &physics)
physx::PxHeightField * readCachedHeightField(const QString &filePath, physx::PxPhysics &physics)