Does anybody successfully port CocosDragon to cocos2d-x?

A few days ago, Zynga publilshed a tutorial about how to make a jump game with cocosBuilder,here is the link:
http://code.zynga.com/2012/10/creating-a-game-with-cocosbuilder/

when I try to port it with cocos2d-x, I seems that the current version of cocosBuilder reader can’t read custom property defined in CCB files.
And I can’t use pNode->getUserObject() to get a CCBAnimationManager instance in custom class.

regards,
zilongshanren

I have the same problem and have being discussing about it here: http://www.cocos2d-x.org/boards/6/topics/17607?r=17692

The problem is basically that in Cocos2d-x we can’t get access to inner timelines like in cocos2d, or that’s my guess

@Jesus Bosch

Thanks for mention the thread, next time I’ll not create new thread before search:)

@Jesus Bosch, excuse me, what do you mean by “inner timelines”?

sorry the forum alert went to spam and I didn’t see your message :-S

Ok, imagine you have a ccb A with a timeline animation called MainAnimation. Then you have a CCB B with an animation called SubAnimation. Then, in cocosbuilder, you drag B to A. So you have a CCB scene with a sub CCB file (A is parent of B).

Imagine that you want to execute the SubAnimation (in B) only when the player clicks a button or executes a particular event, this is, to be able from A to call animations in B.

With the current version of the framework (2.0.3) I think it’s not possible, because we are missing of a feature that is in Cocos2D for iphone. They store the sub animation managers in the UserObject or UserData properties. I have fixed this in the framework assigning the animationmanager to the UserObject of each node when needed, in the readCCgrapFromNode function. I have also had to disable a function in the ccbreader that always cleans up the user object information (I’m not sure why).

I don’t know if it’s the best approach, but with those small changes it works.

I hope the explanation is more clear now… thanks.

@Jesus Can you post your code changes?

the problem Herman is that I did some changes here and there :-S I’ll try to wrap up everything and upload it here. But please be careful because it might produce unexpected problems… I’m not used to edit the engine code…

this is my modified CCBReader, that allows me to do things like this:

dynamic_cast(child->getUserObject())->runAnimations("subAnimation");


#include "CCBReader.h"

#include 

#include "CCNodeLoader.h"
#include "CCNodeLoaderLibrary.h"
#include "CCNodeLoaderListener.h"
#include "CCBMemberVariableAssigner.h"
#include "CCBSelectorResolver.h"
#include "CCData.h"
#include "CCBAnimationManager.h"
#include "CCBSequenceProperty.h"
#include "CCBKeyframe.h"
#include "CCBValue.h"

#include 

#ifdef __CC_PLATFORM_IOS
#include 
#endif

using namespace std;

NS_CC_EXT_BEGIN;

/*************************************************************************
 Implementation of CCBFile
 *************************************************************************/

CCBFile::CCBFile():mCCBFileNode(NULL) {}

CCBFile* CCBFile::create()
{
    CCBFile *ret = new CCBFile();

    if (ret)
    {
        ret->autorelease();
    }

    return ret;
}

CCNode* CCBFile::getCCBFileNode()
{
    return mCCBFileNode;
}

void CCBFile::setCCBFileNode(CCNode *pNode)
{
    CC_SAFE_RELEASE(mCCBFileNode);
    mCCBFileNode = pNode;
    CC_SAFE_RETAIN(mCCBFileNode);
}

/*************************************************************************
 Implementation of CCBReader
 *************************************************************************/

CCBReader::CCBReader(CCNodeLoaderLibrary * pCCNodeLoaderLibrary, CCBMemberVariableAssigner * pCCBMemberVariableAssigner, CCBSelectorResolver * pCCBSelectorResolver, CCNodeLoaderListener * pCCNodeLoaderListener) 
: mData(NULL)
, mBytes(NULL)
, mCurrentByte(-1)
, mCurrentBit(-1)
, mOwner(NULL)
, mActionManager(NULL)
, mAnimatedProps(NULL)
, hasScriptingOwner(false)
{
    this->mCCNodeLoaderLibrary = pCCNodeLoaderLibrary;
    this->mCCNodeLoaderLibrary->retain();
    this->mCCBMemberVariableAssigner = pCCBMemberVariableAssigner;
    this->mCCBSelectorResolver = pCCBSelectorResolver;
    this->mCCNodeLoaderListener = pCCNodeLoaderListener;
}

