• 【Cocos2d-X开发学习笔记】第30期:游戏中数据的存储(下)


     本系列学习教程使用的是cocos2d-x-2.1.4(最新版为3.0alpha0-pre) ,PC开发环境Windows7,C++开发环境VS2010

    一、对数据进行编解码

          在上一期中,我们使用了CCUserDefault对游戏数据进行存储与读取,但是细心的我们肯定会想到,其游戏数据

    储文件是否安全呢?通过上一期对CCUserDefault的讲解,想必大家也很容易看出Cocos2D-X是以键值对进行存储

    的,其存储方式其实是个xml文件,也就是说,游戏数据并不是那么安全。

         这里我们一起来学习一个简单的对游戏数据进行加密的方法:使用Base64编码解码来实现。

         对于Base64,由于篇幅限制,这里就不过多的介绍了,不是很熟悉的话可以自行查阅相关资料。

    二、Base64C++代码

    下面首先来看看Base64的C++版本的代码,我们将编码解码封装在一个BaseData类中,这个类的代码说明如下:

    BaseData.h

    #ifndef EDCodeTest_BaseData_h
    #define EDCodeTest_BaseData_h
    #include <string>
    
    std::string saveData( char const* , unsigned int len);
    std::string parseData(std::string const& s);
    
    #endif

    BaseData.cpp

    #include "BaseData.h"
    
    
    static const std::string dataChars = 
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";
    
    static inline bool isData(unsigned char c) {
        return (isalnum(c) || (c == '+') || (c == '/'));
    }
    
    std::string saveData( char const* bytes_to_encode, unsigned int in_len) {
        std::string ret;
        int i = 0;
        int j = 0;
        unsigned char char_array_3[3];
        unsigned char char_array_4[4];
        
        while (in_len--) {
            char_array_3[i++] = *(bytes_to_encode++);
            if (i == 3) {
                char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
                char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
                char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
                char_array_4[3] = char_array_3[2] & 0x3f;
                
                for(i = 0; (i <4) ; i++)
                    ret += dataChars[char_array_4[i]];
                i = 0;
            }
        }
        
        if (i)
        {
            for(j = i; j < 3; j++)
                char_array_3[j] = '';
            
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;
            
            for (j = 0; (j < i + 1); j++)
                ret += dataChars[char_array_4[j]];
            
            while((i++ < 3))
                ret += '=';
            
        }
        return ret;
        
    }
    
    std::string parseData(std::string const& encoded_string) {
        int in_len = encoded_string.size();
        int i = 0;
        int j = 0;
        int in_ = 0;
        unsigned char char_array_4[4], char_array_3[3];
        std::string ret;
        
        while (in_len-- && ( encoded_string[in_] != '=') && isData(encoded_string[in_])) {
            char_array_4[i++] = encoded_string[in_]; in_++;
            if (i ==4) {
                for (i = 0; i <4; i++)
                    char_array_4[i] = dataChars.find(char_array_4[i]);
                
                char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
                char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
                char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
                
                for (i = 0; (i < 3); i++)
                    ret += char_array_3[i];
                i = 0;
            }
        }
        
        if (i) {
            for (j = i; j <4; j++)
                char_array_4[j] = 0;
            
            for (j = 0; j <4; j++)
                char_array_4[j] = dataChars.find(char_array_4[j]);
            
            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
            
            for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
        }
        
        return ret;
    }


    此类中我们封装了两个重要的函数saveData和parseData,它们分别对数据进行编码和解码。

    <1> std::string saveData(char const * ,unsigned int len)

    作用:对字符串数据进行编码。

    参数1:需要编码的目标字符串。

    参数2:目标字符串长度。

    <2> std::string parseData(std::string const & s)

    作用:对字符串数据进行解码。

    参数:需要解码的目标字符串。

    三、项目实例

    1、首先新建Cocos2D-X项目,取名为“MyDataSave02”,然后在项目中加入之前的BaseData类,最后在

    HelloWorldScene.cpp文件的init函数中添加如下所示代码。

    bool HelloWorld::init()
    {
        bool bRet = false;
        do 
        {
            CC_BREAK_IF(! CCLayer::init());
    
            //需要存储的字符串
    		string sValue = "Yangyu";
    		//对存储的数据进行编码
    		string saveBaseString = saveData(sValue.c_str(), sValue.length());
    		//将得到编码后的数据进行存储
    		CCUserDefault::sharedUserDefault()->setStringForKey("Base", saveBaseString);
    		CCUserDefault::sharedUserDefault()->flush();
        
    		//从存储中获取编码的数据
    		string loadBaseString  =  CCUserDefault::sharedUserDefault()->getStringForKey("Base");
    		//对编码数据进行解码得到真实数据
    		string trueValue = parseData(loadBaseString);
        
        
    		//---用以观察数据
    		CCLabelTTF* label = CCLabelTTF::create("", "Helvetica", 20);
    		label->setPosition(ccp(240,185));
    		addChild(label);
    		//编码之前的数据
    		string logStr = "Before Encoding:";
    		logStr+=sValue;
    		//编码之后的数据
    		logStr+="    After Encoding:";
    		logStr+=saveBaseString;
    		label->setString(logStr.c_str());
       
        
    		CCLabelTTF*label2 = CCLabelTTF::create("", "Helvetica", 20);
    		label2->setPosition(ccp(240,140));
    		addChild(label2);
    		//解码之前的数据
    		logStr = "Before Decode:";
    		logStr+=loadBaseString;
    		//解码之后的数据
    		logStr+="     After Decode:";
    		logStr+=trueValue;
    		label2->setString(logStr.c_str());
    
            bRet = true;
        } while (0);
    
        return bRet;
    }


     

    2、运行效果图

    四、Base64编码解码工具

         如果我们只是简单的将数据进行Base64编码,其实也是不安全的,对于稍微有些技术背景的人来说,对Base64解

    码轻而易举!下图所示是我从网上随意使用了一个Base64解码工具,对刚编码的数据进行解码的例子。

    在使用我提供的BaseData工具类时,我们需要在保存数据和读取数据时注意以下几点。

    保存数据时:

    <1> 当Base64对游戏数据进行编码后,不要着急进行存储,可以对编码后的字符串进行一些处理。例如在编码后的字符串中添加一

    些无用字符、数字;或者在这个编码后的字符串进行位置调换操作等。

    <2> 当对数据编码后的字符串进行处理后,在利用CCUserDefault进行保存。

    读取数据时:

    <1> 从存储文件中获取到数据后(已经做了处理的Base64编码字符串),首先对获取的数据进行反操作(按照存储

    之前对编码字符串的操作进行反操作)。

    <2> 反操作得到正确的Base64编码字符串后再进行Base64解码,以得到真实数据。

    这里一来,除非破解者知道你做处理的过程,或者有你的源码,否则会很难破解你的数据。

    源码下载地址

  • 相关阅读:
    二叉树中序遍历的非递归实现
    求树的遍历、树的叶子节点个数、树的高度、copy树
    javascript知识点汇总(running)
    IOS零碎知识点(积累中)
    Cuda learn record three
    Cuda learn record two
    找出字符串中的最长的回文子串
    Cuda learn record one
    Chrome 安装失败 错误代码 0X80070057
    Vs 2015 项目中include 无法打开源文件
  • 原文地址:https://www.cnblogs.com/pangblog/p/3299450.html
Copyright © 2020-2023  润新知