1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-23 11:13:19 +01:00

Edat / Sdata: On the fly decryption for edat, fix EDAT_FLAG_0x20 decryption

This commit is contained in:
Jake 2017-03-10 03:06:36 -06:00
parent 596db2c29c
commit a6f8e4f6b3
7 changed files with 247 additions and 111 deletions

View File

@ -142,6 +142,7 @@ std::array<u8, 0x10> 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<u8> 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<u8> 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<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file)
{
std::array<u8, 0x10> rapkey{ 0 };
std::array<u8, 0x10> rifkey{ 0 };
rap_file.read<std::array<u8, 0x10>>(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<u8, 0x10>& 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<u8, 0x10> 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<std::vector<u8>>();
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<u8, 0x10>{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<u32>(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<u64>(writeOffset - startOffset, size);
memmove(data, &data_buf[startOffset], bytesWrote);
memcpy(data, &data_buf[startOffset], bytesWrote);
return bytesWrote;
}

View File

@ -2,6 +2,8 @@
#include <stdio.h>
#include <string.h>
#include <array>
#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<u8, 0x10> devKlic{};
std::array<u8, 0x10> 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<u8,0x10>& custom_klic);
extern std::array<u8, 0x10> 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<u8, 0x10> dec_key{};
// edat usage
std::array<u8, 0x10> rif_key{};
std::array<u8, 0x10> 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<u8, 0x10>& dev_key, const std::array<u8, 0x10>& 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; }
};

View File

@ -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++)
{

View File

@ -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]),

View File

@ -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<u8> k_licensee_addr, vm::cptr<char> drm_path)
}
std::string k_licensee_str = "0";
u8 k_licensee[0x10];
std::array<u8,0x10> k_licensee;
if (k_licensee_addr)
{
@ -58,20 +59,30 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> 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<u32>(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<EdatKeys_t>();
// Search for a compatible RAP file.
for (const auto& entry : fs::dir(rap_lpath))
{
@ -85,24 +96,23 @@ s32 npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> 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<u8>());
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;
}

View File

@ -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<void> arg);

View File

@ -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<char> path, s32 flags, vm::ptr<u32> fd, s32 mode
if ((flags & CELL_FS_O_MSELF) && (!verify_mself(*fd, file)))
return CELL_ENOTMSELF;
// sdata encryption arg flag
const be_t<u32>* casted_args = static_cast<const be_t<u32> *>(arg.get_ptr());
if (size == 8 && casted_args[0] == 0x180 && casted_args[1] == 0x10)
const auto casted_arg = vm::static_ptr_cast<const u64>(arg);//static_cast<const be_t<u32> *>(arg.get_ptr());
if (size == 8)
{
// check if the file has the NPD header, or else assume its not encrypted
u32 magic;
file.read<u32>(magic);
file.seek(0);
if (magic == "NPD\0"_u32)
// check for sdata
if (*casted_arg == 0x18000000010)
{
auto sdata_file = std::make_unique<SDATADecrypter>(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<u32>(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<EDATADecrypter>(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<u32>(magic);
file.seek(0);
if (magic == "NPD\0"_u32)
{
auto edatkeys = fxm::get_always<EdatKeys_t>();
auto sdata_file = std::make_unique<EDATADecrypter>(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<lv2_fs_object, lv2_file>(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<void> _arg, u32 _size)
return CELL_EBADF;
}
auto sdata_file = std::make_unique<SDATADecrypter>(lv2_file::make_view(file, arg->offset));
auto sdata_file = std::make_unique<EDATADecrypter>(lv2_file::make_view(file, arg->offset));
if (!sdata_file->ReadHeader())
{