cellFsSdataOpenByFd draft

LLE-compatible implementation
lv2_file::make_view implemented
This commit is contained in:
Nekotekina 2017-03-04 14:54:53 +03:00
parent 67ac8bf070
commit 8e4a09d9e5
3 changed files with 173 additions and 128 deletions

View file

@ -551,113 +551,6 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr<void(s32 xfd, u64 xsize)>
return CELL_OK;
}
bool sdata_check(u32 version, u32 flags, u64 filesizeInput, u64 filesizeTmp)
{
if (version > 4 || flags & 0x7EFFFFC0){
printf("ERROR: unknown version");
return false;
}
if ((version == 1 && (flags & 0x7FFFFFFE)) ||
(version == 2 && (flags & 0x7EFFFFC0))){
printf("ERROR: unknown or unsupported type");
return false;
}
if (filesizeTmp > filesizeInput){
printf("ERROR: input file size is too short.");
return false;
}
if (!(flags & 0x80000000)){
printf("ERROR: cannot extract finalized edata.");
return false;
}
return true;
}
s32 sdata_unpack(const std::string& packed_file, const std::string& unpacked_file)
{
fs::file packed_stream(vfs::get(packed_file));
fs::file unpacked_stream(vfs::get(unpacked_file), fs::rewrite);
if (!packed_stream)
{
cellFs.error("File '%s' not found!", packed_file);
return CELL_ENOENT;
}
if (!unpacked_stream)
{
cellFs.error("File '%s' couldn't be created!", unpacked_file);
return CELL_ENOENT;
}
char buffer[10200];
packed_stream.read(buffer, 256);
u32 format = *(be_t<u32>*)&buffer[0];
if (format != 0x4E504400) // "NPD\x00"
{
cellFs.error("Illegal format. Expected 0x4E504400, but got 0x%08x", format);
return CELL_EFSSPECIFIC;
}
u32 version = *(be_t<u32>*)&buffer[0x04];
u32 flags = *(be_t<u32>*)&buffer[0x80];
u32 blockSize = *(be_t<u32>*)&buffer[0x84];
u64 filesizeOutput = *(be_t<u64>*)&buffer[0x88];
u64 filesizeInput = packed_stream.size();
u32 blockCount = (u32)((filesizeOutput + blockSize - 1) / blockSize);
// SDATA file is compressed
if (flags & 0x1)
{
cellFs.warning("cellFsSdataOpen: Compressed SDATA files are not supported yet.");
return CELL_EFSSPECIFIC;
}
// SDATA file is NOT compressed
else
{
u32 t1 = (flags & 0x20) ? 0x20 : 0x10;
u32 startOffset = (blockCount * t1) + 0x100;
u64 filesizeTmp = (filesizeOutput + 0xF) & 0xFFFFFFF0 + startOffset;
if (!sdata_check(version, flags, filesizeInput, filesizeTmp))
{
cellFs.error("cellFsSdataOpen: Wrong header information.");
return CELL_EFSSPECIFIC;
}
if (flags & 0x20)
{
packed_stream.seek(0x100);
}
else
{
packed_stream.seek(startOffset);
}
for (u32 i = 0; i < blockCount; i++)
{
if (flags & 0x20)
{
packed_stream.seek(t1, fs::seek_cur);
}
if (!(blockCount - i - 1))
{
blockSize = (u32)(filesizeOutput - i * blockSize);
}
packed_stream.read(buffer + 256, blockSize);
unpacked_stream.write(buffer + 256, blockSize);
}
}
return CELL_OK;
}
s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
{
cellFs.notice("cellFsSdataOpen(path=%s, flags=%#o, fd=*0x%x, arg=*0x%x, size=0x%llx)", path, flags, fd, arg, size);
@ -667,33 +560,52 @@ s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<vo
return CELL_EINVAL;
}
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({ 0x180, 0x10 }), 8);
// Don't implement sdata decryption in this function, it should be done in sys_fs_open() syscall or somewhere else
/*
std::string suffix = path.substr(path.length() - 5, 5);
if (suffix != ".sdat" && suffix != ".SDAT")
return CELL_ENOTSDATA;
std::string::size_type last_slash = path.rfind('/'); //TODO: use a filesystem library to solve this more robustly
last_slash = last_slash == std::string::npos ? 0 : last_slash+1;
std::string unpacked_path = "/dev_hdd1/"+path.substr(last_slash,path.length()-last_slash)+".unpacked";
s32 ret = sdata_unpack(path, unpacked_path);
if (ret) return ret;
fd = idm::GetNewID(Emu.GetVFS().OpenFile(unpacked_path, vfsRead), TYPE_FS_FILE);
return CELL_OK;
*/
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({0x180, 0x10}), 8);
}
s32 cellFsSdataOpenByFd(u32 mself_fd, s32 flags, vm::ptr<u32> sdata_fd, u64 offset, vm::cptr<void> arg, u64 size)
{
cellFs.todo("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
cellFs.notice("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
// TODO:
if (!sdata_fd)
{
return CELL_EFAULT;
}
*sdata_fd = -1;
if (mself_fd < 3 || mself_fd > 255)
{
return CELL_EBADF;
}
if (flags)
{
return CELL_EINVAL;
}
vm::var<lv2_file_op_09> ctrl;
ctrl->_vtable = vm::cast(0xfa880000); // Intentionally wrong (provide correct vtable if necessary)
ctrl->op = 0x80000009;
ctrl->fd = mself_fd;
ctrl->offset = offset;
ctrl->_vtabl2 = vm::cast(0xfa880020);
ctrl->arg1 = 0x180;
ctrl->arg2 = 0x10;
ctrl->arg_ptr = arg.addr();
ctrl->arg_size = u32(size);
if (const s32 rc = sys_fs_fcntl(mself_fd, 0x80000009, ctrl, 0x40))
{
return rc;
}
if (const s32 rc = ctrl->out_code)
{
return rc;
}
*sdata_fd = ctrl->out_fd;
return CELL_OK;
}

View file

@ -46,6 +46,73 @@ u64 lv2_file::op_write(vm::ps3::cptr<void> buf, u64 size)
return file.write(local_buf.get(), size);
}
struct lv2_file::file_view : fs::file_base
{
const std::shared_ptr<lv2_file> m_file;
const u64 m_off;
u64 m_pos;
explicit file_view(const std::shared_ptr<lv2_file>& _file, u64 offset)
: m_file(_file)
, m_off(offset)
, m_pos(0)
{
}
~file_view() override
{
}
fs::stat_t stat() override
{
return m_file->file.stat();
}
bool trunc(u64 length) override
{
return false;
}
u64 read(void* buffer, u64 size) override
{
std::lock_guard<std::mutex> lock(m_file->mp->mutex);
const u64 old_pos = m_file->file.pos();
const u64 new_pos = m_file->file.seek(m_off + m_pos);
const u64 result = m_file->file.read(buffer, size);
verify(HERE), old_pos == m_file->file.seek(old_pos);
m_pos += result;
return result;
}
u64 write(const void* buffer, u64 size) override
{
return 0;
}
u64 seek(s64 offset, fs::seek_mode whence) override
{
return
whence == fs::seek_set ? m_pos = offset :
whence == fs::seek_cur ? m_pos = offset + m_pos :
whence == fs::seek_end ? m_pos = offset + size() :
(fmt::raw_error("lv2_file::file_view::seek(): invalid whence"), 0);
}
u64 size() override
{
return m_off + m_file->file.size();
}
};
fs::file lv2_file::make_view(const std::shared_ptr<lv2_file>& _file, u64 offset)
{
fs::file result;
result.reset(std::make_unique<lv2_file::file_view>(_file, offset));
return result;
}
error_code sys_fs_test(u32 arg1, u32 arg2, vm::ptr<u32> arg3, u32 arg4, vm::ptr<char> arg5, u32 arg6)
{
sys_fs.todo("sys_fs_test(arg1=0x%x, arg2=0x%x, arg3=*0x%x, arg4=0x%x, arg5=*0x%x, arg6=0x%x) -> CELL_OK", arg1, arg2, arg3, arg4, arg5, arg6);
@ -475,6 +542,35 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr<void> _arg, u32 _size)
break;
}
case 0x80000009: // cellFsSdataOpenByFd
{
const auto arg = vm::static_ptr_cast<lv2_file_op_09>(_arg);
if (_size < arg.size())
{
return CELL_EINVAL;
}
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
return CELL_EBADF;
}
// TODO
if (const u32 id = idm::make<lv2_fs_object, lv2_file>(file->mp, lv2_file::make_view(file, arg->offset), file->mode, file->flags))
{
arg->out_code = CELL_OK;
arg->out_fd = id;
return CELL_OK;
}
// Out of file descriptors
return CELL_EMFILE;
}
default:
{
sys_fs.todo("sys_fs_fcntl(): Unknown operation 0x%08x (fd=%d, arg=*0x%x, size=0x%x)", op, fd, _arg, _size);

View file

@ -126,11 +126,25 @@ struct lv2_file final : lv2_fs_object
{
}
lv2_file(lv2_fs_mount_point* mp, fs::file&& file, s32 mode, s32 flags)
: lv2_fs_object(mp)
, file(std::move(file))
, mode(mode)
, flags(flags)
{
}
// File reading with intermediate buffer
u64 op_read(vm::ps3::ptr<void> buf, u64 size);
// File writing with intermediate buffer
u64 op_write(vm::ps3::cptr<void> buf, u64 size);
// For MSELF support
struct file_view;
// Make file view from lv2_file object (for MSELF support)
static fs::file make_view(const std::shared_ptr<lv2_file>& _file, u64 offset);
};
struct lv2_dir final : lv2_fs_object
@ -181,6 +195,29 @@ struct lv2_file_op_rw : lv2_file_op
CHECK_SIZE(lv2_file_op_rw, 0x38);
// sys_fs_fcntl: cellFsSdataOpenByFd
struct lv2_file_op_09 : lv2_file_op
{
vm::bptrb<vtable::lv2_file_op> _vtable;
be_t<u32> op;
be_t<u32> _x8;
be_t<u32> _xc;
be_t<u32> fd;
be_t<u64> offset;
be_t<u32> _vtabl2;
be_t<u32> arg1; // 0x180
be_t<u32> arg2; // 0x10
be_t<u32> arg_size; // 6th arg
be_t<u32> arg_ptr; // 5th arg
be_t<s32> out_code;
be_t<u32> out_fd;
};
CHECK_SIZE(lv2_file_op_09, 0x40);
// Syscalls
error_code sys_fs_test(u32 arg1, u32 arg2, vm::ps3::ptr<u32> arg3, u32 arg4, vm::ps3::ptr<char> arg5, u32 arg6);