CCBReader::CCBReader(CCBReader * pCCBReader) 
: mData(NULL)
, mBytes(NULL)
, mCurrentByte(-1)
, mCurrentBit(-1)
, mOwner(NULL)
, mActionManager(NULL)
, mAnimatedProps(NULL)
, hasScriptingOwner(false)
{
    this->mLoadedSpriteSheets = pCCBReader->mLoadedSpriteSheets;
    this->mCCNodeLoaderLibrary = pCCBReader->mCCNodeLoaderLibrary;
    this->mCCNodeLoaderLibrary->retain();

    this->mCCBMemberVariableAssigner = pCCBReader->mCCBMemberVariableAssigner;
    this->mCCBSelectorResolver = pCCBReader->mCCBSelectorResolver;
    this->mCCNodeLoaderListener = pCCBReader->mCCNodeLoaderListener;
}

CCBReader::CCBReader()
: mData(NULL)
, mBytes(NULL)
, mCurrentByte(-1)
, mCurrentBit(-1)
, mOwner(NULL)
, mActionManager(NULL)
, mCCNodeLoaderLibrary(NULL)
, mCCNodeLoaderListener(NULL)
, mCCBMemberVariableAssigner(NULL)
, mCCBSelectorResolver(NULL)
, mAnimatedProps(NULL)
, hasScriptingOwner(false)
{}

CCBReader::~CCBReader() {
    CC_SAFE_RELEASE_NULL(mOwner);
    CC_SAFE_RELEASE_NULL(mData);

    this->mCCNodeLoaderLibrary->release();

    // Clear string cache.
    std::vector::iterator stringCacheIterator;
    for (stringCacheIterator = this->mStringCache.begin(); stringCacheIterator != this->mStringCache.end(); stringCacheIterator++) {
        (*stringCacheIterator)->release();
    }
    this->mStringCache.clear();

    setAnimationManager(NULL);
}

bool CCBReader::initWithData(CCData *pData, CCObject *pOwner)
{
    // Setup action manager
    CCBAnimationManager *pActionManager = new CCBAnimationManager();
    setAnimationManager(pActionManager);
    pActionManager->release();

    // Setup byte array
    mData = pData;
    CC_SAFE_RETAIN(mData);
    mBytes = mData->getBytes();
    mCurrentByte = 0;
    mCurrentBit = 0;

    mOwner = pOwner;
    CC_SAFE_RETAIN(mOwner);

    // Setup resolution scale and container size
    mActionManager->setRootContainerSize(CCDirector::sharedDirector()->getWinSize());

    return true;
}

CCBAnimationManager* CCBReader::getAnimationManager()
{
    return mActionManager;
}

void CCBReader::setAnimationManager(CCBAnimationManager *pAnimationManager)
{
    CC_SAFE_RELEASE(mActionManager);
    mActionManager = pAnimationManager;
    CC_SAFE_RETAIN(mActionManager);
}

CCBMemberVariableAssigner * CCBReader::getCCBMemberVariableAssigner() {
    return this->mCCBMemberVariableAssigner;
}

CCBSelectorResolver * CCBReader::getCCBSelectorResolver() {
    return this->mCCBSelectorResolver;
}

set* CCBReader::getAnimatedProperties()
{
    return mAnimatedProps;
}

set& CCBReader::getLoadedSpriteSheet()
{
    return mLoadedSpriteSheets;
}

CCObject* CCBReader::getOwner()
{
    return mOwner;
}

CCNode* CCBReader::readNodeGraphFromFile(const char *pCCBFileName)
{
    return this->readNodeGraphFromFile(pCCBFileName, NULL);
}

CCNode* CCBReader::readNodeGraphFromFile(const char* pCCBFileName, CCObject* pOwner) 
{
    return this->readNodeGraphFromFile(pCCBFileName, pOwner, CCDirector::sharedDirector()->getWinSize());
}

CCNode * CCBReader::readNodeGraphFromFile(const char * pCCBFileName, CCObject *pOwner, const CCSize &parentSize) 
{
    return this->readNodeGraphFromFile(pCCBFileName, pOwner, parentSize, NULL);
}

CCNode* CCBReader::readNodeGraphFromFile(const char *pCCBFileName, CCObject *pOwner, CCBAnimationManager **ppAnimationManager)
{
    return this->readNodeGraphFromFile(pCCBFileName, pOwner, CCDirector::sharedDirector()->getWinSize(), ppAnimationManager);
}

