逆向工程這種事情,類似軟件破解,不是萬(wàn)不得已不想去做。
最近有個(gè)需求姻蚓,我們要將一個(gè)老的cocos2d-x 3.2 + cocos Studio1.6做的游戲進(jìn)行改造,必須修改ui和特效里面的東東秋茫。但是由于種種原因史简,我們的UI工程已經(jīng)找不到了。
這就意味著只有兩條路可走肛著,要么結(jié)合程序中用到的各種層級(jí)關(guān)系圆兵,重建一個(gè)新的工程;要么就是根據(jù)原來(lái)的csb去做逆向工程枢贿,重新構(gòu)建一個(gè)可用的工程文件殉农。
先在網(wǎng)上看一看有沒(méi)有相關(guān)的代碼或工具,避免重復(fù)造輪子局荚。
首先找到的超凳,是一個(gè)叫x-studio365的工具,根據(jù)它的介紹:
- 支持導(dǎo)入CocosStudio ccs工程及反導(dǎo)入CocosStudio發(fā)布的json和csb格式ui, 【文件】
【導(dǎo)入】【CocosStudio(.ccs/.json)】耀态,【文件】【導(dǎo)入】【CocosStudio(*.csb)】需先新建一個(gè)空工程
- 支持導(dǎo)入csb(將csb文件拖入編輯器場(chǎng)景即可)和發(fā)布到csb給ccoos2d-x引擎直接使用
按道理是可以用它來(lái)代替cocosStudio的轮傍,但是實(shí)測(cè)了一下,它只支持cocosStudio2.0以上的版本首装,而且最不可思議的是创夜,它不支持打包成plist + png這樣的圖集建立的工程∠陕撸總之驰吓,完全沒(méi)法用。
這里我要順便吐槽一下系奉,cocosStudio2.0以上版本檬贰,相比cocosStudio1.6功能差太多,觸控當(dāng)年如果沒(méi)有收編cocosStudio缺亮,也許今天我們能用到一個(gè)更加強(qiáng)大的動(dòng)畫和UI編輯軟件翁涤,沒(méi)spine和dragonBone什么事了。
然后我看到了這篇文章:cocos2d 由導(dǎo)出文件.csb反推出cocosUI工程。作者首先一通貌似強(qiáng)大的分析迷雪,然后在百度網(wǎng)盤上分享了源碼限书,活雷鋒啊虫蝶,必須給作者一萬(wàn)個(gè)贊章咧!下載"源碼"一看:尼瑪這是啥?完全是cocos2d-x 3.4 cocos studio部分的源碼呀能真,跟作者有半毛錢關(guān)系赁严?然后仔細(xì)看作者的分析,牛頭不對(duì)馬嘴粉铐,根本沒(méi)找到要點(diǎn)疼约。嗯,必須給作者一萬(wàn)個(gè)踩蝙泼!假的技術(shù)文章也好意思寫程剥?浪費(fèi)大家時(shí)間!
那首歌怎么唱的汤踏?從來(lái)沒(méi)有什么救世主织鲸,一切要靠我們自己。
前面談過(guò)溪胶,為了填CocosStudio的坑搂擦,我其實(shí)多次翻過(guò)這部分源碼,大致理解它讀取csb的機(jī)制哗脖,現(xiàn)在要重構(gòu)工程瀑踢,完全可以利用cocos自身的類來(lái)為我們解析。大家可以仔細(xì)研究分析一下CCSGUIReader.cpp中的GUIReader::WidgetFromBinaryFile這個(gè)類才避,怎么讀出json結(jié)構(gòu)的算法就有了橱夭。
由于算法中要用到cocos2d-x里面的很多類,懶得去分析各個(gè)類的依賴關(guān)系桑逝,直接用cocos新建一個(gè)工程來(lái)做這個(gè)轉(zhuǎn)換工程棘劣。我機(jī)器上的cocos2d-x現(xiàn)在是3.6版本,已經(jīng)填過(guò)坑肢娘,可以完美讀取cocosStudio1.6的csb文件呈础,我們就用它來(lái)建新工程吧。
cocos new csb2json -l cpp
然后新建一個(gè)Csb2Json類橱健,包含兩個(gè)文件:Csb2Json.cpp和Csb2Json.h而钞。vs中直接用類向?qū)Ы⒓纯伞4笾聝?nèi)容如下:
1.Csb2Json.h:
#pragma once
#include "cocos2d.h"
#include "cocostudio/CocoStudio.h"
#include <fstream>
USING_NS_CC;
using namespace std;
using namespace cocostudio;
class Csb2Json
{
public:
Csb2Json();
~Csb2Json();
static Csb2Json * getInstance() {
static Csb2Json m_instance;
return &m_instance;
};
int run(const string csbName);
private:
void getTree(CocoLoader *pLoader, stExpCocoNode *pNode, int nTabNum);
ofstream * m_pOutStream;
};
2.Csb2Json.cpp
#include "Csb2Json.h"
Csb2Json::Csb2Json()
{
}
Csb2Json::~Csb2Json()
{
}
int Csb2Json::run(const string csbName)
{
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(csbName);
auto len = csbName.find(".");
auto pureName = csbName.substr(0, len);
auto jsonName = pureName + ".json";
auto fileData = FileUtils::getInstance()->getDataFromFile(fullPath);
auto fileDataBytes = fileData.getBytes();
ofstream oStream(jsonName, ios::out);
m_pOutStream = &oStream;
CocoLoader tCocoLoader;
if (true == tCocoLoader.ReadCocoBinBuff((char*)fileDataBytes))
{
*m_pOutStream << "{" << endl;
stExpCocoNode* tpRootCocoNode = tCocoLoader.GetRootCocoNode();
rapidjson::Type tType = tpRootCocoNode->GetType(&tCocoLoader);
if (rapidjson::kObjectType == tType || rapidjson::kArrayType == tType)
{
getTree(&tCocoLoader, tpRootCocoNode,1);
}
*m_pOutStream << "}" << endl;
m_pOutStream->close();
return 0;
}
else {
return 1;
}
}
void Csb2Json::getTree(CocoLoader *pLoader, stExpCocoNode *pNode, int nTabNum)
{
stExpCocoNode* pArray = pNode->GetChildArray(pLoader);
int nChildNum = pNode->GetChildNum();
//strTab用于控制縮排拘荡,按cocos原來(lái)的標(biāo)準(zhǔn)臼节,一次縮排2個(gè)空格。
std::string strTab = "";
for (int i = 0; i < nTabNum; ++i) {
strTab = strTab + " ";
}
for (int i = 0; i < nChildNum; ++i) {
stExpCocoNode *pSubNode = &pArray[i];
string strKey = pSubNode->GetName(pLoader);
string strVal = pSubNode->GetValue(pLoader);
rapidjson::Type tType = pSubNode->GetType(pLoader);
if (strKey != "") {
strKey = "\"" + strKey + "\": ";
}
if (rapidjson::kObjectType == tType) {
if (strKey == "\"children\": ") {
strKey = "";
}
*m_pOutStream << strTab << strKey << "{" << endl;
getTree(pLoader, pSubNode, nTabNum + 1);
*m_pOutStream << strTab << "}";
}
else if (rapidjson::kArrayType == tType) {
*m_pOutStream << strTab << strKey << "[";
int subNum = pSubNode->GetChildNum();
if (subNum == 0) { //避免空數(shù)組換行。
*m_pOutStream << "]";
}
else {
*m_pOutStream << endl;
getTree(pLoader, pSubNode, nTabNum + 1);
*m_pOutStream << strTab << "]";
}
}
else {
if (rapidjson::kNullType == tType) {
strVal = "null";
}
else if (rapidjson::kStringType == tType || 83 == tType) {
strVal = "\"" + strVal + "\"";
}
else if ((rapidjson::kNumberType == tType || -8 == tType) && strVal == "1") {
strVal = "true";
}
else if ((rapidjson::kNumberType == tType || -8 == tType) && strVal == "0") {
strVal = "false";
}
*m_pOutStream << strTab << strKey << strVal;
}
//判斷并合理使用結(jié)尾的逗號(hào)网缝。
if (i == nChildNum - 1) {
*m_pOutStream << endl;
}
else {
*m_pOutStream << "," << endl;
}
}
}
**注意:
1.當(dāng)讀取到的類型為0bject或Array時(shí)巨税,getTree函數(shù)要被遞歸調(diào)用。
2.tType是個(gè)1-6的枚舉粉臊,但是實(shí)際取出來(lái)有8草添,-8 , 83等等奇怪的值,必須一一處理扼仲。
3.true和false并不在kTrueType和kFalseType里面远寸,這兩個(gè)類型設(shè)計(jì)出來(lái)就沒(méi)用過(guò)。
4.類型-8以及類型6(Number)里面的1表示true, 0表示false屠凶。
5.真正包含0和1的整數(shù)驰后,大概都在類型8里面,但是依然有很多數(shù)在類型6中矗愧。
6.類型83是要求強(qiáng)制加引號(hào)的灶芝,與類型string一個(gè)意思。
7.當(dāng)對(duì)象名為children時(shí)唉韭,它的上層數(shù)組也是children的時(shí)候夜涕,不顯示……
8.亂到這種程度我已經(jīng)無(wú)力吐槽,前面為cocoStudio叫屈的文字全部作廢纽哥。
9.數(shù)組或?qū)ο罄锩孀詈笠粋€(gè)成員后面必須不能有逗號(hào)钠乏,否則工程報(bào)錯(cuò)!
**
選擇一個(gè)現(xiàn)有的工程來(lái)測(cè)試春塌,同時(shí)輸出csb和json晓避。做個(gè)接口調(diào)用這個(gè)類,將csb文件作為輸入只壳,生成的同名json對(duì)比原工程輸出的json俏拱,一點(diǎn)一點(diǎn)找到匹配規(guī)律,排除各種坑以后吼句,最終形成上面的算法锅必。最后將原來(lái)需要逆向的csb導(dǎo)入,生成的json放回工程中測(cè)試惕艳,順利啟動(dòng)并完全符合需求搞隐。剩下的工作,就是做個(gè)選擇文件的接口远搪,具體不細(xì)說(shuō)了劣纲,有點(diǎn)基礎(chǔ)的朋友都應(yīng)該能自己搞定。