読者です 読者をやめる 読者になる 読者になる

cocos2d-x バイナリファイルの入出力

UserDefaultを使いデータを保存すると簡易に書き換えられてしまう。

そこで、バイナリとして保存して その後暗号化したりして保存する

unsigned char *配列などのバイナリデータを書き込む際にはcocos2d::Dataを使う

ファイルからDataに取り込み

FileUtils* fileUtil = FileUtils::getInstance();

Data data = fileUtil->getDataFromFile("ファイルパス");

Dataからunsigned char*配列

unsigned char * uchardata = data->getBytes();

ssize_t size = data->getSize();

unsigned char *配列からDataに格納

Data data;
data.fastSet(uchardata, size);

Dataからファイルへ書き出し

FileUtils* fileUtil = FileUtils::getInstance();
bool ret = fileUtil->writeDataToFile(data,"ファイルパス");

 

基本はこの4つです。

それで作ってみたクラスはこちら

 

ファイル入出力クラス

DataFile.h

#ifndef __DATA_FILE_H__
#define __DATA_FILE_H__

#include "cocos2d.h"
#include "ConfigData.h"

#define CONFIG_FILE_NAME "config.data"

class DataFile
{
public:
    static DataFile* getInstance();
    void destroyInstance();
    bool isConfigFileExist();
    void initFilePath();
    bool createConfigFile();
    const std::string& getConfigFilePath();
    bool loadConfigFile();
    bool writeConfigFile();
protected:
    DataFile();
    virtual ~DataFile();

private:
    static DataFile* _dataFile;
    std::string _configFilePath;
    bool _isFilePathInitialized;
};

#endif

DataFile.cpp

#include "DataFile.h"

using namespace std;

USING_NS_CC;

DataFile* DataFile::_dataFile = nullptr;

DataFile::DataFile()
{
}

DataFile::~DataFile()
{
}

DataFile* DataFile::getInstance()
{
    if (!_dataFile)
    {
        _dataFile = new (std::nothrow) DataFile();
        _dataFile->_configFilePath = string("");
        _dataFile->_isFilePathInitialized = false;
        _dataFile->initFilePath();

        if (!_dataFile->isConfigFileExist() && !_dataFile->createConfigFile())
        {
            return nullptr;
        }
    }

    return _dataFile;
}

void DataFile::destroyInstance()
{
    CC_SAFE_DELETE(_dataFile);
}

bool DataFile::isConfigFileExist()
{
    return FileUtils::getInstance()->isFileExist(_configFilePath);
}

void DataFile::initFilePath()
{
    if (!_isFilePathInitialized)
    {
        _dataFile->_configFilePath += FileUtils::getInstance()->getWritablePath() + CONFIG_FILE_NAME;
        _dataFile->_isFilePathInitialized = true;
    }
}
bool DataFile::createConfigFile()
{
    bool bRet = false;
    FileUtils* fileUtil = FileUtils::getInstance();
    ConfigData* configdata = ConfigData::getInstance();

    return fileUtil->writeDataToFile(configdata->GetData(), _configFilePath);
}
const string& DataFile::getConfigFilePath()
{
    return _configFilePath;
}
bool DataFile::loadConfigFile()
{
    if (!isConfigFileExist())
    {
        return false;
    }
    FileUtils* fileUtil = FileUtils::getInstance();
    Data load = fileUtil->getDataFromFile(_configFilePath);
    ConfigData* cdata = ConfigData::getInstance();
    bool ret = cdata->setData(&load);
    load.fastSet(nullptr, 0);
    return ret;
}
bool DataFile::writeConfigFile()
{
    if (!isConfigFileExist())
    {
        return false;
    }
    FileUtils* fileUtil = FileUtils::getInstance();
    ConfigData* cdata = ConfigData::getInstance();
    Data data = cdata->GetData();
    bool ret = fileUtil->writeDataToFile(data, _configFilePath);
    data.fastSet(nullptr, 0);
    return ret;
}

入出力データの基本クラス

 DataModel.h

#ifndef __DATA_MODEL_H__
#define __DATA_MODEL_H__

#include "cocos2d.h"

class DataModel
{
public:
    void GetData(unsigned char *& data, std::string str);
    void GetData(unsigned char *& data, uint64_t num);
    void GetData(unsigned char *& data, uint32_t num);
    void GetData(unsigned char *& data, uint16_t num);
    void GetData(unsigned char *& data, uint8_t num);
    void GetData(unsigned char *& data, bool flg);
    void setData(unsigned char *& data, std::string * setdata, size_t length);
    void setData(unsigned char *& data, uint64_t * setdata);
    void setData(unsigned char *& data, uint32_t * setdata);
    void setData(unsigned char *& data, uint16_t * setdata);
    void setData(unsigned char *& data, uint8_t * setdata);
    void setData(unsigned char *& data, bool * setdata);
protected:
    DataModel();
    virtual ~DataModel();
};
#endif

DataModel.cpp

#include "DataModel.h"

using namespace std;

USING_NS_CC;

DataModel::DataModel()
{
}

DataModel::~DataModel()
{
}

void DataModel::GetData(unsigned char *& data, std::string str)
{
    size_t length = str.length();
    const char* cchar = str.c_str();
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)cchar[i];
    }
    data += length;
}

