PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)

Dependents:   YATTT sd_map_test cPong SnowDemo ... more

PokittoLib

Library for programming Pokitto hardware

How to Use

  1. Import this library to online compiler (see button "import" on the right hand side
  2. DO NOT import mbed-src anymore, a better version is now included inside PokittoLib
  3. Change My_settings.h according to your project
  4. Start coding!

POKITTO_CORE/PokittoCookie.cpp

Committer:
Pokitto
Date:
2019-03-23
Revision:
66:6281a40d73e6
Parent:
58:5f58a2846a20

File content as of revision 66:6281a40d73e6:

/**************************************************************************/
/*!
    @file     PokittoCookie.cpp
    @author   Jonne Valola

    @section LICENSE

    Software License Agreement (BSD License)

    Copyright (c) 2018, Jonne Valola
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/

#include "Pokitto_settings.h"
#include "Pokitto.h"
#include "PokittoCookie.h"

#ifndef POK_SIM
#else
#include "PokittoSimulator.h"
#endif

using namespace Pokitto;

//char Cookie::_key[SBKEYSIZE];
//char Cookie::_keyorder;
//bool Cookie::_status;

#define HARDCODEDOFFSET 25 //bypasses Cookie parent instance general data (that does not need to be saved in EEPROM)

Cookie::Cookie() {
    _status = false;
    _keyorder = SBINVALIDSLOT;
}

int Cookie::initialize() {
    //initialize is called from begin() and can be called several times during program run
    int datasize = _datasize;
    // check if key already exists
    _keyorder = exists(_key);
    if (_keyorder < SBMAXKEYS) {
            // key already exists
            // check amount of existing storage reserved for cookie
            datasize -= getAssignedBlocks()*SBBLOCKSIZE;
            if (datasize<=0) {
                // the size of data matches the size requested
                // therefore retrieve data from storage
                _status = true; //were good to go
                loadCookie();
            } else {
            // if that does not cover the whole size (maybe a newer version of program, who knows)
            // then do not load but reserve more blocks and store a new version
                while (datasize>0) {
                    if(reserveBlock()) datasize -= SBBLOCKSIZE;
                    else return SBNOTENOUGHBLOCKSFREE; //no space to allocate
                }
                _status = true; //were good to go
                eraseKeytableEntry(_keyorder);
                writeKeyToKeytable(_key,_keyorder); // write the key in the key table in EEPROM
                saveCookie();
            }
    } else {
            // new key needed
            // check if we have free keyslots
            _keyorder = getFreeKeytableSlot();
            if (_keyorder>=SBMAXKEYS) return SBNOMOREKEYS; //no space for key
            // check if we have free storage blocks
            if (getFreeBlocks()*SBBLOCKSIZE<datasize) return SBNOTENOUGHBLOCKSFREE; //no space to allocate
            while (datasize>0) {
                //reserve enough blocks for the data until all data can fit
                if(reserveBlock()) datasize -= SBBLOCKSIZE;
                else return SBNOTENOUGHBLOCKSFREE; //no space to allocate
            }
    }
    _status = true; //were good to go
    eraseKeytableEntry(_keyorder);
    writeKeyToKeytable(_key,_keyorder); // write the key in the key table in EEPROM
    return 0;
}

int Cookie::begin(const char* idkey, int datasize, char* ptr) {
    _status=false;
    _datasize=datasize-HARDCODEDOFFSET;// warning! hardcoded! sizeof(this); //do not include the data of the parent Cookie instance
    _pointer = ptr + HARDCODEDOFFSET;// warning! hardcoded! sizeof(this); //point to the beginning of the inherited instance
    char _idkey[8];
    // make _idkey exactly 8 readable characters long
    for (int t = 0 ; t < 8 ; t++) _idkey[t]=' ';
    for (int t = 0 ; t < 8 ; t++) {if (idkey[t]==0) break; _idkey[t]=idkey[t];}
    // clean Keytable of keys with no storage
    cleanKeytable();
    memcpy(_key, _idkey, SBKEYSIZE); //store name of key
    initialize();
    return 0; //success
}

bool Cookie::saveCookie() {
    if (!_status || !_pointer) return false; //return if not initialized
    char* p = _pointer;
    _head=0;
    _block=0;
    _block=findMyNextBlock();
    for (int i=0; i<_datasize; i++) writeQueue(*p++);
    #if POK_ENABLE_SOUND
    Pokitto::soundInit(true); //re-init sound
    #endif
    return true;
}

bool Cookie::loadCookie() {
    if (!_status || !_pointer) return false;
    char* p = _pointer;
    _head=0;
    _block=0;
    _block=findMyNextBlock();
    for (int i=0; i<_datasize; i++) *p++ = readQueue();
    return true;
}

void Cookie::deleteCookie() {
    if (!_status) return;
    // free all blocks held by Cookie
    for (int i=0; i<SBMAXBLOCKS; i++) {
            if (isMyBlock(i)) freeBlock(i);
    }
    // erase Cookie entry from keytable
    eraseKeytableEntry(_keyorder);
    // set status to deleted
    _status = false;
}

int Cookie::exists(const char* idkey) {
    for (int i=0; i< SBMAXKEYS; i++) {

            if(eeprom_read_byte((uint16_t*)(i*SBKEYSIZE))==idkey[0]) {
                    int total=0;
                    for (int j=0; j<SBKEYSIZE;j++) {
                        if(eeprom_read_byte((uint16_t*)(i*SBKEYSIZE+j))==idkey[j]) total++;
                    }
                    if (total==SBKEYSIZE) return i; // return the keyslot number where key exists
            }

    }
    return SBINVALIDSLOT; //not found
}

int Cookie::getFreeKeytableSlot() {
    int freeslot=SBINVALIDSLOT;
    for (int i=0; i<SBMAXKEYS; i++) {

    if (eeprom_read_byte((uint16_t*)(i*SBKEYSIZE))==0) {freeslot=i; break;}

    }
    return freeslot;
}

int Cookie::getAssignedBlocks() {
    int assignedblocks=0;
    for (int i=0;i<SBMAXBLOCKS;i++) {
        if (isMyBlock(i)) assignedblocks++;
    }
    return assignedblocks;
}

int Cookie::getFreeBlocks() {
    int freeblocks=0;
    for (int i=0;i<SBMAXBLOCKS;i++) {
        if (isFreeBlock(i)) freeblocks++;
    }
    return freeblocks;
}

bool Cookie::isFreeBlock(int n) {
    if (n>=SBMAXBLOCKS) return false;

    if (!(eeprom_read_byte((uint16_t*)(SBMAXKEYS*SBKEYSIZE+n))&0x80)) return true; //highest bit 0, its free

    return false; //its not free
}

bool Cookie::isMyBlock(int n) {
    if (n>=SBMAXBLOCKS) return false;
    if (isFreeBlock(n)) return false; //"free" blocks can not be "reserved" at the same time!

    char temp; int address;
    address = (SBMAXKEYS*SBKEYSIZE+n);
    temp = eeprom_read_byte((uint16_t*)address);
    if ((temp&0x7F) ==_keyorder) return true;

    return false; //its not your block
}

bool Cookie::blockIsOwnedBy(int n, int k) {
    if (n>=SBMAXBLOCKS) return false;
    if (k>=SBMAXKEYS) return false;
    if (isFreeBlock(n)) return false; //"free" blocks can not be "owned" by anyone

    char temp; int address;
    address = (SBMAXKEYS*SBKEYSIZE+n);
    temp = eeprom_read_byte((uint16_t*)address);
    if ((temp&0x7F) == k) return true;

    return false; //its not your block
}

void Cookie::writeKeyToKeytable(const char* key, int slot) {
    for (int i=0; i<SBKEYSIZE; i++) {

    if (key[i]) eeprom_write_byte((uint16_t*)(slot*SBKEYSIZE+i),key[i]);
    else eeprom_write_byte((uint16_t*)(slot*SBKEYSIZE+i),0);

    }
}

void Cookie::readKeytableEntry(int n, char* answer) {
    answer[8]=0;
    if (n >= SBMAXKEYS) n=SBMAXKEYS-1;
    for (int i=0; i<SBKEYSIZE; i++) {

        answer[i] = eeprom_read_byte((uint16_t*)(n*SBKEYSIZE+i));

    }
}

char Cookie::getBlockTableEntry(int n) {
    if (n>=SBMAXBLOCKS) return 0x80; // out of bounds will return a reserved block marker

        return eeprom_read_byte((uint16_t*)(SBKEYSIZE*SBMAXKEYS+n));

    return 0x80;
}

void Cookie::readBlock(int n, char* data) {
    for (int i=0; i<SBBLOCKSIZE; i++) {
    data[i]=0;

        if (n < SBMAXBLOCKS) data[i] = eeprom_read_byte((uint16_t*)(SBKEYSIZE*SBMAXKEYS+SBMAXBLOCKS+n*SBBLOCKSIZE+i));

    }
}

void Cookie::formatKeytable() {
    for (int j=0; j<SBMAXKEYS; j++) {
    for (int i=0; i<SBKEYSIZE; i++) {

        eeprom_write_byte((uint16_t*)(j*SBKEYSIZE+i),0);

    }
    }
}

void Cookie::freeBlock(int n) {
    if (n >= SBMAXBLOCKS) return; //out of bounds

        // delete entry from blocktable
        eeprom_write_byte((uint16_t*)(SBKEYSIZE*SBMAXKEYS+n),0);

    for (int i=0; i<SBBLOCKSIZE;i++) {

        // wipe data in the block
        eeprom_write_byte((uint16_t*)(SBKEYSIZE*SBMAXKEYS+SBMAXBLOCKS+n*SBBLOCKSIZE+i),0);

    }
}

bool Cookie::reserveBlock() {
    for (int i=0; i<SBMAXBLOCKS;i++) {

        // reserve block from blocktable
        if (isFreeBlock(i)) {
                //free block found, mark it for us in the blocktable
                eeprom_write_byte((uint16_t*)(SBKEYSIZE*SBMAXKEYS+i),_keyorder | 0x80);
                return true;
        }

    }
    return false; // no free block found
}

void Cookie::eraseKeytableEntry(int n) {
    if (n >= SBMAXKEYS) n=SBMAXKEYS-1;
    for (int i=0; i<SBKEYSIZE; i++) {

        eeprom_write_byte((uint16_t*)(n*SBKEYSIZE+i),0);

    }
}

void Cookie::cleanKeytable() {
    //Remove any keys without blocks
    for (int entry=0; entry<SBMAXKEYS; entry++) {
            if (eeprom_read_byte((uint16_t*)(entry*SBKEYSIZE))) {
                bool isEmpty=true;
                for (int block=0; block<SBMAXBLOCKS; block++) if (blockIsOwnedBy(block,entry)) {isEmpty=false;break;}
                //this entry has no blocks reserved, so lets clean it from the keytable
                if (isEmpty) eraseKeytableEntry(entry);
            }
    }
    for (int block=0;block<SBMAXBLOCKS;block++) {
            int blockentry = eeprom_read_byte((uint16_t*)(SBMAXKEYS*SBKEYSIZE+block));
            if (blockentry&0x80) {
                    blockentry &= 0x7F;
                    bool isEmpty=true;
                    for (int key=0;key<SBMAXKEYS;key++) {
                            if (eeprom_read_byte((uint16_t*)(key*SBKEYSIZE))) {isEmpty=false;break;}
                    }
                    if (isEmpty) eeprom_write_byte((uint16_t*)(SBMAXKEYS*SBKEYSIZE+block),0);
            }
    }
}

char Cookie::readQueue() {
    char data=0;

    int address;
    address = SBMAXKEYS*SBKEYSIZE+SBMAXBLOCKS+SBBLOCKSIZE*_block+_head%SBBLOCKSIZE;
    data=eeprom_read_byte((uint16_t*)address);

    _head++;
    if (_head%SBBLOCKSIZE==0 && _head) {
            _block++;
            _block=findMyNextBlock();
    }
    return data;
}

void Cookie::writeQueue(char data) {

    eeprom_write_byte((uint16_t*)(SBMAXKEYS*SBKEYSIZE+SBMAXBLOCKS+SBBLOCKSIZE*_block+_head%SBBLOCKSIZE),data);

    _head++;
    if (_head%SBBLOCKSIZE==0 && _head) {
            _block++;
            _block=findMyNextBlock();
    }
}

int Cookie::findMyNextBlock() {
    if (!_status) return SBINVALIDBLOCK;
    for (int i=_block; i<SBMAXBLOCKS;i++) if (isMyBlock(i)) return i;
    return SBINVALIDBLOCK;
}