CCNode* CCBReader::readNodeGraphFromFile(const char *pCCBFileName, CCObject *pOwner, const CCSize &parentSize, CCBAnimationManager **ppAnimationManager)
{
    const char *pPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(pCCBFileName);
    unsigned long size;

    unsigned char * pBytes = CCFileUtils::sharedFileUtils()->getFileData(pPath, "rb", &size);
    CCData *data = new CCData(pBytes, size);

    CCNode *ret =  this->readNodeGraphFromData(data, pOwner, parentSize, ppAnimationManager);

    data->release();

    return ret;
}

CCNode* CCBReader::readNodeGraphFromData(CCData *pData, CCObject *pOwner, const CCSize &parentSize)
{
    return this->readNodeGraphFromData(pData, pOwner, parentSize, NULL);
}

CCNode* CCBReader::readNodeGraphFromData(CCData *pData, CCObject *pOwner, const CCSize &parentSize, CCBAnimationManager **ppAnimationManager)
{
    initWithData(pData, pOwner);
    mActionManager->setRootContainerSize(parentSize);

    CCNode *pNodeGraph = readFileWithCleanUp(false);

    if (pNodeGraph && mActionManager->getAutoPlaySequenceId() != -1)
    {
        // Auto play animations
        mActionManager->runAnimations(mActionManager->getAutoPlaySequenceId(), 0);
    }

    // Return action manager by reference
    if (ppAnimationManager)
    {
        *ppAnimationManager = mActionManager;
    }

    return pNodeGraph;
}

CCScene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName)
{
    return createSceneWithNodeGraphFromFile(pCCBFileName, NULL);
}

CCScene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName, CCObject *pOwner)
{
    return createSceneWithNodeGraphFromFile(pCCBFileName, pOwner, CCDirector::sharedDirector()->getWinSize());
}

CCScene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName, CCObject *pOwner, const CCSize &parentSize)
{
    return createSceneWithNodeGraphFromFile(pCCBFileName, pOwner, parentSize, NULL);
}

CCScene* CCBReader::createSceneWithNodeGraphFromFile(const char *pCCBFileName, CCObject *pOwner, const CCSize &parentSize, CCBAnimationManager **ppAnimationManager)
{
    CCNode *pNode = readNodeGraphFromFile(pCCBFileName, pOwner, parentSize, ppAnimationManager);
    CCScene *pScene = CCScene::create();
    pScene->addChild(pNode);

    return pScene;
}

bool CCBReader::readHeader() {
    /* If no bytes loaded, don't crash about it. */
    if(this->mBytes == NULL) {
        return false;
    }

    /* Read magic bytes */
    int magicBytes = *((int*)(this->mBytes + this->mCurrentByte));
    this->mCurrentByte += 4;

    if(CC_SWAP_INT32_LITTLE_TO_HOST(magicBytes) != 'ccbi') {
        return false; 
    }

    /* Read version. */
    int version = this->readInt(false);
    if(version != kCCBVersion) {
        CCLog("WARNING! Incompatible ccbi file version (file: %d reader: %d)", version, kCCBVersion);
        return false;
    }

    return true;
}

void CCBReader::cleanUpNodeGraph(CCNode *pNode)
{
    pNode->setUserObject(NULL);

    CCObject *pChild = NULL;
    CCARRAY_FOREACH(pNode->getChildren(), pChild)
    {
        cleanUpNodeGraph((CCNode*)pChild);
    }
}

CCNode* CCBReader::readFileWithCleanUp(bool bCleanUp)
{
    if (! readHeader())
    {
        return NULL;
    }

    if (! readStringCache())
    {
        return NULL;
    }

    if (! readSequences())
    {
        return NULL;
    }

    CCNode *pNode = readNodeGraph();

    if (bCleanUp)
    {
        cleanUpNodeGraph(pNode);
    }

    return pNode;
}

bool CCBReader::readStringCache() {
    int numStrings = this->readInt(false);

    for(int i = 0; i < numStrings; i++) {
        this->readStringCacheEntry();
    }

    return true;
}

