state.c 11.8 KB
#include "os.h"
#include "os_bb.h"
#include "bcp.h"
#include "bbint.h"
#include "state.h"

#define TMP_FILE        "state.tmp"
#define NAME_LEN        BB_STATENAME_LEN
#define DONT_SIGN_BIT 0x80000000
#define MAGIC_STASH_ADDR        ((void*)PHYS_TO_K1(USB1_BDT_SRAM+256))
#define STATE_STASH_ADDR        ((void*)(MAGIC_STASH_ADDR+4))
#define BIND_STASH_ADDR         ((void*)(STATE_STASH_ADDR+sizeof(OSBbStateVector)))
#define NAME_STASH_ADDR         ((void*)(BIND_STASH_ADDR+sizeof(s32)*4))
#define TID_STASH_ADDR          ((void*)(NAME_STASH_ADDR+NAME_LEN))
#define STATE_DIRTY_STASH_ADDR  ((void*)(TID_STASH_ADDR+4))

#define BLOCK_ROUND(x) (((x) + (BB_FL_BLOCK_SIZE-1))&~(BB_FL_BLOCK_SIZE-1))

static void
_wcopy(u32 *src, u32 *dst, int bytes)
{
    int i;
    int num_words = (bytes+3)>>2;
    for (i = 0; i < num_words; ++i) {
        *dst++ = *src++;
    }
}

static void
__osBbUnstashState(char *name, BbTicketId* tid, OSBbStateVector *sv, s32 binding[MAXCONTROLLERS], u32* stateDirty)
{
    u32 dummy;
    if (IO_READ(MAGIC_STASH_ADDR) == STASH_MAGIC) {
        _wcopy(STATE_STASH_ADDR, (u32*)sv, sizeof(OSBbStateVector));
        _wcopy(BIND_STASH_ADDR, (u32*)binding, 4*sizeof(s32));
        _wcopy(NAME_STASH_ADDR, (u32*)name, NAME_LEN);
        _wcopy(TID_STASH_ADDR, &dummy, sizeof(BbTicketId));
        *tid = dummy;
        _wcopy(STATE_DIRTY_STASH_ADDR, stateDirty, sizeof(u32));
    } else {
        bzero(sv, sizeof(OSBbStateVector));
        binding[0] = binding[1] = binding[2] = binding[3] = -1;
        name[0] = 0;
        tid[0] = 0;
        stateDirty[0] = 0;
    }
}

static void
__osBbGameUnstashState(char *name, OSBbStateVector *sv, s32 binding[MAXCONTROLLERS], 
                       u32* stateDirty)
{
    int i;
    /* Called from game, __osBbStashMagic setup by viewer, version 
       maybe different */
    if (STASH_OKAY(__osBbStashMagic))
    {
        sv->eepromSize    = __osBbEepromSize;
        sv->eepromAddress = (u32) __osBbEepromAddress;
        sv->flashSize     = __osBbFlashSize;
        sv->flashAddress  = (u32) __osBbFlashAddress;
        sv->sramSize      = __osBbSramSize;
        sv->sramAddress   = (u32) __osBbSramAddress;
        sv->pakSize       = __osBbPakSize;
        for (i = 0; i < MAXCONTROLLERS; i++)
            sv->pakAddress[i] = (u32) __osBbPakAddress[i];
        for (i = 0; i < MAXCONTROLLERS; i++) 
            binding[i] = __osBbPakBindings[i];
        memcpy(name, __osBbStateName, NAME_LEN);
        *stateDirty = __osBbStateDirty;
    } else {
        bzero(sv, sizeof(OSBbStateVector));
        binding[0] = binding[1] = binding[2] = binding[3] = -1;
        name[0] = 0;
        stateDirty[0] = 0;
    }
}

static void
__osBbStashState(const char *name, BbTicketId tid, OSBbStateVector *sv, s32 binding[MAXCONTROLLERS], u32 stateDirty)
{
    u32 dummy = tid;
    IO_WRITE(MAGIC_STASH_ADDR, STASH_MAGIC);
    _wcopy((u32*)sv, STATE_STASH_ADDR, sizeof(OSBbStateVector));
    _wcopy((u32*)binding, BIND_STASH_ADDR, 4*sizeof(s32));
    _wcopy((u32*)name, NAME_STASH_ADDR, NAME_LEN);
    _wcopy((u32*)&dummy, TID_STASH_ADDR, sizeof(BbTicketId));
    _wcopy((u32*)&stateDirty, STATE_DIRTY_STASH_ADDR, sizeof(u32));
}

static void 
__osBbClearState(void)
{
    IO_WRITE(MAGIC_STASH_ADDR, 0);
}

static void
__osBbGetStateFilename(const char *name, char *state_name)
{
    int i;
    for(i = 0; i < 8 && name[i] != '.' && name[i]; i++) {
        state_name[i] = name[i];
    }
    state_name[i] = '.';
    state_name[i+1] = 's';
    state_name[i+2] = 't';
    state_name[i+3] = 'a';
    state_name[i+4] = '\0';
}

