From 1460a690a55cbcffc91046943ada3130141e7c10 Mon Sep 17 00:00:00 2001 From: bucanero Date: Wed, 11 Dec 2024 09:57:47 -0300 Subject: [PATCH] add formats --- include/saves.h | 3 + source/exec_cmd.c | 12 ++++ source/import_ps2.c | 167 ++++++++++++++++++++++++++++++++++++++++++++ source/psv_resign.c | 125 ++++++++++++++++++++++++--------- 4 files changed, 274 insertions(+), 33 deletions(-) diff --git a/include/saves.h b/include/saves.h index 855efdf..904c321 100644 --- a/include/saves.h +++ b/include/saves.h @@ -273,6 +273,9 @@ 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); +int vmc_import_xps(const char *input); +int vmc_import_max(const char *input); +int vmc_import_cbs(const char *input); uint8_t* loadVmcIcon(const char *save, const char* icon); int sjis2ascii(uint8_t* bData); diff --git a/source/exec_cmd.c b/source/exec_cmd.c index d300bb5..2f1a1f6 100644 --- a/source/exec_cmd.c +++ b/source/exec_cmd.c @@ -1003,6 +1003,18 @@ static void import_save2vmc(const char* src, int type) ret = vmc_import_psu(src); break; + case FILE_TYPE_XPS: + ret = vmc_import_xps(src); + break; + + case FILE_TYPE_MAX: + ret = vmc_import_max(src); + break; + + case FILE_TYPE_CBS: + ret = vmc_import_cbs(src); + break; + default: break; } diff --git a/source/import_ps2.c b/source/import_ps2.c index 499f0bf..df11e7e 100644 --- a/source/import_ps2.c +++ b/source/import_ps2.c @@ -842,3 +842,170 @@ int exportCBS(const char *save, const char* cbs_path, const char* title) return 1; } + +int vmc_import_max(const char *save) +{ + int r, fd; + maxEntry_t *entry; + maxHeader_t header; + char dstName[256]; + + if (!isMAXFile(save)) + return 0; + + FILE *f = fopen(save, "rb"); + if(!f) + return 0; + + fread(&header, 1, sizeof(maxHeader_t), f); + + r = mcio_mcMkDir(header.dirName); + if (r < 0) + LOG("Error: can't create directory '%s'... (%d)", header.dirName, r); + else + mcio_mcClose(r); + + // Get compressed file entries + uint8_t *compressed = malloc(header.compressedSize); + + fseek(f, sizeof(maxHeader_t) - 4, SEEK_SET); // Seek to beginning of LZARI stream. + uint32_t ret = fread(compressed, 1, header.compressedSize, f); + fclose(f); + if(ret != header.compressedSize) + { + LOG("Compressed size: actual=%d, expected=%d\n", ret, header.compressedSize); + free(compressed); + return 0; + } + + uint8_t *decompressed = malloc(header.decompressedSize); + ret = unlzari(compressed, header.compressedSize, decompressed, header.decompressedSize); + free(compressed); + + // As with other save formats, decompressedSize isn't acccurate. + if(ret == 0) + { + LOG("Decompression failed.\n"); + free(decompressed); + return 0; + } + + LOG("Save contents:\n"); + // Write the file's data + for(uint32_t offset = 0; header.numFiles > 0; header.numFiles--) + { + entry = (maxEntry_t*) &decompressed[offset]; + offset += sizeof(maxEntry_t); + LOG(" %8d bytes : %s", entry->length, entry->name); + update_progress_bar(offset, header.decompressedSize, entry->name); + + snprintf(dstName, sizeof(dstName), "%s/%s", header.dirName, entry->name); + fd = mcio_mcOpen(dstName, sceMcFileCreateFile | sceMcFileAttrWriteable | sceMcFileAttrFile); + if (fd < 0) + return 0; + + r = mcio_mcWrite(fd, &decompressed[offset], entry->length); + mcio_mcClose(fd); + + if (r != (int)entry->length) + return 0; + + offset = roundUp(offset + entry->length + 8, 16) - 8; + } + + free(decompressed); + + return 1; +} + +int vmc_import_cbs(const char *save) +{ + int r, fd; + uint8_t *cbsData; + uint8_t *compressed; + uint8_t *decompressed; + cbsHeader_t header; + cbsEntry_t entryHeader; + uLong decompressedSize; + size_t cbsLen; + char dstName[256]; + struct io_dirent mcEntry; + + if(!isCBSFile(save)) + return 0; + + if(read_buffer(save, &cbsData, &cbsLen) < 0) + return 0; + + memcpy(&header, cbsData, sizeof(cbsHeader_t)); + r = mcio_mcMkDir(header.name); + if (r < 0) + LOG("Error: can't create directory '%s'... (%d)", header.name, r); + else + mcio_mcClose(r); + + // Get data for file entries + compressed = cbsData + sizeof(cbsHeader_t); + // Some tools create .CBS saves with an incorrect compressed size in the header. + // It can't be trusted! + cbsCrypt(compressed, cbsLen - sizeof(cbsHeader_t)); + decompressedSize = header.decompressedSize; + decompressed = malloc(decompressedSize); + r = uncompress(decompressed, &decompressedSize, compressed, cbsLen - sizeof(cbsHeader_t)); + free(cbsData); + + if(r != Z_OK) + { + // Compression failed. + LOG("Decompression failed! (Z_ERR = %d)", r); + free(decompressed); + return 0; + } + + LOG("Save contents:\n"); + + // Write the file's data + for(uint32_t offset = 0; offset < (decompressedSize - sizeof(cbsEntry_t)); offset += entryHeader.length) + { + /* Entry header can't be read directly because it might not be 32-bit aligned. + GCC will likely emit an lw instruction for reading the 32-bit variables in the + struct which will halt the processor if it tries to load from an address + that's misaligned. */ + memcpy(&entryHeader, &decompressed[offset], sizeof(cbsEntry_t)); + offset += sizeof(cbsEntry_t); + LOG(" %8d bytes : %s", entryHeader.length, entryHeader.name); + update_progress_bar(offset + sizeof(cbsEntry_t), decompressedSize, entryHeader.name); + + snprintf(dstName, sizeof(dstName), "%s/%s", header.name, entryHeader.name); + fd = mcio_mcOpen(dstName, sceMcFileCreateFile | sceMcFileAttrWriteable | sceMcFileAttrFile); + if (fd < 0) + { + free(decompressed); + return 0; + } + + r = mcio_mcWrite(fd, &decompressed[offset], entryHeader.length); + mcio_mcClose(fd); + + if (r != (int)entryHeader.length) + { + free(decompressed); + return 0; + } + + mcio_mcStat(dstName, &mcEntry); + memcpy(&mcEntry.stat.ctime, &entryHeader.created, sizeof(struct sceMcStDateTime)); + memcpy(&mcEntry.stat.mtime, &entryHeader.modified, sizeof(struct sceMcStDateTime)); + mcEntry.stat.mode = entryHeader.mode; + mcio_mcSetStat(dstName, &mcEntry); + } + free(decompressed); + + mcio_mcStat(header.name, &mcEntry); + memcpy(&mcEntry.stat.ctime, &header.created, sizeof(struct sceMcStDateTime)); + memcpy(&mcEntry.stat.mtime, &header.modified, sizeof(struct sceMcStDateTime)); + mcEntry.stat.mode = header.mode; + mcio_mcSetStat(header.name, &mcEntry); + + return 1; +} diff --git a/source/psv_resign.c b/source/psv_resign.c index 775935d..38bd6be 100644 --- a/source/psv_resign.c +++ b/source/psv_resign.c @@ -483,43 +483,14 @@ int vmc_import_psv(const char *input) struct io_dirent entry; ps2_MainDirInfo_t *ps2md; ps2_FileInfo_t *ps2fi; - - FILE *fh = fopen(input, "rb"); - if (fh == NULL) - return 0; - - fread(filepath, 1, 4, fh); - if (memcmp(PSV_MAGIC, filepath, 4) != 0) { - LOG("Not a .PSV file"); - fclose(fh); - return 0; - } - - fseek(fh, 0, SEEK_END); - int filesize = ftell(fh); - if (!filesize) { - fclose(fh); - return 0; - } - fseek(fh, 0, SEEK_SET); + uint8_t *p; + size_t filesize; LOG("Reading file: '%s'...", input); - - uint8_t *p = malloc(filesize); - if (p == NULL) { + if (read_buffer(input, &p, &filesize) < 0) return 0; - } - - r = fread(p, 1, filesize, fh); - if (r != filesize) { - fclose(fh); - free(p); - return 0; - } - - fclose(fh); - if (p[0x3C] != 0x02) { + if (filesize < 0x200 || memcmp(PSV_MAGIC, p, 4) != 0 || p[0x3C] != 0x02) { LOG("Not a PS2 save file"); free(p); return 0; @@ -663,6 +634,94 @@ int vmc_import_psu(const char *input) return 1; } +int vmc_import_xps(const char *save) +{ + int r, fd; + FILE *xpsFile; + char path[256]; + uint8_t *data; + xpsEntry_t entry; + struct io_dirent dirEntry, mcEntry; + + xpsFile = fopen(save, "rb"); + if(!xpsFile) + return 0; + + fread(path, 1, 0x20, xpsFile); + if (memcmp(&path[4], "SharkPortSave\0\0\0", 16) != 0) + { + fclose(xpsFile); + return 0; + } + + // Skip the variable size header + fread(&r, 1, sizeof(int), xpsFile); + fseek(xpsFile, r, SEEK_CUR); + fread(&r, 1, sizeof(int), xpsFile); + fseek(xpsFile, r, SEEK_CUR); + fread(&r, 1, sizeof(int), xpsFile); + fread(&r, 1, sizeof(int), xpsFile); + + // Read main directory entry + fread(&entry, 1, sizeof(xpsEntry_t), xpsFile); + + r = mcio_mcMkDir(entry.name); + if (r < 0) + LOG("Error: can't create directory '%s'... (%d)", entry.name, r); + else + mcio_mcClose(r); + + // Root dir + memset(&mcEntry, 0, sizeof(struct io_dirent)); + memset(&dirEntry, 0, sizeof(struct io_dirent)); + memcpy(&dirEntry.stat.ctime, &entry.created, sizeof(struct sceMcStDateTime)); + memcpy(&dirEntry.stat.mtime, &entry.modified, sizeof(struct sceMcStDateTime)); + memcpy(dirEntry.name, entry.name, sizeof(entry.name)); + dirEntry.stat.mode = ES16(entry.mode); + dirEntry.stat.size = entry.length; + + LOG("Save contents:"); + // Copy each file entry + for(int numFiles = entry.length - 2; numFiles > 0; numFiles--) + { + fread(&entry, 1, sizeof(xpsEntry_t), xpsFile); + LOG(" %8d bytes : %s", entry.length, entry.name); + update_progress_bar(dirEntry.stat.size - numFiles, dirEntry.stat.size, entry.name); + + snprintf(path, sizeof(path), "%s/%s", dirEntry.name, entry.name); + fd = mcio_mcOpen(path, sceMcFileCreateFile | sceMcFileAttrWriteable | sceMcFileAttrFile); + if (fd < 0) + { + fclose(xpsFile); + return 0; + } + + data = malloc(entry.length); + fread(data, 1, entry.length, xpsFile); + + r = mcio_mcWrite(fd, data, entry.length); + mcio_mcClose(fd); + free(data); + + if (r != (int)entry.length) + { + fclose(xpsFile); + return 0; + } + + mcio_mcStat(path, &mcEntry); + memcpy(&mcEntry.stat.ctime, &entry.created, sizeof(struct sceMcStDateTime)); + memcpy(&mcEntry.stat.mtime, &entry.modified, sizeof(struct sceMcStDateTime)); + mcEntry.stat.mode = ES16(entry.mode); + mcio_mcSetStat(path, &mcEntry); + } + + fclose(xpsFile); + mcio_mcSetStat(dirEntry.name, &dirEntry); + + return 1; +} + int vmc_export_psv(const char* save, const char* out_path) { FILE *psvFile;