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; }