[cocos2d-x]ccbファイルから埋め込み文字列を抜き出す

# 概要
ツールで作ったccbファイルの中の、ソースから参照されてない文字列を見つけ出すお話。

cocos2d-xで開発をする上で画面をインタラクティブに作成したいなーと思った場合、CocosBuilderとか[SpriteBuider](http://www.spritebuilder.com/)とかにお世話になると思います。
以下は、これらのツールで作成したLabelなどのパーツに文字列を入れていた場合に、あとからそれを一覧したいなーと思った場合の対処方法です。

# やり方
結論から言いますと、**ccbファイルはplist**構造です。なので、Obj-cからとても簡単に扱うことが可能です。
各ccbファイル内は、親となるノードが存在するのでそちらから情報を抜き出していきます。

```lang:objc
    NSBundle *bundle = [NSBundle mainBundle];    
    NSString *bundlePath = [bundle pathForResource:@"filename"
                                                ofType:@"ccb"];
        
    NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
    NSDictionary *nodeGraph = [dic objectForKey:@"nodeGraph"];
```

とか。この場合ccbをプロジェクトに追加しないとですが、実際にはccbの入っているフォルダを指定して、全ファイルを対象に行ったりすることになるかと思います。
次に、テキスト部分を見つけ出します。

## テキストを入力する部分を見つけ出す
plist構造を眺めてると気づくと思うのですが、テキストを入れる部分は

```
..
<string>Text</string>
<key>value</key>
..
```

に続く部分だとわかるかと思います。
これは、そのパーツの各設定が記述されるpropertyの中に設定されます。なので、上で取得したDictionaryからこの部分を抜き出せばOKです。

```objc
NSArray *properties = [dic objectForKey:@"properties"];
for (NSDictionary *itemDic in properties) {
    if ([[itemDic objectForKey:@"name"] isEqualToString:@"string"]
        && [[itemDic objectForKey:@"type"] isEqualToString:@"Text"]){
            NSLog(@"%@" ,[itemDic objectForKey:@"value"]);
    }
}
```

あとは、さらに条件を付けたりします。

## ソース参照されてるかのチェック
今回特に調べたいのは、ソースから内容を変更しないようなラベルです。
なので、ソースから参照される可能性があるかどうかをチェックします。

SpriteBuilderなどで作ったノードをソースから参照したい場合、そのパーツには名前を付ける必要があります。
これは、ccbファイル上では以下の部分に当たることが調べるとわかります。

```
..
<key>memberVarAssignmentName</key>
<string>[設定した名前]</string>
<key>memberVarAssignmentType</key>
<integer>1</integer>
..
```

* memberVarAssignmentName : ノードに設定した名前
* memberVarAssignmentType
    * なし、ルートノードに帰属、親ノードに帰属、でそれぞれ0,1,2
    * SpriteBuilderはデフォルトで1。CocosBuilderだと0に設定可能

なので、Dictionaryの中身をチェックする際にここを見れば、そのラベルが完全に埋め込みなのか、ソースに参照されているのかがわかるということです。

## コード例
というわけで、まとめたものが以下になります。
最初に取得した親ノードのDictionaryを投げると、子ノードが更に下の階層にあった場合にも再帰的にチェックすることができます。

```objc
-(void) checkNodeInfoDic:(NSDictionary *)dic{
    NSString *memberVarAssignmentName = [dic objectForKey:@"memberVarAssignmentName"];
    NSNumber *memberVarAssignmentType = [dic objectForKey:@"memberVarAssignmentType"];
    NSArray *properties = [dic objectForKey:@"properties"];

    // ノードに名前が付けられている かつ propertyが設定されている
    if ((memberVarAssignmentName == nil
        || [memberVarAssignmentName isEqualToString:@""])
        && properties) {
        // プロパティのうち、Textを設定している箇所を抜き出す
        for (NSDictionary *itemDic in properties) {
            if ([[itemDic objectForKey:@"name"] isEqualToString:@"string"]
                && [[itemDic objectForKey:@"type"] isEqualToString:@"Text"]){
                // CocosBuilderの場合
                NSLog(@"%@" ,[itemDic objectForKey:@"value"]);
                // SpriteBuilderは中身の文字列がArrayになります
            }
        }
    }
    
    // ノードに子がいるなら、そちらも再帰的にチェックする
    NSArray *children = [dic objectForKey:@"children"];
    if (children && [children count] > 0) {
        for (NSDictionary *childDic in children) {
            [self checkNodeInfoDic:childDic];
        }
    }
}
```

## おわりに
ローカライズ対応で必要に駆られて調べました。Text部分を探すだけでしたらgrepでも可能なのですが、役立つ場面もあるかなと…。
これのために１プロジェクト作成するのもちょっと微妙かもですが。

（ここまで言っておいて最近出た[Cocos Creator](http://blog.cocos2d-x.org/2016/03/announcing-the-cocos-creator-public-beta/)だと上記の問題点は解消できてたりするんですかね。そのうち触りたいです…）