//0 - 18446744073709551615
void DataModel::GetData(unsigned char *& data, uint64_t num)
{
    size_t length = 8;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 4294967295
void DataModel::GetData(unsigned char *& data, uint32_t num)
{
    size_t length = 4;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 65535
void DataModel::GetData(unsigned char *& data, uint16_t num)
{
    size_t length = 2;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

//0 - 255
void DataModel::GetData(unsigned char *& data, uint8_t num)
{
    size_t length = 1;
    for (unsigned int i = 0; i < length; i++) {
        data[i] = (unsigned char)( (unsigned char*)&num)[i];
    }
    data += length;
}

void DataModel::GetData(unsigned char *& data, bool flg)
{
    size_t length = 1;
    if (flg) {
        data[0] = (unsigned char)1;
    }
    else {
        data[0] = (unsigned char)0;
    }
    data += length;
}

void DataModel::setData(unsigned char *& data, std::string * setdata, size_t length)
{
    if (length <= 0) {
        *setdata = "";
        return;
    }
    *setdata = std::string(reinterpret_cast<const char*>(data), length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint64_t * setdata)
{
    size_t length = 8;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint32_t * setdata)
{
    size_t length = 4;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint16_t * setdata)
{
    size_t length = 2;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, uint8_t * setdata)
{
    size_t length = 1;
    memcpy(&(*setdata), data, length);
    data += length;
}

void DataModel::setData(unsigned char *& data, bool * setdata)
{
    size_t length = 1;
    memcpy(&(*setdata), data, length);
    data += length;
}

コンフィグデータ

ConfigData.h

#ifndef __CONFIG_DATA_H__
#define __CONFIG_DATA_H__

#include "cocos2d.h"
#include "DataModel.h"

class ConfigData : public DataModel
{
public:
    static ConfigData* getInstance();
    cocos2d::Data GetData();
    bool setData(cocos2d::Data * data);
    std::string language;
    uint8_t bgmVolume;
    uint8_t seVolume;
protected:
    ConfigData();
    virtual ~ConfigData();
private:
    void LoadDefault();
    static ConfigData* _configdata;
};

#endif

ConfigData.cpp

#include "ConfigData.h"

using namespace std;

USING_NS_CC;

ConfigData* ConfigData::_configdata = nullptr;

ConfigData::ConfigData()
{
}

ConfigData::~ConfigData()
{
}

ConfigData* ConfigData::getInstance()
{
    if (!_configdata)
    {
        _configdata = new (std::nothrow) ConfigData();
        _configdata->LoadDefault();
    }
    return _configdata;
}

Data ConfigData::GetData()
{
    size_t size = 0;
    size += sizeof(size_t);
    size += _configdata->language.length();
    size += sizeof(uint8_t);
    size += sizeof(uint8_t);
    unsigned char * uchardata = new unsigned char[size + 1];
    uchardata[size] = '\0';
    unsigned char * udata = uchardata;
    DataModel::GetData(udata, _configdata->language.length());
    DataModel::GetData(udata, _configdata->language);
    DataModel::GetData(udata, _configdata->bgmVolume);
    DataModel::GetData(udata, _configdata->seVolume);

//もし暗号化するなら unchardataとsizeを使ってここでする
    Data datasource;
    datasource.fastSet(uchardata, size);
    return datasource;
}

bool ConfigData::setData(Data * data)
{
    unsigned char * uchardata = data->getBytes();

//もし復号化するなら unchardataとdata->getSize()を使ってここでする
    size_t len;
    DataModel::setData(uchardata, &len);
    DataModel::setData(uchardata, &_configdata->language, len);
    DataModel::setData(uchardata, &_configdata->bgmVolume);
    DataModel::setData(uchardata, &_configdata->seVolume);
    if (_configdata->language != "")
    {
        return true;
    }
    else
    {
        _configdata->LoadDefault();
        return false;
    }
}

void ConfigData::LoadDefault()
{
    _configdata->language = string("");
    _configdata->bgmVolume = 10;
    _configdata->seVolume = 5;
}

言語と音量の保存機能を付けてみました

stringなどの文字を格納する時はサイズが分からないので1つ前に文字サイズを格納するのがコツです

 

使い方

AppDelegate.cpp

bool AppDelegate::applicationDidFinishLaunching()内

コンフィグを読み込みConfigDataに格納する

DataFile *df = DataFile::getInstance();
df->loadConfigFile();

ConfigDataの言語が空(初期状態)だったら 英語をとりあえずいれて、

もし端末の言語が日本語だったら、日本語をいれてファイルに書き込み

ConfigData* cfdata = ConfigData::getInstance();
if (cfdata->language.size()<=0) {
    cfdata->language = "en";
    LanguageType lang = Application::getInstance()->getCurrentLanguage();
    if (lang == LanguageType::JAPANESE) {
        cfdata->language = "ja";
    }
    df->writeConfigFile();
}

 こうする事で初回起動は自分の端末から言語を取得するけど、

その後オプションで言語を変えると、2回目からはオプションで変えた言語を読み込みます

 

出力されたファイル内容

C:\Users\ユーザ名\AppData\Local\アプリ名\config.data

f:id:FALStar:20160811100925j:plain

最初の4バイトに文字のバイト数が格納され、この場合は2

次の2バイトに文字が格納され、

次にBGM音量

次にSE音量

が格納されています。

 

暗号復号について、簡易的に行いたいならこちらのサイトに書かれているxorEncodeが参考になります

cocos2d-xでユーザーデータを暗号化して保存したい – KUDE WORKS

 

例えばこんな感じ。

void DataModel::xorEncode(unsigned char *& data, size_t inputLength, std::string keycode)
{
    const unsigned int keyN = keycode.size();
    unsigned int keyIndex = 0;
    for (unsigned int i = 0; i < inputLength; i++) {
        data[i] ^= keycode[keyIndex];
        keyIndex = (keyIndex + 1) % keyN;
    }
}