From d241a3010c28fa7f29f8ac47f2f54eeacd61ea85 Mon Sep 17 00:00:00 2001 From: Damian Parrino Date: Wed, 13 Mar 2024 09:02:16 -0300 Subject: [PATCH] ps2 vmc management fix #144 --- data/tag_vmc.png | Bin 0 -> 2946 bytes include/ps2mc.h | 2 +- include/saves.h | 12 ++ source/exec_cmd.c | 81 ++++++++- source/main.c | 13 ++ source/mcio.c | 45 +++-- source/menu_main.c | 28 +++ source/psv_ps2.c | 30 +-- source/psv_resign.c | 433 +++++++++++++++++++++++++++++++++++++++++++- source/saves.c | 258 +++++++++++++++++++++++++- 10 files changed, 859 insertions(+), 43 deletions(-) create mode 100644 data/tag_vmc.png diff --git a/data/tag_vmc.png b/data/tag_vmc.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1b1702cce0151c897fcb5eae638dce6d1c9f42 GIT binary patch literal 2946 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 C06KR7 literal 0 HcmV?d00001 diff --git a/include/ps2mc.h b/include/ps2mc.h index ba6edd6..ff99f5a 100644 --- a/include/ps2mc.h +++ b/include/ps2mc.h @@ -35,7 +35,7 @@ typedef struct // size = 512 uint32_t unused2[7]; // 36 char name[32]; // 64 uint8_t unused3[416]; // 96 -} ps2_McFsEntry; +} McFsEntry; typedef struct { diff --git a/include/saves.h b/include/saves.h index 464a8d0..448fae4 100644 --- a/include/saves.h +++ b/include/saves.h @@ -109,6 +109,8 @@ enum cmd_code_enum CMD_RESIGN_VMP, CMD_EXP_SAVES_VMC1, CMD_EXP_ALL_SAVES_VMC1, + CMD_EXP_SAVES_VMC2, + CMD_EXP_ALL_SAVES_VMC2, // Export commands CMD_EXP_EXDATA_USB, @@ -119,6 +121,7 @@ enum cmd_code_enum CMD_EXP_PSV_PSU, CMD_EXP_VM2_RAW, CMD_EXP_VMC1SAVE, + CMD_EXP_VMC2SAVE, CMD_EXP_VMP2MCR, // Import commands @@ -127,6 +130,7 @@ enum cmd_code_enum CMD_IMP_PS2_CONFIG, CMD_IMP_PS2VMC_USB, CMD_IMP_VMC1SAVE, + CMD_IMP_VMC2SAVE, CMD_IMP_MCR2VMP, CMD_CREATE_ACT_DAT, CMD_EXTRACT_ARCHIVE, @@ -167,6 +171,7 @@ enum save_type_enum FILE_TYPE_MCS, // PS2 File Types + FILE_TYPE_PS2, FILE_TYPE_PSU, FILE_TYPE_MAX, FILE_TYPE_CBS, @@ -261,6 +266,7 @@ 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); int sortSaveList_Compare(const void* A, const void* B); @@ -272,6 +278,7 @@ 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); void http_end(void); @@ -324,4 +331,9 @@ int ps2_xps2psv(const char *save, const char *psv_path); int ps1_psv2mcs(const char* save, const char* mcs_path); int ps2_psv2psu(const char *save, const char* psu_path); +int vmc_export_psv(const char* save, const char* out_path); +int vmc_export_psu(const char* path, const char* output); +int vmc_import_psv(const char *input); +int vmc_import_psu(const char *input); + char* sjis2utf8(char* input); diff --git a/source/exec_cmd.c b/source/exec_cmd.c index 45208c8..54a8256 100644 --- a/source/exec_cmd.c +++ b/source/exec_cmd.c @@ -677,8 +677,11 @@ 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) + if (item->type == FILE_TYPE_PS1) (saveSingleSave(outPath, save->path[strlen(save->path)+1], PS1SAVE_PSV) ? done++ : err_count++); + + if (item->type == FILE_TYPE_PS2) + (vmc_export_psv(item->dir_name, outPath) ? done++ : err_count++); } end_progress_bar(); @@ -1590,6 +1593,66 @@ static void export_vmp2mcr(const save_entry_t* save) show_message("Error exporting memory card:\n%s", save->path); } +static void export_vmc2save(const save_entry_t* save, int type, int dst_id) +{ + int ret = 0; + char outPath[256]; + struct tm t; + + _set_dest_path(outPath, dst_id, (type == FILE_TYPE_PSV) ? PSV_SAVES_PATH_USB : PS2_IMP_PATH_USB); + mkdirs(outPath); + if (type != FILE_TYPE_PSV) + { + // build file path + gmtime_r(&(time_t){time(NULL)}, &t); + sprintf(strrchr(outPath, '/'), "/%s_%d-%02d-%02d_%02d%02d%02d.psu", save->title_id, + t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + } + + switch (type) + { + case FILE_TYPE_PSV: + ret = vmc_export_psv(save->dir_name, outPath); + break; + + case FILE_TYPE_PSU: + ret = vmc_export_psu(save->dir_name, outPath); + break; + + default: + break; + } + + if (ret) + show_message("Save successfully exported to:\n%s", outPath); + else + show_message("Error exporting save:\n%s", save->path); +} + +static void import_save2vmc(const char* src, int type) +{ + int ret = 0; + + switch (type) + { + case FILE_TYPE_PSV: + ret = vmc_import_psv(src); + break; + + case FILE_TYPE_PSU: + ret = vmc_import_psu(src); + break; + + default: + break; + } + + if (ret) + show_message("Successfully imported to VMC:\n%s", src); + else + show_message("Error importing save:\n%s", src); +} + void execCodeCommand(code_entry_t* code, const char* codecmd) { switch (codecmd[0]) @@ -1784,6 +1847,22 @@ void execCodeCommand(code_entry_t* code, const char* codecmd) code->activated = 0; break; + case CMD_EXP_SAVES_VMC2: + case CMD_EXP_ALL_SAVES_VMC2: + exportAllSavesVMC(selected_entry, codecmd[1], codecmd[0] == CMD_EXP_ALL_SAVES_VMC2); + code->activated = 0; + break; + + case CMD_EXP_VMC2SAVE: + export_vmc2save(selected_entry, code->options[0].id, codecmd[1]); + code->activated = 0; + break; + + case CMD_IMP_VMC2SAVE: + import_save2vmc(code->file, codecmd[1]); + code->activated = 0; + break; + case CMD_IMP_MCR2VMP: import_mcr2vmp(selected_entry, code->options[0].name[code->options[0].sel]); code->activated = 0; diff --git a/source/main.c b/source/main.c index 7bee540..05aeff2 100644 --- a/source/main.c +++ b/source/main.c @@ -178,6 +178,19 @@ save_list_t vmc1_saves = { .UpdatePath = &update_vmc_path, }; +/* +* PS2 VMC list +*/ +save_list_t vmc2_saves = { + .icon_id = cat_usb_png_index, + .title = "PS2 Virtual Memory Card", + .list = NULL, + .path = "", + .ReadList = &ReadVmc2List, + .ReadCodes = &ReadVmc2Codes, + .UpdatePath = &update_vmc_path, +}; + static void release_all(void) { if(inited & INITED_CALLBACK) diff --git a/source/mcio.c b/source/mcio.c index d8b3eef..7da3677 100644 --- a/source/mcio.c +++ b/source/mcio.c @@ -346,8 +346,8 @@ static int Card_GetSpecs(uint16_t *pagesize, uint16_t *blocksize, int32_t *cards // check for non-ECC images *flags = mcdi->cardflags & ((vmc_size % 0x800000 == 0) ? ~CF_USE_ECC : 0xFF); - *pagesize = read_le_uint16((uint8_t*)&mcdi->pagesize); - *blocksize = read_le_uint16((uint8_t*)&mcdi->blocksize); + append_le_uint16((uint8_t*)pagesize, read_le_uint16((uint8_t*)&mcdi->pagesize)); + append_le_uint16((uint8_t*)blocksize, read_le_uint16((uint8_t*)&mcdi->blocksize)); *cardsize = read_le_uint32((uint8_t*)&mcdi->clusters_per_card) * read_le_uint16((uint8_t*)&mcdi->pages_per_cluster); return sceMcResSucceed; @@ -2585,8 +2585,9 @@ static int Card_FileOpen(const char *filename, int flags) fh->fsindex = cacheDir.fsindex; if (r == 0) { + uint16_t dircache_mode = read_le_uint16((uint8_t *)&mcio_dircache[1].mode); - if ((wrflag != 0) && ((mcio_dircache[1].mode & sceMcFileAttrWriteable) == 0)) + if ((wrflag != 0) && ((dircache_mode & sceMcFileAttrWriteable) == 0)) return sceMcResDeniedPermit; r = Card_ReadDirEntry(cacheDir.cluster, 0, &fse2); @@ -2596,20 +2597,20 @@ static int Card_FileOpen(const char *filename, int flags) fh->parent_cluster = read_le_uint32((uint8_t *)&fse2->cluster); fh->parent_fsindex = read_le_uint32((uint8_t *)&fse2->dir_entry); - if ((mcio_dircache[1].mode & sceMcFileAttrSubdir) != 0) { - if ((mcio_dircache[1].mode & sceMcFileAttrReadable) == 0) + if ((dircache_mode & sceMcFileAttrSubdir) != 0) { + if ((dircache_mode & sceMcFileAttrReadable) == 0) return sceMcResDeniedPermit; if ((flags & sceMcFileAttrSubdir) == 0) return sceMcResNotFile; - fh->freeclink = mcio_dircache[1].cluster; + fh->freeclink = read_le_uint32((uint8_t *)&mcio_dircache[1].cluster); fh->rdflag = 0; fh->wrflag = 0; fh->unknown1 = 0; fh->drdflag = 1; fh->status = 1; - fh->filesize = mcio_dircache[1].length; + fh->filesize = read_le_uint32((uint8_t *)&mcio_dircache[1].length); fh->clink = fh->freeclink; return fd; @@ -2641,8 +2642,8 @@ static int Card_FileOpen(const char *filename, int flags) cacheDir.maxent = cacheDir.fsindex; } else { - fh->freeclink = mcio_dircache[1].cluster; - fh->filesize = mcio_dircache[1].length; + fh->freeclink = read_le_uint32((uint8_t *)&mcio_dircache[1].cluster); + fh->filesize = read_le_uint32((uint8_t *)&mcio_dircache[1].length); fh->clink = fh->freeclink; if (fh->rdflag != 0) @@ -2651,7 +2652,7 @@ static int Card_FileOpen(const char *filename, int flags) fh->rdflag = 0; if (fh->wrflag != 0) - fh->wrflag = (mcio_dircache[1].mode >> 1) & sceMcFileAttrReadable; + fh->wrflag = (dircache_mode >> 1) & sceMcFileAttrReadable; else fh->wrflag = 0; @@ -2670,20 +2671,22 @@ static int Card_FileOpen(const char *filename, int flags) return r; memcpy((void *)&mcio_dircache[2], (void *)fse1, sizeof(struct MCFsEntry)); + uint32_t dircache_length = read_le_uint32((uint8_t *)&mcio_dircache[2].length); + uint32_t dircache_cluster = read_le_uint32((uint8_t *)&mcio_dircache[2].cluster); i = -1; - if (mcio_dircache[2].length == (uint32_t) cacheDir.maxent) { + if (dircache_length == (uint32_t) cacheDir.maxent) { int32_t cluster_size = (int32_t)read_le_uint32((uint8_t *)&mcdi->cluster_size); - fsindex = mcio_dircache[2].length / (cluster_size >> 9); - fsoffset = mcio_dircache[2].length % (cluster_size >> 9); + fsindex = dircache_length / (cluster_size >> 9); + fsoffset = dircache_length % (cluster_size >> 9); if (fsoffset == 0) { - fat_index = mcio_dircache[2].cluster; + fat_index = dircache_cluster; i = fsindex; - if ((mcio_dircache[2].cluster == 0) && (i >= 2)) { + if ((dircache_cluster == 0) && (i >= 2)) { if (read_le_uint32((uint8_t *)&mcio_fatcache.entry[i-1]) >= 0) { fat_index = read_le_uint32((uint8_t *)&mcio_fatcache.entry[i-1]); i = 1; @@ -2727,7 +2730,8 @@ static int Card_FileOpen(const char *filename, int flags) i = -1; - mcio_dircache[2].length++; + dircache_length++; + append_le_uint32((uint8_t *)&mcio_dircache[2].length, dircache_length); } do { @@ -2754,7 +2758,7 @@ static int Card_FileOpen(const char *filename, int flags) mcio_getmcrtime(&mcio_dircache[2].modified); - r = Card_ReadDirEntry(mcio_dircache[2].cluster, cacheDir.maxent, &fse2); + r = Card_ReadDirEntry(dircache_cluster, cacheDir.maxent, &fse2); if (r != sceMcResSucceed) return r; @@ -2781,7 +2785,7 @@ static int Card_FileOpen(const char *filename, int flags) append_le_uint32((uint8_t *)&fse2->cluster, mcfree); append_le_uint32((uint8_t *)&fse2->length, 2); - r = Card_CreateDirEntry(mcio_dircache[2].cluster, cacheDir.maxent, mcfree, (struct sceMcStDateTime *)&fse2->created); + r = Card_CreateDirEntry(dircache_cluster, cacheDir.maxent, mcfree, (struct sceMcStDateTime *)&fse2->created); if (r != sceMcResSucceed) return -46; @@ -2807,7 +2811,7 @@ static int Card_FileOpen(const char *filename, int flags) append_le_uint16((uint8_t *)&fse2->mode, fmode); append_le_uint32((uint8_t *)&fse2->cluster, -1); - fh->cluster = mcio_dircache[2].cluster; + fh->cluster = dircache_cluster; fh->status = 1; fh->fsindex = cacheDir.maxent; @@ -3462,8 +3466,9 @@ int mcio_mcGetInfo(int *pagesize, int *blocksize, int *cardsize, int *cardflags) if (Card_GetSpecs(&_pagesize, &_blocksize, &_cardsize, &_cardflags) != sceMcResSucceed) return r; + _pagesize = read_le_uint16((uint8_t *)&_pagesize); *pagesize = (int)_pagesize; - *blocksize = (int)_blocksize; + *blocksize = (int)read_le_uint16((uint8_t *)&_blocksize); *cardsize = (int)_cardsize * _pagesize; *cardflags = (int)_cardflags; diff --git a/source/menu_main.c b/source/menu_main.c index 04b29b9..3e3f9a9 100644 --- a/source/menu_main.c +++ b/source/menu_main.c @@ -10,6 +10,7 @@ #include "menu_gui.h" #include "ttf_render.h" #include "ps1card.h" +#include "mcio.h" #include #include @@ -20,6 +21,7 @@ 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; extern padData paddata[]; @@ -184,6 +186,15 @@ static void SetMenu(int id) saveMemoryCard(vmc1_saves.path, 0, 0); } + case MENU_PS2VMC_SAVES: + if (id == MENU_MAIN_SCREEN) + { + LOG("Saving PS2 VMC changes..."); + UnloadGameList(vmc2_saves.list); + vmc2_saves.list = NULL; + mcio_vmcFinish(); + } + case MENU_MAIN_SCREEN: //Main Menu case MENU_TROPHIES: case MENU_USB_SAVES: //USB Saves Menu @@ -263,6 +274,14 @@ static void SetMenu(int id) Draw_UserCheatsMenu_Ani(&vmc1_saves); break; + case MENU_PS2VMC_SAVES: //Trophies Menu + if (!vmc2_saves.list && !ReloadUserSaves(&vmc2_saves)) + return; + + if (apollo_config.doAni) + Draw_UserCheatsMenu_Ani(&vmc2_saves); + break; + case MENU_CREDITS: //About Menu if (apollo_config.doAni) Draw_AboutMenu_Ani(); @@ -414,6 +433,12 @@ static void doSaveMenu(save_list_t * save_list) strncpy(vmc1_saves.path, selected_entry->path, sizeof(vmc1_saves.path)); SetMenu(MENU_PS1VMC_SAVES); } + else + { + strncpy(vmc2_saves.path, selected_entry->path, sizeof(vmc2_saves.path)); + SetMenu(MENU_PS2VMC_SAVES); + } + return; } @@ -915,5 +940,8 @@ void drawScene(void) doSaveMenu(&vmc1_saves); break; + case MENU_PS2VMC_SAVES: //PS2 VMC Menu + doSaveMenu(&vmc2_saves); + break; } } diff --git a/source/psv_ps2.c b/source/psv_ps2.c index a98a668..ed705b1 100644 --- a/source/psv_ps2.c +++ b/source/psv_ps2.c @@ -312,14 +312,14 @@ int ps2_psu2psv(const char *save, const char* psv_path) int numFiles, next, i; char dstName[256]; u8 *data; - ps2_McFsEntry entry; + McFsEntry entry; psuFile = fopen(save, "rb"); if(!psuFile) return 0; // Read main directory entry - fread(&entry, 1, sizeof(ps2_McFsEntry), psuFile); + fread(&entry, 1, sizeof(McFsEntry), psuFile); numFiles = ES32(entry.length) - 2; get_psv_filename(dstName, psv_path, entry.name); @@ -349,12 +349,12 @@ int ps2_psu2psv(const char *save, const char* psv_path) write_psv_header(psvFile, 2); // Skip "." and ".." - fseek(psuFile, sizeof(ps2_McFsEntry)*2, SEEK_CUR); + fseek(psuFile, sizeof(McFsEntry)*2, SEEK_CUR); // Find the icon.sys (need to know the icons names) for(i = 0; i < numFiles; i++) { - fread(&entry, 1, sizeof(ps2_McFsEntry), psuFile); + fread(&entry, 1, sizeof(McFsEntry), psuFile); entry.length = ES32(entry.length); if(strcmp(entry.name, "icon.sys") == 0) @@ -375,7 +375,7 @@ int ps2_psu2psv(const char *save, const char* psv_path) ps2h.displaySize = ES32(ps2h.displaySize); // Skip "." and ".." - fseek(psuFile, sizeof(ps2_McFsEntry)*3, SEEK_SET); + fseek(psuFile, sizeof(McFsEntry)*3, SEEK_SET); // Calculate the start offset for the file's data dataPos = sizeof(psv_header_t) + sizeof(ps2_header_t) + sizeof(ps2_MainDirInfo_t) + sizeof(ps2_FileInfo_t)*numFiles; @@ -385,7 +385,7 @@ int ps2_psu2psv(const char *save, const char* psv_path) // Build the PS2 FileInfo entries for(i = 0; i < numFiles; i++) { - fread(&entry, 1, sizeof(ps2_McFsEntry), psuFile); + fread(&entry, 1, sizeof(McFsEntry), psuFile); ps2fi[i].attribute = ((uint32_t)entry.mode << 16); ps2fi[i].positionInFile = ES32(dataPos); @@ -412,12 +412,12 @@ int ps2_psu2psv(const char *save, const char* psv_path) free(ps2fi); // Skip "." and ".." - fseek(psuFile, sizeof(ps2_McFsEntry)*3, SEEK_SET); + fseek(psuFile, sizeof(McFsEntry)*3, SEEK_SET); // Copy each file entry for(i = 0; i < numFiles; i++) { - fread(&entry, 1, sizeof(ps2_McFsEntry), psuFile); + fread(&entry, 1, sizeof(McFsEntry), psuFile); entry.length = ES32(entry.length); data = malloc(entry.length); @@ -758,7 +758,7 @@ int ps2_psv2psu(const char *save, const char* psu_path) int numFiles, next; char dstName[256]; u8 *data; - ps2_McFsEntry entry; + McFsEntry entry; ps2_MainDirInfo_t ps2md; ps2_FileInfo_t ps2fi; @@ -780,24 +780,24 @@ int ps2_psv2psu(const char *save, const char* psu_path) fread(&ps2md, sizeof(ps2_MainDirInfo_t), 1, psvFile); numFiles = ES32(ps2md.numberOfFilesInDir); - memset(&entry, 0, sizeof(entry)); + memset(&entry, 0, sizeof(McFsEntry)); memcpy(&entry.created, &ps2md.created, sizeof(sceMcStDateTime)); memcpy(&entry.modified, &ps2md.modified, sizeof(sceMcStDateTime)); memcpy(entry.name, ps2md.filename, sizeof(entry.name)); entry.mode = (ps2md.attribute >> 16); entry.length = ps2md.numberOfFilesInDir; - fwrite(&entry, sizeof(entry), 1, psuFile); + fwrite(&entry, sizeof(McFsEntry), 1, psuFile); // "." memset(entry.name, 0, sizeof(entry.name)); strcpy(entry.name, "."); entry.length = 0; - fwrite(&entry, sizeof(entry), 1, psuFile); + fwrite(&entry, sizeof(McFsEntry), 1, psuFile); numFiles--; // ".." strcpy(entry.name, ".."); - fwrite(&entry, sizeof(entry), 1, psuFile); + fwrite(&entry, sizeof(McFsEntry), 1, psuFile); numFiles--; while (numFiles > 0) @@ -805,13 +805,13 @@ int ps2_psv2psu(const char *save, const char* psu_path) fread(&ps2fi, sizeof(ps2_FileInfo_t), 1, psvFile); dataPos = ftell(psvFile); - memset(&entry, 0, sizeof(entry)); + memset(&entry, 0, sizeof(McFsEntry)); memcpy(&entry.created, &ps2fi.created, sizeof(sceMcStDateTime)); memcpy(&entry.modified, &ps2fi.modified, sizeof(sceMcStDateTime)); memcpy(entry.name, ps2fi.filename, sizeof(entry.name)); entry.mode = (ps2fi.attribute >> 16); entry.length = ps2fi.filesize; - fwrite(&entry, sizeof(entry), 1, psuFile); + fwrite(&entry, sizeof(McFsEntry), 1, psuFile); ps2fi.positionInFile = ES32(ps2fi.positionInFile); ps2fi.filesize = ES32(ps2fi.filesize); diff --git a/source/psv_resign.c b/source/psv_resign.c index 6871a6e..2c1352c 100644 --- a/source/psv_resign.c +++ b/source/psv_resign.c @@ -14,6 +14,7 @@ #include "types.h" #include "util.h" #include "ps2mc.h" +#include "mcio.h" #include "shiftjis.h" #define PSV_SEED_OFFSET 0x8 @@ -153,11 +154,6 @@ int psv_resign(const char *src_psv) free(input); return 0; } - - LOG("Old signature: "); - for(int i = 0; i < 0x14; i++ ) { - LOG("%02X ", input[PSV_HASH_OFFSET + i]); - } generateHash(input, input + PSV_SEED_OFFSET, input + PSV_HASH_OFFSET, sz, input[PSV_TYPE_OFFSET]); @@ -488,3 +484,430 @@ char* sjis2utf8(char* input) output[indexOutput] = 0; return output; } + +int vmc_import_psv(const char *input) +{ + int fd, r; + uint8_t *p; + size_t filesize; + char filepath[256]; + struct io_dirent entry; + ps2_MainDirInfo_t *ps2md; + ps2_FileInfo_t *ps2fi; + + if (read_buffer(input, &p, &filesize) < 0) + return 0; + + if (memcmp(PSV_MAGIC, p, 4) != 0 || p[0x3C] != 0x02) { + LOG("Not a PS2 .PSV file"); + free(p); + return 0; + } + + ps2md = (ps2_MainDirInfo_t *)&p[0x68]; + ps2fi = (ps2_FileInfo_t *)&ps2md[1]; + + LOG("Writing data to: '/%s'...", ps2md->filename); + + r = mcio_mcMkDir(ps2md->filename); + if (r < 0) + LOG("Error: can't create directory '%s'... (%d)", ps2md->filename, r); + else + mcio_mcClose(r); + + for (int total = (read_le_uint32((uint8_t*)&ps2md->numberOfFilesInDir) - 2), i = 0; i < total; i++, ps2fi++) + { + filesize = read_le_uint32((uint8_t*)&ps2fi->filesize); + + snprintf(filepath, sizeof(filepath), "%s/%s", ps2md->filename, ps2fi->filename); + LOG("Adding %-48s | %8d bytes", filepath, filesize); + + fd = mcio_mcOpen(filepath, sceMcFileCreateFile | sceMcFileAttrWriteable | sceMcFileAttrFile); + if (fd < 0) { + free(p); + return 0; + } + + r = mcio_mcWrite(fd, &p[read_le_uint32((uint8_t*)&ps2fi->positionInFile)], filesize); + mcio_mcClose(fd); + if (r != filesize) { + free(p); + return 0; + } + + mcio_mcStat(filepath, &entry); + memcpy(&entry.stat.ctime, &ps2fi->created, sizeof(struct sceMcStDateTime)); + memcpy(&entry.stat.mtime, &ps2fi->modified, sizeof(struct sceMcStDateTime)); + entry.stat.mode = read_le_uint32((uint8_t*)&ps2fi->attribute); + mcio_mcSetStat(filepath, &entry); + } + + mcio_mcStat(ps2md->filename, &entry); + memcpy(&entry.stat.ctime, &ps2md->created, sizeof(struct sceMcStDateTime)); + memcpy(&entry.stat.mtime, &ps2md->modified, sizeof(struct sceMcStDateTime)); + entry.stat.mode = read_le_uint32((uint8_t*)&ps2md->attribute); + mcio_mcSetStat(ps2md->filename, &entry); + + free(p); + LOG("Save succesfully imported: %s", input); + + return 1; +} + +int vmc_import_psu(const char *input) +{ + int fd, r; + char filepath[256]; + struct io_dirent entry; + McFsEntry psu_entry, file_entry; + + FILE *fh = fopen(input, "rb"); + if (fh == NULL) + return 0; + + fseek(fh, 0, SEEK_END); + r = ftell(fh); + + if (!r || r % 512) { + printf("Not a .PSU file"); + fclose(fh); + return 0; + } + + LOG("Reading file: '%s'...", input); + fseek(fh, 0, SEEK_SET); + + r = fread(&psu_entry, sizeof(McFsEntry), 1, fh); + if (!r) { + fclose(fh); + return 0; + } + + // Skip "." and ".." + fseek(fh, sizeof(McFsEntry)*2, SEEK_CUR); + + LOG("Writing data to: '/%s'...", psu_entry.name); + + r = mcio_mcMkDir(psu_entry.name); + if (r < 0) + LOG("Error: can't create directory '%s'... (%d)", psu_entry.name, r); + else + mcio_mcClose(r); + + for (int total = (ES32(psu_entry.length) - 2), i = 0; i < total; i++) + { + fread(&file_entry, 1, sizeof(McFsEntry), fh); + file_entry.length = ES32(file_entry.length); + + snprintf(filepath, sizeof(filepath), "%s/%s", psu_entry.name, file_entry.name); + LOG("Adding %-48s | %8d bytes", filepath, file_entry.length); + + fd = mcio_mcOpen(filepath, sceMcFileCreateFile | sceMcFileAttrWriteable | sceMcFileAttrFile); + if (fd < 0) + return 0; + + uint8_t *p = malloc(file_entry.length); + if (!p) + { + mcio_mcClose(fd); + return 0; + } + + fread(p, 1, file_entry.length, fh); + r = mcio_mcWrite(fd, p, file_entry.length); + mcio_mcClose(fd); + free(p); + + if (r != (int)file_entry.length) + return 0; + + mcio_mcStat(filepath, &entry); + memcpy(&entry.stat.ctime, &file_entry.created, sizeof(struct sceMcStDateTime)); + memcpy(&entry.stat.mtime, &file_entry.modified, sizeof(struct sceMcStDateTime)); + entry.stat.mode = ES16(file_entry.mode); + mcio_mcSetStat(filepath, &entry); + + r = 1024 - (file_entry.length % 1024); + if(r < 1024) + fseek(fh, r, SEEK_CUR); + } + + mcio_mcStat(psu_entry.name, &entry); + memcpy(&entry.stat.ctime, &psu_entry.created, sizeof(struct sceMcStDateTime)); + memcpy(&entry.stat.mtime, &psu_entry.modified, sizeof(struct sceMcStDateTime)); + entry.stat.mode = ES16(psu_entry.mode); + mcio_mcSetStat(psu_entry.name, &entry); + + return 1; +} + +int vmc_export_psv(const char* save, const char* out_path) +{ + FILE *psvFile; + ps2_header_t ps2h; + ps2_FileInfo_t *ps2fi; + ps2_MainDirInfo_t ps2md; + ps2_IconSys_t iconsys; + uint32_t dataPos, i; + char filePath[256]; + uint8_t *data; + int r, dd, fd; + struct io_dirent dirent; + + snprintf(filePath, sizeof(filePath), "%s/icon.sys", save); + fd = mcio_mcOpen(filePath, sceMcFileAttrReadable | sceMcFileAttrFile); + if (fd < 0) + { + LOG("Error! '%s' not found", filePath); + return 0; + } + + r = mcio_mcRead(fd, &iconsys, sizeof(ps2_IconSys_t)); + mcio_mcClose(fd); + + if (r != sizeof(ps2_IconSys_t)) + return 0; + + get_psv_filename(filePath, out_path, save); + + LOG("Export %s -> %s ...", save, filePath); + psvFile = fopen(filePath, "wb"); + if(!psvFile) + return 0; + + write_psv_header(psvFile, 2); + + memset(&ps2h, 0, sizeof(ps2_header_t)); + memset(&ps2md, 0, sizeof(ps2_MainDirInfo_t)); + + // Read main directory entry + mcio_mcStat(save, &dirent); + + // PSV root directory values + ps2h.numberOfFiles = dirent.stat.size - 2; + ps2md.attribute = ES32(dirent.stat.mode); + ps2md.numberOfFilesInDir = ES32(dirent.stat.size); + memcpy(&ps2md.created, &dirent.stat.ctime, sizeof(sceMcStDateTime)); + memcpy(&ps2md.modified, &dirent.stat.mtime, sizeof(sceMcStDateTime)); + memcpy(ps2md.filename, dirent.name, sizeof(ps2md.filename)); + + dd = mcio_mcDopen(save); + if (dd < 0) + { + fclose(psvFile); + return 0; + } + + // Calculate the start offset for the file's data + dataPos = sizeof(psv_header_t) + sizeof(ps2_header_t) + sizeof(ps2_MainDirInfo_t) + sizeof(ps2_FileInfo_t)*ps2h.numberOfFiles; + ps2fi = malloc(sizeof(ps2_FileInfo_t)*ps2h.numberOfFiles); + + // Build the PS2 FileInfo entries + i = 0; + do { + r = mcio_mcDread(dd, &dirent); + if (r && (strcmp(dirent.name, ".")) && (strcmp(dirent.name, ".."))) + { + snprintf(filePath, sizeof(filePath), "%s/%s", save, dirent.name); + mcio_mcStat(filePath, &dirent); + + ps2fi[i].attribute = ES32(dirent.stat.mode); + ps2fi[i].positionInFile = ES32(dataPos); + ps2fi[i].filesize = ES32(dirent.stat.size); + memcpy(&ps2fi[i].created, &dirent.stat.ctime, sizeof(sceMcStDateTime)); + memcpy(&ps2fi[i].modified, &dirent.stat.mtime, sizeof(sceMcStDateTime)); + memcpy(ps2fi[i].filename, dirent.name, sizeof(ps2fi[i].filename)); + + dataPos += dirent.stat.size; + ps2h.displaySize += dirent.stat.size; + + if (strcmp(ps2fi[i].filename, iconsys.IconName) == 0) + { + ps2h.icon1Size = ps2fi[i].filesize; + ps2h.icon1Pos = ps2fi[i].positionInFile; + } + + if (strcmp(ps2fi[i].filename, iconsys.copyIconName) == 0) + { + ps2h.icon2Size = ps2fi[i].filesize; + ps2h.icon2Pos = ps2fi[i].positionInFile; + } + + if (strcmp(ps2fi[i].filename, iconsys.deleteIconName) == 0) + { + ps2h.icon3Size = ps2fi[i].filesize; + ps2h.icon3Pos = ps2fi[i].positionInFile; + } + + if(strcmp(ps2fi[i].filename, "icon.sys") == 0) + { + ps2h.sysSize = ps2fi[i].filesize; + ps2h.sysPos = ps2fi[i].positionInFile; + } + i++; + } + } while (r); + + mcio_mcDclose(dd); + + LOG("%d Files: %8d Total bytes", i, ps2h.displaySize); + ps2h.displaySize = ES32(ps2h.displaySize); + ps2h.numberOfFiles = ES32(ps2h.numberOfFiles); + + fwrite(&ps2h, sizeof(ps2_header_t), 1, psvFile); + fwrite(&ps2md, sizeof(ps2_MainDirInfo_t), 1, psvFile); + fwrite(ps2fi, sizeof(ps2_FileInfo_t), i, psvFile); + free(ps2fi); + + // Write the file's data + dd = mcio_mcDopen(save); + + ps2h.numberOfFiles = i; + i = 0; + do { + r = mcio_mcDread(dd, &dirent); + if (r && (strcmp(dirent.name, ".")) && (strcmp(dirent.name, ".."))) + { + snprintf(filePath, sizeof(filePath), "%s/%s", save, dirent.name); + LOG("(%d/%d) Add '%s'", ++i, ps2h.numberOfFiles, filePath); + + fd = mcio_mcOpen(filePath, sceMcFileAttrReadable | sceMcFileAttrFile); + if (fd < 0) + { + i = 0; + break; + } + + data = malloc(dirent.stat.size); + if (!data) + { + i = 0; + break; + } + + r = mcio_mcRead(fd, data, dirent.stat.size); + mcio_mcClose(fd); + + if (r != (int)dirent.stat.size) + { + i = 0; + free(data); + break; + } + + fwrite(data, 1, dirent.stat.size, psvFile); + free(data); + } + } while (r); + + mcio_mcDclose(dd); + + fclose(psvFile); + get_psv_filename(filePath, out_path, save); + psv_resign(filePath); + + return (i > 0); +} + +int vmc_export_psu(const char* path, const char* output) +{ + int r, fd, dd; + uint32_t total, i = 0; + struct io_dirent dirent; + McFsEntry entry; + char filepath[256]; + + LOG("Exporting '%s' to %s...", path, output); + + dd = mcio_mcDopen(path); + if (dd < 0) + return 0; + + FILE *fh = fopen(output, "wb"); + if (fh == NULL) { + mcio_mcDclose(dd); + return 0; + } + + // Read main directory entry + mcio_mcStat(path, &dirent); + + memset(&entry, 0, sizeof(McFsEntry)); + memcpy(&entry.created, &dirent.stat.ctime, sizeof(sceMcStDateTime)); + memcpy(&entry.modified, &dirent.stat.mtime, sizeof(sceMcStDateTime)); + memcpy(entry.name, dirent.name, sizeof(entry.name)); + entry.mode = ES32(dirent.stat.mode) >> 16; + entry.length = ES32(dirent.stat.size); + fwrite(&entry, sizeof(McFsEntry), 1, fh); + total = dirent.stat.size - 2; + + // "." + memset(entry.name, 0, sizeof(entry.name)); + strncpy(entry.name, ".", sizeof(entry.name)); + entry.length = 0; + fwrite(&entry, sizeof(McFsEntry), 1, fh); + + // ".." + strncpy(entry.name, "..", sizeof(entry.name)); + fwrite(&entry, sizeof(McFsEntry), 1, fh); + + do { + r = mcio_mcDread(dd, &dirent); + if (r && (strcmp(dirent.name, ".")) && (strcmp(dirent.name, ".."))) + { + snprintf(filepath, sizeof(filepath), "%s/%s", path, dirent.name); + LOG("Adding (%d/%d) %-40s | %8d bytes", ++i, total, filepath, dirent.stat.size); + + mcio_mcStat(filepath, &dirent); + + memset(&entry, 0, sizeof(McFsEntry)); + memcpy(&entry.created, &dirent.stat.ctime, sizeof(sceMcStDateTime)); + memcpy(&entry.modified, &dirent.stat.mtime, sizeof(sceMcStDateTime)); + memcpy(entry.name, dirent.name, sizeof(entry.name)); + entry.mode = ES32(dirent.stat.mode) >> 16; + entry.length = ES32(dirent.stat.size); + fwrite(&entry, sizeof(McFsEntry), 1, fh); + + fd = mcio_mcOpen(filepath, sceMcFileAttrReadable | sceMcFileAttrFile); + if (fd < 0) { + i = 0; + break; + } + + uint8_t *p = malloc(dirent.stat.size); + if (p == NULL) { + i = 0; + break; + } + + r = mcio_mcRead(fd, p, dirent.stat.size); + mcio_mcClose(fd); + + if (r != (int)dirent.stat.size) { + free(p); + i = 0; + break; + } + + r = fwrite(p, 1, dirent.stat.size, fh); + if (r != (int)dirent.stat.size) { + fclose(fh); + free(p); + i = 0; + break; + } + free(p); + + entry.length = 1024 - (dirent.stat.size % 1024); + while(--entry.length < 1023) + fputc(0xFF, fh); + } + } while (r); + + mcio_mcDclose(dd); + fclose(fh); + + LOG("Save succesfully exported to %s.", output); + + return (i > 0); +} diff --git a/source/saves.c b/source/saves.c index a0449b7..d709aef 100644 --- a/source/saves.c +++ b/source/saves.c @@ -19,6 +19,8 @@ #include "pfd.h" #include "trophy.h" #include "ps1card.h" +#include "ps2mc.h" +#include "mcio.h" #define UTF8_CHAR_STAR "\xE2\x98\x85" @@ -814,7 +816,7 @@ int ReadVmc1Codes(save_entry_t * save) 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); + 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); @@ -846,6 +848,112 @@ int ReadVmc1Codes(save_entry_t * save) return list_count(save->codes); } +static void add_vmc2_import_saves(list_t* list, const char* path, const char* folder) +{ + code_entry_t * cmd; + DIR *d; + struct dirent *dir; + char psvPath[256]; + int type; + + snprintf(psvPath, sizeof(psvPath), "%s%s", path, folder); + d = opendir(psvPath); + + if (!d) + return; + + while ((dir = readdir(d)) != NULL) + { + if (dir->d_type != DT_REG) + continue; + + if (endsWith(dir->d_name, ".PSV")) + { +// toff = 0x80; + type = FILE_TYPE_PSV; + } + else if (endsWith(dir->d_name, ".PSU")) + { +// toff = 0x40; + type = FILE_TYPE_PSU; + } + else continue; + + snprintf(psvPath, sizeof(psvPath), "%s%s%s", path, folder, dir->d_name); + LOG("Reading %s...", psvPath); + +/* + FILE *fp = fopen(psvPath, "rb"); + if (!fp) { + LOG("Unable to open '%s'", psvPath); + continue; + } + fseek(fp, toff, SEEK_SET); + fread(data, 1, sizeof(data), fp); + fclose(fp); +*/ + + cmd = _createCmdCode(PATCH_COMMAND, psvPath, CMD_IMP_VMC2SAVE); + cmd->file = strdup(psvPath); + cmd->codes[1] = type; + sprintf(cmd->name, "%s %s", CHAR_ICON_COPY, dir->d_name); + list_append(list, cmd); + + LOG("[%s] F(%X) name '%s'", cmd->file, cmd->flags, cmd->name+2); + } + + closedir(d); +} + +int ReadVmc2Codes(save_entry_t * save) +{ + code_entry_t * cmd; + + save->codes = list_alloc(); + + if (save->type == FILE_TYPE_MENU) + { + add_vmc2_import_saves(save->codes, save->path, PS2_IMP_PATH_USB); + add_vmc2_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_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 .PSU format", CMD_CODE_NULL); + cmd->options_count = 1; + cmd->options = _createOptions(3, "Export .PSU save to USB", CMD_EXP_VMC2SAVE); + asprintf(&cmd->options->name[2], "Export .PSU save to HDD"); + asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VMC2SAVE, STORAGE_HDD); + cmd->options[0].id = FILE_TYPE_PSU; + 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, "Export .PSV save to USB", CMD_EXP_VMC2SAVE); + asprintf(&cmd->options->name[2], "Export .PSV save to HDD"); + asprintf(&cmd->options->value[2], "%c%c", CMD_EXP_VMC2SAVE, STORAGE_HDD); + cmd->options[0].id = FILE_TYPE_PSV; + list_append(save->codes, cmd); + + LOG("Loaded %ld codes", list_count(save->codes)); + + return list_count(save->codes); +} + /* * Function: ReadOnlineSaves() * File: saves.c @@ -1509,6 +1617,43 @@ static void read_savegames(const char* userPath, list_t *list, uint32_t flag) closedir(d); } +static void read_vmc2_files(const char* userPath, list_t *list) +{ + DIR *d; + struct dirent *dir; + save_entry_t *item; + char psvPath[256]; + uint64_t size; + + d = opendir(userPath); + if (!d) + return; + + while ((dir = readdir(d)) != NULL) + { + if (dir->d_type != DT_REG || !(endsWith(dir->d_name, ".VMC") || endsWith(dir->d_name, ".VM2") || endsWith(dir->d_name, ".BIN") || endsWith(dir->d_name, ".PS2"))) + continue; + + snprintf(psvPath, sizeof(psvPath), "%s%s", userPath, dir->d_name); + get_file_size(psvPath, &size); + + LOG("Adding %s...", psvPath); + if (size % 0x840000 != 0 && size % 0x800000 != 0) + continue; + + item = _createSaveEntry(SAVE_FLAG_PS2 | SAVE_FLAG_VMC, dir->d_name); + item->type = FILE_TYPE_VMC; + item->path = strdup(psvPath); + item->title_id = strdup("VMC"); + item->dir_name = strdup(userPath); + + LOG("[%s] F(%X) name '%s'", item->title_id, item->flags, item->name); + list_append(list, item); + } + + closedir(d); +} + static void read_psv_savegames(const char* userPath, list_t *list) { DIR *d; @@ -1745,10 +1890,17 @@ list_t * ReadUserList(const char* userPath) snprintf(savePath, sizeof(savePath), "%s%s", userPath, VMC_PS1_PATH_USB); read_vmc1_files(savePath, list); + + snprintf(savePath, sizeof(savePath), "%s%s", userPath, VMC_PS2_PATH_USB); + read_vmc2_files(savePath, list); + + snprintf(savePath, sizeof(savePath), "%s%s", userPath, "VMC/"); + read_vmc2_files(savePath, list); } else { read_vmc1_files(VMC_PS2_PATH_HDD, list); + read_vmc2_files(VMC_PS2_PATH_HDD, list); } return list; @@ -1931,6 +2083,110 @@ list_t * ReadVmc1List(const char* userPath) return list; } +list_t * ReadVmc2List(const char* userPath) +{ + char filePath[256]; + save_entry_t *item; + code_entry_t *cmd; + list_t *list; + ps2_IconSys_t iconsys; + int r, dd, fd; + + r = mcio_vmcInit(userPath); + if (r < 0) + { + LOG("Error: no PS2 Memory Card detected! (%d)", r); + return NULL; + } + + list = list_alloc(); + + item = _createSaveEntry(SAVE_FLAG_PS2, 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; + + 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_VMC2); + 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_VMC2); + list_append(item->codes, cmd); + list_append(list, item); + + item = _createSaveEntry(SAVE_FLAG_PS2, CHAR_ICON_COPY " Import Saves to Virtual Card"); + if (strncmp(userPath, USB_PATH, 8) == 0) + { + item->path = strdup(userPath); + 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->type = FILE_TYPE_MENU; + list_append(list, item); + + dd = mcio_mcDopen("/"); + if (dd < 0) + { + LOG("mcio Dopen Error %d", dd); + return list; + } + + struct io_dirent dirent; + + do { + r = mcio_mcDread(dd, &dirent); + if ((r) && (strcmp(dirent.name, ".")) && (strcmp(dirent.name, ".."))) + { + snprintf(filePath, sizeof(filePath), "%s/icon.sys", dirent.name); + LOG("Reading %s...", filePath); + + fd = mcio_mcOpen(filePath, sceMcFileAttrReadable | sceMcFileAttrFile); + if (fd < 0) { + LOG("Unable to read from '%s'", filePath); + continue; + } + + r = mcio_mcRead(fd, &iconsys, sizeof(ps2_IconSys_t)); + mcio_mcClose(fd); + + if (r != sizeof(ps2_IconSys_t)) + continue; + + if (iconsys.secondLineOffset) + { + iconsys.secondLineOffset = ES16(iconsys.secondLineOffset); + memmove(&iconsys.title[iconsys.secondLineOffset+2], &iconsys.title[iconsys.secondLineOffset], sizeof(iconsys.title) - iconsys.secondLineOffset); + iconsys.title[iconsys.secondLineOffset] = 0x81; + iconsys.title[iconsys.secondLineOffset+1] = 0x50; + } + + char* title = sjis2utf8(iconsys.title); + item = _createSaveEntry(SAVE_FLAG_PS2 | SAVE_FLAG_VMC, title); + item->type = FILE_TYPE_PS2; + item->dir_name = strdup(dirent.name); + asprintf(&item->title_id, "%.10s", dirent.name+2); + asprintf(&item->path, "%s\n%s/\n%s", userPath, dirent.name, iconsys.copyIconName); + free(title); + + LOG("[%s] F(%X) name '%s'", item->title_id, item->flags, item->name); + list_append(list, item); + } + } while (r); + + mcio_mcDclose(dd); + + return list; +} + list_t * ReadTrophyList(const char* userPath) { DIR *d;