Add PVR.CCZ encryption support to ZipUtils based upon http://www.codeandweb.com/texturepacker/contentprotection

Add sample to TestCPP.
This commit is contained in:
Ed Bartley 2013-04-25 13:40:49 -04:00
parent bd4f2b51a2
commit f89d39953e
7 changed files with 430 additions and 163 deletions

View File

@ -33,6 +33,106 @@ THE SOFTWARE.
NS_CC_BEGIN
static unsigned int caw_key[4] = {0,0,0,0};
static unsigned int caw_longKey[1024];
static bool caw_longKeyValid = false;
void caw_setkey_part(int index, unsigned int value)
{
CCAssert(index >= 0, "Cocos2d: key part index cannot be less than 0");
CCAssert(index <= 3, "Cocos2d: key part index cannot be greater than 3");
if(caw_key[index] != value)
{
caw_key[index] = value;
caw_longKeyValid = false;
}
}
static inline void caw_encdec (unsigned int *data, int len)
{
const int enclen = 1024;
const int securelen = 512;
const int distance = 64;
// check if key was set
// make sure to call caw_setkey_part() for all 4 key parts
CCAssert(caw_key[0] != 0, "Cocos2D: CCZ file is encrypted but key part 0 is not set. Did you call caw_setkey_part(...)?");
CCAssert(caw_key[1] != 0, "Cocos2D: CCZ file is encrypted but key part 1 is not set. Did you call caw_setkey_part(...)?");
CCAssert(caw_key[2] != 0, "Cocos2D: CCZ file is encrypted but key part 2 is not set. Did you call caw_setkey_part(...)?");
CCAssert(caw_key[3] != 0, "Cocos2D: CCZ file is encrypted but key part 3 is not set. Did you call caw_setkey_part(...)?");
// create long key
if(!caw_longKeyValid)
{
unsigned int y, p, e;
unsigned int rounds = 6;
unsigned int sum = 0;
unsigned int z = caw_longKey[enclen-1];
do
{
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (caw_key[(p&3)^e] ^ z)))
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < enclen - 1; p++)
{
y = caw_longKey[p + 1];
z = caw_longKey[p] += MX;
}
y = caw_longKey[0];
z = caw_longKey[enclen - 1] += MX;
} while (--rounds);
caw_longKeyValid = true;
}
int b = 0;
int i = 0;
// encrypt first part completely
for(; i < len && i < securelen; i++)
{
data[i] ^= caw_longKey[b++];
if(b >= enclen)
{
b = 0;
}
}
// encrypt second section partially
for(; i < len; i += distance)
{
data[i] ^= caw_longKey[b++];
if(b >= enclen)
{
b = 0;
}
}
}
static inline unsigned int caw_checksum(const unsigned int *data, int len)
{
unsigned int cs = 0;
const int cslen = 128;
len = (len < cslen) ? len : cslen;
for(int i = 0; i < len; i++)
{
cs = cs ^ data[i];
}
return cs;
}
// --------------------- ZipUtils ---------------------
// memory in iPhone is precious
@ -232,13 +332,8 @@ int ZipUtils::ccInflateCCZFile(const char *path, unsigned char **out)
struct CCZHeader *header = (struct CCZHeader*) compressed;
// verify header
if( header->sig[0] != 'C' || header->sig[1] != 'C' || header->sig[2] != 'Z' || header->sig[3] != '!' )
if( header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == '!' )
{
CCLOG("cocos2d: Invalid CCZ file");
delete [] compressed;
return -1;
}
// verify header version
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST( header->version );
if( version > 2 )
@ -255,6 +350,54 @@ int ZipUtils::ccInflateCCZFile(const char *path, unsigned char **out)
delete [] compressed;
return -1;
}
}
else if( header->sig[0] == 'C' && header->sig[1] == 'C' && header->sig[2] == 'Z' && header->sig[3] == 'p' )
{
// encrypted ccz file
header = (struct CCZHeader*) compressed;
// verify header version
unsigned int version = CC_SWAP_INT16_BIG_TO_HOST( header->version );
if( version > 0 )
{
CCLOG("cocos2d: Unsupported CCZ header format");
delete [] compressed;
return -1;
}
// verify compression format
if( CC_SWAP_INT16_BIG_TO_HOST(header->compression_type) != CCZ_COMPRESSION_ZLIB )
{
CCLOG("cocos2d: CCZ Unsupported compression method");
delete [] compressed;
return -1;
}
// decrypt
unsigned int* ints = (unsigned int*)(compressed+12);
int enclen = (fileLen-12)/4;
caw_encdec(ints, enclen);
#if COCOS2D_DEBUG > 0
// verify checksum in debug mode
unsigned int calculated = caw_checksum(ints, enclen);
unsigned int required = CC_SWAP_INT32_BIG_TO_HOST( header->reserved );
if(calculated != required)
{
CCLOG("cocos2d: Can't decrypt image file: Invalid decryption key");
delete [] compressed;
return -1;
}
#endif
}
else
{
CCLOG("cocos2d: Invalid CCZ file");
delete [] compressed;
return -1;
}
unsigned int len = CC_SWAP_INT32_BIG_TO_HOST( header->len );
@ -266,7 +409,6 @@ int ZipUtils::ccInflateCCZFile(const char *path, unsigned char **out)
return -1;
}
unsigned long destlen = len;
unsigned long source = (unsigned long) compressed + sizeof(*header);
int ret = uncompress(*out, &destlen, (Bytef*)source, fileLen - sizeof(*header) );

View File