void CCBReader::readStringCacheEntry() {
    int b0 = this->readByte();
    int b1 = this->readByte();

    int numBytes = b0 << 8 | b1;

    const unsigned char * src = (const unsigned char *) (this->mBytes + this->mCurrentByte);
    CCString * string = CCString::createWithData(src, (unsigned long)numBytes);
    string->retain();

    this->mCurrentByte += numBytes;

    this->mStringCache.push_back(string);
}

unsigned char CCBReader::readByte() {
    unsigned char byte = this->mBytes[this->mCurrentByte];
    this->mCurrentByte++;
    return byte;
}

bool CCBReader::readBool() {
    return 0 == this->readByte() ? false : true;
}

int CCBReader::readInt(bool pSigned) {
    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 = (int)(current / 2);
        } else {
            num = (int)(-current / 2);
        }
    } else {
        num = current - 1;
    }

    this->alignBits();

    return num;
}


float CCBReader::readFloat() {
    unsigned char type = this->readByte();

    switch (type) {
        case kCCBFloat0:
            return 0;    
        case kCCBFloat1:
            return 1;
        case kCCBFloatMinus1:
            return -1;
        case kCCBFloat05:
            return 0.5f;
        case kCCBFloatInteger:
            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++ ? */
            float * pF = (float*)(this->mBytes + this->mCurrentByte);
            float f = 0;
            memcpy(&f, pF, sizeof(float));
            this->mCurrentByte += 4;
            return f;
    }
}


bool CCBReader::getBit() {
    bool bit;
    unsigned char byte = *(this->mBytes + this->mCurrentByte);
    if(byte & (1 << this->mCurrentBit)) {
        bit = true;
    } else {
        bit = false;
    }

    this->mCurrentBit++;

    if(this->mCurrentBit >= 8) {
        this->mCurrentBit = 0;
        this->mCurrentByte++;
    }

    return bit;
}

void CCBReader::alignBits() {
    if(this->mCurrentBit) {
        this->mCurrentBit = 0;
        this->mCurrentByte++;
    }
}

CCString * CCBReader::readCachedString() {
    int i = this->readInt(false);
    return this->mStringCache[i];
}

