add save tools

This commit is contained in:
tpu 2024-01-18 21:36:51 +08:00
parent 756c19e422
commit 3fb1d9af9c
8 changed files with 1467 additions and 2 deletions

View file

@ -1,6 +1,6 @@
/obj/*
/ramimage.bin
/ramimage.elf
/ssfirm.bin
/ssfirm.elf
/dump.txt

3
tools/savetool/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/*.exe

7
tools/savetool/Makefile Normal file
View file

@ -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

65
tools/savetool/bup.h Normal file
View file

@ -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

367
tools/savetool/main.c Normal file
View file

@ -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<argc){
if(argv[p][0]=='-'){
switch(argv[p][1]){
case 't':
if(p+1==argc)
goto _invalid_params;
slot_id = atoi(argv[p+1]);
p += 1;
break;
case 's':
if(p+1==argc)
goto _invalid_params;
save_id = atoi(argv[p+1]);
p += 1;
break;
case 'c':
if(p+1==argc)
goto _invalid_params;
create = 1;
game_id = argv[p+1];
p += 1;
break;
case 'i':
import = 1;
if(p+1<argc){
save_name = argv[p+1];
p += 1;
}
break;
case 'd':
delete = 1;
break;
default:
_invalid_params:
printf("Invalid params: %s\n", argv[p]);
return -1;
}
}else if(bup_name==NULL){
bup_name = argv[p];
}
p += 1;
}
if(bup_name==NULL){
printf("Need a Save file!\n");
return -1;
}
if(create){
if(game_id==NULL){
printf("Need a Game ID!\n");
return -1;
}
}
retv = bup_load(bup_name);
if(retv<0){
printf("Faield to load save file!\n");
return retv;
}
if(create){
retv = bup_create(game_id);
}else if(import){
retv = bup_import(slot_id, save_id, save_name);
}else if(delete){
retv = bup_delete(slot_id, save_id);
}else if(save_id>=0){
retv = bup_export(slot_id, save_id);
}else{
retv = bup_list(slot_id);
}
return retv;
}

437
tools/savetool/sr_bup.c Normal file
View file

@ -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; i<block_need; i++){
block = get_free_block(block);
set_bitmap(bp+0x40, block, 1);
block += 1;
}
// 写数据
access_data(hdr, save->dbuf, 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;
}
/******************************************************************************/

342
tools/savetool/sr_mems.c Normal file
View file

@ -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; i<block_need; i++){
block = get_free_block(block);
put_be16(bp+0x40+i*2, block);
block += 1;
}
put_be16(bp+0x40+i*2, 0);
// 写数据
access_data(hdr, save->dbuf, 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;
}
/******************************************************************************/

244
tools/savetool/ss_bup.c Normal file
View file

@ -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; i<binfo->total_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; block<binfo->total_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; i<binfo->total_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;
}
/******************************************************************************/