@ -28,6 +28,26 @@ THE SOFTWARE.
namespace cocos2d
{
/**
* Set the TexturePacker encryption key
*
* If your key used to encrypt the pvr.ccz file is
* aaaaaaaabbbbbbbbccccccccdddddddd
* you have to call this function 4 times:
* caw_setkey_part(0, 0xaaaaaaaa);
* caw_setkey_part(1, 0xbbbbbbbb);
* caw_setkey_part(2, 0xcccccccc);
* caw_setkey_part(3, 0xdddddddd);
*
* Distribute the call accross some files but make sure
* to call all of the parts *before* loading the first
* spritesheet.
*
* @param index part of the key [0..3]
* @param value value of the key part
*/
void caw_setkey_part(int index, uint32_t value);
/* XXX: pragma pack ??? */
/** @struct CCZHeader
*/

View File

@ -0,0 +1,74 @@
#include "TextureAtlasEncryptionTest.h"
#include "../testResource.h"
#include "support/zip_support/ZipUtils.h"
std::string TextureAtlasEncryptionDemo::title()
{
return "Texture Atlas Encryption";
}
std::string TextureAtlasEncryptionDemo::subtitle()
{
return "";
}
void TextureAtlasEncryptionDemo::onEnter()
{
CCLayer::onEnter();
CCSize s = CCDirector::sharedDirector()->getWinSize();
CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 28);
label->setPosition( ccp(s.width/2, s.height * 0.75f) );
this->addChild(label, 1);
std::string strSubtitle = subtitle();
if(strSubtitle.empty() == false)
{
CCLabelTTF* subLabel = CCLabelTTF::create(strSubtitle.c_str(), "Thonburi", 16);
subLabel->setPosition( ccp(s.width/2, s.height-80) );
this->addChild(subLabel, 1);
}
// Load the non-encrypted atlas
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("Images/nonencryptedAtlas.plist", "Images/nonencryptedAtlas.pvr.ccz");
// Create a sprite from the non-encrypted atlas
CCSprite *nonencryptedSprite = CCSprite::createWithSpriteFrameName("Icon.png");
nonencryptedSprite->setPosition(ccp(s.width * 0.25f, s.height * 0.5f));
this->addChild(nonencryptedSprite);
CCLabelTTF* nonencryptedSpriteLabel = CCLabelTTF::create("non-encrypted", "Arial", 28);
nonencryptedSpriteLabel->setPosition(ccp(s.width * 0.25f, nonencryptedSprite->boundingBox().getMinY() - nonencryptedSprite->getContentSize().height/2));
this->addChild(nonencryptedSpriteLabel, 1);
// Load the encrypted atlas
// 1) Set the encryption keys or step 2 will fail
caw_setkey_part(0, 0xaaaaaaaa);
caw_setkey_part(1, 0xbbbbbbbb);
caw_setkey_part(2, 0xcccccccc);
caw_setkey_part(3, 0xdddddddd);
// 2) Load the encrypted atlas
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("Images/encryptedAtlas.plist", "Images/encryptedAtlas.pvr.ccz");
// 3) Create a sprite from the encrypted atlas
CCSprite *encryptedSprite = CCSprite::createWithSpriteFrameName("powered.png");
encryptedSprite->setPosition(ccp(s.width * 0.75f, s.height * 0.5f));
this->addChild(encryptedSprite);
CCLabelTTF* encryptedSpriteLabel = CCLabelTTF::create("encrypted", "Arial", 28);
encryptedSpriteLabel->setPosition(ccp(s.width * 0.75f, encryptedSprite->boundingBox().getMinY() - encryptedSpriteLabel->getContentSize().height/2));
this->addChild(encryptedSpriteLabel, 1);
}
void TextureAtlasEncryptionTestScene::runThisTest()
{
CCLayer *layer = new TextureAtlasEncryptionDemo;
layer->autorelease();
addChild(layer);
CCDirector::sharedDirector()->replaceScene(this);
}

View File

@ -0,0 +1,25 @@
#ifndef _TextureAtlasEncryption_TEST_H_
#define _TextureAtlasEncryption_TEST_H_
#include "cocos2d.h"
#include "../testBasic.h"
#include <string>
class TextureAtlasEncryptionDemo : public CCLayer
{
public:
virtual std::string title();
virtual std::string subtitle();
virtual void onEnter();
protected:
std::string m_strTitle;
};
class TextureAtlasEncryptionTestScene : public TestScene
{
public:
virtual void runThisTest();
};
#endif

View File

@ -122,6 +122,9 @@ static TestScene* CreateTestScene(int nIdx)
case TEST_SPINE:
pScene = new SpineTestScene();
break;
case TEST_TEXTUREPACKER_ENCRYPTION:
pScene = new TextureAtlasEncryptionTestScene();
break;
default:
break;
}

View File

@ -54,6 +54,7 @@
#endif
#include "FileUtilsTest/FileUtilsTest.h"
#include "SpineTest/SpineTest.h"
#include "TexturePackerEncryptionTest/TextureAtlasEncryptionTest.h"
enum
{
@ -111,6 +112,7 @@ enum
#endif
TEST_FILEUTILS,
TEST_SPINE,
TEST_TEXTUREPACKER_ENCRYPTION,
TESTS_COUNT,
};
@ -168,7 +170,8 @@ const std::string g_aTestNames[TESTS_COUNT] = {
"ClippingNodeTest",
#endif
"FileUtilsTest",
"SpineTest"
"SpineTest",
"TexturePackerEncryption"
};
#endif

View File

@ -1 +1 @@
655a6ce855dabb133120b19537ed3879b2fc8194
2560c2b2abf0916bf27c47de3987b1567c690df9