Quem usa o VBA-M pelo RetroArch, provavelmente se deparou com o problema de incompatibilidade entre o
.sav (usado pelo VBA-M) e o
.srm (usado pelo RetroArch). Eu peguei o meu
save de um jogo aqui do meu PC e tentei usar no RetroArch do Android, e não deu certo. Estou falando do
save nativo do jogo (bateria, memória eeprom, flash...), não dos Saves States.
Como eu não queria começar tudo de novo, procurei um pouco e achei uma solução. É possível converter um save para outro, com o
gbaconv:
https://github.com/libretro/vbam-libretro/blob/master/src/libretro/gbaconv/gbaconv.cÉ preciso compilar, é fácil, baixe ou copie e cole no gedit/mousepad/nano/leafpad e salve como
gbaconv.c, vá até a pasta onde você salvou com o comando
cd e use o
gcc, assim:
cd /home/seu-usuario/pasta-onde-foi-salvo
gcc -std=c99 -o gbaconv gbaconv.c
Vai dar alguns warnings, mas não tem problema. Agora é só usar o programa pelo terminal:
./gbaconv seu-save.sav
No meu caso, como o jogo é o Kingdom Hearts (coloque o seu arquivo .sav na mesma pasta do gbaconv):
./gbaconv Kingdom\ Hearts\ -\ Chain\ of\ Memories\ \(E\).sav
Ou ainda,
arraste e solte seu arquivo .sav sobre o executável gbaconv.
Após isso, você vai ver um arquivo em .srm na mesma pasta!
É possível converter um .srm para .sav também, usando o mesmo programa.
Bem útil esse gbaconv, agora dá para continuar a jogatina no celular, ou eventualmente no próprio ubuntu, usando o retroarch.
http://emulation.gametechwiki.com/index.php/Game_Boy_Advance_emulatorsObs: se não conseguir copiar lá do link:
#include <stdio.h>
#include <stdint.h>
#ifndef __CELLOS_LV2__
#include <getopt.h>
#endif
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdbool.h>
enum save_type
{
EEPROM_512B,
EEPROM_8K,
FLASH_64K,
FLASH_128K,
SAVE_UNKNOWN
};
static const char *save_type_to_string(enum save_type type)
{
switch (type)
{
case EEPROM_512B:
return "EEPROM 4kbit";
case EEPROM_8K:
return "EEPROM 64kbit";
case FLASH_64K:
return "FLASH 512kbit";
case FLASH_128K:
return "FLASH 1MBit";
default:
return "Unknown type";
}
}
static bool scan_section(const uint8_t *data, unsigned size)
{
for (unsigned i = 0; i < size; i++)
{
if (data[i] != 0xff)
return true;
}
return false;
}
static enum save_type detect_save_type(const uint8_t *data, unsigned size)
{
if (size == 512)
return EEPROM_512B;
if (size == 0x2000)
return EEPROM_8K;
if (size == 0x10000)
return FLASH_64K;
if (size == 0x20000)
return FLASH_128K;
if (size == (0x20000 + 0x2000))
{
if (scan_section(data, 0x10000) && !scan_section(data + 0x10000, 0x10000))
return FLASH_64K;
if (scan_section(data, 0x20000))
return FLASH_128K;
if (scan_section(data + 0x20000, 512) && !scan_section(data + 0x20000 + 512, 0x20000 - 512))
return EEPROM_512B;
if (scan_section(data + 0x20000, 0x2000))
return EEPROM_8K;
}
return SAVE_UNKNOWN;
}
static void dump_srm(FILE *file, const uint8_t *data, enum save_type type)
{
void *buf = malloc(0x20000 + 0x2000);
memset(buf, 0xff, 0x20000 + 0x2000);
switch (type)
{
case EEPROM_512B:
fwrite(buf, 1, 0x20000, file);
fwrite(data, 1, 512, file);
fwrite(buf, 1, 0x2000 - 512, file);
break;
case EEPROM_8K:
fwrite(buf, 1, 0x20000, file);
fwrite(data, 1, 0x2000, file);
break;
case FLASH_64K:
fwrite(data, 1, 0x10000, file);
fwrite(buf, 1, 0x20000 + 0x2000 - 0x10000, file);
break;
case FLASH_128K:
fwrite(data, 1, 0x20000, file);
fwrite(buf, 1, 0x2000, file);
break;
default:
break;
}
free(buf);
}
static void dump_sav(FILE *file, const uint8_t *data, enum save_type type)
{
switch (type)
{
case EEPROM_512B:
fwrite(data + 0x20000, 1, 512, file);
break;
case EEPROM_8K:
fwrite(data + 0x20000, 1, 0x2000, file);
break;
case FLASH_64K:
fwrite(data, 1, 0x10000, file);
break;
case FLASH_128K:
fwrite(data, 1, 0x20000, file);
break;
default:
break;
}
}
// One shot cowboy code :)
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return 1;
}
FILE *file = fopen(argv[1], "rb");
if (!file)
{
fprintf(stderr, "Failed to open file \"%s\"\n", argv[1]);
goto error;
}
fseek(file, 0, SEEK_END);
long len = ftell(file);
rewind(file);
uint8_t *buffer = malloc(len);
if (!buffer)
{
fprintf(stderr, "Failed to allocate memory!\n");
goto error;
}
fread(buffer, 1, len, file);
fclose(file);
file = NULL;
char *out_path = strdup(argv[1]);
char *split = strrchr(out_path, '.');
const char *ext = NULL;
if (split)
{
*split = '\0';
ext = split + 1;
if (strcasecmp(ext, "srm") == 0)
strcat(out_path, ".sav");
else if (strlen(ext) >= 3)
strcat(out_path, ".srm");
else
ext = NULL;
}
if (!ext)
{
fprintf(stderr, "Cannot detect extension!\n");
goto error;
}
enum save_type type = detect_save_type(buffer, len);
printf("Detected save type: %s\n", save_type_to_string(type));
if (type == SAVE_UNKNOWN)
{
fprintf(stderr, "Cannot infer save type ...\n");
goto error;
}
file = fopen(out_path, "wb");
if (!file)
goto error;
if (len == (0x20000 + 0x2000))
dump_sav(file, buffer, type);
else
dump_srm(file, buffer, type);
fclose(file);
return 0;
error:
if (file)
fclose(file);
return 1;
}