diff --git a/include/saves.h b/include/saves.h index f451fcb..61ff3cd 100644 --- a/include/saves.h +++ b/include/saves.h @@ -54,7 +54,7 @@ #define EXP_PSV_PATH_USB1 USB1_PATH PSV_SAVES_PATH_USB #define VMC_PS2_PATH_USB "PS2/VMC/" -#define VMC_PS2_PATH_HDD "/dev_hdd0/savedata/vmc/" +#define VMC_PS1_PATH_USB "PS1/VMC/" #define IMP_PS2VMC_PATH_USB USB_PATH "PS2/VMC/" #define IMPORT_RAP_PATH_USB USB_PATH PS3_LICENSE_PATH @@ -115,8 +115,11 @@ enum cmd_code_enum // Export commands CMD_EXP_KEYSTONE, CMD_EXP_PS2_VM2, + CMD_EXP_PS2_RAW, CMD_EXP_VMC2SAVE, - CMD_EXP_VM2_RAW, + CMD_EXP_VMC1SAVE, + CMD_EXP_PS1_VMP, + CMD_EXP_PS1_VM1, CMD_EXP_DATABASE, CMD_DB_REBUILD, CMD_DB_DEL_FIX, @@ -126,6 +129,7 @@ enum cmd_code_enum CMD_IMP_KEYSTONE, CMD_IMP_DATABASE, CMD_IMP_VMC2SAVE, + CMD_IMP_VMC1SAVE, CMD_CREATE_ACT_DAT, CMD_EXTRACT_ARCHIVE, CMD_URL_DOWNLOAD, @@ -159,11 +163,15 @@ enum save_type_enum FILE_TYPE_NULL, FILE_TYPE_MENU, FILE_TYPE_PS4, - FILE_TYPE_PSV, FILE_TYPE_TRP, FILE_TYPE_VMC, // PS1 File Types + FILE_TYPE_PS1, + FILE_TYPE_PSX, + FILE_TYPE_MCS, + + // Misc FILE_TYPE_ZIP, FILE_TYPE_SQL, FILE_TYPE_NET, @@ -179,7 +187,7 @@ enum save_type_enum FILE_TYPE_MAX, FILE_TYPE_CBS, FILE_TYPE_XPS, - FILE_TYPE_VM2, + FILE_TYPE_PSV, }; enum char_flag_enum @@ -259,6 +267,7 @@ 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); list_t * ReadVmc2List(const char* userPath); void UnloadGameList(list_t * list); char * readTextFile(const char * path, long* size); @@ -270,6 +279,7 @@ 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 ReadVmc2Codes(save_entry_t * save); int http_init(void); @@ -319,5 +329,8 @@ int ps2_xps2psv(const char *save, const char *psv_path); int ps2_cbs2psv(const char *save, const char *psv_path); int ps2_max2psv(const char *save, const char *psv_path); +int psv_resign(const char *src_psv); +int vmp_resign(const char *src_vmp); + char* sjis2utf8(char* input); uint8_t* getIconPS2(const char* folder, const char* iconfile); diff --git a/source/exec_cmd.c b/source/exec_cmd.c index ce274c8..6d36217 100644 --- a/source/exec_cmd.c +++ b/source/exec_cmd.c @@ -14,6 +14,7 @@ #include "util.h" #include "sfo.h" #include "mcio.h" +#include "ps1card.h" static char host_buf[256]; @@ -760,8 +761,8 @@ static void exportAllSavesVMC(const save_entry_t* save, int dev, int all) if (!all && !(item->flags & SAVE_FLAG_SELECTED)) continue; -// if (item->type == FILE_TYPE_PS1) -// (saveSingleSave(outPath, save->dir_name[0], PS1SAVE_PSV) ? done++ : err_count++); + if (item->type == FILE_TYPE_PS1) + (saveSingleSave(outPath, save->dir_name[0], PS1SAVE_PSV) ? done++ : err_count++); if (item->type == FILE_TYPE_PS2) (vmc_export_psv(item->dir_name, outPath) ? done++ : err_count++); @@ -772,6 +773,28 @@ static void exportAllSavesVMC(const save_entry_t* save, int dev, int all) 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, (type == PS1SAVE_PSV) ? PSV_SAVES_PATH_USB : 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->dir_name[0], type)) + show_message("Save successfully exported to:\n%s", outPath); + else + show_message("Error exporting save:\n%s", save->path); +} + static void export_vmc2save(const save_entry_t* save, int type, int dst_id) { int ret = 0; @@ -851,7 +874,7 @@ static void deleteVmcSave(const save_entry_t* save) if (!show_dialog(DIALOG_TYPE_YESNO, "Do you want to delete %s?", save->dir_name)) return; - if ((save->flags & SAVE_FLAG_PS1) ? /*formatSave(save->dir_name[0])*/0 : vmc_delete_save(save->dir_name)) + if ((save->flags & SAVE_FLAG_PS1) ? formatSave(save->dir_name[0]) : vmc_delete_save(save->dir_name)) show_message("Save successfully deleted:\n%s", save->dir_name); else show_message("Error! Couldn't delete save:\n%s", save->dir_name); @@ -1444,11 +1467,26 @@ void execCodeCommand(code_entry_t* code, const char* codecmd) break; case CMD_EXP_PS2_VM2: - case CMD_EXP_VM2_RAW: + case CMD_EXP_PS2_RAW: exportVM2raw(code->file, codecmd[1], codecmd[0] == CMD_EXP_PS2_VM2); 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)) + show_message("Save successfully imported:\n%s", code->file); + else + show_message("Error! Couldn't import save:\n%s", code->file); + + selected_entry->flags |= SAVE_FLAG_UPDATED; + code->activated = 0; + break; + default: break; } diff --git a/source/main.c b/source/main.c index 4b9300b..699030a 100644 --- a/source/main.c +++ b/source/main.c @@ -137,6 +137,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, +}; + /* * PS2 VMC list */ diff --git a/source/menu_main.c b/source/menu_main.c index bf5effa..82288da 100644 --- a/source/menu_main.c +++ b/source/menu_main.c @@ -11,12 +11,14 @@ #include "ttf_render.h" #include "common.h" #include "mcio.h" +#include "ps1card.h" extern save_list_t hdd_saves; 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 save_list_t vmc2_saves; extern int close_app; @@ -113,6 +115,14 @@ static void SetMenu(int id) { switch (menu_id) //Leaving menu { + case MENU_PS1VMC_SAVES: + if (id == MENU_MAIN_SCREEN) + { + UnloadGameList(vmc1_saves.list); + vmc1_saves.list = NULL; + } + break; + case MENU_PS2VMC_SAVES: if (id == MENU_MAIN_SCREEN) { @@ -150,6 +160,12 @@ static void SetMenu(int id) selected_entry->flags ^= SAVE_FLAG_UPDATED; ReloadUserSaves(&vmc2_saves); } + else if (selected_entry->flags & SAVE_FLAG_UPDATED && id == MENU_PS1VMC_SAVES) + { + selected_entry->flags ^= SAVE_FLAG_UPDATED; + saveMemoryCard(vmc1_saves.path, 0, 0); + ReloadUserSaves(&vmc1_saves); + } break; case MENU_SAVE_DETAILS: @@ -209,6 +225,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_PS2VMC_SAVES: //Trophies Menu if (!vmc2_saves.list && !ReloadUserSaves(&vmc2_saves)) return; @@ -238,7 +262,7 @@ 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 || menu_id == MENU_PS2VMC_SAVES) + menu_id == MENU_TROPHIES || menu_id == MENU_PS1VMC_SAVES || menu_id == MENU_PS2VMC_SAVES) menu_old_sel[MENU_PATCHES] = 0; char iconfile[256]; @@ -255,6 +279,9 @@ static void SetMenu(int id) else if (selected_entry->flags & SAVE_FLAG_VMC && selected_entry->type == FILE_TYPE_PS2) LoadVmcTexture(128, 128, getIconPS2(selected_entry->dir_name, strrchr(selected_entry->path, '\n')+1)); + else if (selected_entry->flags & SAVE_FLAG_VMC && selected_entry->type == FILE_TYPE_PS1) + LoadVmcTexture(16, 16, getIconRGBA(selected_entry->dir_name[0], 0)); + else if (selected_entry->flags & SAVE_FLAG_HDD) snprintf(iconfile, sizeof(iconfile), PS4_SAVES_PATH_HDD "%s/%s_icon0.png", apollo_config.user_id, selected_entry->title_id, selected_entry->dir_name); @@ -381,7 +408,7 @@ static void doSaveMenu(save_list_t * save_list) if (selected_entry->flags & SAVE_FLAG_PS1) { -// strncpy(vmc1_saves.path, selected_entry->path, sizeof(vmc1_saves.path)); + strncpy(vmc1_saves.path, selected_entry->path, sizeof(vmc1_saves.path)); SetMenu(MENU_PS1VMC_SAVES); } else @@ -865,6 +892,10 @@ void drawScene(void) doHexEditor(); break; + case MENU_PS1VMC_SAVES: //PS1 VMC Menu + doSaveMenu(&vmc1_saves); + break; + case MENU_PS2VMC_SAVES: //PS2 VMC Menu doSaveMenu(&vmc2_saves); break; diff --git a/source/saves.c b/source/saves.c index 26308da..615e812 100644 --- a/source/saves.c +++ b/source/saves.c @@ -15,6 +15,7 @@ #include "util.h" #include "ps2mc.h" #include "mcio.h" +#include "ps1card.h" #define UTF8_CHAR_STAR "\xE2\x98\x85" @@ -976,6 +977,135 @@ 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; + + // check for PS1 PSV saves + if (endsWith(dir->d_name, ".PSV")) + { + snprintf(psvPath, sizeof(psvPath), "%s%s%s", path, folder, dir->d_name); + if (read_file(psvPath, (uint8_t*) psvPath, 0x40) < 0 || psvPath[0x3C] != 0x01) + 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); + add_vmc_import_saves(save->codes, save->path, PSV_SAVES_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_COMMAND, CHAR_ICON_WARN " Delete Save Game", CMD_DELETE_VMCSAVE); + list_append(save->codes, cmd); + + cmd = _createCmdCode(PATCH_NULL, "----- " UTF8_CHAR_STAR " Save Game 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); +} + static void add_vmc2_import_saves(list_t* list, const char* path, const char* folder) { code_entry_t * cmd; @@ -1959,6 +2089,106 @@ list_t * ReadOnlineList(const char* urlPath) return list; } +static void add_vmp_commands(save_entry_t* save) +{ + 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); + + cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export Memory Card to .VM1", CMD_EXP_PS1_VM1); + list_append(save->codes, cmd); + + cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export Memory Card to .VMP", CMD_EXP_PS1_VMP); + list_append(save->codes, cmd); + + return; +} + +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->title_id = strdup("VMC"); + item->codes = list_alloc(); + //bulk management hack + item->dir_name = malloc(sizeof(void**)); + ((void**)item->dir_name)[0] = list; + + 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_VMC); + 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_VMC); + 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"); + item->path = strdup(FAKE_USB_PATH); + item->title_id = strdup("HDD"); + item->dir_name = strdup(userPath); + item->type = FILE_TYPE_MENU; + list_append(list, item); + + for (int i = 0; i <= MAX_USB_DEVICES; i++) + { + snprintf(filePath, sizeof(filePath), USB_PATH, i); + if (i && dir_exists(filePath) != SUCCESS) + continue; + + item = _createSaveEntry(SAVE_FLAG_PS1, CHAR_ICON_COPY " Import Saves to Virtual Card"); + asprintf(&item->path, USB_PATH, i); + asprintf(&item->title_id, "USB %d", i); + 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->title_id = strdup(mcdata[i].saveProdCode); + //hack to keep the save block + asprintf(&item->dir_name, "%c%s", i, mcdata[i].saveName); + asprintf(&item->path, "%s\n%s", userPath, mcdata[i].saveName); + free(tmp); + + LOG("[%s] F(%X) name '%s'", item->title_id, item->flags, item->name); + list_append(list, item); + } + + return list; +} + list_t * ReadVmc2List(const char* userPath) { char filePath[256]; @@ -2010,9 +2240,9 @@ list_t * ReadVmc2List(const char* userPath) cmd = _createCmdCode(PATCH_COMMAND, CHAR_ICON_COPY " Export Memory Card to .VMC format (No ECC)", CMD_CODE_NULL); cmd->file = strdup(strrchr(userPath, '/')+1); cmd->options_count = 1; - cmd->options = _createOptions(3, "Save .VMC Memory Card to USB", CMD_EXP_VM2_RAW); + cmd->options = _createOptions(3, "Save .VMC Memory Card to USB", CMD_EXP_PS2_RAW); asprintf(&cmd->options->name[2], "Save .VMC Memory Card to HDD"); - asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VM2_RAW, STORAGE_HDD); + asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_PS2_RAW, STORAGE_HDD); list_append(item->codes, cmd); item = _createSaveEntry(SAVE_FLAG_PS2, CHAR_ICON_COPY " Import Saves to Virtual Card");