auxdata.c 7.7 KB
#include "os.h"
#include "os_bb.h"
#include "os_bbfs.h"
#include "bcp.h"
#include "bbint.h"
#include "state.h"

#define NIBBLE_TO_ASCII(v) ((v)<10?('0'+(v)):('a'+(v)-10))

#define TMP_FILE       "temp.tmp"
#define NAME_LEN       BB_STATENAME_LEN

/* NOTE: The file system will overwrite PI_FLASH_CTRL_REG, which is 
 *       also used for DMA to/from the card.  To ensure that FS accesses
 *       by a game does not break DMA, it is important to save and restore 
 *       this register every time the file system is accessed.  
 */

/* Does not use FS */
char*
__osBbGameGetStateName()
{
    if (STASH_OKAY(__osBbStashMagic)) {
        return __osBbStateName;
    }
    return NULL;
}

/* Does not use FS */
static s32
__osBbGetAuxDataFilename(u8 id, char *file_name)
{
    int i;
    const char* state_name;
    if (id >= BB_MAX_AUXDATA) return BBFS_ERR_INVALID;    
    if (file_name == NULL) return BBFS_ERR_INVALID;
    
    state_name = __osBbGameGetStateName();
    if (state_name == NULL || !state_name[0])
        return BBFS_ERR_STATE; /* No state information - wrong viewer? */

    for(i = 0; i < 8 && state_name[i] != '.' && state_name[i]; i++) {
        file_name[i] = state_name[i];
    }
    file_name[i] = '.';
    file_name[i+1] = 'u';
    file_name[i+2] = NIBBLE_TO_ASCII( (id >> 4) & 0xf);
    file_name[i+3] = NIBBLE_TO_ASCII( id & 0xf );
    file_name[i+4] = '\0';
    return 0;
}

/*
 * Initializes the card for auxiliary game data.  Must be called each
 * time a new card is inserted, before any other auxiliary game data 
 * functions or osBbGameCommitState can be used.
 * Parameters:
 *  buf - Buffer for internal state. Must be 16 byte aligned.
 *        NOTE: buf should not be reused by the calling application
 *              for any other purpose
 *  len - Length of the buffer, must be at least 32KB.
 * Returns: 0 if successful, <0 if unsuccessful
 */