static s32
__osBbSaveState(const char* state_name, 
                OSBbStateVector sv,
                s32 binding[MAXCONTROLLERS], u32 stateDirty)
{
    int i, fd;
    u32 state_size = 0, off = 0;
    s32 rv = 0;
    u32 block = 0;

    if ((state_name == NULL) || !state_name[0]) return -1;

    osBbFDelete(TMP_FILE);
    state_size = BLOCK_ROUND(sv.eepromSize) + BLOCK_ROUND(sv.flashSize) + BLOCK_ROUND(sv.sramSize);

    if (state_size) {
        if ((fd = osBbFCreate(TMP_FILE, 1, state_size)) >= 0) {
            if (sv.eepromSize) {
                osBbFWrite(fd, off, (void*)sv.eepromAddress, BLOCK_ROUND(sv.eepromSize));
                off += BLOCK_ROUND(sv.eepromSize);
                block = sv.eepromAddress;
            }
            if (sv.flashSize) {
                osBbFWrite(fd, off, (void*)sv.flashAddress, BLOCK_ROUND(sv.flashSize));
                off += BLOCK_ROUND(sv.flashSize);
                block = sv.flashAddress;
            }
            if (sv.sramSize) {
                osBbFWrite(fd, off, (void*)sv.sramAddress, BLOCK_ROUND(sv.sramSize));
                off += BLOCK_ROUND(sv.sramSize);
                block = sv.sramAddress;
            }
            osBbFClose(fd);
            osBbFRename(TMP_FILE, state_name);
            if (!stateDirty)
              osBbSignFile(state_name, (u8*)block);
        }
    }

    osBbFDelete(TMP_FILE);
    for (i = 0; i < MAXCONTROLLERS; ++i) {
        if (binding[i] != -1 && sv.pakAddress[i]) {
            if ((fd = osBbFCreate(TMP_FILE, 1, BLOCK_ROUND(sv.pakSize))) >= 0) {
                char pak_name[NAME_LEN];
                u32 sign;
                osBbFWrite(fd, 0, (void*)sv.pakAddress[i], BLOCK_ROUND(sv.pakSize));
                osBbFClose(fd);
                sign = binding[i] & DONT_SIGN_BIT;
                binding[i] &= ~DONT_SIGN_BIT;
                sprintf(pak_name, "%ld.pak", binding[i]);
                osBbFRename(TMP_FILE, pak_name);
                if (!sign) {
                    osBbSignFile(pak_name, (u8*)sv.pakAddress[i]);
                } 
            }
        }
    }

    return rv;
}

s32
osBbSaveState(char stateName[] /* OUTPUT, 8.3 filename */, BbTicketId* tid)
{
    char state_name[NAME_LEN];
    OSBbStateVector sv;
    s32 binding[MAXCONTROLLERS];
    u32 stateDirty = 0;
    int rv, i;

    stateName[0]=0;
    *tid=0;

    /* retrieve name from stash, retrieve state, write to file */
    __osBbUnstashState(state_name, tid, &sv, binding, &stateDirty);
    if (!state_name[0]) return -1;

    for(i=0; state_name[i] != '\0' && i < NAME_LEN; i++){
        stateName[i] = state_name[i];
    }

    rv = __osBbSaveState(state_name, sv, binding, stateDirty);

    /* clear to avoid multiple saves */
    __osBbClearState();

    return rv;
}


/* Commits the state to the BBCard from within a Game.
 *
 * 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.
 *
 * NOTE: osBbAuxDataInit should be called before using this function.
 */
s32
osBbGameCommitState()
{
    char state_name[NAME_LEN];
    OSBbStateVector sv;
    s32 rv, binding[MAXCONTROLLERS];
    u32 stateDirty = 0;

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

    /* retrieve name from stash, retrieve state, write to file */
    __osBbGameUnstashState(state_name, &sv, binding, &stateDirty);
    rv = __osBbSaveState(state_name, sv, binding, stateDirty);
    IO_WRITE(PI_FLASH_CTRL_REG, old);      // Restore reg
    return rv;
}

/* Initialize the game state parameters */
void osBbInitState(const char* name) 
{
    int i;

    __osBbStashMagic = STASH_MAGIC;
    /* Initialize parameters used by the game */
    memcpy(__osBbStateName, name, NAME_LEN);

    /* No other state information */
    __osBbEepromSize = 0;
    __osBbEepromAddress = NULL;
    __osBbFlashSize = 0;
    __osBbFlashAddress = NULL;
    __osBbSramSize = 0;
    __osBbSramAddress = NULL;
    __osBbPakSize = 0;
    for (i = 0; i < MAXCONTROLLERS; i++)
        __osBbPakAddress[i] = NULL;
    for (i = 0; i < MAXCONTROLLERS; i++) 
        __osBbPakBindings[i] = -1;
    __osBbStateDirty = 0;    

    /* Clear to indicate no state info for the viewer */
    __osBbClearState();    
}

