text: #include "CCBReader.h"
  
  #include <algorithm>
  
  #include "CCNodeLoader.h"
  #include "CCNodeLoaderLibrary.h"
  #include "CCNodeLoaderListener.h"
  #include "CCBMemberVariableAssigner.h"
  #include "CCBSelectorResolver.h"
  #include "CCBAnimationManager.h"
  #include "CCBSequenceProperty.h"
  #include "CCBKeyframe.h"
  #include "CCBValue.h"
  
  #include <ctype.h>
  
  using namespace std;
  using namespace cocos2d;
  using namespace cocos2d::extension;
  
  namespace spritebuilder {;
  
  /*************************************************************************
   Implementation of CCBFile
   *************************************************************************/
  
  CCBFile::CCBFile():_CCBFileNode(NULL) {}
  
  CCBFile* CCBFile::create()
  {
      CCBFile *ret = new CCBFile();
      
      if (ret)
      {
          ret->autorelease();
      }
      
      return ret;
  }
  
  Node* CCBFile::getCCBFileNode()
  {
      return _CCBFileNode;
  }
  
  void CCBFile::setCCBFileNode(Node *pNode)
  {
      CC_SAFE_RELEASE(_CCBFileNode);
      _CCBFileNode = pNode;
      CC_SAFE_RETAIN(_CCBFileNode);
  }
  
  /*************************************************************************
   Implementation of CCBReader
   *************************************************************************/
   
  static CCBReader * sSharedCCBReader = NULL;
    
  CCBReader* CCBReader::getInstance()
  {
      if(sSharedCCBReader == NULL) {
          sSharedCCBReader = new CCBReader(NodeLoaderLibrary::getInstance());
      }
      return sSharedCCBReader;
  }
  
  void CCBReader::end()
  {
      CC_SAFE_DELETE(sSharedCCBReader);
  }
  
  CCBReader::CCBReader(NodeLoaderLibrary * pNodeLoaderLibrary, CCBMemberVariableAssigner * pCCBMemberVariableAssigner, CCBSelectorResolver * pCCBSelectorResolver, NodeLoaderListener * pNodeLoaderListener) 
  : _data(NULL)
  , _bytes(NULL)
  , _currentByte(-1)
  , _currentBit(-1)
  , _owner(NULL)
  , _actionManager(NULL)
  , _actionManagers(NULL)
  , _animatedProps(NULL)
  , _ownerOutletNodes(NULL)
  , _nodesWithAnimationManagers(NULL)
  , _animationManagersForNodes(NULL)
  , _ownerCallbackNodes(NULL)
  {
      this->_nodeLoaderLibrary = pNodeLoaderLibrary;
      this->_nodeLoaderLibrary->retain();
      this->_CCBMemberVariableAssigner = pCCBMemberVariableAssigner;
      this->_CCBSelectorResolver = pCCBSelectorResolver;
      this->_nodeLoaderListener = pNodeLoaderListener;
      init();
  }
  
  CCBReader::CCBReader(CCBReader * ccbReader) 
  : _data(NULL)
  , _bytes(NULL)
  , _currentByte(-1)
  , _currentBit(-1)
  , _owner(NULL)
  , _actionManager(NULL)
  , _actionManagers(NULL)
  , _animatedProps(NULL)
  , _ownerOutletNodes(NULL)
  , _nodesWithAnimationManagers(NULL)
  , _animationManagersForNodes(NULL)
  , _ownerCallbackNodes(NULL)
  {
      this->_loadedSpriteSheets = ccbReader->_loadedSpriteSheets;
      this->_nodeLoaderLibrary = ccbReader->_nodeLoaderLibrary;
      this->_nodeLoaderLibrary->retain();
  
      this->_CCBMemberVariableAssigner = ccbReader->_CCBMemberVariableAssigner;
      this->_CCBSelectorResolver = ccbReader->_CCBSelectorResolver;
      this->_nodeLoaderListener = ccbReader->_nodeLoaderListener;
      
      this->_CCBRootPath = ccbReader->getCCBRootPath();
      
      init();
  }
  
  CCBReader::CCBReader()
  : _data(NULL)
  , _bytes(NULL)
  , _currentByte(-1)
  , _currentBit(-1)
  , _owner(NULL)
  , _actionManager(NULL)
  , _actionManagers(NULL)
  , _nodeLoaderLibrary(NULL)
  , _nodeLoaderListener(NULL)
  , _CCBMemberVariableAssigner(NULL)
  , _CCBSelectorResolver(NULL)
  , _nodesWithAnimationManagers(NULL)
  , _animationManagersForNodes(NULL)
  {
      init();
  }
  
  CCBReader::~CCBReader()
  {
      CC_SAFE_RELEASE_NULL(_owner);
      CC_SAFE_RELEASE_NULL(_data);
  
      this->_nodeLoaderLibrary->release();
  
      CC_SAFE_RELEASE(_ownerOutletNodes);
      _ownerOutletNames.clear();
      CC_SAFE_RELEASE(_ownerCallbackNodes);
      _ownerCallbackNames.clear();
      CC_SAFE_RELEASE(_ownerOwnerCallbackControlEvents);
      
      // Clear string cache.
      this->_stringCache.clear();
      CC_SAFE_RELEASE(_nodesWithAnimationManagers);
      CC_SAFE_RELEASE(_animationManagersForNodes);
  
      setAnimationManager(NULL);
  }
  
  void CCBReader::setCCBRootPath(const char* ccbRootPath)
  {
      CCASSERT(ccbRootPath != NULL, "");
      _CCBRootPath = ccbRootPath;
  }
  
  const std::string& CCBReader::getCCBRootPath() const
  {
      return _CCBRootPath;
  }
  
  bool CCBReader::init()
  {
      _ownerOutletNodes = new Array();
      _ownerOutletNodes->init();
      _ownerCallbackNodes = new Array();
      _ownerCallbackNodes->init();
      _ownerOwnerCallbackControlEvents = new Array();
      _ownerOwnerCallbackControlEvents->init();
  
      // Setup action manager
      CCBAnimationManager *pActionManager = new CCBAnimationManager();
      setAnimationManager(pActionManager);
      pActionManager->release();
      
      // Setup resolution scale and container size
      _actionManager->setRootContainerSize(Director::getInstance()->getWinSize());
      
      return true;
  }
  
  CCBAnimationManager* CCBReader::getAnimationManager()
  {
      return _actionManager;
  }
  
  void CCBReader::setAnimationManager(CCBAnimationManager *pAnimationManager)
  {
      CC_SAFE_RELEASE(_actionManager);
      _actionManager = pAnimationManager;
      CC_SAFE_RETAIN(_actionManager);
  }
  
  Dictionary* CCBReader::getAnimationManagers()
  {
      return _actionManagers;
  }
  
  void CCBReader::setAnimationManagers(Dictionary* x)
  {
      _actionManagers = x;
  }
  
  CCBMemberVariableAssigner * CCBReader::getCCBMemberVariableAssigner() {
      return this->_CCBMemberVariableAssigner;
  }
  
  CCBSelectorResolver * CCBReader::getCCBSelectorResolver() {
      return this->_CCBSelectorResolver;
  }
  
  set<string>* CCBReader::getAnimatedProperties()
  {
      return _animatedProps;
  }
  
  set<string>& CCBReader::getLoadedSpriteSheet()
  {
      return _loadedSpriteSheets;
  }
  
  Object* CCBReader::getOwner()
  {
      return _owner;
  }
  
  Node* CCBReader::readNodeGraphFromFile(const char *pCCBFileName)
  {
      return this->readNodeGraphFromFile(pCCBFileName, NULL);
  }
  
  Node* CCBReader::readNodeGraphFromFile(const char* pCCBFileName, Object* pOwner) 
  {
      return this->readNodeGraphFromFile(pCCBFileName, pOwner, Director::getInstance()->getWinSize());
  }
  
  Node* CCBReader::readNodeGraphFromFile(const char *pCCBFileName, Object *pOwner, const Size &parentSize)
  {
      if (NULL == pCCBFileName || strlen(pCCBFileName) == 0)
      {
          return NULL;
      }
  
      std::string strCCBFileName(pCCBFileName);
      std::string strSuffix(".ccbi");
      // Add ccbi suffix
      if (!CCBReader::endsWith(strCCBFileName.c_str(), strSuffix.c_str()))
      {
          strCCBFileName += strSuffix;
      }
  
      std::string strPath = FileUtils::getInstance()->fullPathForFilename(strCCBFileName.c_str());
      long size = 0;
  
      unsigned char * pBytes = FileUtils::getInstance()->getFileData(strPath.c_str(), "rb", &size);
      Data *data = new Data(pBytes, size);
      CC_SAFE_DELETE_ARRAY(pBytes);
  
      Node *ret =  this->readNodeGraphFromData(data, pOwner, parentSize);
      
      data->release();
      
      return ret;
  }
  
  Node* CCBReader::readNodeGraphFromData(Data *pData, Object *pOwner, const Size &parentSize)
  {
     _data = pData;
      CC_SAFE_RETAIN(_data);
      _bytes =_data->getBytes();
      _currentByte = 0;
      _currentBit = 0;
      _owner = pOwner;
      CC_SAFE_RETAIN(_owner);
  
      _actionManager->setRootContainerSize(parentSize);
      _actionManager->_owner = _owner;
      
      Dictionary* animationManagers = Dictionary::create();
      Node *pNodeGraph = readFileWithCleanUp(true, animationManagers);
      
      if (pNodeGraph && _actionManager->getAutoPlaySequenceId() != -1)
      {
          // Auto play animations
          _actionManager->runAnimationsForSequenceIdTweenDuration(_actionManager->getAutoPlaySequenceId(), 0);
      }
      
      // Assign actionManagers to userObject
      if(_jsControlled)
      {
          _nodesWithAnimationManagers = new Array();
          _nodesWithAnimationManagers->init();
          _animationManagersForNodes = new Array();
          _animationManagersForNodes->init();
      }
      
      DictElement* pElement = NULL;
      CCDICT_FOREACH(animationManagers, pElement)
      {
          Node* pNode = (Node*)pElement->getIntKey();
          CCBAnimationManager* manager = static_cast<CCBAnimationManager*>(animationManagers->objectForKey((intptr_t)pNode));
          pNode->setUserObject(manager);
  
          if (_jsControlled)
          {
              _nodesWithAnimationManagers->addObject(pNode);
              _animationManagersForNodes->addObject(manager);
          }
      }
      
      return pNodeGraph;
  }
  
  Scene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName)
  {
      return createSceneWithNodeGraphFromFile(pCCBFileName, NULL);
  }
  
  Scene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName, Object *pOwner)
  {
      return createSceneWithNodeGraphFromFile(pCCBFileName, pOwner, Director::getInstance()->getWinSize());
  }
  
  Scene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName, Object *pOwner, const Size &parentSize)
  {
      Node *pNode = readNodeGraphFromFile(pCCBFileName, pOwner, parentSize);
      Scene *pScene = Scene::create();
      pScene->addChild(pNode);
      
      return pScene;
  }
  
  void CCBReader::cleanUpNodeGraph(Node *pNode)
  {
      pNode->setUserObject(NULL);
      
      Object *pChild = NULL;
      CCARRAY_FOREACH(pNode->getChildren(), pChild)
      {
          cleanUpNodeGraph(static_cast<Node*>(pChild));
      }
  }
  
  Node* CCBReader::readFileWithCleanUp(bool bCleanUp, Dictionary* am)
  {
      if (! readHeader())
      {
          return NULL;
      }
      
      if (! readStringCache())
      {
          return NULL;
      }
      
      if (! readSequences())
      {
          return NULL;
      }
      
      setAnimationManagers(am);
  
      Node *pNode = readNodeGraph(NULL);
  
      _actionManagers->setObject(_actionManager, intptr_t(pNode));
  
      if (bCleanUp)
      {
          cleanUpNodeGraph(pNode);
      }
      
      return pNode;
  }
  
  bool CCBReader::readStringCache() {
      int numStrings = this->readInt(false);
  
      for(int i = 0; i < numStrings; i++) {
          this->_stringCache.push_back(this->readUTF8());
      }
  
      return true;
  }
  
  bool CCBReader::readHeader()
  {
      /* If no bytes loaded, don't crash about it. */
      if(this->_bytes == NULL) {
          return false;
      }
  
      /* Read magic bytes */
      int magicBytes = *((int*)(this->_bytes + this->_currentByte));
      this->_currentByte += 4;
  
      if(CC_SWAP_INT32_LITTLE_TO_HOST(magicBytes) != (*reinterpret_cast<const int*>("ibcc"))) {
          return false; 
      }
  
      /* Read version. */
      int version = this->readInt(false);
      if(version != SB_VERSION) {
          log("WARNING! Incompatible ccbi file version (file: %d reader: %d)", version, SB_VERSION);
          return false;
      }
  
      // Read JS check
      _jsControlled = false; // this->readBool();
      _actionManager->_jsControlled = _jsControlled;
  
      return true;
  }
  
  unsigned char CCBReader::readByte()
  {
      unsigned char byte = this->_bytes[this->_currentByte];
      this->_currentByte++;
      return byte;
  }
  
  bool CCBReader::readBool()
  {
      return 0 == this->readByte() ? false : true;
  }
  
  std::string CCBReader::readUTF8()
  {
      std::string ret;
  
      int b0 = this->readByte();
      int b1 = this->readByte();
  
      int numBytes = b0 << 8 | b1;
  
      char* pStr = (char*)malloc(numBytes+1);
      memcpy(pStr, _bytes+_currentByte, numBytes);
      pStr[numBytes] = '\0';
      ret = pStr;
      free(pStr);
  
      _currentByte += numBytes;
  
      return ret;
  }
  
  bool CCBReader::getBit() {
      bool bit;
      unsigned char byte = *(this->_bytes + this->_currentByte);
      if(byte & (1 << this->_currentBit)) {
          bit = true;
      } else {
          bit = false;
      }
  
      this->_currentBit++;
  
      if(this->_currentBit >= 8) {
          this->_currentBit = 0;
          this->_currentByte++;
      }
  
      return bit;
  }
  
  void CCBReader::alignBits() {
      if(this->_currentBit) {
          this->_currentBit = 0;
          this->_currentByte++;
      }
  }
  
  int CCBReader::readInt(bool pSigned) {
      // Read encoded int
      int numBits = 0;
      while(!this->getBit()) {
          numBits++;
      }
      
      long long current = 0;
      for(int a = numBits - 1; a >= 0; a--) {
          if(this->getBit()) {
              current |= 1LL << a;
          }
      }
      current |= 1LL << numBits;
      
      int num;
      if(pSigned) {
          int s = current % 2;
          if(s) {
              num = static_cast<int>(current / 2);
          } else {
              num = static_cast<int>(-current / 2);
          }
      } else {
          num = static_cast<int>(current - 1);
      }
      
      this->alignBits();
      
      return num;
  }
  
  
  float CCBReader::readFloat()
  {
      FloatType type = static_cast<FloatType>(this->readByte());
      
      switch (type)
      {
          case FloatType::_0:
              return 0;    
          case FloatType::_1:
              return 1;
          case FloatType::MINUS1:
              return -1;
          case FloatType::_05:
              return 0.5f;
          case FloatType::INTEGER:
              return (float)this->readInt(true);
          default:
              {
                  /* using a memcpy since the compiler isn't
                   * doing the float ptr math correctly on device.
                   * TODO still applies in C++ ? */
                  unsigned char* pF = (this->_bytes + this->_currentByte);
                  float f = 0;
                  
                  // N.B - in order to avoid an unaligned memory access crash on 'memcpy()' the the (void*) casts of the source and
                  // destination pointers are EXTREMELY important for the ARM compiler.
                  //
                  // Without a (void*) cast, the ARM compiler makes the assumption that the float* pointer is naturally aligned
                  // according to it's type size (aligned along 4 byte boundaries) and thus tries to call a more optimized
                  // version of memcpy() which makes this alignment assumption also. When reading back from a file of course our pointers
                  // may not be aligned, hence we need to avoid the compiler making this assumption. The (void*) cast serves this purpose,
                  // and causes the ARM compiler to choose the slower, more generalized (unaligned) version of memcpy()
                  //
                  // For more about this compiler behavior, see:
                  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html
                  memcpy((void*) &f, (const void*) pF, sizeof(float));
                  
                  this->_currentByte += sizeof(float);
                  return f;
              }
      }
  }
  
  std::string CCBReader::readCachedString()
  {
      int n = this->readInt(false);
      return this->_stringCache[n];
  }
  
  Node * CCBReader::readNodeGraph(Node * pParent)
  {
      /* Read class name. */
      std::string className = this->readCachedString();
  
      std::string _jsControlledName;
      
      if(_jsControlled) {
          _jsControlledName = this->readCachedString();
      }
      
      // Read assignment type and name
      TargetType memberVarAssignmentType = static_cast<TargetType>(this->readInt(false));
      std::string memberVarAssignmentName;
      if(memberVarAssignmentType != TargetType::NONE)
      {
          memberVarAssignmentName = this->readCachedString();
      }
      
      NodeLoader *ccNodeLoader = this->_nodeLoaderLibrary->getNodeLoader(className.c_str());
       
      if (! ccNodeLoader)
      {
          log("no corresponding node loader for %s", className.c_str());
          return NULL;
      }
  
      Node *node = ccNodeLoader->loadNode(pParent, this);
  
      // Set root node
      if (! _actionManager->getRootNode())
      {
          _actionManager->setRootNode(node);
      }
      
      // Assign controller
      if(_jsControlled && node == _actionManager->getRootNode())
      {
          _actionManager->setDocumentControllerName(_jsControlledName);
      }
  
      // Read animated properties
      Dictionary *seqs = Dictionary::create();
      _animatedProps = new set<string>();
      
      int numSequence = readInt(false);
      for (int i = 0; i < numSequence; ++i)
      {
          int seqId = readInt(false);
          Dictionary *seqNodeProps = Dictionary::create();
          
          int numProps = readInt(false);
          
          for (int j = 0; j < numProps; ++j)
          {
              CCBSequenceProperty *seqProp = new CCBSequenceProperty();
              seqProp->autorelease();
              
              seqProp->setName(readCachedString().c_str());
              seqProp->setType(readInt(false));
              _animatedProps->insert(seqProp->getName());
              
              int numKeyframes = readInt(false);
              
              for (int k = 0; k < numKeyframes; ++k)
              {
                  CCBKeyframe *keyframe = readKeyframe(static_cast<PropertyType>(seqProp->getType()));
                  
                  seqProp->getKeyframes()->addObject(keyframe);
              }
              
              seqNodeProps->setObject(seqProp, seqProp->getName());
          }
          
          seqs->setObject(seqNodeProps, seqId);
      }
      
      if (seqs->count() > 0)
      {
          _actionManager->addNode(node, seqs);
      }
      
      // Read properties
      ccNodeLoader->parseProperties(node, pParent, this);
      
      bool isCCBFileNode = (NULL == dynamic_cast<CCBFile*>(node)) ? false : true;
      // Handle sub ccb files (remove middle node)
      if (isCCBFileNode)
      {
          CCBFile *ccbFileNode = (CCBFile*)node;
          
          Node *embeddedNode = ccbFileNode->getCCBFileNode();
          embeddedNode->setPosition(ccbFileNode->getPosition());
          embeddedNode->setRotation(ccbFileNode->getRotation());
          embeddedNode->setScaleX(ccbFileNode->getScaleX());
          embeddedNode->setScaleY(ccbFileNode->getScaleY());
          embeddedNode->setTag(ccbFileNode->getTag());
          embeddedNode->setVisible(true);
          //embeddedNode->ignoreAnchorPointForPosition(ccbFileNode->isIgnoreAnchorPointForPosition());
          
          _actionManager->moveAnimationsFromNode(ccbFileNode, embeddedNode);
  
          ccbFileNode->setCCBFileNode(NULL);
          
          node = embeddedNode;
      }
  
  #ifdef SB_ENABLE_JAVASCRIPT
      /*
       if (memberVarAssignmentType && memberVarAssignmentName && ![memberVarAssignmentName isEqualToString:@""])
       {
       [[JSCocoa sharedController] setObject:node withName:memberVarAssignmentName];
       }*/
  #else
      if (memberVarAssignmentType != TargetType::NONE)
      {
          if(!_jsControlled)
          {
              Object * target = NULL;
              if(memberVarAssignmentType == TargetType::DOCUMENT_ROOT)
              {
                  target = _actionManager->getRootNode();
              } 
              else if(memberVarAssignmentType == TargetType::OWNER)
              {
                  target = this->_owner;
              }
              
              if(target != NULL)
              {
                  CCBMemberVariableAssigner * targetAsCCBMemberVariableAssigner = dynamic_cast<CCBMemberVariableAssigner *>(target);
                  
                  bool assigned = false;
                  if (memberVarAssignmentType != TargetType::NONE)
                  {
                      if(targetAsCCBMemberVariableAssigner != NULL)
                      {
                          assigned = targetAsCCBMemberVariableAssigner->onAssignCCBMemberVariable(target, memberVarAssignmentName.c_str(), node);
                      }
                      
                      if(!assigned && this->_CCBMemberVariableAssigner != NULL)
                      {
                          assigned = this->_CCBMemberVariableAssigner->onAssignCCBMemberVariable(target, memberVarAssignmentName.c_str(), node);
                      }
                  }
              }
          }
          else
          {
              if(memberVarAssignmentType == TargetType::DOCUMENT_ROOT)
              {
                  _actionManager->addDocumentOutletName(memberVarAssignmentName);
                  _actionManager->addDocumentOutletNode(node);
              }
              else
              {
                  _ownerOutletNames.push_back(memberVarAssignmentName);
                  _ownerOutletNodes->addObject(node);
              }
          }
      }
      
      // Assign custom properties.
      if (ccNodeLoader->getCustomProperties()->count() > 0)
      {
          bool customAssigned = false;
          
          if(!_jsControlled)
          {
              Object * target = node;
              if(target != NULL)
              {
                  CCBMemberVariableAssigner * targetAsCCBMemberVariableAssigner = dynamic_cast<CCBMemberVariableAssigner *>(target);
                  if(targetAsCCBMemberVariableAssigner != NULL)
                  {
                      Dictionary* pCustomPropeties = ccNodeLoader->getCustomProperties();
                      DictElement* pElement;
                      CCDICT_FOREACH(pCustomPropeties, pElement)
                      {
                          customAssigned = targetAsCCBMemberVariableAssigner->onAssignCCBCustomProperty(target, pElement->getStrKey(), static_cast<CCBValue*>(pElement->getObject()));
  
                          if(!customAssigned && this->_CCBMemberVariableAssigner != NULL)
                          {
                              customAssigned = this->_CCBMemberVariableAssigner->onAssignCCBCustomProperty(target, pElement->getStrKey(), static_cast<CCBValue*>(pElement->getObject()));
                          }
                      }
                  }
              }
          }
      }
  
  #endif // SB_ENABLE_JAVASCRIPT
      
      delete _animatedProps;
      _animatedProps = NULL;
  
  	bool hasPhysicsBody = this->readBool();
  	if (hasPhysicsBody)
  	{
  		// Read body shape
  		int bodyShape = this->readInt(false);
  		float cornerRadius = this->readFloat();
          
  		// Read points
  		int numPoints = this->readInt(false);
  		Point* points = new Point[sizeof(Point)*numPoints];
  		for (int i = 0; i < numPoints; i++)
  		{
  			float x = this->readFloat();
  			float y = this->readFloat();
              
  			points[i] = Point(x, y);
  		}
          
  		// Create body
  		/*
  		CCPhysicsBody* body = NULL;
          
  		if (bodyShape == 0)
  		{
  			body = [CCPhysicsBody bodyWithPolygonFromPoints:points count:numPoints cornerRadius:cornerRadius];
  		}
  		else if (bodyShape == 1)
  		{
  			body = [CCPhysicsBody bodyWithCircleOfRadius:cornerRadius andCenter:points[0]];
  		}
  		*/
          
  		bool dynamic = this->readBool();
  		bool affectedByGravity = this->readBool();
  		bool allowsRotation = this->readBool();
          
  		/*
  		if (dynamic) body.type = CCPhysicsBodyTypeDynamic;
  		else body.type = CCPhysicsBodyTypeStatic;
  		*/
          
  		float density = this->readFloat();
  		float friction = this->readFloat();
  		float elasticity = this->readFloat();
          
  		//body.affectedByGravity = affectedByGravity;
  		//body.allowsRotation = allowsRotation;
          
  		//body.density = density;
  		/*
  		body.friction = friction;
  		body.elasticity = elasticity;
          
  		node.physicsBody = body;
  		*/
  	}
  
      /* Read and add children. */
      int numChildren = this->readInt(false);
      for(int i = 0; i < numChildren; i++)
      {
          Node * child = this->readNodeGraph(node);
          node->addChild(child);
      }
  
      // FIX ISSUE #1860: "onNodeLoaded will be called twice if ccb was added as a CCBFile".
      // If it's a sub-ccb node, skip notification to NodeLoaderListener since it will be
      // notified at LINE #734: Node * child = this->readNodeGraph(node);
      if (!isCCBFileNode)
      {
          // Call onNodeLoaded
          NodeLoaderListener * nodeAsNodeLoaderListener = dynamic_cast<NodeLoaderListener *>(node);
          if(nodeAsNodeLoaderListener != NULL)
          {
              nodeAsNodeLoaderListener->onNodeLoaded(node, ccNodeLoader);
          }
          else if(this->_nodeLoaderListener != NULL)
          {
              this->_nodeLoaderListener->onNodeLoaded(node, ccNodeLoader);
          }
      }
      return node;
  }
  
  CCBKeyframe* CCBReader::readKeyframe(PropertyType type)
  {
      CCBKeyframe *keyframe = new CCBKeyframe();
      keyframe->autorelease();
      
      keyframe->setTime(readFloat());
      
      CCBKeyframe::EasingType easingType = static_cast<CCBKeyframe::EasingType>(readInt(false));
      float easingOpt = 0;
      Object *value = NULL;
      
      if (easingType == CCBKeyframe::EasingType::CUBIC_IN
          || easingType == CCBKeyframe::EasingType::CUBIC_OUT
          || easingType == CCBKeyframe::EasingType::CUBIC_INOUT
          || easingType == CCBKeyframe::EasingType::ELASTIC_IN
          || easingType == CCBKeyframe::EasingType::ELASTIC_OUT
          || easingType == CCBKeyframe::EasingType::ELASTIC_INOUT)
      {
          easingOpt = readFloat();
      }
      keyframe->setEasingType(easingType);
      keyframe->setEasingOpt(easingOpt);
      
      if (type == PropertyType::CHECK)
      {
          value = CCBValue::create(readBool());
      }
      else if (type == PropertyType::BYTE)
      {
          value = CCBValue::create(readByte());
      }
      else if (type == PropertyType::COLOR3)
      {
          int r = readByte();
          int g = readByte();
          int b = readByte();
          
          Color3B c = Color3B(r,g,b);
          value = Color3BWapper::create(c);
      }
      else if (type == PropertyType::DEGREES)
      {
          value = CCBValue::create(readFloat());
      }
      else if (type == PropertyType::SCALE_LOCK || type == PropertyType::POSITION
  	     || type == PropertyType::FLOAT_XY)
      {
          float a = readFloat();
          float b = readFloat();
          
          value = Array::create(CCBValue::create(a),
                                  CCBValue::create(b),
                                  NULL);
      }
      else if (type == PropertyType::SPRITEFRAME)
      {
          std::string spriteSheet = readCachedString();
          std::string spriteFile = readCachedString();
          
          SpriteFrame* spriteFrame;
  
          if (spriteSheet.length() == 0)
          {
              spriteFile = _CCBRootPath + spriteFile;
  
              Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(spriteFile.c_str());
              Rect bounds = Rect(0, 0, texture->getContentSize().width, texture->getContentSize().height);
              
              spriteFrame = SpriteFrame::createWithTexture(texture, bounds);
          }
          else
          {
              spriteSheet = _CCBRootPath + spriteSheet;
              SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
              
              // Load the sprite sheet only if it is not loaded            
              if (_loadedSpriteSheets.find(spriteSheet) == _loadedSpriteSheets.end())
              {
                  frameCache->addSpriteFramesWithFile(spriteSheet.c_str());
                  _loadedSpriteSheets.insert(spriteSheet);
              }
              
              spriteFrame = frameCache->getSpriteFrameByName(spriteFile.c_str());
          }
          value = spriteFrame;
      }
      
      keyframe->setValue(value);
      
      return  keyframe;
  }
  
  
  bool CCBReader::readCallbackKeyframesForSeq(CCBSequence* seq)
  {
      int numKeyframes = readInt(false);
      if(!numKeyframes) return true;
      
      CCBSequenceProperty* channel = new CCBSequenceProperty();
      channel->autorelease();
  
      for(int i = 0; i < numKeyframes; ++i) {
        
          float time = readFloat();
          std::string callbackName = readCachedString();
        
          int callbackType = readInt(false);
        
          Array* value = Array::create();
          value->addObject(String::create(callbackName));
          value->addObject(String::createWithFormat("%d", callbackType));
          
          CCBKeyframe* keyframe = new CCBKeyframe();
          keyframe->autorelease();
          
          keyframe->setTime(time);
          keyframe->setValue(value);
          
          if(_jsControlled) {
              string callbackIdentifier;
              _actionManager->getKeyframeCallbacks()->addObject(String::createWithFormat("%d:%s",callbackType, callbackName.c_str()));
          }
      
          channel->getKeyframes()->addObject(keyframe);
      }
      
      seq->setCallbackChannel(channel);
      
      return true;
  }
  
  bool CCBReader::readSoundKeyframesForSeq(CCBSequence* seq) {
      int numKeyframes = readInt(false);
      if(!numKeyframes) return true;
      
      CCBSequenceProperty* channel = new CCBSequenceProperty();
      channel->autorelease();
  
      for(int i = 0; i < numKeyframes; ++i) {
          
          float time = readFloat();
          std::string soundFile = readCachedString();
          float pitch = readFloat();
          float pan = readFloat();
          float gain = readFloat();
                  
          Array* value = Array::create();
          
          value->addObject(String::create(soundFile));
          value->addObject(String::createWithFormat("%f", pitch));
          value->addObject(String::createWithFormat("%f", pan));
          value->addObject(String::createWithFormat("%f", gain));
          
          CCBKeyframe* keyframe = new CCBKeyframe();
          keyframe->setTime(time);
          keyframe->setValue(value);
          channel->getKeyframes()->addObject(keyframe);
          keyframe->release();
      }
      
      seq->setSoundChannel(channel);
      
      return true;
  }
  
  
  Node * CCBReader::readNodeGraph() {
      return this->readNodeGraph(NULL);
  }
  
  bool CCBReader::readSequences()
  {
      Array *sequences = _actionManager->getSequences();
      
      int numSeqs = readInt(false);
      
      for (int i = 0; i < numSeqs; i++)
      {
          CCBSequence *seq = new CCBSequence();
          seq->autorelease();
          
          seq->setDuration(readFloat());
          seq->setName(readCachedString().c_str());
          seq->setSequenceId(readInt(false));
          seq->setChainedSequenceId(readInt(true));
          
          if(!readCallbackKeyframesForSeq(seq)) return false;
          if(!readSoundKeyframesForSeq(seq)) return false;
          
          sequences->addObject(seq);
      }
      
      _actionManager->setAutoPlaySequenceId(readInt(true));
      return true;
  }
  
  std::string CCBReader::lastPathComponent(const char* pPath) {
      std::string path(pPath);
      size_t slashPos = path.find_last_of("/");
      if(slashPos != std::string::npos) {
          return path.substr(slashPos + 1, path.length() - slashPos);
      }
      return path;
  }
  
  std::string CCBReader::deletePathExtension(const char* pPath) {
      std::string path(pPath);
      size_t dotPos = path.find_last_of(".");
      if(dotPos != std::string::npos) {
          return path.substr(0, dotPos);
      }
      return path;
  }
  
  std::string CCBReader::toLowerCase(const char* pString) {
      std::string copy(pString);
      std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower);
      return copy;
  }
  
  bool CCBReader::endsWith(const char* pString, const char* pEnding) {
      std::string string(pString);
      std::string ending(pEnding);
      if(string.length() >= ending.length()) {
          return (string.compare(string.length() - ending.length(), ending.length(), ending) == 0);
      } else {
          return false;
      }
  }
  
  bool CCBReader::isJSControlled()
  {
      return _jsControlled;
  }
  
  void CCBReader::addOwnerCallbackName(const std::string& name)
  {
      _ownerCallbackNames.push_back(name);
  }
  
  void CCBReader::addOwnerCallbackNode(Node *node)
  {
      _ownerCallbackNodes->addObject(node);
  }
  
  void CCBReader::addOwnerCallbackControlEvents(Control::EventType type)
  {
      _ownerOwnerCallbackControlEvents->addObject(Integer::create((int)type));
  }
  
  void CCBReader::addDocumentCallbackName(const std::string& name)
  {
      _actionManager->addDocumentCallbackName(name);
  }
  
  void CCBReader::addDocumentCallbackNode(Node *node)
  {
      _actionManager->addDocumentCallbackNode(node);
  }
  
  void CCBReader::addDocumentCallbackControlEvents(Control::EventType eventType)
  {
      _actionManager->addDocumentCallbackControlEvents(eventType);
  }
  
  Array* CCBReader::getOwnerCallbackNames()
  {
      Array* pRet = Array::createWithCapacity(_ownerCallbackNames.size());
      std::vector<std::string>::iterator it = _ownerCallbackNames.begin();
      for (; it != _ownerCallbackNames.end(); ++it)
      {
          pRet->addObject(String::create(*it));
      }
      
      return pRet;
  }
  
  Array* CCBReader::getOwnerCallbackNodes()
  {
      return _ownerCallbackNodes;
  }
  
  Array* CCBReader::getOwnerCallbackControlEvents()
  {
      return _ownerOwnerCallbackControlEvents;
  }
  
  Array* CCBReader::getOwnerOutletNames()
  {
      Array* pRet = Array::createWithCapacity(_ownerOutletNames.size());
      std::vector<std::string>::iterator it = _ownerOutletNames.begin();
      for (; it != _ownerOutletNames.end(); ++it)
      {
          pRet->addObject(String::create(*it));
      }
      return pRet;
  }
  
  Array* CCBReader::getOwnerOutletNodes()
  {
      return _ownerOutletNodes;
  }
  
  Array* CCBReader::getNodesWithAnimationManagers()
  {
      return _nodesWithAnimationManagers;
  }
  
  Array* CCBReader::getAnimationManagersForNodes()
  {
      return _animationManagersForNodes;
  }
  
  void CCBReader::addOwnerOutletName(std::string name)
  {
      _ownerOutletNames.push_back(name);
  }
  
  void CCBReader::addOwnerOutletNode(Node *node)
  {
      if (NULL == node)
          return;
      
      _ownerOutletNodes->addObject(node);
  }
  
  void CCBReader::addNodeByTagName(const char * name, Node *node)
  {
      if (NULL == node)
          return;
  
      _nodeByTagName[name] = node;
  }
  
  cocos2d::Node* CCBReader::getNodeByTagName(const char * name) {
      auto itr = _nodeByTagName.find(name);
      if (itr == _nodeByTagName.end()) {
          return NULL;
      }
      
      return (*itr).second;
  }
  
  /************************************************************************
   Static functions
   ************************************************************************/
  
  static float __ccbResolutionScale = 1.0f;
  
  float CCBReader::getResolutionScale()
  {
      return __ccbResolutionScale;
  }
  
  void CCBReader::setResolutionScale(float scale)
  {
      __ccbResolutionScale = scale;
  }
  
  };
  