diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 7946880976..70ce7506a6 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -142,6 +142,7 @@ std::array get_block_key(int block, NPD_HEADER *npd) } // for out data, allocate a buffer the size of 'edat->block_size' +// Also, set 'in file' to the beginning of the encrypted data, which may be offset if inside another file, but normally just reset to beginning of file // returns number of bytes written, -1 for error s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left) { @@ -193,7 +194,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np else if ((edat->flags & EDAT_FLAG_0x20) != 0) { // If FLAG 0x20, the metadata precedes each data block. - metadata_sec_offset = metadata_offset + (u64) block_num * (metadata_section_size + length); + metadata_sec_offset = metadata_offset + (u64) block_num * (metadata_section_size + edat->block_size); in->seek(file_offset + metadata_sec_offset); unsigned char metadata[0x20]; @@ -217,7 +218,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np in->seek(file_offset + metadata_sec_offset); in->read(hash_result, 0x10); - offset = metadata_offset + (u64) block_num * edat->block_size + (u64) block_num * metadata_section_size; + offset = metadata_offset + (u64) block_num * edat->block_size + total_blocks * metadata_section_size; length = edat->block_size; if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size)) @@ -280,7 +281,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data.get(), dec_data.get(), length, key_result, iv, hash, hash_result)) { LOG_ERROR(LOADER, "EDAT: Block at offset 0x%llx has invalid hash!", (u64)offset); - return 1; + return -1; } } @@ -296,7 +297,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np if (res < 0) { LOG_ERROR(LOADER, "EDAT: Decompression failed!"); - return 1; + return -1; } } return res; @@ -528,19 +529,10 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs: return 0; } -int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEADER *npd, bool verbose) +int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd) { - int title_hash_result = 0; - int dev_hash_result = 0; - - const int file_name_length = (int) strlen(file_name); - std::unique_ptr buf(new u8[0x30 + file_name_length]); unsigned char dev[0x60] = { 0 }; unsigned char key[0x10] = { 0 }; - - // Build the title buffer (content_id + file_name). - memcpy(buf.get(), npd->content_id, 0x30); - memcpy(buf.get() + 0x30, file_name, file_name_length); // Build the dev buffer (first 0x60 bytes of NPD header in big-endian). memcpy(dev, npd, 0x60); @@ -553,17 +545,6 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA memcpy(dev + 0x8, &license, 4); memcpy(dev + 0xC, &type, 4); - // Hash with NPDRM_OMAC_KEY_3 and compare with title_hash. - title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), 0x30 + file_name_length, npd->title_hash, 0x10); - - if (verbose) - { - if (title_hash_result) - LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!"); - else - LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!"); - } - // Check for an empty dev_hash (can't validate if devklic is NULL); bool isDevklicEmpty = true; for (int i = 0; i < 0x10; i++) @@ -577,11 +558,8 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA if (isDevklicEmpty) { - if (verbose) - LOG_WARNING(LOADER, "EDAT: NPD dev hash is empty!"); - // Allow empty dev hash. - dev_hash_result = 1; + return 1; } else { @@ -589,16 +567,35 @@ int validate_npd_hashes(const char* file_name, unsigned char *klicensee, NPD_HEA xor_key(key, klicensee, NP_OMAC_KEY_2); // Hash with generated key and compare with dev_hash. - dev_hash_result = cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10); - - if (verbose) - { - if (dev_hash_result) - LOG_NOTICE(LOADER, "EDAT: NPD dev hash is valid!"); - else - LOG_WARNING(LOADER, "EDAT: NPD dev hash is invalid!"); - } + return cmac_hash_compare(key, 0x10, dev, 0x60, npd->dev_hash, 0x10); } +} + +int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, bool verbose) +{ + int title_hash_result = 0; + int dev_hash_result = 0; + + const int file_name_length = (int) strlen(file_name); + std::unique_ptr buf(new u8[0x30 + file_name_length]); + + // Build the title buffer (content_id + file_name). + memcpy(buf.get(), npd->content_id, 0x30); + memcpy(buf.get() + 0x30, file_name, file_name_length); + + // Hash with NPDRM_OMAC_KEY_3 and compare with title_hash. + title_hash_result = cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), 0x30 + file_name_length, npd->title_hash, 0x10); + + if (verbose) + { + if (title_hash_result) + LOG_NOTICE(LOADER, "EDAT: NPD title hash is valid!"); + else + LOG_WARNING(LOADER, "EDAT: NPD title hash is invalid!"); + } + + + dev_hash_result = validate_dev_klic(klicensee, npd); return (title_hash_result && dev_hash_result); } @@ -758,6 +755,55 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char* return 0; } +std::array GetEdatRifKeyFromRapFile(const fs::file& rap_file) +{ + std::array rapkey{ 0 }; + std::array rifkey{ 0 }; + + rap_file.read>(rapkey); + + rap_to_rif(rapkey.data(), rifkey.data()); + + return rifkey; +} + +bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array& custom_klic) +{ + // Setup NPD and EDAT/SDAT structs. + NPD_HEADER NPD; + EDAT_HEADER EDAT; + + // Read in the NPD and EDAT/SDAT headers. + read_npd_edat_header(&input, NPD, EDAT); + + unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0 + if (memcmp(&NPD.magic, npd_magic, 4)) + { + LOG_ERROR(LOADER, "EDAT: %s has invalid NPD header or already decrypted.", input_file_name); + return false; + } + + if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG) + { + LOG_ERROR(LOADER, "EDAT: SDATA file given to edat function"); + return false; + } + + // Perform header validation (EDAT only). + char real_file_name[MAX_PATH]; + extract_file_name(input_file_name.c_str(), real_file_name); + if (!validate_npd_hashes(real_file_name, custom_klic.data(), &NPD, false)) + { + // Ignore header validation in DEBUG data. + if ((EDAT.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG) + { + LOG_ERROR(LOADER, "EDAT: NPD hash validation failed!"); + return false; + } + } + return true; +} + // Decrypts full file fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose) { @@ -765,7 +811,7 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, input.seek(0); // Set keys (RIF and DEVKLIC). - unsigned char rifkey[0x10] = { 0 }; + std::array rifKey{ 0 }; unsigned char devklic[0x10] = { 0 }; // Select the EDAT key mode. @@ -815,16 +861,12 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, { fs::file rap(rap_file_name); - unsigned char rapkey[0x10] = { 0 }; - - rap.read(rapkey, 0x10); - - rap_to_rif(rapkey, rifkey); + rifKey = GetEdatRifKeyFromRapFile(rap); } // Delete the bad output file if any errors arise. fs::file output = fs::make_stream>(); - if (extract_all_data(&input, &output, input_file_name.c_str(), devklic, rifkey, verbose)) + if (extract_all_data(&input, &output, input_file_name.c_str(), devklic, rifKey.data(), verbose)) { output.release(); return fs::file{}; @@ -834,16 +876,11 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, return output; } -SDATADecrypter::SDATADecrypter(fs::file&& input, u64 offset) - : sdata_file(std::move(input)), file_offset(offset) +bool EDATADecrypter::ReadHeader() { -} - -bool SDATADecrypter::ReadHeader() -{ - sdata_file.seek(file_offset); + edata_file.seek(0); // Read in the NPD and EDAT/SDAT headers. - read_npd_edat_header(&sdata_file, npdHeader, edatHeader); + read_npd_edat_header(&edata_file, npdHeader, edatHeader); unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0 if (memcmp(&npdHeader.magic, npd_magic, 4)) @@ -852,18 +889,44 @@ bool SDATADecrypter::ReadHeader() } // Check for SDAT flag. - if ((edatHeader.flags & SDAT_FLAG) != SDAT_FLAG) + if ((edatHeader.flags & SDAT_FLAG) == SDAT_FLAG) { - fmt::raw_error("Only SDATA decyption supported in sdatadecrypter"); - return false; + // Generate SDAT key. + xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY); } - // Generate SDAT key. - xor_key(dec_key.data(), npdHeader.dev_hash, SDAT_KEY); + else + { + // verify key + if (validate_dev_klic(dev_key.data(), &npdHeader) == 0) + { + LOG_ERROR(LOADER, "EDAT: Failed validating klic"); + return false; + } - sdata_file.seek(file_offset); + // Select EDAT key. + if ((npdHeader.license & 0x3) == 0x3) // Type 3: Use supplied devklic. + dec_key = std::move(dev_key); + else if ((npdHeader.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key). + { + dec_key = std::move(rif_key); + + if (dec_key == std::array{0}) + { + LOG_ERROR(LOADER, "EDAT: A valid RAP file is needed for this EDAT file!"); + return false; + } + } + else if ((npdHeader.license & 0x1) == 0x1) // Type 1: Use network activation. + { + LOG_ERROR(LOADER, "EDAT: Network license not supported!"); + return false; + } + } - // k the ecdsa_verify function in this check_data function takes a ridiculous amount of time - // like it slows down load time by a factor of x20, at least, so its ignored for now + edata_file.seek(0); + + // k the ecdsa_verify function in this check_data function takes a ridiculous amount of time + // like it slows down load time by a factor of x20, at least, so its ignored for now /*if (check_data(dec_key.data(), &edatHeader, &npdHeader, &sdata_file, false)) { @@ -876,7 +939,7 @@ bool SDATADecrypter::ReadHeader() return true; } -u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size) +u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size) { if (pos > edatHeader.file_size) return 0; @@ -894,11 +957,12 @@ u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size) // find and decrypt block range covering pos + size const u32 starting_block = static_cast(pos / edatHeader.block_size); + const u32 ending_block = std::min(starting_block + num_blocks, total_blocks); u64 writeOffset = 0; - for (u32 i = starting_block; i < (starting_block + num_blocks); ++i) + for (u32 i = starting_block; i < ending_block; ++i) { - sdata_file.seek(file_offset); - u64 res = decrypt_block(&sdata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size); + edata_file.seek(0); + u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key.data(), i, total_blocks, edatHeader.file_size); if (res == -1) { LOG_ERROR(LOADER, "Error Decrypting data"); @@ -909,6 +973,6 @@ u64 SDATADecrypter::ReadData(u64 pos, u8* data, u64 size) const u64 bytesWrote = std::min(writeOffset - startOffset, size); - memmove(data, &data_buf[startOffset], bytesWrote); + memcpy(data, &data_buf[startOffset], bytesWrote); return bytesWrote; } diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index 14ba8876bb..405171fd9f 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -2,6 +2,8 @@ #include #include +#include + #include "utils.h" constexpr u32 SDAT_FLAG = 0x01000000; @@ -12,6 +14,12 @@ constexpr u32 EDAT_FLAG_0x10 = 0x00000010; constexpr u32 EDAT_FLAG_0x20 = 0x00000020; constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000; +struct EdatKeys_t +{ + std::array devKlic{}; + std::array rifKey{}; +}; + struct NPD_HEADER { u32 magic; @@ -36,11 +44,14 @@ struct EDAT_HEADER // Decrypts full file, or null/empty file extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose); -struct SDATADecrypter final : fs::file_base +extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array& custom_klic); + +extern std::array GetEdatRifKeyFromRapFile(const fs::file& rap_file); + +struct EDATADecrypter final : fs::file_base { // file stream - const fs::file sdata_file; - const u64 file_offset; + const fs::file edata_file; u64 file_size{0}; u32 total_blocks{0}; u64 pos{0}; @@ -53,9 +64,19 @@ struct SDATADecrypter final : fs::file_base u64 data_buf_size{0}; std::array dec_key{}; + + // edat usage + std::array rif_key{}; + std::array dev_key{}; public: - SDATADecrypter(fs::file&& input, u64 offset=0); - ~SDATADecrypter() override {} + // SdataByFd usage + EDATADecrypter(fs::file&& input) + : edata_file(std::move(input)) {} + // Edat usage + EDATADecrypter(fs::file&& input, const std::array& dev_key, const std::array& rif_key) + : edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {} + + ~EDATADecrypter() override {} // false if invalid bool ReadHeader(); u64 ReadData(u64 pos, u8* data, u64 size); @@ -92,7 +113,7 @@ public: whence == fs::seek_set ? pos = offset : whence == fs::seek_cur ? pos = offset + pos : whence == fs::seek_end ? pos = offset + size() : - (fmt::raw_error("SDATADecrypter::seek(): invalid whence"), 0); + (fmt::raw_error("EDATADecrypter::seek(): invalid whence"), 0); } u64 size() override { return file_size; } }; diff --git a/rpcs3/Crypto/utils.cpp b/rpcs3/Crypto/utils.cpp index 9e66e993b2..52f646a378 100644 --- a/rpcs3/Crypto/utils.cpp +++ b/rpcs3/Crypto/utils.cpp @@ -10,7 +10,7 @@ // Auxiliary functions (endian swap, xor and prng). -void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2) +void xor_key(unsigned char *dest, const u8* src1, const u8* src2) { for(int i = 0; i < 0x10; i++) { diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 5565cc0060..f3558d713e 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -42,7 +42,7 @@ inline u64 swap64(u64 i) #endif } -void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2); +void xor_key(unsigned char *dest, const u8* src1, const u8* src2); inline void xor_key_sse(u8* dest, const u8* src1, const u8* src2) { _mm_storeu_si128(&(((__m128i*)dest)[0]), diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index b1515ad3e7..bffef1ab61 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -3,6 +3,7 @@ #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" +#include "Emu/IdManager.h" #include "Crypto/unedat.h" #include "sceNp.h" @@ -47,7 +48,7 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) } std::string k_licensee_str = "0"; - u8 k_licensee[0x10]; + std::array k_licensee; if (k_licensee_addr) { @@ -58,20 +59,30 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) } } + const std::string& enc_drm_path_local = vfs::get(enc_drm_path); + const fs::file enc_file(vfs::get(enc_drm_path)); + + u32 magic; + enc_file.read(magic); + enc_file.seek(0); + if (magic != "NPD\0"_u32) + { + // for now assume its just unencrypted + sceNp.notice("npDrmIsAvailable(): Assuming edat file is unencrypted at %s", enc_drm_path); + return CELL_OK; + } + sceNp.warning("npDrmIsAvailable(): Found DRM license file at %s", enc_drm_path); sceNp.warning("npDrmIsAvailable(): Using k_licensee 0x%s", k_licensee_str); - // Set the necessary file paths. - const std::string& drm_file_name = enc_drm_path.substr(enc_drm_path.find_last_of('/') + 1); - // TODO: Make more explicit what this actually does (currently it copies "XXXXXXXX" from drm_path (== "/dev_hdd0/game/XXXXXXXXX/*" assumed) const std::string& drm_file_dir = enc_drm_path.substr(15); const std::string& title_id = drm_file_dir.substr(0, drm_file_dir.find_first_of('/')); - const std::string& dec_drm_path = "/dev_hdd1/cache/" + drm_file_name; - std::string rap_lpath = vfs::get("/dev_hdd0/home/00000001/exdata/"); // TODO: Allow multiple profiles. Use default for now. + auto edatkeys = fxm::get_always(); + // Search for a compatible RAP file. for (const auto& entry : fs::dir(rap_lpath)) { @@ -85,24 +96,23 @@ s32 npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) if (rap_lpath.back() == '/') { sceNp.warning("npDrmIsAvailable(): Can't find RAP file for %s", enc_drm_path); - rap_lpath.clear(); + edatkeys->rifKey.fill(0); } + else + edatkeys->rifKey = GetEdatRifKeyFromRapFile(fs::file{ rap_lpath }); - const std::string& enc_drm_path_local = vfs::get(enc_drm_path); - const fs::file enc_file(enc_drm_path_local); - - if (const fs::file dec_file = DecryptEDAT(enc_file, enc_drm_path_local, 8, rap_lpath, k_licensee, false)) + if (VerifyEDATHeaderWithKLicense(enc_file, enc_drm_path_local, k_licensee)) { - // If decryption succeeds, replace the encrypted file with it. - const std::string& dec_drm_path_local = vfs::get(dec_drm_path); - - fs::file dec_out(dec_drm_path_local, fs::rewrite); - dec_out.write(dec_file.to_vector()); - - fs::remove_file(enc_drm_path_local); - fs::rename(dec_drm_path_local, enc_drm_path_local); + edatkeys->devKlic = std::move(k_licensee); + return CELL_OK; + } + else + { + sceNp.error("npDrmIsAvailable(): Failed to verify edat file %s", enc_drm_path); + edatkeys->devKlic.fill(0); + edatkeys->rifKey.fill(0); + return SCE_NP_DRM_ERROR_FORMAT; } - return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index d7f6b6c7e8..2da869b99d 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -119,6 +119,25 @@ enum // DRM SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND = 0x80029521, + SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501, + SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502, + SCE_NP_DRM_ERROR_SERVER_RESPONSE = 0x80029509, + SCE_NP_DRM_ERROR_NO_ENTITLEMENT = 0x80029513, + SCE_NP_DRM_ERROR_BAD_ACT = 0x80029514, + SCE_NP_DRM_ERROR_BAD_FORMAT = 0x80029515, + SCE_NP_DRM_ERROR_NO_LOGIN = 0x80029516, + SCE_NP_DRM_ERROR_INTERNAL = 0x80029517, + SCE_NP_DRM_ERROR_BAD_PERM = 0x80029519, + SCE_NP_DRM_ERROR_UNKNOWN_VERSION = 0x8002951a, + SCE_NP_DRM_ERROR_TIME_LIMIT = 0x8002951b, + SCE_NP_DRM_ERROR_DIFFERENT_ACCOUNT_ID = 0x8002951c, + SCE_NP_DRM_ERROR_DIFFERENT_DRM_TYPE = 0x8002951d, + SCE_NP_DRM_ERROR_SERVICE_NOT_STARTED = 0x8002951e, + SCE_NP_DRM_ERROR_BUSY = 0x80029520, + SCE_NP_DRM_ERROR_IO = 0x80029525, + SCE_NP_DRM_ERROR_FORMAT = 0x80029530, + SCE_NP_DRM_ERROR_FILENAME = 0x80029533, + SCE_NP_DRM_ERROR_K_LICENSEE = 0x80029534, }; using SceNpBasicEventHandler = s32(s32 event, s32 retCode, u32 reqId, vm::ptr arg); diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index b629f5b8ca..5e55861829 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -133,7 +133,7 @@ struct lv2_file::file_view : fs::file_base u64 size() override { - return m_off + m_file->file.size(); + return m_file->file.size(); } }; @@ -255,27 +255,49 @@ error_code sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode if ((flags & CELL_FS_O_MSELF) && (!verify_mself(*fd, file))) return CELL_ENOTMSELF; - // sdata encryption arg flag - const be_t* casted_args = static_cast *>(arg.get_ptr()); - if (size == 8 && casted_args[0] == 0x180 && casted_args[1] == 0x10) + const auto casted_arg = vm::static_ptr_cast(arg);//static_cast *>(arg.get_ptr()); + if (size == 8) { - // check if the file has the NPD header, or else assume its not encrypted - u32 magic; - file.read(magic); - file.seek(0); - if (magic == "NPD\0"_u32) + // check for sdata + if (*casted_arg == 0x18000000010) { - auto sdata_file = std::make_unique(std::move(file)); - if (!sdata_file->ReadHeader()) + // check if the file has the NPD header, or else assume its not encrypted + u32 magic; + file.read(magic); + file.seek(0); + if (magic == "NPD\0"_u32) { - sys_fs.error("sys_fs_open(%s): Error reading sdata header!", path); - return CELL_EFSSPECIFIC; - } + auto sdata_file = std::make_unique(std::move(file)); + if (!sdata_file->ReadHeader()) + { + sys_fs.error("sys_fs_open(%s): Error reading sdata header!", path); + return CELL_EFSSPECIFIC; + } - file.reset(std::move(sdata_file)); + file.reset(std::move(sdata_file)); + } + } + // edata + else if (*casted_arg == 0x2) + { + // check if the file has the NPD header, or else assume its not encrypted + u32 magic; + file.read(magic); + file.seek(0); + if (magic == "NPD\0"_u32) + { + auto edatkeys = fxm::get_always(); + auto sdata_file = std::make_unique(std::move(file), edatkeys->devKlic, edatkeys->rifKey); + if (!sdata_file->ReadHeader()) + { + sys_fs.error("sys_fs_open(%s): Error reading edata header!", path); + return CELL_EFSSPECIFIC; + } + + file.reset(std::move(sdata_file)); + } } } - if (const u32 id = idm::make(path.get_ptr(), std::move(file), mode, flags)) { *fd = id; @@ -624,7 +646,7 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr _arg, u32 _size) return CELL_EBADF; } - auto sdata_file = std::make_unique(lv2_file::make_view(file, arg->offset)); + auto sdata_file = std::make_unique(lv2_file::make_view(file, arg->offset)); if (!sdata_file->ReadHeader()) {