diff --git a/include/menu.h b/include/menu.h
index ce79d51..58bc94e 100644
--- a/include/menu.h
+++ b/include/menu.h
@@ -21,6 +21,8 @@ enum menu_screen_ids
MENU_CODE_OPTIONS, /* 10 - Code Menu (View Cheat Options) */
MENU_SAVE_DETAILS,
MENU_HEX_EDITOR,
+ MENU_PS1VMC_SAVES, /* 13 - PS1 VMC Menu */
+ MENU_PS2VMC_SAVES, /* 14 - PS2 VMC Menu */
TOTAL_MENU_IDS
};
@@ -64,7 +66,7 @@ enum texture_index
cat_warning_png_index,
tag_lock_png_index,
tag_own_png_index,
- tag_pce_png_index,
+ tag_vmc_png_index,
tag_ps1_png_index,
tag_ps2_png_index,
tag_ps3_png_index,
diff --git a/include/saves.h b/include/saves.h
index 427a4e4..464a8d0 100644
--- a/include/saves.h
+++ b/include/saves.h
@@ -38,6 +38,7 @@
#define EXPORT_RAP_PATH_USB USB_PATH PS3_LICENSE_PATH
#define EXPORT_RAP_PATH_HDD "/dev_hdd0/" PS3_LICENSE_PATH
+#define VMC_PS1_PATH_USB "PS1/VMC/"
#define VMC_PS2_PATH_USB "PS2/VMC/"
#define VMC_PS2_PATH_HDD "/dev_hdd0/savedata/vmc/"
@@ -105,6 +106,9 @@ enum cmd_code_enum
CMD_COPY_ALL_SAVES_USB,
CMD_COPY_ALL_SAVES_HDD,
CMD_SAVE_WEB_SERVER,
+ CMD_RESIGN_VMP,
+ CMD_EXP_SAVES_VMC1,
+ CMD_EXP_ALL_SAVES_VMC1,
// Export commands
CMD_EXP_EXDATA_USB,
@@ -114,12 +118,16 @@ enum cmd_code_enum
CMD_EXP_PSV_MCS,
CMD_EXP_PSV_PSU,
CMD_EXP_VM2_RAW,
+ CMD_EXP_VMC1SAVE,
+ CMD_EXP_VMP2MCR,
// Import commands
CMD_IMP_EXDATA_USB,
CMD_IMP_PS2_ISO,
CMD_IMP_PS2_CONFIG,
CMD_IMP_PS2VMC_USB,
+ CMD_IMP_VMC1SAVE,
+ CMD_IMP_MCR2VMP,
CMD_CREATE_ACT_DAT,
CMD_EXTRACT_ARCHIVE,
CMD_URL_DOWNLOAD,
@@ -143,15 +151,18 @@ enum cmd_code_enum
#define SAVE_FLAG_TROPHY 128
#define SAVE_FLAG_ONLINE 256
#define SAVE_FLAG_SELECTED 512
+#define SAVE_FLAG_VMC 1024
enum save_type_enum
{
FILE_TYPE_NULL,
+ FILE_TYPE_MENU,
FILE_TYPE_PSV,
FILE_TYPE_TRP,
- FILE_TYPE_MENU,
+ FILE_TYPE_VMC,
// PS1 File Types
+ FILE_TYPE_PS1,
FILE_TYPE_PSX,
FILE_TYPE_MCS,
@@ -191,7 +202,7 @@ enum char_flag_enum
CHAR_TAG_TRANSFER,
CHAR_TAG_ZIP,
CHAR_RES_CR,
- CHAR_TAG_PCE,
+ CHAR_TAG_VMC,
CHAR_TAG_WARNING,
CHAR_BTN_X,
CHAR_BTN_S,
@@ -220,6 +231,7 @@ enum save_sort_enum
SORT_DISABLED,
SORT_BY_NAME,
SORT_BY_TITLE_ID,
+ SORT_BY_TYPE,
};
typedef struct save_entry
@@ -248,15 +260,18 @@ list_t * ReadUserList(const char* userPath);
list_t * ReadOnlineList(const char* urlPath);
list_t * ReadBackupList(const char* userPath);
list_t * ReadTrophyList(const char* userPath);
+list_t * ReadVmc1List(const char* userPath);
void UnloadGameList(list_t * list);
char * readTextFile(const char * path, long* size);
int sortSaveList_Compare(const void* A, const void* B);
+int sortSaveList_Compare_Type(const void* A, const void* B);
int sortSaveList_Compare_TitleID(const void* A, const void* B);
int sortCodeList_Compare(const void* A, const void* B);
int ReadCodes(save_entry_t * save);
int ReadTrophies(save_entry_t * game);
int ReadOnlineSaves(save_entry_t * game);
int ReadBackupCodes(save_entry_t * bup);
+int ReadVmc1Codes(save_entry_t * save);
int http_init(void);
void http_end(void);
diff --git a/include/settings.h b/include/settings.h
index cb4732d..bc155da 100644
--- a/include/settings.h
+++ b/include/settings.h
@@ -1,5 +1,5 @@
-#define APOLLO_VERSION "1.8.7" //Apollo PS3 version (about menu)
+#define APOLLO_VERSION "2.0.0" //Apollo PS3 version (about menu)
#define MENU_TITLE_OFF 30 //Offset of menu title text from menu mini icon
#define MENU_ICON_OFF 70 //X Offset to start printing menu mini icon
diff --git a/sfo.xml b/sfo.xml
index 6d51e4a..9c18a19 100644
--- a/sfo.xml
+++ b/sfo.xml
@@ -1,7 +1,7 @@
- 01.87
+ 02.00
133
@@ -34,6 +34,6 @@
NP0APOLLO
- 01.87
+ 02.00
diff --git a/source/exec_cmd.c b/source/exec_cmd.c
index 83cefea..45208c8 100644
--- a/source/exec_cmd.c
+++ b/source/exec_cmd.c
@@ -11,6 +11,7 @@
#include "util.h"
#include "pfd.h"
#include "sfo.h"
+#include "ps1card.h"
static char host_buf[256];
@@ -656,6 +657,57 @@ static void importTrophy(const char* src_path)
free(tmp);
}
+static void exportAllSavesVMC(const save_entry_t* save, int dev, int all)
+{
+ char outPath[256];
+ int done = 0, err_count = 0;
+ list_node_t *node;
+ save_entry_t *item;
+ uint64_t progress = 0;
+ list_t *list = ((void**)save->dir_name)[0];
+
+ init_progress_bar("Exporting all VMC saves...", save->path);
+ _set_dest_path(outPath, dev, PS1_IMP_PATH_USB);
+ mkdirs(outPath);
+
+ LOG("Exporting all saves from '%s' to %s...", save->path, outPath);
+ for (node = list_head(list); (item = list_get(node)); node = list_next(node))
+ {
+ update_progress_bar(progress++, list_count(list), item->name);
+ if (!all && !(item->flags & SAVE_FLAG_SELECTED))
+ continue;
+
+ if (item->type & FILE_TYPE_PS1)
+ (saveSingleSave(outPath, save->path[strlen(save->path)+1], PS1SAVE_PSV) ? done++ : err_count++);
+ }
+
+ end_progress_bar();
+
+ show_message("%d/%d Saves exported to\n%s", done, done+err_count, outPath);
+}
+
+static void exportVmcSave(const save_entry_t* save, int type, int dst_id)
+{
+ char outPath[256];
+ struct tm t;
+
+ _set_dest_path(outPath, dst_id, PS1_IMP_PATH_USB);
+ mkdirs(outPath);
+ if (type != PS1SAVE_PSV)
+ {
+ // build file path
+ gmtime_r(&(time_t){time(NULL)}, &t);
+ sprintf(strrchr(outPath, '/'), "/%s_%d-%02d-%02d_%02d%02d%02d.%s", save->title_id,
+ t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
+ (type == PS1SAVE_MCS) ? "mcs" : "psx");
+ }
+
+ if (saveSingleSave(outPath, save->path[strlen(save->path)+1], type))
+ show_message("Save successfully exported to:\n%s", outPath);
+ else
+ show_message("Error exporting save:\n%s", save->path);
+}
+
static void resignPSVfile(const char* psv_path)
{
init_loading_screen("Resigning PSV file...");
@@ -1509,6 +1561,35 @@ static void downloadLink(const char* path)
show_message("Error! File couldn't be downloaded");
}
+static void import_mcr2vmp(const save_entry_t* save, const char* src)
+{
+ char mcrPath[256];
+ uint8_t *data = NULL;
+ size_t size = 0;
+
+ snprintf(mcrPath, sizeof(mcrPath), VMC_PS2_PATH_HDD "%s/%s", save->title_id, src);
+ read_buffer(mcrPath, &data, &size);
+
+ if (openMemoryCardStream(data, size, 0) && saveMemoryCard(save->path, 0, 0))
+ show_message("Memory card successfully imported to:\n%s", save->path);
+ else
+ show_message("Error importing memory card:\n%s", mcrPath);
+}
+
+static void export_vmp2mcr(const save_entry_t* save)
+{
+ char mcrPath[256];
+
+ snprintf(mcrPath, sizeof(mcrPath), VMC_PS2_PATH_HDD "%s/%s", save->title_id, strrchr(save->path, '/') + 1);
+ strcpy(strrchr(mcrPath, '.'), ".MCR");
+ mkdirs(mcrPath);
+
+ if (saveMemoryCard(mcrPath, PS1CARD_RAW, 0))
+ show_message("Memory card successfully exported to:\n%s", mcrPath);
+ else
+ show_message("Error exporting memory card:\n%s", save->path);
+}
+
void execCodeCommand(code_entry_t* code, const char* codecmd)
{
switch (codecmd[0])
@@ -1668,6 +1749,46 @@ void execCodeCommand(code_entry_t* code, const char* codecmd)
code->activated = 0;
break;
+ case CMD_RESIGN_VMP:
+ if (vmp_resign(selected_entry->path))
+ show_message("Memory card successfully resigned:\n%s", selected_entry->path);
+ else
+ show_message("Error resigning memory card:\n%s", selected_entry->path);
+ code->activated = 0;
+ break;
+
+ case CMD_EXP_VMP2MCR:
+ export_vmp2mcr(selected_entry);
+ code->activated = 0;
+ break;
+
+ case CMD_EXP_SAVES_VMC1:
+ case CMD_EXP_ALL_SAVES_VMC1:
+ exportAllSavesVMC(selected_entry, codecmd[1], codecmd[0] == CMD_EXP_ALL_SAVES_VMC1);
+ code->activated = 0;
+ break;
+
+ case CMD_EXP_VMC1SAVE:
+ exportVmcSave(selected_entry, code->options[0].id, codecmd[1]);
+ code->activated = 0;
+ break;
+
+ case CMD_IMP_VMC1SAVE:
+ if (openSingleSave(code->file, (int*) host_buf))
+ {
+ saveMemoryCard(selected_entry->dir_name, 0, 0);
+ show_message("Save successfully imported:\n%s", code->file);
+ }
+ else
+ show_message("Error! Couldn't import save:\n%s", code->file);
+ code->activated = 0;
+ break;
+
+ case CMD_IMP_MCR2VMP:
+ import_mcr2vmp(selected_entry, code->options[0].name[code->options[0].sel]);
+ code->activated = 0;
+ break;
+
case CMD_IMP_PS2VMC_USB:
importPS2VMC(selected_entry->path, code->file);
code->activated = 0;
diff --git a/source/main.c b/source/main.c
index c34e8a4..7bee540 100644
--- a/source/main.c
+++ b/source/main.c
@@ -64,6 +64,7 @@ void update_usb_path(char *p);
void update_hdd_path(char *p);
void update_trophy_path(char *p);
void update_db_path(char *p);
+void update_vmc_path(char *p);
app_config_t apollo_config = {
.app_name = "APOLLO",
@@ -164,6 +165,19 @@ save_list_t user_backup = {
.UpdatePath = NULL,
};
+/*
+* PS1 VMC list
+*/
+save_list_t vmc1_saves = {
+ .icon_id = cat_usb_png_index,
+ .title = "PS1 Virtual Memory Card",
+ .list = NULL,
+ .path = "",
+ .ReadList = &ReadVmc1List,
+ .ReadCodes = &ReadVmc1Codes,
+ .UpdatePath = &update_vmc_path,
+};
+
static void release_all(void)
{
if(inited & INITED_CALLBACK)
@@ -310,7 +324,7 @@ static void LoadTextures_Menu(void)
load_menu_texture(logo_text, png);
load_menu_texture(tag_lock, png);
load_menu_texture(tag_own, png);
- load_menu_texture(tag_pce, png);
+ load_menu_texture(tag_vmc, png);
load_menu_texture(tag_ps1, png);
load_menu_texture(tag_ps2, png);
load_menu_texture(tag_ps3, png);
@@ -440,6 +454,14 @@ void update_db_path(char* path)
strcpy(path, apollo_config.save_db);
}
+void update_vmc_path(char* path)
+{
+ if (file_exists(path) == SUCCESS)
+ return;
+
+ path[0] = 0;
+}
+
static void registerSpecialChars(void)
{
// Register save tags
@@ -448,7 +470,7 @@ static void registerSpecialChars(void)
RegisterSpecialCharacter(CHAR_TAG_PS3, 2, 1.5, &menu_textures[tag_ps3_png_index]);
RegisterSpecialCharacter(CHAR_TAG_PSP, 2, 1.5, &menu_textures[tag_psp_png_index]);
RegisterSpecialCharacter(CHAR_TAG_PSV, 2, 1.5, &menu_textures[tag_psv_png_index]);
- RegisterSpecialCharacter(CHAR_TAG_PCE, 2, 1.5, &menu_textures[tag_pce_png_index]);
+ RegisterSpecialCharacter(CHAR_TAG_VMC, 2, 1.0, &menu_textures[tag_vmc_png_index]);
RegisterSpecialCharacter(CHAR_TAG_LOCKED, 0, 1.5, &menu_textures[tag_lock_png_index]);
RegisterSpecialCharacter(CHAR_TAG_OWNER, 0, 1.5, &menu_textures[tag_own_png_index]);
RegisterSpecialCharacter(CHAR_TAG_WARNING, 0, 1.5, &menu_textures[tag_warning_png_index]);
diff --git a/source/menu_cheats.c b/source/menu_cheats.c
index 9479828..e8f849b 100644
--- a/source/menu_cheats.c
+++ b/source/menu_cheats.c
@@ -110,14 +110,6 @@ void Draw_CheatsMenu_Options_Ani_Exit(void)
DrawTexture(&menu_textures[edit_shadow_png_index], left - menu_textures[edit_shadow_png_index].texture.width + 1, 0, 0, menu_textures[edit_shadow_png_index].texture.width, 512, icon_a);
DrawHeader(cat_cheats_png_index, left, selected_centry->name, "Options", APP_FONT_TITLE_COLOR | icon_a, 0xffffffff, 1);
-/*
- int _game_a = (int)(icon_a - (max / 2)) * 2;
- if (_game_a > 0xFF)
- _game_a = 0xFF;
- u8 game_a = (u8)(_game_a < 0 ? 0 : _game_a);
-*/
- //DrawOptions(selected_centry->options[option_index], game_a, 18, menu_old_sel[7]);
- //DrawScrollBar2(menu_old_sel[7], selected_centry->options[option_index].size, 18, 700, game_a);
tiny3d_Flip();
@@ -272,12 +264,7 @@ void Draw_CheatsMenu_View_Ani_Exit(void)
DrawTexture(&menu_textures[edit_shadow_png_index], left - menu_textures[edit_shadow_png_index].texture.width + 1, 0, 0, menu_textures[edit_shadow_png_index].texture.width, 512, icon_a);
DrawHeader(cat_cheats_png_index, left, "Details", selected_centry->name, APP_FONT_TITLE_COLOR | icon_a, 0xffffffff, 1);
-/*
- int _game_a = (int)(icon_a - (max / 2)) * 2;
- if (_game_a > 0xFF)
- _game_a = 0xFF;
- u8 game_a = (u8)(_game_a < 0 ? 0 : _game_a);
-*/
+
tiny3d_Flip();
if (left == 848)
@@ -391,6 +378,7 @@ void DrawGameList(int selIndex, list_t * games, u8 alpha)
tmp[1] = (item->flags & SAVE_FLAG_OWNER) ? CHAR_TAG_OWNER : ' ';
tmp[2] = (item->flags & SAVE_FLAG_LOCKED) ? CHAR_TAG_LOCKED : ' ';
if (item->flags & SAVE_FLAG_PSV) tmp[1] = CHAR_TAG_PSV;
+ if (item->type == FILE_TYPE_VMC) tmp[2] = CHAR_TAG_VMC;
DrawString(800 - (MENU_ICON_OFF * 1), game_y, tmp);
node = list_next(node);
diff --git a/source/menu_main.c b/source/menu_main.c
index 9e92e4b..04b29b9 100644
--- a/source/menu_main.c
+++ b/source/menu_main.c
@@ -9,6 +9,7 @@
#include "menu.h"
#include "menu_gui.h"
#include "ttf_render.h"
+#include "ps1card.h"
#include
#include
@@ -18,6 +19,7 @@ extern save_list_t usb_saves;
extern save_list_t trophies;
extern save_list_t online_saves;
extern save_list_t user_backup;
+extern save_list_t vmc1_saves;
extern int close_app;
extern padData paddata[];
@@ -86,6 +88,8 @@ static int ReloadUserSaves(save_list_t* save_list)
list_bubbleSort(save_list->list, &sortSaveList_Compare);
else if (apollo_config.doSort == SORT_BY_TITLE_ID)
list_bubbleSort(save_list->list, &sortSaveList_Compare_TitleID);
+ else if (apollo_config.doSort == SORT_BY_TYPE)
+ list_bubbleSort(save_list->list, &sortSaveList_Compare_Type);
stop_loading_screen();
@@ -171,6 +175,15 @@ static void SetMenu(int id)
{
switch (menu_id) //Leaving menu
{
+ case MENU_PS1VMC_SAVES:
+ if (id == MENU_MAIN_SCREEN)
+ {
+ LOG("Saving PS1 VMC changes...");
+ UnloadGameList(vmc1_saves.list);
+ vmc1_saves.list = NULL;
+ saveMemoryCard(vmc1_saves.path, 0, 0);
+ }
+
case MENU_MAIN_SCREEN: //Main Menu
case MENU_TROPHIES:
case MENU_USB_SAVES: //USB Saves Menu
@@ -242,6 +255,14 @@ static void SetMenu(int id)
Draw_UserCheatsMenu_Ani(&online_saves);
break;
+ case MENU_PS1VMC_SAVES: //VMC Menu
+ if (!vmc1_saves.list && !ReloadUserSaves(&vmc1_saves))
+ return;
+
+ if (apollo_config.doAni)
+ Draw_UserCheatsMenu_Ani(&vmc1_saves);
+ break;
+
case MENU_CREDITS: //About Menu
if (apollo_config.doAni)
Draw_AboutMenu_Ani();
@@ -262,7 +283,8 @@ static void SetMenu(int id)
case MENU_PATCHES: //Cheat Selection Menu
//if entering from game list, don't keep index, otherwise keep
- if (menu_id == MENU_USB_SAVES || menu_id == MENU_HDD_SAVES || menu_id == MENU_ONLINE_DB || menu_id == MENU_TROPHIES)
+ if (menu_id == MENU_USB_SAVES || menu_id == MENU_HDD_SAVES || menu_id == MENU_ONLINE_DB ||
+ menu_id == MENU_TROPHIES || menu_id == MENU_PS1VMC_SAVES || menu_id == MENU_PS2VMC_SAVES)
menu_old_sel[MENU_PATCHES] = 0;
char iconfile[256];
@@ -273,7 +295,7 @@ static void SetMenu(int id)
snprintf(iconfile, sizeof(iconfile), APOLLO_LOCAL_CACHE "%s.PNG", selected_entry->title_id);
if (file_exists(iconfile) != SUCCESS)
- http_download(selected_entry->path, "ICON0.PNG", iconfile, 0);
+ http_download(selected_entry->path, "ICON0.PNG", iconfile, 1);
}
if (file_exists(iconfile) == SUCCESS)
@@ -385,6 +407,16 @@ static void doSaveMenu(save_list_t * save_list)
{
selected_entry = list_get_item(save_list->list, menu_sel);
+ if (selected_entry->type == FILE_TYPE_VMC && selected_entry->flags & SAVE_FLAG_VMC)
+ {
+ if (selected_entry->flags & SAVE_FLAG_PS1)
+ {
+ strncpy(vmc1_saves.path, selected_entry->path, sizeof(vmc1_saves.path));
+ SetMenu(MENU_PS1VMC_SAVES);
+ }
+ return;
+ }
+
if (!selected_entry->codes && !save_list->ReadCodes(selected_entry))
{
show_message("No data found in folder:\n%s", selected_entry->path);
@@ -878,5 +910,10 @@ void drawScene(void)
case MENU_HEX_EDITOR: //Hex Editor Menu
doHexEditor();
break;
+
+ case MENU_PS1VMC_SAVES: //PS1 VMC Menu
+ doSaveMenu(&vmc1_saves);
+ break;
+
}
}
diff --git a/source/menu_options.c b/source/menu_options.c
index 49ff9e7..e57bede 100644
--- a/source/menu_options.c
+++ b/source/menu_options.c
@@ -61,7 +61,7 @@ static void _draw_OptionsMenu(u8 alpha)
}
}
-void Draw_OptionsMenu_Ani()
+void Draw_OptionsMenu_Ani(void)
{
for (int ani = 0; ani < MENU_ANI_MAX; ani++)
{
@@ -91,7 +91,7 @@ void Draw_OptionsMenu_Ani()
}
}
-void Draw_OptionsMenu()
+void Draw_OptionsMenu(void)
{
DrawHeader(cat_opt_png_index, 0, "Settings", NULL, APP_FONT_TITLE_COLOR | 0xFF, 0xffffffff, 0);
_draw_OptionsMenu(0xFF);
diff --git a/source/saves.c b/source/saves.c
index 340ce9d..a0449b7 100644
--- a/source/saves.c
+++ b/source/saves.c
@@ -18,15 +18,18 @@
#include "util.h"
#include "pfd.h"
#include "trophy.h"
+#include "ps1card.h"
#define UTF8_CHAR_STAR "\xE2\x98\x85"
#define CHAR_ICON_NET "\x09"
#define CHAR_ICON_ZIP "\x0C"
+#define CHAR_ICON_VMC "\x0E"
#define CHAR_ICON_COPY "\x0B"
#define CHAR_ICON_SIGN "\x06"
#define CHAR_ICON_USER "\x07"
#define CHAR_ICON_LOCK "\x08"
+#define CHAR_ICON_WARN "\x0F"
/*
@@ -151,7 +154,12 @@ static option_entry_t* _getFileOptions(const char* save_path, const char* mask,
d = opendir(save_path);
if (!d)
- return NULL;
+ {
+ opt = _initOptions(1);
+ asprintf(&opt->name[0], CHAR_ICON_WARN " --- %s%s --- " CHAR_ICON_WARN, save_path, mask);
+ asprintf(&opt->value[0], "%c", CMD_CODE_NULL);
+ return opt;
+ }
LOG("Loading filenames {%s} from '%s'...", mask, save_path);
@@ -172,7 +180,12 @@ static option_entry_t* _getFileOptions(const char* save_path, const char* mask,
closedir(d);
if (i == 0)
- return NULL;
+ {
+ opt = _initOptions(1);
+ asprintf(&opt->name[0], CHAR_ICON_WARN " --- %s%s --- " CHAR_ICON_WARN, save_path, mask);
+ asprintf(&opt->value[0], "%c", CMD_CODE_NULL);
+ return opt;
+ }
opt = _initOptions(i);
i = 0;
@@ -417,6 +430,33 @@ static int set_ps2_codes(save_entry_t* item)
return list_count(item->codes);
}
+static void add_vmp_commands(save_entry_t* save)
+{
+ char path[256];
+ code_entry_t* cmd;
+
+ cmd = _createCmdCode(PATCH_NULL, "----- " UTF8_CHAR_STAR " Virtual Memory Card " UTF8_CHAR_STAR " -----", CMD_CODE_NULL);
+ list_append(save->codes, cmd);
+
+ if (endsWith(save->path, ".VMP"))
+ {
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_SIGN " Resign Memory Card", CMD_RESIGN_VMP);
+ list_append(save->codes, cmd);
+ }
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export Memory Card to .MCR", CMD_EXP_VMP2MCR);
+ list_append(save->codes, cmd);
+
+ snprintf(path, sizeof(path), CHAR_ICON_COPY " Import .MCR files to %s", strrchr(save->path, '/')+1);
+ cmd = _createCmdCode(PATCH_COMMAND, path, CMD_CODE_NULL);
+ cmd->options_count = 1;
+ snprintf(path, sizeof(path), VMC_PS2_PATH_HDD "%s/", save->title_id);
+ cmd->options = _getFileOptions(path, "*.MCR", CMD_IMP_MCR2VMP);
+ list_append(save->codes, cmd);
+
+ return;
+}
+
static option_entry_t* get_file_entries(const char* path, const char* mask)
{
return _getFileOptions(path, mask, CMD_CODE_NULL);
@@ -689,6 +729,123 @@ int ReadTrophies(save_entry_t * game)
return list_count(game->codes);
}
+static void add_vmc_import_saves(list_t* list, const char* path, const char* folder)
+{
+ code_entry_t * cmd;
+ DIR *d;
+ struct dirent *dir;
+ char psvPath[256];
+
+ snprintf(psvPath, sizeof(psvPath), "%s%s", path, folder);
+ d = opendir(psvPath);
+
+ if (!d)
+ return;
+
+ while ((dir = readdir(d)) != NULL)
+ {
+ if (!endsWith(dir->d_name, ".PSV") && !endsWith(dir->d_name, ".MCS") && !endsWith(dir->d_name, ".PSX") &&
+ !endsWith(dir->d_name, ".PS1") && !endsWith(dir->d_name, ".MCB") && !endsWith(dir->d_name, ".PDA"))
+ continue;
+
+ snprintf(psvPath, sizeof(psvPath), "%s %s", CHAR_ICON_COPY, dir->d_name);
+ cmd = _createCmdCode(PATCH_COMMAND, psvPath, CMD_IMP_VMC1SAVE);
+ asprintf(&cmd->file, "%s%s%s", path, folder, dir->d_name);
+ cmd->codes[1] = FILE_TYPE_PS1;
+ list_append(list, cmd);
+
+ LOG("[%s] F(%X) name '%s'", cmd->file, cmd->flags, cmd->name+2);
+ }
+
+ closedir(d);
+}
+
+static void read_vmc1_files(const char* vmcPath, list_t* list)
+{
+ save_entry_t *item;
+ DIR *d;
+ struct dirent *dir;
+
+ d = opendir(vmcPath);
+ if (!d)
+ return;
+
+ while ((dir = readdir(d)) != NULL)
+ {
+ if (!endsWith(dir->d_name, ".VMP") && !endsWith(dir->d_name, ".MCR") && !endsWith(dir->d_name, ".GME") &&
+ !endsWith(dir->d_name, ".VM1") && !endsWith(dir->d_name, ".MCD") && !endsWith(dir->d_name, ".VGS") &&
+ !endsWith(dir->d_name, ".VMC") && !endsWith(dir->d_name, ".BIN") && !endsWith(dir->d_name, ".SRM"))
+ continue;
+
+ item = _createSaveEntry(SAVE_FLAG_PS1 | SAVE_FLAG_VMC, dir->d_name);
+ item->type = FILE_TYPE_VMC;
+ item->title_id = strdup("VMC");
+ item->dir_name = strdup(VMC_PS1_PATH_USB);
+ asprintf(&item->path, "%s%s", vmcPath, dir->d_name);
+ list_append(list, item);
+
+ LOG("[%s] F(%X) name '%s'", item->path, item->flags, item->name);
+ }
+
+ closedir(d);
+}
+
+int ReadVmc1Codes(save_entry_t * save)
+{
+ code_entry_t * cmd;
+
+ save->codes = list_alloc();
+
+ if (save->type == FILE_TYPE_MENU)
+ {
+ add_vmc_import_saves(save->codes, save->path, PS1_IMP_PATH_USB);
+ if (!list_count(save->codes))
+ {
+ list_free(save->codes);
+ save->codes = NULL;
+ return 0;
+ }
+
+ list_bubbleSort(save->codes, &sortCodeList_Compare);
+
+ return list_count(save->codes);
+ }
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_USER " View Save Details", CMD_VIEW_DETAILS);
+ list_append(save->codes, cmd);
+
+ cmd = _createCmdCode(PATCH_NULL, "----- " UTF8_CHAR_STAR " Save Backup " UTF8_CHAR_STAR " -----", CMD_CODE_NULL);
+ list_append(save->codes, cmd);
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export save game to MCS format", CMD_CODE_NULL);
+ cmd->options_count = 1;
+ cmd->options = _createOptions(3, "Copy .MCS Save to USB", CMD_EXP_VMC1SAVE);
+ asprintf(&cmd->options->name[2], "Copy .MCS Save to HDD");
+ asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VMC1SAVE, STORAGE_HDD);
+ cmd->options[0].id = PS1SAVE_MCS;
+ list_append(save->codes, cmd);
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export save game to PSV format", CMD_CODE_NULL);
+ cmd->options_count = 1;
+ cmd->options = _createOptions(3, "Copy .PSV Save to USB", CMD_EXP_VMC1SAVE);
+ asprintf(&cmd->options->name[2], "Copy .PSV Save to HDD");
+ asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VMC1SAVE, STORAGE_HDD);
+ cmd->options[0].id = PS1SAVE_PSV;
+ list_append(save->codes, cmd);
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export save game to PSX format", CMD_CODE_NULL);
+ cmd->options_count = 1;
+ cmd->options = _createOptions(3, "Copy .PSX Save to USB", CMD_EXP_VMC1SAVE);
+ asprintf(&cmd->options->name[2], "Copy .PSX Save to HDD");
+ asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VMC1SAVE, STORAGE_HDD);
+ cmd->options[0].id = PS1SAVE_AR;
+ list_append(save->codes, cmd);
+
+ LOG("Loaded %ld codes", list_count(save->codes));
+
+ return list_count(save->codes);
+}
+
/*
* Function: ReadOnlineSaves()
* File: saves.c
@@ -1268,6 +1425,19 @@ int sortSaveList_Compare(const void* a, const void* b)
return strcasecmp(((save_entry_t*) a)->name, ((save_entry_t*) b)->name);
}
+int sortSaveList_Compare_Type(const void* a, const void* b)
+{
+ int ta = ((save_entry_t*) a)->type;
+ int tb = ((save_entry_t*) b)->type;
+
+ if (ta == tb)
+ return 0;
+ else if (ta < tb)
+ return -1;
+ else
+ return 1;
+}
+
int sortSaveList_Compare_TitleID(const void* a, const void* b)
{
char* ta = ((save_entry_t*) a)->title_id;
@@ -1562,7 +1732,7 @@ list_t * ReadUserList(const char* userPath)
snprintf(savePath, sizeof(savePath), "%s%s", userPath, save_paths[2]);
read_savegames(savePath, list, SAVE_FLAG_PSP);
- if (strncmp(userPath, "/dev_usb00", 10) == 0)
+ if (strncmp(userPath, USB_PATH, 8) == 0)
{
snprintf(savePath, sizeof(savePath), "%s%s", userPath, PSV_SAVES_PATH_USB);
read_psv_savegames(savePath, list);
@@ -1572,6 +1742,13 @@ list_t * ReadUserList(const char* userPath)
snprintf(savePath, sizeof(savePath), "%s%s", userPath, PS1_IMP_PATH_USB);
read_psx_savegames(savePath, list);
+
+ snprintf(savePath, sizeof(savePath), "%s%s", userPath, VMC_PS1_PATH_USB);
+ read_vmc1_files(savePath, list);
+ }
+ else
+ {
+ read_vmc1_files(VMC_PS2_PATH_HDD, list);
}
return list;
@@ -1673,6 +1850,87 @@ list_t * ReadOnlineList(const char* urlPath)
return list;
}
+list_t * ReadVmc1List(const char* userPath)
+{
+ char filePath[256];
+ save_entry_t *item;
+ code_entry_t *cmd;
+ list_t *list;
+ ps1mcData_t* mcdata;
+
+ if (!openMemoryCard(userPath, 0))
+ {
+ LOG("Error: no PS1 Memory Card detected! (%s)", userPath);
+ return NULL;
+ }
+
+ mcdata = getMemoryCardData();
+ if (!mcdata)
+ return NULL;
+
+ list = list_alloc();
+
+ item = _createSaveEntry(SAVE_FLAG_PS1, CHAR_ICON_VMC " Memory Card Management");
+ item->type = FILE_TYPE_MENU;
+ item->path = strdup(userPath);
+ item->codes = list_alloc();
+ //bulk management hack
+ item->dir_name = malloc(sizeof(void**));
+ ((void**)item->dir_name)[0] = list;
+
+ strncpy(filePath, userPath, sizeof(filePath));
+ strrchr(filePath, '/')[0] = 0;
+ item->title_id = strdup(strrchr(filePath, '/')+1);
+
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export selected Saves to USB", CMD_CODE_NULL);
+ cmd->options_count = 1;
+ cmd->options = _createOptions(2, "Copy selected Saves to USB", CMD_EXP_SAVES_VMC1);
+ list_append(item->codes, cmd);
+ cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export all Saves to USB", CMD_CODE_NULL);
+ cmd->options_count = 1;
+ cmd->options = _createOptions(2, "Copy all Saves to USB", CMD_EXP_ALL_SAVES_VMC1);
+ list_append(item->codes, cmd);
+ add_vmp_commands(item);
+ list_append(list, item);
+
+ item = _createSaveEntry(SAVE_FLAG_PS1, CHAR_ICON_COPY " Import Saves to Virtual Card");
+ if (strncmp(filePath, USB_PATH, 8) == 0)
+ {
+ item->path = strdup(filePath);
+ strchr(item->path + 1, '/')[1] = 0;
+ }
+ else
+ item->path = strdup(FAKE_USB_PATH);
+
+ item->title_id = strdup(item->path);
+ strchr(item->title_id + 1, '/')[0] = 0;
+ item->dir_name = strdup(userPath);
+ item->type = FILE_TYPE_MENU;
+ list_append(list, item);
+
+ for (int i = 0; i < PS1CARD_MAX_SLOTS; i++)
+ {
+ if (mcdata[i].saveType != PS1BLOCK_INITIAL)
+ continue;
+
+ LOG("Reading '%s'...", mcdata[i].saveName);
+
+ char* tmp = sjis2utf8(mcdata[i].saveTitle);
+ item = _createSaveEntry(SAVE_FLAG_PS1 | SAVE_FLAG_VMC, tmp);
+ item->type = FILE_TYPE_PS1;
+ item->dir_name = strdup(mcdata[i].saveName);
+ item->title_id = strdup(mcdata[i].saveProdCode);
+ //hack to keep the save block
+ asprintf(&item->path, "%s\n%s/%c%c", userPath, mcdata[i].saveName, 0, i);
+ free(tmp);
+
+ LOG("[%s] F(%X) name '%s'", item->title_id, item->flags, item->name);
+ list_append(list, item);
+ }
+
+ return list;
+}
+
list_t * ReadTrophyList(const char* userPath)
{
DIR *d;
diff --git a/source/settings.c b/source/settings.c
index 4fcd993..41f2ca8 100644
--- a/source/settings.c
+++ b/source/settings.c
@@ -8,7 +8,7 @@
#include "common.h"
static uint8_t owner_sel = 0;
-static char * sort_opt[] = {"Disabled", "by Name", "by Title ID", NULL};
+static char * sort_opt[] = {"Disabled", "by Name", "by Title ID", "by Type", NULL};
static void log_callback(int sel);
static void sort_callback(int sel);
diff --git a/source/sfo.c b/source/sfo.c
index 7b44e67..bf1da83 100644
--- a/source/sfo.c
+++ b/source/sfo.c
@@ -400,6 +400,7 @@ int patch_sfo(const char *in_file_path, sfo_patch_t* patches) {
sfo = sfo_alloc();
if (sfo_read(sfo, in_file_path) < 0) {
+ sfo_free(sfo);
LOG("Unable to read from '%s'", in_file_path);
return -1;
}
@@ -428,12 +429,14 @@ int build_sfo(const char *in_file_path, const char *out_file_path, const char *t
sfo1 = sfo_alloc();
if (sfo_read(sfo1, in_file_path) < 0) {
+ sfo_free(sfo1);
LOG("Unable to read from '%s'", in_file_path);
return -1;
}
sfo2 = sfo_alloc();
if (sfo_read(sfo2, tpl_file_path) < 0) {
+ sfo_free(sfo2);
LOG("Unable to read from '%s'", tpl_file_path);
return -1;
}
@@ -462,6 +465,7 @@ int patch_sfo_trophy(const char *in_file_path, const char* account) {
sfo = sfo_alloc();
if (sfo_read(sfo, in_file_path) < 0) {
+ sfo_free(sfo);
LOG("Unable to read from '%s'", in_file_path);
return -1;
}