CCNode * CCBReader::readNodeGraph(CCNode * pParent) {
    /* Read class name. */
    CCString * className = this->readCachedString();

    // Read assignment type and name
    int memberVarAssignmentType = this->readInt(false);
    CCString * memberVarAssignmentName;
    if(memberVarAssignmentType != kCCBTargetTypeNone) {
        memberVarAssignmentName = this->readCachedString();
    }

    CCNodeLoader *ccNodeLoader = this->mCCNodeLoaderLibrary->getCCNodeLoader(className);
    if (! ccNodeLoader)
    {
        CCLog("no corresponding node loader for %s", className->getCString());
        return NULL;
    }

    CCNode *node = ccNodeLoader->loadCCNode(pParent, this);

    // Set root node
    if (! mActionManager->getRootNode())
    {
        mActionManager->setRootNode(node);
    }

    // Read animated properties
    CCDictionary *seqs = CCDictionary::create();
    mAnimatedProps = new set();

    int numSequence = readInt(false);
    for (int i = 0; i < numSequence; ++i)
    {
        int seqId = readInt(false);
        CCDictionary *seqNodeProps = CCDictionary::create();

        int numProps = readInt(false);

        for (int j = 0; j < numProps; ++j)
        {
            CCBSequenceProperty *seqProp = new CCBSequenceProperty();
            seqProp->autorelease();
            seqProp->setName(readCachedString()->getCString());
            seqProp->setType(readInt(false));
            mAnimatedProps->insert(seqProp->getName());

            int numKeyframes = readInt(false);

            for (int k = 0; k < numKeyframes; ++k)
            {
                CCBKeyframe *keyframe = readKeyframe(seqProp->getType());

                seqProp->getKeyframes()->addObject(keyframe);
            }

            seqNodeProps->setObject(seqProp, seqProp->getName());
        }

        seqs->setObject(seqNodeProps, seqId);
    }

    if (seqs->count() > 0)
    {
        mActionManager->addNode(node, seqs);
    }

    node->setUserObject(mActionManager);

    // Read properties
    ccNodeLoader->parseProperties(node, pParent, this);

    // Handle sub ccb files (remove middle node)
    if (dynamic_cast(node))
    {
        CCBFile *ccbFileNode = (CCBFile*)node;

        CCNode *embeddedNode = ccbFileNode->getCCBFileNode();
        embeddedNode->setPosition(ccbFileNode->getPosition());
        embeddedNode->setRotation(ccbFileNode->getRotation());
        embeddedNode->setScale(ccbFileNode->getScale());
        embeddedNode->setTag(ccbFileNode->getTag());
        embeddedNode->setVisible(ccbFileNode->isVisible());
        embeddedNode->ignoreAnchorPointForPosition(ccbFileNode->isIgnoreAnchorPointForPosition());

        ccbFileNode->setCCBFileNode(NULL);

        node = embeddedNode;
    }



#ifdef CCB_ENABLE_JAVASCRIPT
    /*
     if (memberVarAssignmentType && memberVarAssignmentName && ![memberVarAssignmentName isEqualToString:@""])
     {
     [[JSCocoa sharedController] setObject:node withName:memberVarAssignmentName];
     }*/
#else
    if(memberVarAssignmentType != kCCBTargetTypeNone) {
        CCObject * target = NULL;
        if(memberVarAssignmentType == kCCBTargetTypeDocumentRoot) 
        {
            target = mActionManager->getRootNode();
        } 
        else if(memberVarAssignmentType == kCCBTargetTypeOwner) 
        {
            target = this->mOwner;
        }

        if(target != NULL) {
            bool assigned = false;

            CCBMemberVariableAssigner * targetAsCCBMemberVariableAssigner = dynamic_cast(target);

            if(targetAsCCBMemberVariableAssigner != NULL) {
                assigned = targetAsCCBMemberVariableAssigner->onAssignCCBMemberVariable(target, memberVarAssignmentName, node);
            }

            if(!assigned && this->mCCBMemberVariableAssigner != NULL) {
                this->mCCBMemberVariableAssigner->onAssignCCBMemberVariable(target, memberVarAssignmentName, node);
            }
        }
    }
#endif // CCB_ENABLE_JAVASCRIPT

    delete mAnimatedProps;
    mAnimatedProps = NULL;



    /* Read and add children. */
    int numChildren = this->readInt(false);
    for(int i = 0; i < numChildren; i++) {
        CCNode * child = this->readNodeGraph(node);
        node->addChild(child);
    }


    // Call onNodeLoaded
    CCNodeLoaderListener * nodeAsCCNodeLoaderListener = dynamic_cast(node);
    if(nodeAsCCNodeLoaderListener != NULL) {
        nodeAsCCNodeLoaderListener->onNodeLoaded(node, ccNodeLoader);
    } else if(this->mCCNodeLoaderListener != NULL) {
        this->mCCNodeLoaderListener->onNodeLoaded(node, ccNodeLoader);
    }



    return node;
}

CCBKeyframe* CCBReader::readKeyframe(int type)
{
    CCBKeyframe *keyframe = new CCBKeyframe();
    keyframe->autorelease();

    keyframe->setTime(readFloat());

    int easingType = readInt(false);
    float easingOpt = 0;
    CCObject *value = NULL;

    if (easingType == kCCBKeyframeEasingCubicIn
        || easingType == kCCBKeyframeEasingCubicOut
        || easingType == kCCBKeyframeEasingCubicInOut
        || easingType == kCCBKeyframeEasingElasticIn
        || easingType == kCCBKeyframeEasingElasticOut
        || easingType == kCCBKeyframeEasingElasticInOut)
    {
        easingOpt = readFloat();
    }
    keyframe->setEasingType(easingType);
    keyframe->setEasingOpt(easingOpt);

    if (type == kCCBPropTypeCheck)
    {
        value = CCBValue::create(readBool());
    }
    else if (type == kCCBPropTypeByte)
    {
        value = CCBValue::create(readByte());
    }
    else if (type == kCCBPropTypeColor3)
    {
        int r = readByte();
        int g = readByte();
        int b = readByte();

        ccColor3B c = ccc3(r,g,b);
        value = ccColor3BWapper::create(c);
    }
    else if (type == kCCBPropTypeDegrees)
    {
        value = CCBValue::create(readFloat());
    }
    else if (type == kCCBPropTypeScaleLock || type == kCCBPropTypePosition)
    {
        float a = readFloat();
        float b = readFloat();

        value = CCArray::create(CCBValue::create(a),
                                CCBValue::create(b),
                                NULL);
    }
    else if (type == kCCBPropTypeSpriteFrame)
    {
        CCString *spriteSheet = readCachedString();
        CCString *spriteFile = readCachedString();

        CCSpriteFrame* spriteFrame;
        CCString empty("");
        if (spriteSheet->isEqual(∅))
        {
            CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(spriteFile->getCString());
            CCRect bounds = CCRectMake(0, 0, texture->getContentSize().width, texture->getContentSize().height);
            spriteFrame = CCSpriteFrame::createWithTexture(texture, bounds);
        }
        else
        {
            CCSpriteFrameCache* frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();

            // Load the sprite sheet only if it is not loaded            
            if (mLoadedSpriteSheets.find(spriteSheet->getCString()) == mLoadedSpriteSheets.end())
            {
                frameCache->addSpriteFramesWithFile(spriteSheet->getCString());
                mLoadedSpriteSheets.insert(spriteSheet->getCString());
            }

            spriteFrame = frameCache->spriteFrameByName(spriteFile->getCString());
        }
        value = spriteFrame;
    }

    keyframe->setValue(value);

    return  keyframe;
}

