游戏开发中常遇到资源保护的问题。目前游戏开发中常加密的文件类型有:图片,Lua文件,音频等文件,而其实加密也是一把双刃剑。需要安全那就得耗费一定的资源去实现它。目前网上也有用TexturePacker工具来加密的,不过针对性还是不够强。
分析一下原理:
1. 转格式:将需要加密的文件转为流的方式;
2. 加密:根据自己需要使用加密手段,MD5,AES,甚至可以直接改变位移,加一些自己的特殊字符也可以使文件简单加密,加密完后基本保证图片类型基本用特殊软件预览不了也打不开,Lua文件加密后一片乱码;
3. 保存自定义格式文件:另存为自己特殊类型的文件名如"xx.d" "xx.xyz"等。
4. 图片解密:修改cocos2dx底层库的获取路径处,和加载CCImage纹理处理时的源码修改;
5. 特殊Lua文件界面:修改对应Lua加载方法;
基本原理清楚了后,介绍笔者项目中常用的加密方式:
首先是转格式并且加密的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
bool
PublicCommen::recode_getFileByName(string pFileName){
unsigned
long
nSize = 0;
unsigned
char
* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(
pFileName.c_str(),
"rb"
,&nSize);
unsigned
char
* newBuf =
new
unsigned
char
[nSize];
int
newblen = nSize;
if
(pBuffer!=NULL&&nSize>0)
{
for
(
int
i = 0; i<nSize; i++) {
newBuf[i]=pBuffer[i]+MD5;
}
string savepath = pFileName;
savepath = savepath.substr(0,savepath.length()-4);
savepath = savepath +
"xx.X"
;
FILE
*fp =
fopen
(savepath.c_str(),
"wb+"
);
fwrite
(newBuf, 1, newblen, fp);
fclose
(fp);
CCLOG(
"save file ok. path = %s"
,savepath.c_str());
return
true
;
}
return
false
;
}
|
通常可以自己写一个应用程序遍历一下自定义目录下,需要转的资源文件,对应的把所有资源转换并加密;
里面newBuf[i]=pBuffer[i]+MD5;这段可以自由发挥!解密的时候需要对应!
当然你也可以取巧的放进你的游戏中修改cocos2dx底层的CCFileUtils::fullPathForFilename获取全路径的方法中;
下面说一下解密:
图片的解密需要修改cocos2dx CCTexture2D 的CCTextureCache::addImage类里面修改
并且在CCImage的图片类型中添加你加密后的图片类型如:CCImage::xxxxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
CCTexture2D * CCTextureCache::addImage(
const
char
* path)
{
CCAssert(path != NULL,
"TextureCache: fileimage MUST not be NULL"
);
CCTexture2D * texture = NULL;
CCImage* pImage = NULL;
// Split up directory and filename
// MUTEX:
// Needed since addImageAsync calls this method from a different thread
//pthread_mutex_lock(m_pDictLock);
std::string pathKey = path;
pathKey = CCFileUtils::sharedFileUtils()->fullPathForFilename(pathKey.c_str());
if
(pathKey.size() == 0)
{
return
NULL;
}
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());
std::string fullpath = pathKey;
// (CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(path));
if
(! texture)
{
std::string lowerCase(pathKey);
for
(unsigned
int
i = 0; i < lowerCase.length(); ++i)
{
lowerCase[i] =
tolower
(lowerCase[i]);
}
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
if
(std::string::npos != lowerCase.find(
".pvr"
))
{
texture =
this
->addPVRImage(fullpath.c_str());
}
else
if
(std::string::npos != lowerCase.find(
".pkm"
))
{
// ETC1 file format, only supportted on Android
texture =
this
->addETCImage(fullpath.c_str());
}
else
{
CCImage::EImageFormat eImageFormat = CCImage::kFmtUnKnown;
if
(std::string::npos != lowerCase.find(
".png"
))
{
eImageFormat = CCImage::kFmtPng;
}
else
if
(std::string::npos != lowerCase.find(
".jpg"
) || std::string::npos != lowerCase.find(
".jpeg"
))
{
eImageFormat = CCImage::kFmtJpg;
}
else
if
(std::string::npos != lowerCase.find(
".tif"
) || std::string::npos != lowerCase.find(
".tiff"
))
{
eImageFormat = CCImage::kFmtTiff;
}
else
if
(std::string::npos != lowerCase.find(
".webp"
))
{
eImageFormat = CCImage::kFmtWebp;
}
else
if
(std::string::npos != lowerCase.find(
"XX.X"
))
{
eImageFormat = CCImage::xxxxx;
}
pImage =
new
CCImage();
CC_BREAK_IF(NULL == pImage);
bool
bRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);
CC_BREAK_IF(!bRet);
texture =
new
CCTexture2D();
if
( texture &&
texture->initWithImage(pImage) )
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name
VolatileTexture::addImageTexture(texture, fullpath.c_str(), eImageFormat);
#endif
m_pTextures->setObject(texture, pathKey.c_str());
texture->release();
}
else
{
CCLOG(
"cocos2d: Couldn't create texture for file:%s in CCTextureCache"
, path);
}
}
}
while
(0);
}
CC_SAFE_RELEASE(pImage);
//pthread_mutex_unlock(m_pDictLock);
return
texture;
}
|
然后跟到
1
|
bool
bRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);
|
CCImage.mm中的CCImage::initWithImageFile方法;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
bool
CCImage::initWithImageFile(
const
char
* strPath, EImageFormat eImgFmt
/* = eFmtPng*/
)
{
bool
bRet =
false
;
unsigned
long
nSize = 0;
unsigned
char
* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(
CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath).c_str(),
"rb"
,
&nSize);
if
(eImgFmt==xxxxxx)
{
for
(
int
i= 0; i < nSize; i++) {
pBuffer[i] = pBuffer[i]-MD5;
}
pBuffer[nSize] = pBuffer[nSize]-1;
eImgFmt = kFmtPng;
}
if
(pBuffer != NULL && nSize > 0)
{
bRet = initWithImageData(pBuffer, nSize, eImgFmt);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
return
bRet;
}
|
其中,pBuffer[i] = pBuffer[i]-MD5;需要和之前加密的时候对应,自己发挥!
Ok,只要是图片,并且是属于你自定义类型的图片都会得到解密的真实texture。
Lua的解密也是基本一样的思路,不过解密需要单独在需要加载Lua的方法前先解密,要考虑跨平台性。