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 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace Qt3DRender { |
17 | namespace 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 | */ |
30 | AssimpIOStream::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 | */ |
40 | AssimpIOStream::~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 | */ |
50 | size_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 | */ |
63 | size_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 | */ |
74 | aiReturn 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 | */ |
93 | size_t AssimpIOStream::Tell() const |
94 | { |
95 | return m_device->pos(); |
96 | } |
97 | |
98 | /*! |
99 | * Returns the filesize; |
100 | */ |
101 | size_t AssimpIOStream::FileSize() const |
102 | { |
103 | return m_device->size(); |
104 | } |
105 | |
106 | /*! |
107 | * Flushes the current device. |
108 | */ |
109 | void 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 | |
126 | static 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 | */ |
154 | bool 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 | */ |
162 | char 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 | */ |
171 | Assimp::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 | */ |
187 | void 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 | |
196 | QT_END_NAMESPACE |
197 | |