CCNode * CCBReader::readNodeGraph() {
    return this->readNodeGraph(NULL);
}

bool CCBReader::readSequences()
{
    CCArray *sequences = mActionManager->getSequences();

    int numSeqs = readInt(false);

    for (int i = 0; i < numSeqs; i++)
    {
        CCBSequence *seq = new CCBSequence();
        seq->autorelease();

        seq->setDuration(readFloat());
        seq->setName(readCachedString()->getCString());
        seq->setSequenceId(readInt(false));
        seq->setChainedSequenceId(readInt(true));

        sequences->addObject(seq);
    }

    mActionManager->setAutoPlaySequenceId(readInt(true));
    return true;
}

CCString * CCBReader::lastPathComponent(CCString * pPath) {
    std::string path(pPath->getCString());
    int slashPos = path.find_last_of("/");
    if(slashPos != std::string::npos) {
        return CCString::create(path.substr(slashPos + 1, path.length() - slashPos).c_str());
    }
    return CCString::create(path.c_str());
}

CCString * CCBReader::deletePathExtension(CCString * pPath) {
    std::string path(pPath->getCString());
    int dotPos = path.find_last_of(".");
    if(dotPos != std::string::npos) {
        return CCString::create(path.substr(0, dotPos).c_str());
    }
    return CCString::create(path.c_str());
}

CCString * CCBReader::toLowerCase(CCString * pString) {
    std::string copy(pString->getCString());
    std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower);
    return CCString::create(copy.c_str());
}

CCString * CCBReader::concat(CCString * pStringA, CCString * pStringB) {
    int concatenatedLength = pStringA->length() + pStringB->length();
    char* concatenated = (char*) malloc(concatenatedLength+1);
    CCString* pRet = NULL;
    strcpy(concatenated, pStringA->getCString());
    strcat(concatenated, pStringB->getCString());

    concatenated[concatenatedLength] = '\0';
    pRet = CCString::create(concatenated);
    CC_SAFE_FREE(concatenated);
    return pRet;
}

bool CCBReader::endsWith(CCString * pString, CCString * pEnding) {
    std::string string(pString->getCString());
    std::string ending(pEnding->getCString());
    if(string.length() >= ending.length()) {
        return (string.compare(string.length() - ending.length(), ending.length(), ending) == 0);
    } else {
        return false;
    }
}

/************************************************************************
 Static functions
 ************************************************************************/

float CCBReader::getResolutionScale()
{
    // Init resolution scale
    if (CCApplication::sharedApplication()->getTargetPlatform() == kTargetIpad)
    {
        return 2;
    }
    else 
    {
        return 1;
    }
}

NS_CC_EXT_END;

hi Bosch, there is memory leak in your modified class CCBReader, due to set the cleanup to false. It seems difficult to change only several lines to make it work perfectly. Expect the engine author’s change.

Yes, I warned about the risks using that code :stuck_out_tongue: This is why I didn’t added it to github. I hope the cocos2d-x engine developers take the missing features into consideration. I guess it is planed improvements on the CCBReader for the next build.

i have the same problem . and i have to add the child in the code . no use sub ccb.

I have same problem, it can not add complex sub-ccb file. Any body has solution yet?

this works perfectly fine with the last version of cocos2dx

here is : https://github.com/wincax/CocosDragon-cocos2dx