00003 不思议迷宫.0009.1:来,练个手:换肤
来源:互联网 发布:数控编程视频教程 编辑:程序博客网 时间:2024/04/27 21:18
00003不思议迷宫.0009.1:来,练个手:换肤
我偶尔会出去晒太阳,边晒太阳边玩游戏。问题就来了,不管是我的iPhone还是小米,在阳光较多的地方,哪怕是在阴影下,游戏中的地牢背景(掀开石板后显示的那些背景方块)总是黑漆漆的,基本上看不见;而前景方块、怪物或者掉落的物品就没有这个问题。那我们就换掉它吧。
替换图片这种修改无疑是简单的。但首先需要先找到它。
在前面我们曾经遇到过一个“/src/game/ui/form/dungeon/UIDungeonMain.luac”文件,这里面就包含了地牢的界面创建:
--构造函数
function UIDungeonMain:ctor(levelData, floor)
self:setName("UIDungeonMain");
local node =cc.CSLoader:createNode("layout/dungeon/DungeonMain.csb");
……
end
csb是Cocos Studio导出的场景(一般是静态界面,也可以包括动画)文件,里面包含了所用资源(含图片)的名称(含路径)。遗憾的是它是二进制的,但万幸的是,资源名称还是明文的:
那么我们就可以想办法提取出其中的全部图片文件,看看哪些是我们需要的。但这个办法不够友好,我们完全可以采用可视化的方法。
csb可以在cocos2dx中被解析和显示,并可对其中的元素增加事件响应。不幸的是,《不思议迷宫》使用的cocos2dx版本似乎是3.3,而我电脑上的是3.7.1和3.10,而且在cocos商店中也下不到3.3,看到有个3.4Final,结果下下来却是3.7.1,网上找到的几个3.3的下载链接也不能用。用3.10加载之后,报错了。这个方法只能先放放。
如果能找到对应的版本,并且有足够的时间,还可以研究csb的详细加载过程,然后还原成CocosStudio导出前的csd格式。
下面我就试试找到的第一个图片。
当然,我先看到的是images/ui/level.plist,是个plist而不是任何一种图片后缀。我知道一些(合图)工具会使用plist,而cocos2dx也支持这种方式,并且非常好用。我的经验告诉我,images/ui/level.plist会对应一个images/ui/level.xxx,其中xxx是图片后缀。我一找,果然找到一个images/ui/level.png。然后我兴冲冲地打开它。再然后,看图软件报错了——图片格式错误。
好吧,这不算什么。我知道Texture Packer之类的合图工具会给图片加密,并且知道在cocos2dx中使用这个加密图片时,需要设置密码。设置密码的函数为setPvrEncryptionKey。于是,我在ida反编译libcocos2dlua.so中查找setPvrEncryptionKey的引用,然后,就傻眼了——没有引用。
是调用点被某种形式地隐藏了吗?还是确实真的就没有引用?如果是前者,那问题就麻烦了。先确定到底是哪种情况吧。
开始跟踪cc.CSLoader:createNode("layout/dungeon/DungeonMain.csb");的调用。
这是lua中的函数,在cocos2dx-lua中,它会被绑定为C++中的lua_cocos2dx_CSLoader_createNode。在ida反编译libcocos2dlua.so中找到这个函数,发现它又调用了cocos2d::CSLoader::createNode((cocos2d::CSLoader *)&v5),然后又调用了……,下面是过程:
cocos2d::CSLoader::createNodeFromProtocolBuffers(v2,(int)v1);
cocos2d::CSLoader::nodeFromProtocolBuffersFile(v3,v2);
cocos2d::SpriteFrameCache::addSpriteFramesWithFile
cocos2d::TextureCache::addImage(v7)
cocos2d::Image::initWithImageFile(v6)
cocos2d::Image::initWithImageData(v1,v6, v7)
到这个函数中就需要注意了:
void __fastcallcocos2d::Image::initWithImageData(cocos2d::Image *this, const unsigned __int8*a2, __int32 a3)
{
……
if (cocos2d::ZipUtils::isCCZBuffer((cocos2d::ZipUtils *)a2, (const unsigned __int8*)a3, a3) == 1 )
{
v11 = v4;
v7 = (constunsigned __int8 *)cocos2d::ZipUtils::inflateCCZBuffer(v4, v3,(__int32)&v13, v6);
}
else
{
if (cocos2d::ZipUtils::isGZipBuffer(v4, v3, v5) != 1 )
{
v13 =(const unsigned __int8 *)v4;
v11 = v4;
gotoLABEL_9;
}
v11 = v4;
v7 = (constunsigned __int8 *)cocos2d::ZipUtils::inflateMemory(v4, (unsigned __int8 *)v3,(__int32)&v13, v8);
}
v3 = v7;
LABEL_9:
v9 =cocos2d::Image::detectFormat(v12, v13, (__int32)v3);
*((_DWORD *)v12+ 9) = v9;
if ( v9 <= 7)
JUMPOUT(__CS__,(char *)dword_4B6830 + dword_4B6830[v9]);
v10 = (void*)cocos2d::tgaLoadBuffer((cocos2d *)v13, (unsigned __int8 *)v3, (__int32)v12);
if ( v10&& !*(_DWORD *)v10 )
{
cocos2d::Image::initWithTGAData(v12,v10);
}
else
{
……
}
……
}
我瞅了瞅,又瞅了瞅,从头看到尾,又从未看到头,恁是没发现有啥疑点,哪怕是JUMPOUT中的switch。
在揪断几根头发之后,突然想起看看png的初始化函数,没想到这一看就看到了秘密:
signed int__fastcall cocos2d::Image::initWithPngData(cocos2d::Image *this, const unsigned__int8 *a2, __int32 a3)
{
……
v30 = a3;
v3 = a2;
v32 = this;
v4 = a2[2];
v5 = a2[3];
v6 = *a2;
v7 = a2[1];
v41 = (((a2[7] << 8) | a2[6]) <<16) | (a2[5] << 8) | a2[4];
*(_DWORD *)v40 = (((v5 << 8) | v4)<< 16) | (v7 << 8) | v6;
if ( cocos2d::Image::isEncrypted(this, v40,8) == 1 )
{
v8 = 200;
if ( v30 <= 200 )
v8 = v30;
cocos2d::Image::xorEncrypt((cocos2d::Image*)1, (void *)v3, v8);
}
……
}
哇哈哈哈,来吧,写个程序加密解密吧。因为图省事,用了不是最合适的C#:
staticstringxorEncryptKey="// Dump Ref object memory leaks if(__refAllocationList.empty()) { log([memory] All Ref objects successfullycleaned up (no leaks detected).); } else { log([memory] WARNING: %d Ref objectsstill active in memory., (int)__refAllocationList.size()); for (const auto&ref : __refAllocationList) { CC_ASSERT(ref); const char* type =typeid(*ref).name(); log([memory] LEAK: Ref object %s still active withreference count %d., (type ? type : ), ref->getReferenceCount()); }}";
staticvoidxorEncrypt(byte[] v, intlength)
{
for(inti = 0; i < length; ++i)
{
v[i] ^= (byte)xorEncryptKey[(i %xorEncryptKey.Length)];
}
}
staticintxorEncryptingLength(byte[] v)
{
if(v.Length <= 200)return v.Length;
return200;
}
staticboolisEncryptedPng(byte[] v)
{
return(v[0] != 0x89) || (v[1] !='P') ||(v[2] !='N') || (v[3] !='G');
}
privatestaticbyte[]ToPlainPng(byte[] v)
{
if(isEncryptedPng(v)) xorEncrypt(v, xorEncryptingLength(v));
returnv;
}
privatestaticbyte[]ToEncodedPng(byte[] v)
{
xorEncrypt(v, xorEncryptingLength(v));
returnv;
}
解密level.png之后,打开看看,发现一个灰色的图块(红框标注)很像:
这个区域对应的切片名称为“images/ui/level/black_mask.png”。别问我是如何找到这个的,猜测第一,验证第二。唉,网上有一个叫“Anti TexturePacker”的小工具,可以方便地查看plist中定义的小图。我以前也用过,还不错。但是,对这个level.plist的解析却出错了。然后,我又找了一个“Cocos查看器”,结果还是看不了level.plist。实在是让人无语了。求人不如求己,还是抽空自己写个切片工具吧。目前先用PhotoShop应付一次。
找个明亮点的颜色替换进去,保存,加密,再替换掉原来的level.png,然后别忘了更新project.manifest中的相关信息,安装,看效果,才发现搞错了……
虽然闹了个乌龙,但更换图片的方法就是上面讲述的这些。需要说明的是,png图片也可以不用加密,游戏内部会自动检测的。
背景方块在阳光充足的地方看不见的问题我一定会改,只不过由于验证起来太烦,暂时不会进行,而要等到和其他修改一起。唉,没越狱没root的手机真是不方便。- 00003 不思议迷宫.0009.1:来,练个手:换肤
- 00003 不思议迷宫.0009.4:攻防计算
- 00003 不思议迷宫.0009.8:Bug之一
- 00003 不思议迷宫.0001:解密Lua脚本
- 00003 不思议迷宫.0004:客户端数据缓存
- 00003 不思议迷宫.0005:是数据同步吗?
- 00003 不思议迷宫.0009.2.1:自动换装:简单规划
- 00003 不思议迷宫.0010.1.1:csb解析显示
- 00003 不思议迷宫.0010.1.1.2:csb解析显示
- 00003 不思议迷宫.0009.2.2:自动换装:界面模拟
- 00003 不思议迷宫.0009.5:炼金坊自动捡钱
- 00003 不思议迷宫.0011:Android新版中的Lua加密
- 00003 不思议迷宫.0010.2:project.manifest自动生成器
- 00003 不思议迷宫.0009.9:命运之链
- 00003 不思议迷宫.0012:SB的雷霆运营
- 00003 不思议迷宫.0002:修改Lua,虽然实际上没什么卵用
- 00003 不思议迷宫.0003:玩家数据真的就不能改了吗?
- 00003 不思议迷宫.0006:客户端的操作如何反应到服务器?
- ZooKeeper集群环境的配置与安装
- Leetcode刷题记——41. First Missing Positive(第一个丢失的正数)
- Win32 SDK 访问数据库
- Zookeeper使用命令行
- Linux之ip配置
- 00003 不思议迷宫.0009.1:来,练个手:换肤
- 最大 最小 平均值(文件)
- 第一篇博客
- 2017年JAVA后端web开发工程师面试各大公司比较【热门的公司基本都有】
- 数据结构:几种排序算法的介绍。
- 开始在CSDN上面写自己的博客了
- Web.xml配置详解之context-param
- JS生成随机十六进制颜色值
- Maven整合SSH SpringMvc所需jar包