From 3fb1d9af9c601e1c7a9130129cf3de06fe8964ad Mon Sep 17 00:00:00 2001 From: tpu Date: Thu, 18 Jan 2024 21:36:51 +0800 Subject: [PATCH] add save tools --- Firm_Saturn/.gitignore | 4 +- tools/savetool/.gitignore | 3 + tools/savetool/Makefile | 7 + tools/savetool/bup.h | 65 ++++++ tools/savetool/main.c | 367 ++++++++++++++++++++++++++++++++ tools/savetool/sr_bup.c | 437 ++++++++++++++++++++++++++++++++++++++ tools/savetool/sr_mems.c | 342 +++++++++++++++++++++++++++++ tools/savetool/ss_bup.c | 244 +++++++++++++++++++++ 8 files changed, 1467 insertions(+), 2 deletions(-) create mode 100644 tools/savetool/.gitignore create mode 100644 tools/savetool/Makefile create mode 100644 tools/savetool/bup.h create mode 100644 tools/savetool/main.c create mode 100644 tools/savetool/sr_bup.c create mode 100644 tools/savetool/sr_mems.c create mode 100644 tools/savetool/ss_bup.c diff --git a/Firm_Saturn/.gitignore b/Firm_Saturn/.gitignore index 150211a..b2ce855 100644 --- a/Firm_Saturn/.gitignore +++ b/Firm_Saturn/.gitignore @@ -1,6 +1,6 @@ /obj/* -/ramimage.bin -/ramimage.elf +/ssfirm.bin +/ssfirm.elf /dump.txt diff --git a/tools/savetool/.gitignore b/tools/savetool/.gitignore new file mode 100644 index 0000000..a196eac --- /dev/null +++ b/tools/savetool/.gitignore @@ -0,0 +1,3 @@ + +/*.exe + diff --git a/tools/savetool/Makefile b/tools/savetool/Makefile new file mode 100644 index 0000000..fbaacde --- /dev/null +++ b/tools/savetool/Makefile @@ -0,0 +1,7 @@ + + +all: + gcc -Wall -g -o sst main.c sr_bup.c ss_bup.c sr_mems.c + +clean: + rm -f *.exe diff --git a/tools/savetool/bup.h b/tools/savetool/bup.h new file mode 100644 index 0000000..bcc289c --- /dev/null +++ b/tools/savetool/bup.h @@ -0,0 +1,65 @@ + +#ifndef _BUP_H_ +#define _BUP_H_ + +/*****************************************************************************/ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + + +typedef struct { + char file_name[12]; + char comment[11]; + char language; + u32 date; + u32 data_size; + u8 *dbuf; +}SAVEINFO; + +extern u8 save_buf[0x100000]; + +/*****************************************************************************/ + + + +u8 *load_file(char *name, int *size); +int write_file(char *file, void *buf, int size); + +u32 get_be32(void *p); +u32 get_be16(void *p); +void put_be32(void *p, u32 v); +void put_be16(void *p, u32 v); + +void set_bitmap(u8 *bmp, int index, int val); +SAVEINFO *load_saveraw(char *save_name); +void bup_flush(void); + +int sr_bup_init(u8 *buf); +int sr_bup_list(int slot_id); +int sr_bup_export(int slot_id, int save_id); +int sr_bup_import(int slot_id, int save_id, char *save_name); +int sr_bup_delete(int slot_id, int save_id); +int sr_bup_create(char *game_id); + +int ss_bup_init(u8 *buf); +int ss_bup_list(int slot_id); +int ss_bup_export(int slot_id, int save_id); +int ss_bup_import(int slot_id, int save_id, char *save_name); +int ss_bup_delete(int slot_id, int save_id); +int ss_bup_create(char *game_id); + +int sr_mems_init(u8 *buf); +int sr_mems_list(int slot_id); +int sr_mems_export(int slot_id, int save_id); +int sr_mems_import(int slot_id, int save_id, char *save_name); +int sr_mems_delete(int slot_id, int save_id); +int sr_mems_create(char *game_id); + + +/*****************************************************************************/ + +#endif + diff --git a/tools/savetool/main.c b/tools/savetool/main.c new file mode 100644 index 0000000..8508278 --- /dev/null +++ b/tools/savetool/main.c @@ -0,0 +1,367 @@ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "bup.h" + + +/******************************************************************************/ + + +u8 *load_file(char *name, int *size) +{ + FILE *fp; + u8 *buf; + + fp = fopen(name, "rb"); + if(fp==NULL){ + return NULL; + } + + fseek(fp, 0, SEEK_END); + *size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buf = (u8*)malloc((*size)*2); + fread(buf, *size, 1, fp); + fclose(fp); + + return buf; +} + + +int write_file(char *file, void *buf, int size) +{ + FILE *fp; + int written; + + fp = fopen(file, "wb"); + if(fp==NULL) + return -1; + written = fwrite(buf, 1, size, fp); + fclose(fp); + + return written; +} + + +u32 get_be32(void *p) +{ + u8 *d = (u8*)p; + return (d[0]<<24) | (d[1]<<16) | (d[2]<<8) | d[3]; + +} + + +void put_be32(void *p, u32 v) +{ + u8 *d = (u8*)p; + + d[0] = v>>24; + d[1] = v>>16; + d[2] = v>>8; + d[3] = v; +} + + +u32 get_be16(void *p) +{ + u8 *d = (u8*)p; + return (d[0]<<8) | d[1]; + +} + + +void put_be16(void *p, u32 v) +{ + u8 *d = (u8*)p; + + d[0] = v>>8; + d[1] = v; +} + + +void set_bitmap(u8 *bmp, int index, int val) +{ + int byte = index/8; + int mask = 1<<(index&7); + + if(val) + bmp[byte] |= mask; + else + bmp[byte] &= ~mask; +} + + +/******************************************************************************/ + +u8 save_buf[0x100000]; +static SAVEINFO saveinfo; + +SAVEINFO *load_saveraw(char *save_name) +{ + SAVEINFO *sinfo = &saveinfo; + u8 *fbuf; + int fsize; + + fbuf = load_file(save_name, &fsize); + if(fbuf==NULL){ + printf("Faield to load file %s!\n", save_name); + return NULL; + } + if(strcmp((char*)fbuf, "SSAVERAW")){ + printf("%s: Not a SSAVERAW file!\n", save_name); + return NULL; + } + + memset(sinfo, 0, sizeof(SAVEINFO)); + memcpy(sinfo->file_name, fbuf+0x10, 11); + memcpy(sinfo->comment, fbuf+0x20, 10); + memcpy(&sinfo->data_size, fbuf+0x1c, 4); + memcpy(&sinfo->date, fbuf+0x2c, 4); + sinfo->language = fbuf[0x2b]; + + memcpy(save_buf, fbuf+0x40, fsize-0x40); + sinfo->dbuf = save_buf; + return sinfo; +} + +/******************************************************************************/ + +// 0: SAROO save file +// 1: Saturn save file +// 2: SAROO extend save + +static int bup_type; +static u8 *bup_buf; +static int bup_size; +static char *bup_name = NULL; + + +void bup_flush(void) +{ + write_file(bup_name, bup_buf, bup_size); +} + + +int bup_create(char *game_id) +{ + int retv; + + if(bup_type==0){ + retv = sr_bup_create(game_id); + }else if(bup_type==1){ + retv = ss_bup_create(game_id); + }else if(bup_type==2){ + retv = sr_mems_create(game_id); + } + if(retv>0){ + bup_size += retv; + bup_flush(); + return 0; + } + + return retv; +} + + +int bup_import(int slot_id, int save_id, char *save_name) +{ + int retv; + + if(bup_type==0){ + retv = sr_bup_import(slot_id, save_id, save_name); + }else if(bup_type==1){ + retv = ss_bup_import(slot_id, save_id, save_name); + }else if(bup_type==2){ + retv = sr_mems_import(slot_id, save_id, save_name); + } + if(retv==0){ + bup_flush(); + } + + return retv; +} + + +int bup_delete(int slot_id, int save_id) +{ + int retv; + + if(bup_type==0){ + retv = sr_bup_delete(slot_id, save_id); + }else if(bup_type==1){ + retv = ss_bup_delete(slot_id, save_id); + }else if(bup_type==2){ + retv = sr_mems_delete(slot_id, save_id); + } + if(retv==0){ + bup_flush(); + } + + return retv; +} + + +int bup_export(int slot_id, int save_id) +{ + if(bup_type==0){ + return sr_bup_export(slot_id, save_id); + }else if(bup_type==1){ + return ss_bup_export(slot_id, save_id); + }else if(bup_type==2){ + return sr_mems_export(slot_id, save_id); + } + + return -1; +} + + +int bup_list(int slot_id) +{ + if(bup_type==0){ + return sr_bup_list(slot_id); + }else if(bup_type==1){ + return ss_bup_list(slot_id); + }else if(bup_type==2){ + return sr_mems_list(slot_id); + } + + return -1; +} + + +int bup_load(char *bup_name) +{ + bup_buf = load_file(bup_name, &bup_size); + if(bup_buf==NULL) + return -1; + + if(strcmp((char*)bup_buf, "Saroo Save File")==0){ + bup_type = 0; + return sr_bup_init(bup_buf); + } + if(strncmp((char*)bup_buf, "BackUpRam Format", 16)==0){ + bup_type = 1; + return ss_bup_init(bup_buf); + } + if(bup_buf[1]=='B' && bup_buf[3]=='a' && bup_buf[5]=='c' && bup_buf[7]=='k'){ + bup_type = 1; + return ss_bup_init(bup_buf); + } + if(strncmp((char*)bup_buf, "SaroMems", 8)==0){ + bup_type = 2; + return sr_mems_init(bup_buf); + } + + return -2; +} + + +/******************************************************************************/ + + +int main(int argc, char *argv[]) +{ + int slot_id = -1; + int save_id = -1; + int create = 0; + int import = 0; + int delete = 0; + char *save_name = NULL; + char *game_id = NULL; + + int retv; + int p = 1; + + + if(argc==1){ + printf("Usage: sstool save_file [-c \"gameid\"][-t n] [-s n] [-i [file]]\n"); + printf(" -t n Select save slot(for SAROO save).\n"); + printf(" -c \"gameid\" New save slot(for SAROO save).\n"); + printf(" -s n Select and Export(without -i/-d) save.\n"); + printf(" -i [file] Import save.\n"); + printf(" -d Delete save.\n"); + printf(" List save.\n"); + printf("\n"); + return 0; + } + + + while(p=0){ + retv = bup_export(slot_id, save_id); + }else{ + retv = bup_list(slot_id); + } + + return retv; +} + + diff --git a/tools/savetool/sr_bup.c b/tools/savetool/sr_bup.c new file mode 100644 index 0000000..506623f --- /dev/null +++ b/tools/savetool/sr_bup.c @@ -0,0 +1,437 @@ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "bup.h" + + +/******************************************************************************/ + +static u8 *bup_buf; +static u8 *bup_mem; + +#define BUP_NON (1) +#define BUP_UNFORMAT (2) +#define BUP_WRITE_PROTECT (3) +#define BUP_NOT_ENOUGH_MEMORY (4) +#define BUP_NOT_FOUND (5) +#define BUP_FOUND (6) +#define BUP_NO_MATCH (7) +#define BUP_BROKEN (8) + + +typedef struct { + char file_name[12]; + char comment[11]; + char language; + u32 date; + u32 data_size; + u16 block_size; +}BUPDIR; + + +/******************************************************************************/ + +typedef struct { + u32 magic0; // 00 + u32 magic1; + u32 total_size; + u16 block_size; + u16 free_block; + u8 unuse_10[16]; // 10 + u8 gameid[16]; // 20 + u8 unuse_30[14]; // 30 + u16 first_save; // 3e + u8 bitmap[64]; // 40 +}BUPHEADER; + +#define BUPMEM ((BUPHEADER*)(bup_mem)) + +static u16 block_size; +static u16 free_block; + +/******************************************************************************/ + + +static int get_free_block(int pos) +{ + int i, byte, mask; + + for(i=pos; i<512; i++){ + byte = i/8; + mask = 1<<(i&7); + if( (BUPMEM->bitmap[byte]&mask) == 0 ){ + BUPMEM->bitmap[byte] |= mask; + free_block -= 1; + return i; + } + } + + return 0; +} + + +static int get_next_block(u8 *bmp, int pos) +{ + int byte, mask; + + while(pos<512){ + byte = pos/8; + mask = 1<<(pos&7); + if(bmp[byte]&mask){ + return pos; + } + pos += 1; + } + + return 0; +} + + +static u8 *get_block_addr(int id) +{ + return (u8*)(bup_mem + block_size*id); +} + + +static int access_data(int block, u8 *data, int type) +{ + u8 *bp, *bmp; + int dsize, asize, bsize; + + bsize = block_size; + + bp = get_block_addr(block); + bmp = bp+0x40; + dsize = get_be32(bp+0x0c); + block = 0; + + while(dsize>0){ + block = get_next_block(bmp, block); + bp = get_block_addr(block); + + asize = (dsize>bsize)? bsize : dsize; + //printf("dsize=%04x block=%04x asize=%04x\n", dsize, block, asize); + + if(type==0){ + // read + memcpy(data, bp, asize); + }else{ + // write + memcpy(bp, data, asize); + } + data += asize; + dsize -= asize; + block += 1; + } + + return 0; +} + + +/******************************************************************************/ + + +static int sr_bup_format(u8 *gameid) +{ + memset(bup_mem, 0, 0x10000); + + // SaroSave + put_be32(&BUPMEM->magic0, 0x5361726f); + put_be32(&BUPMEM->magic1, 0x53617665); + put_be32(&BUPMEM->total_size, 0x10000); + put_be16(&BUPMEM->block_size, 0x80); + put_be16(&BUPMEM->free_block, 512-1); + memcpy(&BUPMEM->gameid, gameid, 16); + put_be16(&BUPMEM->first_save, 0x0000); + + memset(&BUPMEM->bitmap, 0, 0x40); + BUPMEM->bitmap[0] = 0x01; + + return 0; +} + + +static int sr_bup_select(int slot_id) +{ + char sname[20]; + + if(slot_id<0 || *(u32*)(bup_buf+slot_id*16)==0) + return -1; + + memcpy(sname, bup_buf+slot_id*16, 16); + sname[16] = 0; + printf("[%s] :\n", sname); + bup_mem = bup_buf+slot_id*0x10000; + + // Check "SaroSave" + if(get_be32(&BUPMEM->magic0)!=0x5361726f || get_be32(&BUPMEM->magic1)!=0x53617665){ + printf(" empty bup memory, need format.\n"); + sr_bup_format(bup_buf+slot_id*16); + } + + block_size = get_be16(&BUPMEM->block_size); + free_block = get_be16(&BUPMEM->free_block); + return 0; +} + + +/******************************************************************************/ + + +int sr_bup_export(int slot_id, int save_id) +{ + int block, i; + u8 *bp; + + if(sr_bup_select(slot_id)<0) + return -1; + + i = 0; + block = get_be16(&BUPMEM->first_save); + while(block){ + bp = get_block_addr(block); + if(i == save_id){ + memset(save_buf, 0, 32768); + + strcpy((char*)save_buf, "SSAVERAW"); + memcpy(save_buf+0x10, bp+0, 11); // Filename + memcpy(save_buf+0x1c, bp+0x0c, 4); // Size + memcpy(save_buf+0x20, bp+0x10, 10); // Comment + save_buf[0x2b] = bp[0x1b]; // Language + memcpy(save_buf+0x2c, bp+0x1c, 4); // Date + + access_data(block, save_buf+0x40, 0); + + char fname[16]; + int fsize = get_be32(save_buf+0x1c); + sprintf(fname, "%s.bin", save_buf+0x10); + write_file(fname, save_buf, fsize+0x40); + + printf("Export Save_%d: %s\n", i, fname); + return 0; + } + block = get_be16(bp+0x3e); + i += 1; + } + + printf("Save_%d not found!\n", save_id); + return -1; +} + + +int sr_bup_import(int slot_id, int save_id, char *save_name) +{ + SAVEINFO *save; + int block, block_need, i, last, hdr; + u8 *bp; + + if(sr_bup_select(slot_id)<0) + return -1; + + i = 0; + last = 0; + block = get_be16(&BUPMEM->first_save); + while(block){ + bp = get_block_addr(block); + if(i == save_id){ + break; + } + last = block; + block = get_be16(bp+0x3e); + i += 1; + } + if(block){ + // overwrite + char sname[20]; + if(save_name==NULL){ + sprintf(sname, "%s.bin", bp); + save_name = sname; + } + save = load_saveraw(save_name); + if(save==NULL) + return -1; + + *(u32*)(bp+0x1c) = save->date; + access_data(block, save->dbuf, 2); + printf("Import %s from %s.\n", bp, save_name); + return 0; + } + if(save_id>=0){ + printf("Save_%d not found!\n", save_id); + return -1; + } + + // new save + save = load_saveraw(save_name); + if(save==NULL) + return -1; + + // 计算所需的块数量。起始块+块列表块+数据块 + int dsize = get_be32(&save->data_size); + block_need = (dsize+block_size-1)/(block_size); + printf("block_need=%d\n", block_need+1); + if((block_need+1) > free_block){ + return -1; + } + + // 分配起始块 + block = get_free_block(0); + hdr = block; + + bp = get_block_addr(hdr); + printf("start at %d\n", hdr); + + // 写开始块 + memset(bp, 0, 64*2); + memcpy(bp+0x00, save->file_name, 11); + *(u32*)(bp+0x0c) = save->data_size; + memcpy(bp+0x10, save->comment, 10); + bp[0x1b] = save->language; + *(u32*)(bp+0x1c) = save->date; + + // 分配块 + block = 0; + for(i=0; idbuf, 2); + + bp = get_block_addr(last); + put_be16(bp+0x3e, hdr); + put_be16(&BUPMEM->free_block, free_block); + + printf("Import %s.\n", save->file_name); + return 0; +} + + +int sr_bup_delete(int slot_id, int save_id) +{ + int block, i, last; + u8 *bp; + + if(sr_bup_select(slot_id)<0) + return -1; + + i = 0; + last = 0; + block = get_be16(&BUPMEM->first_save); + while(block){ + bp = get_block_addr(block); + if(i == save_id){ + break; + } + last = block; + block = get_be16(bp+0x3e); + i += 1; + } + if(block==0){ + printf("Save_%d not found!\n", save_id); + return -1; + } + + // 释放开始块 + set_bitmap((u8*)BUPMEM->bitmap, block, 0); + free_block += 1; + + // 释放数据块 + u8 *bmp = bp+0x40; + block = 0; + while(1){ + block = get_next_block(bmp, block); + if(block==0) + break; + set_bitmap((u8*)BUPMEM->bitmap, block, 0); + free_block += 1; + block += 1; + } + + // 更新last指针 + u8 *last_bp = get_block_addr(last); + *(u16*)(last_bp+0x3e) = *(u16*)(bp+0x3e); + + put_be16(&BUPMEM->free_block, free_block); + + printf("Delete Save_%d.\n", save_id); + + return 0; +} + + +int sr_bup_create(char *game_id) +{ + int i; + + printf("sr_bup_create: [%s]\n", game_id); + + for(i=1; i<4096; i++){ + if(*(u32*)(bup_buf+i*16)==0) + break; + if(strncmp((char*)bup_buf+i*16, game_id, 16)==0) + return 0; + } + + memcpy(bup_buf+i*16, game_id, 16); + + bup_mem = bup_buf+i*0x10000; + sr_bup_format((u8*)game_id); + + return 0x10000; +} + + +int sr_bup_list(int slot_id) +{ + int block, i, dsize, bsize; + u8 *bp; + + if(slot_id==-1){ + // List all slot + for(i=1; i<4096; i++){ + char sname[20]; + if(*(u32*)(bup_buf+i*16)==0) + break; + memcpy(sname, bup_buf+i*16, 16); + sname[16] = 0; + printf("Slot_%-2d: [%s]\n", i, sname); + } + }else{ + // List saves in slot + if(sr_bup_select(slot_id)<0) + return -1; + + i = 0; + block = get_be16(&BUPMEM->first_save); + while(block){ + bp = get_block_addr(block); + + dsize = get_be32(bp+0x0c); + bsize = (dsize+block_size-1)/(block_size); + bsize += 1; + + printf(" Save_%-3d: %s size=%5x block=%d\n", i, bp, dsize, bsize); + block = get_be16(bp+0x3e); + i += 1; + } + } + printf("\n"); + + printf(" Total block: %4d Free block: %d\n\n", 512, free_block); + return 0; +} + + +int sr_bup_init(u8 *bbuf) +{ + bup_buf = bbuf; + return 0; +} + + +/******************************************************************************/ + diff --git a/tools/savetool/sr_mems.c b/tools/savetool/sr_mems.c new file mode 100644 index 0000000..1dc77f0 --- /dev/null +++ b/tools/savetool/sr_mems.c @@ -0,0 +1,342 @@ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "bup.h" + + +/******************************************************************************/ + +static u8 *mems_buf; + +/******************************************************************************/ + +typedef struct { + u32 magic0; + u32 magic1; + u32 total_size; + u16 free_block; + u16 first_save; + u8 bitmap[1024-16]; +}MEMSHEADER; + +#define MEMS ((MEMSHEADER*)(mems_buf)) + +static u16 block_size = 1024; +static u16 free_block; + +/******************************************************************************/ + + +static int get_free_block(int pos) +{ + int i, byte, mask; + + for(i=pos; i<8064; i++){ + byte = i/8; + mask = 1<<(i&7); + if( (MEMS->bitmap[byte]&mask) == 0 ){ + MEMS->bitmap[byte] |= mask; + free_block -= 1; + //printf("get_free_block(%d): %d\n", pos, i); + return i; + } + } + + return 0; +} + + +static u8 *get_block_addr(int id) +{ + return (u8*)(mems_buf + block_size*id); +} + + +static int access_data(int block, u8 *data, int type) +{ + u8 *bp, *bmp; + int dsize, asize; + + bp = get_block_addr(block); + dsize = get_be32(bp+0x0c); + if(dsize<=(block_size-64)){ + if(type==0){ // read + memcpy(data, bp+0x40, dsize); + }else{ // write + memcpy(bp+0x40, data, dsize); + } + return 0; + } + + bmp = bp+0x40; + while(dsize>0){ + block = get_be16(bmp); + bmp += 2; + bp = get_block_addr(block); + + asize = (dsize>block_size)? block_size : dsize; + //printf("dsize=%04x block=%04x asize=%04x\n", dsize, block, asize); + + if(type==0){ // read + memcpy(data, bp, asize); + }else{ // write + memcpy(bp, data, asize); + } + data += asize; + dsize -= asize; + } + + return 0; +} + + +/******************************************************************************/ + + +int sr_mems_export(int slot_id, int save_id) +{ + int block, i, index; + u8 *bp; + + index = 0; + for(i=0; i<448; i++){ + bp = (u8*)(mems_buf+1024+i*16); + if(bp[0]==0) + continue; + if(index == save_id){ + block = get_be16(bp+0x0e); + bp = get_block_addr(block); + + memset(save_buf, 0, 0x100000); + + strcpy((char*)save_buf, "SSAVERAW"); + memcpy(save_buf+0x10, bp+0, 11); // Filename + memcpy(save_buf+0x1c, bp+0x0c, 4); // Size + memcpy(save_buf+0x20, bp+0x10, 10); // Comment + save_buf[0x2b] = bp[0x1b]; // Language + memcpy(save_buf+0x2c, bp+0x1c, 4); // Date + + access_data(block, save_buf+0x40, 0); + + char fname[16]; + int fsize = get_be32(save_buf+0x1c); + sprintf(fname, "%s.bin", save_buf+0x10); + write_file(fname, save_buf, fsize+0x40); + + printf("Export Save_%d: %s\n", index, fname); + return 0; + } + index += 1; + } + + printf("Save_%d not found!\n", save_id); + return -1; +} + + +int sr_mems_import(int slot_id, int save_id, char *save_name) +{ + SAVEINFO *save; + int block, block_need, i, index, last, hdr; + u8 *bp; + + index = 0; + block = 0; + last = -1; + for(i=0; i<448; i++){ + bp = (u8*)(mems_buf+1024+i*16); + if(bp[0]==0){ + if(last==-1){ + last = i; + } + continue; + } + if(index == save_id){ + block = get_be16(bp+0x0e); + break; + } + index += 1; + } + + if(block){ + // overwrite + char sname[20]; + if(save_name==NULL){ + sprintf(sname, "%s.bin", bp); + save_name = sname; + } + save = load_saveraw(save_name); + if(save==NULL) + return -1; + + *(u32*)(bp+0x1c) = save->date; + access_data(block, save->dbuf, 2); + printf("Import %s from %s.\n", bp, save_name); + return 0; + } + if(save_id>=0){ + printf("Save_%d not found!\n", save_id); + return -1; + } + + // new save + save = load_saveraw(save_name); + if(save==NULL) + return -1; + + // 计算所需的块数量。起始块+数据块 + int dsize = get_be32(&save->data_size); + if(dsize<=(block_size-64)){ + block_need = 0; + }else{ + block_need = (dsize+block_size-1)/(block_size); + if((block_need+1) > free_block){ + return -1; + } + } + printf("block_need=%d\n", block_need+1); + + // 分配起始块 + block = get_free_block(0); + hdr = block; + + bp = get_block_addr(hdr); + printf("start at %d\n", hdr); + + // 写开始块 + memset(bp, 0, 1024); + memcpy(bp+0x00, save->file_name, 11); + *(u32*)(bp+0x0c) = save->data_size; + memcpy(bp+0x10, save->comment, 10); + bp[0x1b] = save->language; + *(u32*)(bp+0x1c) = save->date; + + // 分配块 + block = 0; + for(i=0; idbuf, 2); + + // 更新目录 + memcpy((u8*)mems_buf+1024+last*16, save->file_name, 11); + put_be16(mems_buf+1024+last*16+0x0e, hdr); + + put_be16(&MEMS->free_block, free_block); + + printf("Import %s.\n", save->file_name); + return 0; +} + + +int sr_mems_delete(int slot_id, int save_id) +{ + int block, i, index, last; + u8 *bp; + + index = 0; + block = 0; + last = -1; + for(i=0; i<448; i++){ + bp = (u8*)(mems_buf+1024+i*16); + if(bp[0]==0) + continue; + if(index == save_id){ + block = get_be16(bp+0x0e); + last = i; + break; + } + index += 1; + } + if(block==0){ + printf("Save_%d not found!\n", save_id); + return -1; + } + + bp = get_block_addr(block); + + // 释放开始块 + set_bitmap((u8*)MEMS->bitmap, block, 0); + free_block += 1; + + // 释放数据块 + int dsize = get_be32(bp+0x0c); + if(dsize>960){ + u8 *bmp = bp+0x40; + block = 0; + while(1){ + block = get_be16(bmp); + if(block==0) + break; + set_bitmap((u8*)MEMS->bitmap, block, 0); + free_block += 1; + bmp += 2; + } + } + + // 更新last指针 + memset((u8*)mems_buf+1024+last*16, 0, 16); + + put_be16(&MEMS->free_block, free_block); + + printf("Delete Save_%d.\n", save_id); + + return 0; +} + + +int sr_mems_create(char *game_id) +{ + return -1; +} + + +int sr_mems_list(int slot_id) +{ + int block, i, index, dsize, bsize; + u8 *bp; + + index = 0; + for(i=0; i<448; i++){ + bp = (u8*)(mems_buf+1024+i*16); + if(bp[0]==0) + continue; + + block = get_be16(bp+0x0e); + bp = get_block_addr(block); + + dsize = get_be32(bp+0x0c); + bsize = 0; + if(dsize>960){ + bsize = (dsize+block_size-1)/(block_size); + } + bsize += 1; + + printf(" Save_%-3d: %s size=%5x block=%d\n", index, bp, dsize, bsize); + + index += 1; + } + printf("\n"); + + printf(" Total block: %4d Free block: %d\n\n", 8064, free_block); + return 0; +} + + +int sr_mems_init(u8 *bbuf) +{ + mems_buf = bbuf; + free_block = get_be16(&MEMS->free_block); + + return 0; +} + + +/******************************************************************************/ + diff --git a/tools/savetool/ss_bup.c b/tools/savetool/ss_bup.c new file mode 100644 index 0000000..62c9c76 --- /dev/null +++ b/tools/savetool/ss_bup.c @@ -0,0 +1,244 @@ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" +#include "bup.h" + + +/******************************************************************************/ + +static u8 bup_mem[32768]; +static int ss_bup_type; + +typedef struct { + u32 total_size; + u32 total_block; + u32 block_size; + u8 block_map[64]; + + // 描述某个存档的信息 + int dsize; // 数据大小 + int bsize; // 一共有多少块 + u16 blist[512]; + int nbp; // blist中下一块的索引 + int dp; // 块中的数据指针 +}BUPINFO; + + +static BUPINFO bup_info; +#define GET_BUP_INFO() (&bup_info) + + +/******************************************************************************/ + + +static u8 *get_block_addr(BUPINFO *binfo, int id) +{ + return (u8*)(bup_mem + binfo->block_size*id); +} + + +static void load_blist(BUPINFO *binfo, int block) +{ + int nbp, dp, i; + u8 *bp; + + binfo->blist[0] = block; + binfo->bsize = 1; + bp = get_block_addr(binfo, block); + + binfo->dsize = get_be32(bp+0x1e); + if(binfo->dsize<=0x1e){ + // 数据可以放在起始块内 + binfo->nbp = 1; + binfo->dp = 0x22; + return; + }else{ + nbp = 1; + dp = 0x22; + i = 1; + while(1){ + block = get_be16(bp+dp); + if(block==0) + break; + + binfo->blist[i] = block; + i += 1; + dp += 2; + if(dp==binfo->block_size){ + // 当前块用完了,取得块列表中下一块的地址。 + bp = get_block_addr(binfo, binfo->blist[nbp]); + nbp += 1; + dp = 4; + } + } + dp += 2; + if(dp==binfo->block_size){ + nbp += 1; + dp = 4; + } + + binfo->bsize = i; + binfo->nbp = nbp; + binfo->dp = dp; + } +} + + +static int read_data(BUPINFO *binfo, u8 *data) +{ + u8 *bp; + int dp, nbp, dsize; + + dsize = binfo->dsize; + nbp = binfo->nbp; + + bp = get_block_addr(binfo, binfo->blist[nbp-1]); + dp = binfo->dp; + //printf("read_data: %d %d\n", binfo->blist[nbp-1], dp); + while(dsize>0){ + *(u16*)data = *(u16*)(bp+dp); + dp += 2; + data += 2; + dsize -= 2; + if(dp==binfo->block_size){ + // 当前块用完了,取得块列表中下一块的地址。 + bp = get_block_addr(binfo, binfo->blist[nbp]); + //printf("next block: %d %08x dsize=%04x\n", binfo->blist[nbp], bp, dsize); + nbp += 1; + dp = 4; + } + } + + return 0; +} + + +static void scan_save(BUPINFO *binfo) +{ + int i; + u8 *bp; + + memset(binfo->block_map, 0, 64); + binfo->block_map[0] = 0x03; + + for(i=2; itotal_block; i++){ + bp = get_block_addr(binfo, i); + if(get_be32(bp)==0x80000000){ + // 找到了save起始块 + load_blist(binfo, i); + } + } +} + + +/******************************************************************************/ + + +int ss_bup_export(int slot_id, int index) +{ + BUPINFO *binfo = GET_BUP_INFO(); + int block, i; + u8 *bp; + + i = 0; + for(block=2; blocktotal_block; block++){ + bp = get_block_addr(binfo, block); + if(get_be32(bp)==0x80000000){ + // 找到了save起始块 + if(i == index){ + memset(save_buf, 0, 32768); + strcpy((char*)save_buf, "SSAVERAW"); + memcpy(save_buf+0x10, bp+4, 11); // Filename + memcpy(save_buf+0x1c, bp+0x1e, 4); // Size + memcpy(save_buf+0x20, bp+0x10, 10); // Comment + save_buf[0x2b] = bp[0x0f]; // Language + memcpy(save_buf+0x2c, bp+0x1a, 4); // Date + + load_blist(binfo, block); + read_data(binfo, save_buf+0x40); + + char fname[16]; + int fsize = get_be32(save_buf+0x1c); + sprintf(fname, "%s.bin", save_buf+0x10); + write_file(fname, save_buf, fsize+0x40); + + printf("Export Save_%d: %s\n", index, fname); + return 0; + } + i += 1; + } + } + + return -1; +} + + +int ss_bup_import(int slot_id, int save_id, char *save_name) +{ + return -1; +} + + +int ss_bup_delete(int slot_id, int save_id) +{ + return -1; +} + + +int ss_bup_create(char *game_id) +{ + return -1; +} + + +int ss_bup_list(int slot_id) +{ + BUPINFO *binfo = GET_BUP_INFO(); + int i, snum; + u8 *bp; + + snum = 0; + for(i=2; itotal_block; i++){ + bp = get_block_addr(binfo, i); + if(get_be32(bp)==0x80000000){ + // 找到了save起始块 + printf("Save_%d: %s\n", snum, bp+4); + snum += 1; + } + } + + return 0; +} + + +int ss_bup_init(u8 *bbuf) +{ + BUPINFO *binfo = GET_BUP_INFO(); + int i; + + if(bbuf[1]=='B' && bbuf[3]=='a' && bbuf[5]=='c' && bbuf[7]=='k'){ + ss_bup_type = 0; + for(i=0; i<32768; i++){ + bup_mem[i] = bbuf[i*2+1]; + } + }else if(strncmp((char*)bbuf, "BackUpRam Format", 16)==0){ + ss_bup_type = 1; + memcpy(bup_mem, bbuf, 32768); + } + + memset(binfo, 0, sizeof(BUPINFO)); + + binfo->total_size = 0x8000; + binfo->total_block = 512; + binfo->block_size = 64; + + scan_save(binfo); + + return 0; +} + + +/******************************************************************************/ + +