s32 osBbLoadState(const char *name, BbTicketId tid, OSBbStateVector *sv, s32 binding[MAXCONTROLLERS])
{
    s32 rv = 0;
    char state_name[NAME_LEN];
    char pak_name[NAME_LEN];
    int i, fd;
    u32 off = 0;
    u8 *block;
    u32 stateDirty = 0;
    
    bzero(state_name, NAME_LEN);
    __osBbGetStateFilename(name, state_name);
    __osBbStashMagic = STASH_MAGIC;
    memcpy(__osBbStateName, state_name, NAME_LEN);
    __osBbEepromSize = sv->eepromSize;
    __osBbEepromAddress = (void*)sv->eepromAddress;
    __osBbFlashSize = sv->flashSize;
    __osBbFlashAddress = (void*)sv->flashAddress;
    __osBbSramSize = sv->sramSize;
    __osBbSramAddress = (void*)sv->sramAddress;
    if ((block = __osBbEepromAddress) ||
        (block = __osBbFlashAddress) ||
        (block = __osBbSramAddress) ) {

        if ((fd = osBbFOpen(state_name, "r")) >= 0) {
            if (!osBbVerifyFile(state_name, block))
                stateDirty = 1;
            if (sv->eepromSize) {
                osBbFRead(fd, off, (void*)sv->eepromAddress, BLOCK_ROUND(sv->eepromSize));
                off += BLOCK_ROUND(sv->eepromSize);
            }
            if (sv->flashSize) {
                osBbFRead(fd, off, (void*)sv->flashAddress, BLOCK_ROUND(sv->flashSize));
                off += BLOCK_ROUND(sv->flashSize);
            }
            if (sv->sramSize) {
                osBbFRead(fd, off, (void*)sv->sramAddress, BLOCK_ROUND(sv->sramSize));
                off += BLOCK_ROUND(sv->sramSize);
            }
            osBbFClose(fd);
        } else {
            if (sv->eepromSize) {
                bzero((void*)sv->eepromAddress, sv->eepromSize);
            }
            if (sv->flashSize) {
                bzero((void*)sv->flashAddress, sv->flashSize);
            }
            if (sv->sramSize) {
                bzero((void*)sv->sramAddress, sv->sramSize);
            }
        }
    }
    __osBbStateDirty = stateDirty;

    __osBbPakSize = sv->pakSize;
    for (i = 0; i < MAXCONTROLLERS; ++i) {
        __osBbPakBindings[i] = binding[i];
        if (binding[i] != -1 && sv->pakAddress[i]) {
            __osBbPakAddress[i] = (void *)sv->pakAddress[i];
            sprintf(pak_name, "%ld.pak", binding[i]);
            if ((fd = osBbFOpen(pak_name, "r")) >= 0) {
                if (!osBbVerifyFile(pak_name, (u8*)sv->pakAddress[i])) {
                    binding[i] |= DONT_SIGN_BIT;
                }
                osBbFRead(fd, 0, (void*)sv->pakAddress[i], BLOCK_ROUND(sv->pakSize));
            } else {
                /* XXX cheesy hack to try to initialize a cp */
                OSPfs pfs;
                extern u32 __osGetId(OSPfs *);
                bzero (&pfs, sizeof(pfs));
                pfs.channel = i;
                bzero((void*)sv->pakAddress[i], sv->pakSize);
                __osGetId(&pfs);
            }
        } else {
            __osBbPakAddress[i] = 0;
        }
    }

    __osBbStashState(state_name, tid, sv, binding, stateDirty);

    return rv;
}

s32 osBbGetLaunchMetaData(OSBbLaunchMetaData *md, u16 *blockList, s32 listSize)
{
    int i;
    u32 *p = (u32 *)md;
    OSPiHandle* handler;
    s32 rv = 0;
    u16 lastBlock[2];

    lastBlock[0] = blockList[listSize-1];
    lastBlock[1] = 0;
    if ((rv = osBbAtbSetup(PI_DOM1_ADDR2, lastBlock, 2)) < 0)
        goto out;
    handler = osCartRomInit();
    IO_WRITE(PI_AES_CTRL_REG, 0);
    IO_WRITE(PI_ERROR_REG, 0);
    IO_WRITE(PI_FLASH_CTRL_REG, PI_FLASH_CTRL_RDPH |
                                (0xf << PI_FLASH_CTRL_ADPH_SHIFT) |
                                PI_FLASH_CTRL_WRDY |
                                PI_FLASH_CTRL_ECC |
                                0x3ff);
    for (i = BB_FL_BLOCK_SIZE-sizeof(OSBbLaunchMetaData); i < BB_FL_BLOCK_SIZE; i += 4) {
        osEPiReadIo(handler, i, p++);
    }

    if ((md->magic & 0xFFFFFF00) != BB_METADATA_MAGIC)
        rv = -1;

out:
    if (rv < 0) {
        bzero(md, sizeof(OSBbLaunchMetaData));
        md->memSize = 0x400000;
        md->tvType = OS_TV_NTSC;
        md->romBase = PHYS_TO_K1(PI_DOM1_ADDR2);
    }
    return rv;
}