rev |
line source |
nuclear@0
|
1 /*
|
nuclear@0
|
2 Open Asset Import Library (assimp)
|
nuclear@0
|
3 ----------------------------------------------------------------------
|
nuclear@0
|
4
|
nuclear@0
|
5 Copyright (c) 2006-2008, assimp team
|
nuclear@0
|
6 All rights reserved.
|
nuclear@0
|
7
|
nuclear@0
|
8 Redistribution and use of this software in source and binary forms,
|
nuclear@0
|
9 with or without modification, are permitted provided that the
|
nuclear@0
|
10 following conditions are met:
|
nuclear@0
|
11
|
nuclear@0
|
12 * Redistributions of source code must retain the above
|
nuclear@0
|
13 copyright notice, this list of conditions and the
|
nuclear@0
|
14 following disclaimer.
|
nuclear@0
|
15
|
nuclear@0
|
16 * Redistributions in binary form must reproduce the above
|
nuclear@0
|
17 copyright notice, this list of conditions and the
|
nuclear@0
|
18 following disclaimer in the documentation and/or other
|
nuclear@0
|
19 materials provided with the distribution.
|
nuclear@0
|
20
|
nuclear@0
|
21 * Neither the name of the assimp team, nor the names of its
|
nuclear@0
|
22 contributors may be used to endorse or promote products
|
nuclear@0
|
23 derived from this software without specific prior
|
nuclear@0
|
24 written permission of the assimp team.
|
nuclear@0
|
25
|
nuclear@0
|
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
nuclear@0
|
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
nuclear@0
|
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
nuclear@0
|
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
nuclear@0
|
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
nuclear@0
|
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
nuclear@0
|
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
nuclear@0
|
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
nuclear@0
|
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
nuclear@0
|
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
nuclear@0
|
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
nuclear@0
|
37
|
nuclear@0
|
38 ----------------------------------------------------------------------
|
nuclear@0
|
39 */
|
nuclear@0
|
40
|
nuclear@0
|
41 /** @file FileSystemFilter.h
|
nuclear@0
|
42 * Implements a filter system to filter calls to Exists() and Open()
|
nuclear@0
|
43 * in order to improve the sucess rate of file opening ...
|
nuclear@0
|
44 */
|
nuclear@0
|
45 #ifndef AI_FILESYSTEMFILTER_H_INC
|
nuclear@0
|
46 #define AI_FILESYSTEMFILTER_H_INC
|
nuclear@0
|
47
|
nuclear@0
|
48 #include "assimp/IOSystem.hpp"
|
nuclear@0
|
49 #include "fast_atof.h"
|
nuclear@0
|
50 #include "ParsingUtils.h"
|
nuclear@0
|
51 namespace Assimp {
|
nuclear@0
|
52
|
nuclear@0
|
53 inline bool IsHex(char s) {
|
nuclear@0
|
54 return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F');
|
nuclear@0
|
55 }
|
nuclear@0
|
56
|
nuclear@0
|
57 // ---------------------------------------------------------------------------
|
nuclear@0
|
58 /** File system filter
|
nuclear@0
|
59 */
|
nuclear@0
|
60 class FileSystemFilter : public IOSystem
|
nuclear@0
|
61 {
|
nuclear@0
|
62 public:
|
nuclear@0
|
63 /** Constructor. */
|
nuclear@0
|
64 FileSystemFilter(const std::string& file, IOSystem* old)
|
nuclear@0
|
65 : wrapped (old)
|
nuclear@0
|
66 , src_file (file)
|
nuclear@0
|
67 , sep(wrapped->getOsSeparator())
|
nuclear@0
|
68 {
|
nuclear@0
|
69 ai_assert(NULL != wrapped);
|
nuclear@0
|
70
|
nuclear@0
|
71 // Determine base directory
|
nuclear@0
|
72 base = src_file;
|
nuclear@0
|
73 std::string::size_type ss2;
|
nuclear@0
|
74 if (std::string::npos != (ss2 = base.find_last_of("\\/"))) {
|
nuclear@0
|
75 base.erase(ss2,base.length()-ss2);
|
nuclear@0
|
76 }
|
nuclear@0
|
77 else {
|
nuclear@0
|
78 base = "";
|
nuclear@0
|
79 // return;
|
nuclear@0
|
80 }
|
nuclear@0
|
81
|
nuclear@0
|
82 // make sure the directory is terminated properly
|
nuclear@0
|
83 char s;
|
nuclear@0
|
84
|
nuclear@0
|
85 if (base.length() == 0) {
|
nuclear@0
|
86 base = ".";
|
nuclear@0
|
87 base += getOsSeparator();
|
nuclear@0
|
88 }
|
nuclear@0
|
89 else if ((s = *(base.end()-1)) != '\\' && s != '/') {
|
nuclear@0
|
90 base += getOsSeparator();
|
nuclear@0
|
91 }
|
nuclear@0
|
92
|
nuclear@0
|
93 DefaultLogger::get()->info("Import root directory is \'" + base + "\'");
|
nuclear@0
|
94 }
|
nuclear@0
|
95
|
nuclear@0
|
96 /** Destructor. */
|
nuclear@0
|
97 ~FileSystemFilter()
|
nuclear@0
|
98 {
|
nuclear@0
|
99 // haha
|
nuclear@0
|
100 }
|
nuclear@0
|
101
|
nuclear@0
|
102 // -------------------------------------------------------------------
|
nuclear@0
|
103 /** Tests for the existence of a file at the given path. */
|
nuclear@0
|
104 bool Exists( const char* pFile) const
|
nuclear@0
|
105 {
|
nuclear@0
|
106 std::string tmp = pFile;
|
nuclear@0
|
107
|
nuclear@0
|
108 // Currently this IOSystem is also used to open THE ONE FILE.
|
nuclear@0
|
109 if (tmp != src_file) {
|
nuclear@0
|
110 BuildPath(tmp);
|
nuclear@0
|
111 Cleanup(tmp);
|
nuclear@0
|
112 }
|
nuclear@0
|
113
|
nuclear@0
|
114 return wrapped->Exists(tmp);
|
nuclear@0
|
115 }
|
nuclear@0
|
116
|
nuclear@0
|
117 // -------------------------------------------------------------------
|
nuclear@0
|
118 /** Returns the directory separator. */
|
nuclear@0
|
119 char getOsSeparator() const
|
nuclear@0
|
120 {
|
nuclear@0
|
121 return sep;
|
nuclear@0
|
122 }
|
nuclear@0
|
123
|
nuclear@0
|
124 // -------------------------------------------------------------------
|
nuclear@0
|
125 /** Open a new file with a given path. */
|
nuclear@0
|
126 IOStream* Open( const char* pFile, const char* pMode = "rb")
|
nuclear@0
|
127 {
|
nuclear@0
|
128 ai_assert(pFile);
|
nuclear@0
|
129 ai_assert(pMode);
|
nuclear@0
|
130
|
nuclear@0
|
131 // First try the unchanged path
|
nuclear@0
|
132 IOStream* s = wrapped->Open(pFile,pMode);
|
nuclear@0
|
133
|
nuclear@0
|
134 if (!s) {
|
nuclear@0
|
135 std::string tmp = pFile;
|
nuclear@0
|
136
|
nuclear@0
|
137 // Try to convert between absolute and relative paths
|
nuclear@0
|
138 BuildPath(tmp);
|
nuclear@0
|
139 s = wrapped->Open(tmp,pMode);
|
nuclear@0
|
140
|
nuclear@0
|
141 if (!s) {
|
nuclear@0
|
142 // Finally, look for typical issues with paths
|
nuclear@0
|
143 // and try to correct them. This is our last
|
nuclear@0
|
144 // resort.
|
nuclear@0
|
145 tmp = pFile;
|
nuclear@0
|
146 Cleanup(tmp);
|
nuclear@0
|
147 BuildPath(tmp);
|
nuclear@0
|
148 s = wrapped->Open(tmp,pMode);
|
nuclear@0
|
149 }
|
nuclear@0
|
150 }
|
nuclear@0
|
151
|
nuclear@0
|
152 return s;
|
nuclear@0
|
153 }
|
nuclear@0
|
154
|
nuclear@0
|
155 // -------------------------------------------------------------------
|
nuclear@0
|
156 /** Closes the given file and releases all resources associated with it. */
|
nuclear@0
|
157 void Close( IOStream* pFile)
|
nuclear@0
|
158 {
|
nuclear@0
|
159 return wrapped->Close(pFile);
|
nuclear@0
|
160 }
|
nuclear@0
|
161
|
nuclear@0
|
162 // -------------------------------------------------------------------
|
nuclear@0
|
163 /** Compare two paths */
|
nuclear@0
|
164 bool ComparePaths (const char* one, const char* second) const
|
nuclear@0
|
165 {
|
nuclear@0
|
166 return wrapped->ComparePaths (one,second);
|
nuclear@0
|
167 }
|
nuclear@0
|
168
|
nuclear@0
|
169 private:
|
nuclear@0
|
170
|
nuclear@0
|
171 // -------------------------------------------------------------------
|
nuclear@0
|
172 /** Build a valid path from a given relative or absolute path.
|
nuclear@0
|
173 */
|
nuclear@0
|
174 void BuildPath (std::string& in) const
|
nuclear@0
|
175 {
|
nuclear@0
|
176 // if we can already access the file, great.
|
nuclear@0
|
177 if (in.length() < 3 || wrapped->Exists(in)) {
|
nuclear@0
|
178 return;
|
nuclear@0
|
179 }
|
nuclear@0
|
180
|
nuclear@0
|
181 // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
|
nuclear@0
|
182 if (in[1] != ':') {
|
nuclear@0
|
183
|
nuclear@0
|
184 // append base path and try
|
nuclear@0
|
185 const std::string tmp = base + in;
|
nuclear@0
|
186 if (wrapped->Exists(tmp)) {
|
nuclear@0
|
187 in = tmp;
|
nuclear@0
|
188 return;
|
nuclear@0
|
189 }
|
nuclear@0
|
190 }
|
nuclear@0
|
191
|
nuclear@0
|
192 // Chop of the file name and look in the model directory, if
|
nuclear@0
|
193 // this fails try all sub paths of the given path, i.e.
|
nuclear@0
|
194 // if the given path is foo/bar/something.lwo, try
|
nuclear@0
|
195 // <base>/something.lwo
|
nuclear@0
|
196 // <base>/bar/something.lwo
|
nuclear@0
|
197 // <base>/foo/bar/something.lwo
|
nuclear@0
|
198 std::string::size_type pos = in.rfind('/');
|
nuclear@0
|
199 if (std::string::npos == pos) {
|
nuclear@0
|
200 pos = in.rfind('\\');
|
nuclear@0
|
201 }
|
nuclear@0
|
202
|
nuclear@0
|
203 if (std::string::npos != pos) {
|
nuclear@0
|
204 std::string tmp;
|
nuclear@0
|
205 std::string::size_type last_dirsep = std::string::npos;
|
nuclear@0
|
206
|
nuclear@0
|
207 while(true) {
|
nuclear@0
|
208 tmp = base;
|
nuclear@0
|
209 tmp += sep;
|
nuclear@0
|
210
|
nuclear@0
|
211 std::string::size_type dirsep = in.rfind('/', last_dirsep);
|
nuclear@0
|
212 if (std::string::npos == dirsep) {
|
nuclear@0
|
213 dirsep = in.rfind('\\', last_dirsep);
|
nuclear@0
|
214 }
|
nuclear@0
|
215
|
nuclear@0
|
216 if (std::string::npos == dirsep || dirsep == 0) {
|
nuclear@0
|
217 // we did try this already.
|
nuclear@0
|
218 break;
|
nuclear@0
|
219 }
|
nuclear@0
|
220
|
nuclear@0
|
221 last_dirsep = dirsep-1;
|
nuclear@0
|
222
|
nuclear@0
|
223 tmp += in.substr(dirsep+1, in.length()-pos);
|
nuclear@0
|
224 if (wrapped->Exists(tmp)) {
|
nuclear@0
|
225 in = tmp;
|
nuclear@0
|
226 return;
|
nuclear@0
|
227 }
|
nuclear@0
|
228 }
|
nuclear@0
|
229 }
|
nuclear@0
|
230
|
nuclear@0
|
231 // hopefully the underlying file system has another few tricks to access this file ...
|
nuclear@0
|
232 }
|
nuclear@0
|
233
|
nuclear@0
|
234 // -------------------------------------------------------------------
|
nuclear@0
|
235 /** Cleanup the given path
|
nuclear@0
|
236 */
|
nuclear@0
|
237 void Cleanup (std::string& in) const
|
nuclear@0
|
238 {
|
nuclear@0
|
239 char last = 0;
|
nuclear@0
|
240 if(in.empty()) {
|
nuclear@0
|
241 return;
|
nuclear@0
|
242 }
|
nuclear@0
|
243
|
nuclear@0
|
244 // Remove a very common issue when we're parsing file names: spaces at the
|
nuclear@0
|
245 // beginning of the path.
|
nuclear@0
|
246 std::string::iterator it = in.begin();
|
nuclear@0
|
247 while (IsSpaceOrNewLine( *it ))++it;
|
nuclear@0
|
248 if (it != in.begin()) {
|
nuclear@0
|
249 in.erase(in.begin(),it+1);
|
nuclear@0
|
250 }
|
nuclear@0
|
251
|
nuclear@0
|
252 const char sep = getOsSeparator();
|
nuclear@0
|
253 for (it = in.begin(); it != in.end(); ++it) {
|
nuclear@0
|
254 // Exclude :// and \\, which remain untouched.
|
nuclear@0
|
255 // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
|
nuclear@0
|
256 if ( !strncmp(&*it, "://", 3 )) {
|
nuclear@0
|
257 it += 3;
|
nuclear@0
|
258 continue;
|
nuclear@0
|
259 }
|
nuclear@0
|
260 if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
|
nuclear@0
|
261 it += 2;
|
nuclear@0
|
262 continue;
|
nuclear@0
|
263 }
|
nuclear@0
|
264
|
nuclear@0
|
265 // Cleanup path delimiters
|
nuclear@0
|
266 if (*it == '/' || (*it) == '\\') {
|
nuclear@0
|
267 *it = sep;
|
nuclear@0
|
268
|
nuclear@0
|
269 // And we're removing double delimiters, frequent issue with
|
nuclear@0
|
270 // incorrectly composited paths ...
|
nuclear@0
|
271 if (last == *it) {
|
nuclear@0
|
272 it = in.erase(it);
|
nuclear@0
|
273 --it;
|
nuclear@0
|
274 }
|
nuclear@0
|
275 }
|
nuclear@0
|
276 else if (*it == '%' && in.end() - it > 2) {
|
nuclear@0
|
277
|
nuclear@0
|
278 // Hex sequence in URIs
|
nuclear@0
|
279 if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) {
|
nuclear@0
|
280 *it = HexOctetToDecimal(&*it);
|
nuclear@0
|
281 it = in.erase(it+1,it+2);
|
nuclear@0
|
282 --it;
|
nuclear@0
|
283 }
|
nuclear@0
|
284 }
|
nuclear@0
|
285
|
nuclear@0
|
286 last = *it;
|
nuclear@0
|
287 }
|
nuclear@0
|
288 }
|
nuclear@0
|
289
|
nuclear@0
|
290 private:
|
nuclear@0
|
291 IOSystem* wrapped;
|
nuclear@0
|
292 std::string src_file, base;
|
nuclear@0
|
293 char sep;
|
nuclear@0
|
294 };
|
nuclear@0
|
295
|
nuclear@0
|
296 } //!ns Assimp
|
nuclear@0
|
297
|
nuclear@0
|
298 #endif //AI_DEFAULTIOSYSTEM_H_INC
|