s32
osBbAuxDataInit(void* buf, u32 len)
{
    s32 rv;
    u32 old;

    if (buf == NULL) return BBFS_ERR_INVALID;
    if ((u32)buf&15) return BBFS_ERR_INVALID;
    if (len < sizeof(OSBbFs)) return BBFS_ERR_INVALID;

    old = IO_READ(PI_FLASH_CTRL_REG);      // Save reg
    rv = osBbFInit((OSBbFs*) buf);
    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/*
 * Provides a list of user defined data ids on the card for the game
 * Parameters:
 *  ids - Buffer to write the ids into
 * Returns: # of ids if successful, <0 if unsuccessful  
 */
s32
osBbAuxDataIds(u8 ids[BB_MAX_AUXDATA]) 
{
    char file_name[NAME_LEN];
    int rv, i, fd;
    int count=0;

    u32 old = IO_READ(PI_FLASH_CTRL_REG);  // Save reg

    bzero(ids, BB_MAX_AUXDATA);
    if (__osBbGameGetStateName() == NULL) {
        // No FS access yet
        return BBFS_ERR_STATE; /* No state information - wrong viewer? */
    }

    for (i=0; i < BB_MAX_AUXDATA; i++) {
        rv = __osBbGetAuxDataFilename(i, file_name);
        if (rv >= 0) {
            if ((fd = osBbFOpen(file_name, "r")) >= 0) {
                ids[count++] = i;
                osBbFClose(fd);
            }
            else if (fd != BBFS_ERR_ENTRY) {
                /* Abort if any error other than file not found */
                IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
                return fd;
            }
        }
    }

    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return count;
}

/*
 * Provides the size of the user defined data
 * Parameters:
 *   id - Aux Data ID (0-15)
 * Returns: size of user data if successful, <0 if unsuccessful  
 */
s32
osBbAuxDataSize(u8 id) 
{
    s32 rv = 0;
    OSBbStatBuf sb;
    char file_name[NAME_LEN];
    int fd;

    u32 old = IO_READ(PI_FLASH_CTRL_REG);  // Save reg

    rv = __osBbGetAuxDataFilename(id, file_name);
    if (rv < 0)
        return rv;                         // No FS access yet
    if ((fd = osBbFOpen(file_name, "r")) >= 0) {
        rv = osBbFStat(fd, &sb, NULL, 0);
        osBbFClose(fd);
        if (rv >= 0) {
            rv = sb.size;
        }
    }
    else {
        rv = fd;
    }

    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/*
 * Loads the user defined data from the card
 * Parameters:
 *  buf - Buffer to load the data info (must be 16 byte aligned)
 *  len - Length of the buffer, must be a multiple of 16K
 *   id - Aux Data ID (0-15)
 * Returns: number of bytes read if successful, <0 if unsuccessful
 */
s32
osBbAuxDataLoad(u8 id, void* buf, u32 len) {
    s32 rv = 0;
    char file_name[NAME_LEN];
    int fd;

    u32 old = IO_READ(PI_FLASH_CTRL_REG);  // Save reg

    if (buf == NULL) return BBFS_ERR_INVALID;
    if ((u32)buf&15) return BBFS_ERR_INVALID;
    if (len > BB_MAX_AUXDATA_SIZE) return BBFS_ERR_INVALID;
    if (len & (BB_FL_BLOCK_SIZE-1)) return BBFS_ERR_INVALID;

    rv = __osBbGetAuxDataFilename(id, file_name);
    if (rv < 0)
        return rv;                         // No FS access yet
    if ((fd = osBbFOpen(file_name, "r")) >= 0) {
        rv = osBbFRead(fd, 0, buf, len);
        osBbFClose(fd);
    }
    else {
        rv = fd;
        bzero(buf, len);
    }
    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/*
 * Saves the user defined data to the card.  If a file with the 
 *  specifed id already exists, that file will be overwritten. 
 *  NOTE: skExit() should be called after all user defined data 
 *  has been saved to force a save to card of the main game state.
 * Parameters:
 *  buf - Data to write (must be 16 byte aligned)
 *  len - Length of the buffer, must be a multiple of 16K and <= 32K
 *   id - Aux Data ID (0-15)
 * Returns: number of bytes saved if successful, 
 *          <0 (see FS error codes) if unsuccessful
 */
s32
osBbAuxDataSave(u8 id, const void* buf, u32 len) {
    s32 rv = 0;
    char file_name[NAME_LEN];
    int fd;

    u32 old = IO_READ(PI_FLASH_CTRL_REG);  // Save reg

    if ((id < BB_MAX_AUXDATA) && (id >= osBbAuxDataGetLimit())) 
        return BBFS_ERR_STATE_LIMIT;
    if (buf == NULL) return BBFS_ERR_INVALID;
    if ((u32)buf&15) return BBFS_ERR_INVALID;
    if (len > BB_MAX_AUXDATA_SIZE) return BBFS_ERR_INVALID;
    if (len & (BB_FL_BLOCK_SIZE-1)) return BBFS_ERR_INVALID;

    rv = __osBbGetAuxDataFilename(id, file_name);
    if (rv < 0)
        return rv;                         // No FS access yet

    osBbFDelete(TMP_FILE);
    if ((fd = osBbFCreate(TMP_FILE, 1, len)) >= 0) {
        rv = osBbFWrite(fd, 0, buf, len);
        osBbFClose(fd);
        if (rv == len) {
            rv = osBbFRename(TMP_FILE, file_name);
            if (rv >= 0) {
                rv = len;
            }
        }
        else {
            /* Something went wrong */
            osBbFDelete(TMP_FILE);
            if (rv >= 0) rv = BBFS_ERR_FAIL;
        }
    }
    else rv = fd;
    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/*
 * Remove the user defined data from the card
 * Parameters:
 *   id - Aux Data ID (0-15)
 * Returns: 0 if successful, <0 if unsuccessful  
 */
s32
osBbAuxDataDelete(u8 id) {
    s32 rv = 0;
    char file_name[NAME_LEN];

    u32 old = IO_READ(PI_FLASH_CTRL_REG);  // Save reg
    rv = __osBbGetAuxDataFilename(id, file_name);
    if (rv < 0)
        return rv;                         // No FS access yet
    rv = osBbFDelete(file_name);
    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/* Limits the number of aux data files that a game can create */
/* Valid aux data ids are then restricted from 0 to limit-1   */
s32
osBbAuxDataSetLimit(u8 limit)
{
    __osBbAuxDataLimit = (limit > BB_MAX_AUXDATA)? BB_MAX_AUXDATA: limit;
    return __osBbAuxDataLimit;
}

s32
osBbAuxDataGetLimit()
{
    /* __osBbAuxDataLimit added in version 2 */
    if ( STASH_OKAY(__osBbStashMagic) && 
         (STASH_VERSION(__osBbStashMagic) >= 2) )
        return __osBbAuxDataLimit;
    else return 0;
}