/src/assimp/code/AssetLib/3MF/D3MFOpcPackage.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2026, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following conditions are met: |
12 | | |
13 | | * Redistributions of source code must retain the above |
14 | | copyright notice, this list of conditions and the |
15 | | following disclaimer. |
16 | | |
17 | | * Redistributions in binary form must reproduce the above |
18 | | copyright notice, this list of conditions and the |
19 | | following disclaimer in the documentation and/or other |
20 | | materials provided with the distribution. |
21 | | |
22 | | * Neither the name of the assimp team, nor the names of its |
23 | | contributors may be used to endorse or promote products |
24 | | derived from this software without specific prior |
25 | | written permission of the assimp team. |
26 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | |
39 | | ---------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER |
43 | | |
44 | | #include "D3MFOpcPackage.h" |
45 | | #include <assimp/Exceptional.h> |
46 | | #include <assimp/XmlParser.h> |
47 | | #include <assimp/ZipArchiveIOSystem.h> |
48 | | #include <assimp/ai_assert.h> |
49 | | #include <assimp/DefaultLogger.hpp> |
50 | | #include <assimp/IOStream.hpp> |
51 | | #include <assimp/IOSystem.hpp> |
52 | | #include <assimp/texture.h> |
53 | | #include "3MFXmlTags.h" |
54 | | |
55 | | #include <algorithm> |
56 | | #include <cassert> |
57 | | #include <cstdlib> |
58 | | #include <map> |
59 | | #include <vector> |
60 | | |
61 | | namespace Assimp { |
62 | | |
63 | | namespace D3MF { |
64 | | // ------------------------------------------------------------------------------------------------ |
65 | | |
66 | | using OpcPackageRelationshipPtr = std::shared_ptr<OpcPackageRelationship>; |
67 | | |
68 | | class OpcPackageRelationshipReader final { |
69 | | public: |
70 | 0 | explicit OpcPackageRelationshipReader(XmlParser &parser) : mRelations() { |
71 | 0 | XmlNode root = parser.getRootNode(); |
72 | 0 | ParseRootNode(root); |
73 | 0 | } |
74 | | |
75 | 0 | void ParseRootNode(XmlNode &node) { |
76 | 0 | ParseAttributes(node); |
77 | 0 | for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { |
78 | 0 | std::string name = currentNode.name(); |
79 | 0 | if (name == "Relationships") { |
80 | 0 | ParseRelationsNode(currentNode); |
81 | 0 | } |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | 0 | void ParseAttributes(XmlNode & /*node*/) { |
86 | | // empty |
87 | 0 | } |
88 | | |
89 | 0 | bool validateRels(OpcPackageRelationshipPtr &relPtr) { |
90 | 0 | if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { |
91 | 0 | return false; |
92 | 0 | } |
93 | | |
94 | 0 | return true; |
95 | 0 | } |
96 | | |
97 | 0 | void ParseRelationsNode(XmlNode &node) { |
98 | 0 | if (node.empty()) { |
99 | 0 | return; |
100 | 0 | } |
101 | | |
102 | 0 | for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { |
103 | 0 | const std::string name = currentNode.name(); |
104 | 0 | if (name == "Relationship") { |
105 | 0 | OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); |
106 | 0 | relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); |
107 | 0 | relPtr->type = currentNode.attribute(XmlTag::RELS_ATTRIB_TYPE).as_string(); |
108 | 0 | relPtr->target = currentNode.attribute(XmlTag::RELS_ATTRIB_TARGET).as_string(); |
109 | 0 | if (validateRels(relPtr)) { |
110 | 0 | mRelations.push_back(relPtr); |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | std::vector<OpcPackageRelationshipPtr> mRelations; |
117 | | }; |
118 | | |
119 | 0 | static bool IsEmbeddedTexture( const std::string &filename ) { |
120 | 0 | const std::string extension = BaseImporter::GetExtension(filename); |
121 | 0 | if (extension == "jpg" || extension == "png" || extension == "jpeg") { |
122 | 0 | std::string::size_type pos = filename.find("thumbnail"); |
123 | 0 | if (pos != std::string::npos) { |
124 | 0 | return false; |
125 | 0 | } |
126 | 0 | return true; |
127 | 0 | } |
128 | | |
129 | 0 | return false; |
130 | 0 | } |
131 | | // ------------------------------------------------------------------------------------------------ |
132 | | D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : |
133 | 0 | mRootStream(nullptr), |
134 | 0 | mZipArchive() { |
135 | 0 | mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile); |
136 | 0 | if (!mZipArchive->isOpen()) { |
137 | 0 | throw DeadlyImportError("Failed to open file ", rFile, "."); |
138 | 0 | } |
139 | | |
140 | 0 | std::vector<std::string> fileList; |
141 | 0 | mZipArchive->getFileList(fileList); |
142 | |
|
143 | 0 | for (auto &file : fileList) { |
144 | 0 | if (file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) { |
145 | 0 | if (!mZipArchive->Exists(file.c_str())) { |
146 | 0 | continue; |
147 | 0 | } |
148 | | |
149 | 0 | IOStream *fileStream = mZipArchive->Open(file.c_str()); |
150 | 0 | if (nullptr == fileStream) { |
151 | 0 | ASSIMP_LOG_ERROR("Filestream is nullptr."); |
152 | 0 | continue; |
153 | 0 | } |
154 | | |
155 | 0 | std::string rootFile = ReadPackageRootRelationship(fileStream); |
156 | 0 | if (!rootFile.empty() && rootFile[0] == '/') { |
157 | 0 | rootFile = rootFile.substr(1); |
158 | 0 | if (rootFile[0] == '/') { |
159 | | // deal with zip-bug |
160 | 0 | rootFile = rootFile.substr(1); |
161 | 0 | } |
162 | 0 | } |
163 | |
|
164 | 0 | ASSIMP_LOG_VERBOSE_DEBUG(rootFile); |
165 | |
|
166 | 0 | mZipArchive->Close(fileStream); |
167 | |
|
168 | 0 | mRootStream = mZipArchive->Open(rootFile.c_str()); |
169 | 0 | ai_assert(mRootStream != nullptr); |
170 | 0 | if (nullptr == mRootStream) { |
171 | 0 | throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); |
172 | 0 | } |
173 | 0 | } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { |
174 | 0 | ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); |
175 | 0 | } else if (IsEmbeddedTexture(file)) { |
176 | 0 | IOStream *fileStream = mZipArchive->Open(file.c_str()); |
177 | 0 | LoadEmbeddedTextures(fileStream, file); |
178 | 0 | mZipArchive->Close(fileStream); |
179 | 0 | } else { |
180 | 0 | ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | 0 | D3MFOpcPackage::~D3MFOpcPackage() { |
186 | 0 | mZipArchive->Close(mRootStream); |
187 | 0 | delete mZipArchive; |
188 | 0 | } |
189 | | |
190 | 0 | IOStream *D3MFOpcPackage::RootStream() const { |
191 | 0 | return mRootStream; |
192 | 0 | } |
193 | | |
194 | 0 | const std::vector<aiTexture *> &D3MFOpcPackage::GetEmbeddedTextures() const { |
195 | 0 | return mEmbeddedTextures; |
196 | 0 | } |
197 | | |
198 | | static const char *const ModelRef = "3D/3dmodel.model"; |
199 | | |
200 | 0 | bool D3MFOpcPackage::validate() { |
201 | 0 | if (nullptr == mRootStream || nullptr == mZipArchive) { |
202 | 0 | return false; |
203 | 0 | } |
204 | | |
205 | 0 | return mZipArchive->Exists(ModelRef); |
206 | 0 | } |
207 | | |
208 | 0 | std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { |
209 | 0 | XmlParser xmlParser; |
210 | 0 | if (!xmlParser.parse(stream)) { |
211 | 0 | return std::string(); |
212 | 0 | } |
213 | | |
214 | 0 | OpcPackageRelationshipReader reader(xmlParser); |
215 | |
|
216 | 0 | auto itr = std::find_if(reader.mRelations.begin(), reader.mRelations.end(), [](const OpcPackageRelationshipPtr &rel) { |
217 | 0 | return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; |
218 | 0 | }); |
219 | |
|
220 | 0 | if (itr == reader.mRelations.end()) { |
221 | 0 | throw DeadlyImportError("Cannot find ", XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); |
222 | 0 | } |
223 | | |
224 | 0 | return (*itr)->target; |
225 | 0 | } |
226 | | |
227 | 0 | void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { |
228 | 0 | if (nullptr == fileStream) { |
229 | 0 | return; |
230 | 0 | } |
231 | | |
232 | 0 | const size_t size = fileStream->FileSize(); |
233 | 0 | if (0 == size) { |
234 | 0 | return; |
235 | 0 | } |
236 | | |
237 | 0 | unsigned char *data = new unsigned char[size]; |
238 | 0 | fileStream->Read(data, 1, size); |
239 | 0 | aiTexture *texture = new aiTexture; |
240 | 0 | std::string embName = "*" + filename; |
241 | 0 | texture->mFilename.Set(embName.c_str()); |
242 | 0 | texture->mWidth = static_cast<unsigned int>(size); |
243 | 0 | texture->mHeight = 0; |
244 | 0 | texture->achFormatHint[0] = 'p'; |
245 | 0 | texture->achFormatHint[1] = 'n'; |
246 | 0 | texture->achFormatHint[2] = 'g'; |
247 | 0 | texture->achFormatHint[3] = '\0'; |
248 | 0 | texture->pcData = (aiTexel*) data; |
249 | 0 | mEmbeddedTextures.emplace_back(texture); |
250 | 0 | } |
251 | | |
252 | | } // Namespace D3MF |
253 | | } // Namespace Assimp |
254 | | |
255 | | #endif //ASSIMP_BUILD_NO_3MF_IMPORTER |