1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "assimphelpers.h"
5
6#include <QtCore/QFileDevice>
7#include <QtCore/QFileInfo>
8#include <QtCore/QUrl>
9#include <QtCore/QDir>
10#include <QtCore/QDebug>
11
12#include <memory>
13
14QT_BEGIN_NAMESPACE
15
16namespace Qt3DRender {
17namespace AssimpHelper {
18/*!
19 * \class Qt3DRender::AssimpHelper::AssimpIOStream
20 *
21 * \internal
22 *
23 * \brief Provides a custom file stream class to be used by AssimpIOSystem.
24 *
25 */
26
27/*!
28 * Builds a new AssimpIOStream instance.
29 */
30AssimpIOStream::AssimpIOStream(QIODevice *device) :
31 Assimp::IOStream(),
32 m_device(device)
33{
34 Q_ASSERT(m_device != nullptr);
35}
36
37/*!
38 * Clears an AssimpIOStream instance before deletion.
39 */
40AssimpIOStream::~AssimpIOStream()
41{
42 // Owns m_device
43 delete m_device;
44}
45
46/*!
47 * Reads at most \a pCount elements of \a pSize bytes of data into \a pvBuffer.
48 * Returns the number of bytes read or -1 if an error occurred.
49 */
50size_t AssimpIOStream::Read(void *pvBuffer, size_t pSize, size_t pCount)
51{
52 qint64 readBytes = m_device->read(data: (char *)pvBuffer, maxlen: pSize * pCount);
53 if (readBytes < 0)
54 qWarning() << Q_FUNC_INFO << " Reading failed";
55 return readBytes;
56}
57
58
59/*!
60 * Writes \a pCount elements of \a pSize bytes from \a pvBuffer.
61 * Returns the number of bytes written or -1 if an error occurred.
62 */
63size_t AssimpIOStream::Write(const void *pvBuffer, size_t pSize, size_t pCount)
64{
65 qint64 writtenBytes = m_device->write(data: (char *)pvBuffer, len: pSize * pCount);
66 if (writtenBytes < 0)
67 qWarning() << Q_FUNC_INFO << " Writing failed";
68 return writtenBytes;
69}
70
71/*!
72 * Seeks the current file descriptor to a position defined by \a pOrigin and \a pOffset
73 */
74aiReturn AssimpIOStream::Seek(size_t pOffset, aiOrigin pOrigin)
75{
76 qint64 seekPos = pOffset;
77
78 if (pOrigin == aiOrigin_CUR)
79 seekPos += m_device->pos();
80 else if (pOrigin == aiOrigin_END)
81 seekPos += m_device->size();
82
83 if (!m_device->seek(pos: seekPos)) {
84 qWarning() << Q_FUNC_INFO << " Seeking failed";
85 return aiReturn_FAILURE;
86 }
87 return aiReturn_SUCCESS;
88}
89
90/*!
91 * Returns the current position of the read/write cursor.
92 */
93size_t AssimpIOStream::Tell() const
94{
95 return m_device->pos();
96}
97
98/*!
99 * Returns the filesize;
100 */
101size_t AssimpIOStream::FileSize() const
102{
103 return m_device->size();
104}
105
106/*!
107 * Flushes the current device.
108 */
109void AssimpIOStream::Flush()
110{
111 // QIODevice has no flush method
112 if (QFileDevice *file = qobject_cast<QFileDevice *>(object: m_device))
113 file->flush();
114}
115
116/*!
117 * \class Qt3DRender::AssimpHelper::AssimpIOSystem
118 *
119 * \internal
120 *
121 * \brief Provides a custom file handler to the Assimp importer in order to handle
122 * various Qt specificities (QResources ...)
123 *
124 */
125
126static QIODevice::OpenMode openModeFromText(const char *name) noexcept
127{
128 static const struct OpenModeMapping {
129 char name[2];
130 ushort mode;
131 } openModeMapping[] = {
132 { .name: { 'r', 0 }, .mode: QIODevice::ReadOnly },
133 { .name: { 'r', '+' }, .mode: QIODevice::ReadWrite },
134 { .name: { 'w', 0 }, .mode: QIODevice::WriteOnly | QIODevice::Truncate },
135 { .name: { 'w', '+' }, .mode: QIODevice::ReadWrite | QIODevice::Truncate },
136 { .name: { 'a', 0 }, .mode: QIODevice::WriteOnly | QIODevice::Append },
137 { .name: { 'a', '+' }, .mode: QIODevice::ReadWrite | QIODevice::Append },
138 { .name: { 'w', 'b' }, .mode: QIODevice::WriteOnly },
139 { .name: { 'w', 't' }, .mode: QIODevice::WriteOnly | QIODevice::Text },
140 { .name: { 'r', 'b' }, .mode: QIODevice::ReadOnly },
141 { .name: { 'r', 't' }, .mode: QIODevice::ReadOnly | QIODevice::Text },
142 };
143
144 for (auto e : openModeMapping) {
145 if (qstrncmp(str1: e.name, str2: name, len: sizeof(OpenModeMapping::name)) == 0)
146 return static_cast<QIODevice::OpenMode>(e.mode);
147 }
148 return QIODevice::NotOpen;
149}
150
151/*!
152 * Returns true if the file located at pFile exists, false otherwise.
153 */
154bool AssimpIOSystem::Exists(const char *pFile) const
155{
156 return QFileInfo::exists(file: QString::fromUtf8(utf8: pFile));
157}
158
159/*!
160 * Returns the default system separator.
161 */
162char AssimpIOSystem::getOsSeparator() const
163{
164 return QDir::separator().toLatin1();
165}
166
167/*!
168 * Opens the file located at \a pFile with the opening mode
169 * specified by \a pMode.
170 */
171Assimp::IOStream *AssimpIOSystem::Open(const char *pFile, const char *pMode)
172{
173 const QString fileName(QString::fromUtf8(utf8: pFile));
174 const QLatin1String cleanedMode = QLatin1String{pMode}.trimmed();
175
176 if (const QIODevice::OpenMode openMode = openModeFromText(name: cleanedMode.data())) {
177 auto file = std::make_unique<QFile>(args: fileName);
178 if (file->open(flags: openMode))
179 return new AssimpIOStream(file.release());
180 }
181 return nullptr;
182}
183
184/*!
185 * Closes the Assimp::IOStream \a pFile.
186 */
187void AssimpIOSystem::Close(Assimp::IOStream *pFile)
188{
189 // Assimp::IOStream has a virtual destructor which closes the stream
190 delete pFile;
191}
192
193} // namespace AssimpHelper
194} // namespace Qt3DRender
195
196QT_END_NAMESPACE
197

source code of qt3d/src/plugins/sceneparsers/assimp/assimphelpers.cpp