vrshoot
diff libs/assimp/MD5Parser.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/MD5Parser.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,473 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file MD5Parser.cpp 1.46 + * @brief Implementation of the MD5 parser class 1.47 + */ 1.48 +#include "AssimpPCH.h" 1.49 + 1.50 +// internal headers 1.51 +#include "MD5Loader.h" 1.52 +#include "MaterialSystem.h" 1.53 +#include "fast_atof.h" 1.54 +#include "ParsingUtils.h" 1.55 +#include "StringComparison.h" 1.56 + 1.57 +using namespace Assimp; 1.58 +using namespace Assimp::MD5; 1.59 + 1.60 +// ------------------------------------------------------------------------------------------------ 1.61 +// Parse the segment structure fo a MD5 file 1.62 +MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize ) 1.63 +{ 1.64 + ai_assert(NULL != _buffer && 0 != _fileSize); 1.65 + 1.66 + buffer = _buffer; 1.67 + fileSize = _fileSize; 1.68 + lineNumber = 0; 1.69 + 1.70 + DefaultLogger::get()->debug("MD5Parser begin"); 1.71 + 1.72 + // parse the file header 1.73 + ParseHeader(); 1.74 + 1.75 + // and read all sections until we're finished 1.76 + bool running = true; 1.77 + while (running) { 1.78 + mSections.push_back(Section()); 1.79 + Section& sec = mSections.back(); 1.80 + if(!ParseSection(sec)) { 1.81 + break; 1.82 + } 1.83 + } 1.84 + 1.85 + if ( !DefaultLogger::isNullLogger()) { 1.86 + char szBuffer[128]; // should be sufficiently large 1.87 + ::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",(int)mSections.size()); 1.88 + DefaultLogger::get()->debug(szBuffer); 1.89 + } 1.90 +} 1.91 + 1.92 +// ------------------------------------------------------------------------------------------------ 1.93 +// Report error to the log stream 1.94 +/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line) 1.95 +{ 1.96 + char szBuffer[1024]; 1.97 + ::sprintf(szBuffer,"[MD5] Line %i: %s",line,error); 1.98 + throw DeadlyImportError(szBuffer); 1.99 +} 1.100 + 1.101 +// ------------------------------------------------------------------------------------------------ 1.102 +// Report warning to the log stream 1.103 +/*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line) 1.104 +{ 1.105 + char szBuffer[1024]; 1.106 + ::sprintf(szBuffer,"[MD5] Line %i: %s",line,warn); 1.107 + DefaultLogger::get()->warn(szBuffer); 1.108 +} 1.109 + 1.110 +// ------------------------------------------------------------------------------------------------ 1.111 +// Parse and validate the MD5 header 1.112 +void MD5Parser::ParseHeader() 1.113 +{ 1.114 + // parse and validate the file version 1.115 + SkipSpaces(); 1.116 + if (!TokenMatch(buffer,"MD5Version",10)) { 1.117 + ReportError("Invalid MD5 file: MD5Version tag has not been found"); 1.118 + } 1.119 + SkipSpaces(); 1.120 + unsigned int iVer = ::strtoul10(buffer,(const char**)&buffer); 1.121 + if (10 != iVer) { 1.122 + ReportError("MD5 version tag is unknown (10 is expected)"); 1.123 + } 1.124 + SkipLine(); 1.125 + 1.126 + // print the command line options to the console 1.127 + // FIX: can break the log length limit, so we need to be careful 1.128 + char* sz = buffer; 1.129 + while (!IsLineEnd( *buffer++)); 1.130 + DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz)))); 1.131 + SkipSpacesAndLineEnd(); 1.132 +} 1.133 + 1.134 +// ------------------------------------------------------------------------------------------------ 1.135 +// Recursive MD5 parsing function 1.136 +bool MD5Parser::ParseSection(Section& out) 1.137 +{ 1.138 + // store the current line number for use in error messages 1.139 + out.iLineNumber = lineNumber; 1.140 + 1.141 + // first parse the name of the section 1.142 + char* sz = buffer; 1.143 + while (!IsSpaceOrNewLine( *buffer))buffer++; 1.144 + out.mName = std::string(sz,(uintptr_t)(buffer-sz)); 1.145 + SkipSpaces(); 1.146 + 1.147 + bool running = true; 1.148 + while (running) { 1.149 + if ('{' == *buffer) { 1.150 + // it is a normal section so read all lines 1.151 + buffer++; 1.152 + bool run = true; 1.153 + while (run) 1.154 + { 1.155 + if (!SkipSpacesAndLineEnd()) { 1.156 + return false; // seems this was the last section 1.157 + } 1.158 + if ('}' == *buffer) { 1.159 + buffer++; 1.160 + break; 1.161 + } 1.162 + 1.163 + out.mElements.push_back(Element()); 1.164 + Element& elem = out.mElements.back(); 1.165 + 1.166 + elem.iLineNumber = lineNumber; 1.167 + elem.szStart = buffer; 1.168 + 1.169 + // terminate the line with zero 1.170 + while (!IsLineEnd( *buffer))buffer++; 1.171 + if (*buffer) { 1.172 + ++lineNumber; 1.173 + *buffer++ = '\0'; 1.174 + } 1.175 + } 1.176 + break; 1.177 + } 1.178 + else if (!IsSpaceOrNewLine(*buffer)) { 1.179 + // it is an element at global scope. Parse its value and go on 1.180 + sz = buffer; 1.181 + while (!IsSpaceOrNewLine( *buffer++)); 1.182 + out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz)); 1.183 + continue; 1.184 + } 1.185 + break; 1.186 + } 1.187 + return SkipSpacesAndLineEnd(); 1.188 +} 1.189 + 1.190 +// ------------------------------------------------------------------------------------------------ 1.191 +// Some dirty macros just because they're so funny and easy to debug 1.192 + 1.193 +// skip all spaces ... handle EOL correctly 1.194 +#define AI_MD5_SKIP_SPACES() if(!SkipSpaces(&sz)) \ 1.195 + MD5Parser::ReportWarning("Unexpected end of line",(*eit).iLineNumber); 1.196 + 1.197 + // read a triple float in brackets: (1.0 1.0 1.0) 1.198 +#define AI_MD5_READ_TRIPLE(vec) \ 1.199 + AI_MD5_SKIP_SPACES(); \ 1.200 + if ('(' != *sz++) \ 1.201 + MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber); \ 1.202 + AI_MD5_SKIP_SPACES(); \ 1.203 + sz = fast_atoreal_move<float>(sz,(float&)vec.x); \ 1.204 + AI_MD5_SKIP_SPACES(); \ 1.205 + sz = fast_atoreal_move<float>(sz,(float&)vec.y); \ 1.206 + AI_MD5_SKIP_SPACES(); \ 1.207 + sz = fast_atoreal_move<float>(sz,(float&)vec.z); \ 1.208 + AI_MD5_SKIP_SPACES(); \ 1.209 + if (')' != *sz++) \ 1.210 + MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber); 1.211 + 1.212 + // parse a string, enclosed in quotation marks or not 1.213 +#define AI_MD5_PARSE_STRING(out) \ 1.214 + bool bQuota = (*sz == '\"'); \ 1.215 + const char* szStart = sz; \ 1.216 + while (!IsSpaceOrNewLine(*sz))++sz; \ 1.217 + const char* szEnd = sz; \ 1.218 + if (bQuota) { \ 1.219 + szStart++; \ 1.220 + if ('\"' != *(szEnd-=1)) { \ 1.221 + MD5Parser::ReportWarning("Expected closing quotation marks in string", \ 1.222 + (*eit).iLineNumber); \ 1.223 + continue; \ 1.224 + } \ 1.225 + } \ 1.226 + out.length = (size_t)(szEnd - szStart); \ 1.227 + ::memcpy(out.data,szStart,out.length); \ 1.228 + out.data[out.length] = '\0'; 1.229 + 1.230 +// ------------------------------------------------------------------------------------------------ 1.231 +// .MD5MESH parsing function 1.232 +MD5MeshParser::MD5MeshParser(SectionList& mSections) 1.233 +{ 1.234 + DefaultLogger::get()->debug("MD5MeshParser begin"); 1.235 + 1.236 + // now parse all sections 1.237 + for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter){ 1.238 + if ( (*iter).mName == "numMeshes") { 1.239 + mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str())); 1.240 + } 1.241 + else if ( (*iter).mName == "numJoints") { 1.242 + mJoints.reserve(::strtoul10((*iter).mGlobalValue.c_str())); 1.243 + } 1.244 + else if ((*iter).mName == "joints") { 1.245 + // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 ) 1.246 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){ 1.247 + mJoints.push_back(BoneDesc()); 1.248 + BoneDesc& desc = mJoints.back(); 1.249 + 1.250 + const char* sz = (*eit).szStart; 1.251 + AI_MD5_PARSE_STRING(desc.mName); 1.252 + AI_MD5_SKIP_SPACES(); 1.253 + 1.254 + // negative values, at least -1, is allowed here 1.255 + desc.mParentIndex = (int)strtol10(sz,&sz); 1.256 + 1.257 + AI_MD5_READ_TRIPLE(desc.mPositionXYZ); 1.258 + AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there 1.259 + } 1.260 + } 1.261 + else if ((*iter).mName == "mesh") { 1.262 + mMeshes.push_back(MeshDesc()); 1.263 + MeshDesc& desc = mMeshes.back(); 1.264 + 1.265 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){ 1.266 + const char* sz = (*eit).szStart; 1.267 + 1.268 + // shader attribute 1.269 + if (TokenMatch(sz,"shader",6)) { 1.270 + AI_MD5_SKIP_SPACES(); 1.271 + AI_MD5_PARSE_STRING(desc.mShader); 1.272 + } 1.273 + // numverts attribute 1.274 + else if (TokenMatch(sz,"numverts",8)) { 1.275 + AI_MD5_SKIP_SPACES(); 1.276 + desc.mVertices.resize(strtoul10(sz)); 1.277 + } 1.278 + // numtris attribute 1.279 + else if (TokenMatch(sz,"numtris",7)) { 1.280 + AI_MD5_SKIP_SPACES(); 1.281 + desc.mFaces.resize(strtoul10(sz)); 1.282 + } 1.283 + // numweights attribute 1.284 + else if (TokenMatch(sz,"numweights",10)) { 1.285 + AI_MD5_SKIP_SPACES(); 1.286 + desc.mWeights.resize(strtoul10(sz)); 1.287 + } 1.288 + // vert attribute 1.289 + // "vert 0 ( 0.394531 0.513672 ) 0 1" 1.290 + else if (TokenMatch(sz,"vert",4)) { 1.291 + AI_MD5_SKIP_SPACES(); 1.292 + const unsigned int idx = ::strtoul10(sz,&sz); 1.293 + AI_MD5_SKIP_SPACES(); 1.294 + if (idx >= desc.mVertices.size()) 1.295 + desc.mVertices.resize(idx+1); 1.296 + 1.297 + VertexDesc& vert = desc.mVertices[idx]; 1.298 + if ('(' != *sz++) 1.299 + MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber); 1.300 + AI_MD5_SKIP_SPACES(); 1.301 + sz = fast_atoreal_move<float>(sz,(float&)vert.mUV.x); 1.302 + AI_MD5_SKIP_SPACES(); 1.303 + sz = fast_atoreal_move<float>(sz,(float&)vert.mUV.y); 1.304 + AI_MD5_SKIP_SPACES(); 1.305 + if (')' != *sz++) 1.306 + MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber); 1.307 + AI_MD5_SKIP_SPACES(); 1.308 + vert.mFirstWeight = ::strtoul10(sz,&sz); 1.309 + AI_MD5_SKIP_SPACES(); 1.310 + vert.mNumWeights = ::strtoul10(sz,&sz); 1.311 + } 1.312 + // tri attribute 1.313 + // "tri 0 15 13 12" 1.314 + else if (TokenMatch(sz,"tri",3)) { 1.315 + AI_MD5_SKIP_SPACES(); 1.316 + const unsigned int idx = strtoul10(sz,&sz); 1.317 + if (idx >= desc.mFaces.size()) 1.318 + desc.mFaces.resize(idx+1); 1.319 + 1.320 + aiFace& face = desc.mFaces[idx]; 1.321 + face.mIndices = new unsigned int[face.mNumIndices = 3]; 1.322 + for (unsigned int i = 0; i < 3;++i) { 1.323 + AI_MD5_SKIP_SPACES(); 1.324 + face.mIndices[i] = strtoul10(sz,&sz); 1.325 + } 1.326 + } 1.327 + // weight attribute 1.328 + // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )" 1.329 + else if (TokenMatch(sz,"weight",6)) { 1.330 + AI_MD5_SKIP_SPACES(); 1.331 + const unsigned int idx = strtoul10(sz,&sz); 1.332 + AI_MD5_SKIP_SPACES(); 1.333 + if (idx >= desc.mWeights.size()) 1.334 + desc.mWeights.resize(idx+1); 1.335 + 1.336 + WeightDesc& weight = desc.mWeights[idx]; 1.337 + weight.mBone = strtoul10(sz,&sz); 1.338 + AI_MD5_SKIP_SPACES(); 1.339 + sz = fast_atoreal_move<float>(sz,weight.mWeight); 1.340 + AI_MD5_READ_TRIPLE(weight.vOffsetPosition); 1.341 + } 1.342 + } 1.343 + } 1.344 + } 1.345 + DefaultLogger::get()->debug("MD5MeshParser end"); 1.346 +} 1.347 + 1.348 +// ------------------------------------------------------------------------------------------------ 1.349 +// .MD5ANIM parsing function 1.350 +MD5AnimParser::MD5AnimParser(SectionList& mSections) 1.351 +{ 1.352 + DefaultLogger::get()->debug("MD5AnimParser begin"); 1.353 + 1.354 + fFrameRate = 24.0f; 1.355 + mNumAnimatedComponents = UINT_MAX; 1.356 + for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) { 1.357 + if ((*iter).mName == "hierarchy") { 1.358 + // "sheath" 0 63 6 1.359 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit) { 1.360 + mAnimatedBones.push_back ( AnimBoneDesc () ); 1.361 + AnimBoneDesc& desc = mAnimatedBones.back(); 1.362 + 1.363 + const char* sz = (*eit).szStart; 1.364 + AI_MD5_PARSE_STRING(desc.mName); 1.365 + AI_MD5_SKIP_SPACES(); 1.366 + 1.367 + // parent index - negative values are allowed (at least -1) 1.368 + desc.mParentIndex = ::strtol10(sz,&sz); 1.369 + 1.370 + // flags (highest is 2^6-1) 1.371 + AI_MD5_SKIP_SPACES(); 1.372 + if(63 < (desc.iFlags = ::strtoul10(sz,&sz))){ 1.373 + MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",(*eit).iLineNumber); 1.374 + } 1.375 + AI_MD5_SKIP_SPACES(); 1.376 + 1.377 + // index of the first animation keyframe component for this joint 1.378 + desc.iFirstKeyIndex = ::strtoul10(sz,&sz); 1.379 + } 1.380 + } 1.381 + else if((*iter).mName == "baseframe") { 1.382 + // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 ) 1.383 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit) { 1.384 + const char* sz = (*eit).szStart; 1.385 + 1.386 + mBaseFrames.push_back ( BaseFrameDesc () ); 1.387 + BaseFrameDesc& desc = mBaseFrames.back(); 1.388 + 1.389 + AI_MD5_READ_TRIPLE(desc.vPositionXYZ); 1.390 + AI_MD5_READ_TRIPLE(desc.vRotationQuat); 1.391 + } 1.392 + } 1.393 + else if((*iter).mName == "frame") { 1.394 + if (!(*iter).mGlobalValue.length()) { 1.395 + MD5Parser::ReportWarning("A frame section must have a frame index",(*iter).iLineNumber); 1.396 + continue; 1.397 + } 1.398 + 1.399 + mFrames.push_back ( FrameDesc () ); 1.400 + FrameDesc& desc = mFrames.back(); 1.401 + desc.iIndex = strtoul10((*iter).mGlobalValue.c_str()); 1.402 + 1.403 + // we do already know how much storage we will presumably need 1.404 + if (UINT_MAX != mNumAnimatedComponents) { 1.405 + desc.mValues.reserve(mNumAnimatedComponents); 1.406 + } 1.407 + 1.408 + // now read all elements (continous list of floats) 1.409 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){ 1.410 + const char* sz = (*eit).szStart; 1.411 + while (SkipSpacesAndLineEnd(&sz)) { 1.412 + float f;sz = fast_atoreal_move<float>(sz,f); 1.413 + desc.mValues.push_back(f); 1.414 + } 1.415 + } 1.416 + } 1.417 + else if((*iter).mName == "numFrames") { 1.418 + mFrames.reserve(strtoul10((*iter).mGlobalValue.c_str())); 1.419 + } 1.420 + else if((*iter).mName == "numJoints") { 1.421 + const unsigned int num = strtoul10((*iter).mGlobalValue.c_str()); 1.422 + mAnimatedBones.reserve(num); 1.423 + 1.424 + // try to guess the number of animated components if that element is not given 1.425 + if (UINT_MAX == mNumAnimatedComponents) { 1.426 + mNumAnimatedComponents = num * 6; 1.427 + } 1.428 + } 1.429 + else if((*iter).mName == "numAnimatedComponents") { 1.430 + mAnimatedBones.reserve( strtoul10((*iter).mGlobalValue.c_str())); 1.431 + } 1.432 + else if((*iter).mName == "frameRate") { 1.433 + fast_atoreal_move<float>((*iter).mGlobalValue.c_str(),fFrameRate); 1.434 + } 1.435 + } 1.436 + DefaultLogger::get()->debug("MD5AnimParser end"); 1.437 +} 1.438 + 1.439 +// ------------------------------------------------------------------------------------------------ 1.440 +// .MD5CAMERA parsing function 1.441 +MD5CameraParser::MD5CameraParser(SectionList& mSections) 1.442 +{ 1.443 + DefaultLogger::get()->debug("MD5CameraParser begin"); 1.444 + fFrameRate = 24.0f; 1.445 + 1.446 + for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) { 1.447 + if ((*iter).mName == "numFrames") { 1.448 + frames.reserve(strtoul10((*iter).mGlobalValue.c_str())); 1.449 + } 1.450 + else if ((*iter).mName == "frameRate") { 1.451 + fFrameRate = fast_atof ((*iter).mGlobalValue.c_str()); 1.452 + } 1.453 + else if ((*iter).mName == "numCuts") { 1.454 + cuts.reserve(strtoul10((*iter).mGlobalValue.c_str())); 1.455 + } 1.456 + else if ((*iter).mName == "cuts") { 1.457 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){ 1.458 + cuts.push_back(strtoul10((*eit).szStart)+1); 1.459 + } 1.460 + } 1.461 + else if ((*iter).mName == "camera") { 1.462 + for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){ 1.463 + const char* sz = (*eit).szStart; 1.464 + 1.465 + frames.push_back(CameraAnimFrameDesc()); 1.466 + CameraAnimFrameDesc& cur = frames.back(); 1.467 + AI_MD5_READ_TRIPLE(cur.vPositionXYZ); 1.468 + AI_MD5_READ_TRIPLE(cur.vRotationQuat); 1.469 + AI_MD5_SKIP_SPACES(); 1.470 + cur.fFOV = fast_atof(sz); 1.471 + } 1.472 + } 1.473 + } 1.474 + DefaultLogger::get()->debug("MD5CameraParser end"); 1.475 +} 1.476 +