diff --git a/src/graphics/presentation/videoOut.cpp b/src/graphics/presentation/videoOut.cpp index d67d6089..b2763cff 100644 --- a/src/graphics/presentation/videoOut.cpp +++ b/src/graphics/presentation/videoOut.cpp @@ -1675,6 +1675,21 @@ KYTY_SYSV_ABI int VideoOutGetOutputStatus(int handle, VideoOutOutputStatus* stat return OK; } +KYTY_SYSV_ABI int VideoOutGetVrrStatus(int handle, int32_t* status) { + PRINT_NAME(); + + if (status == nullptr) { + return VIDEO_OUT_ERROR_INVALID_ADDRESS; + } + if (DriverState().Get(handle) == nullptr) { + return VIDEO_OUT_ERROR_INVALID_HANDLE; + } + + // Kyty currently presents at a fixed refresh rate and does not negotiate VRR. + *status = 0; + return OK; +} + static int ValidateOutputConfig(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reserved_ptr, uint64_t reserved) { if (!DriverState().IsOpened(handle)) { diff --git a/src/graphics/presentation/videoOut.h b/src/graphics/presentation/videoOut.h index 58f16c67..88e132a4 100644 --- a/src/graphics/presentation/videoOut.h +++ b/src/graphics/presentation/videoOut.h @@ -87,6 +87,7 @@ KYTY_SYSV_ABI int VideoOutGetEventData(const LibKernel::EventQueue::KernelEvent* KYTY_SYSV_ABI int VideoOutGetEventCount(const LibKernel::EventQueue::KernelEvent* ev); KYTY_SYSV_ABI int VideoOutWaitVblank(int handle); KYTY_SYSV_ABI int VideoOutGetOutputStatus(int handle, VideoOutOutputStatus* status); +KYTY_SYSV_ABI int VideoOutGetVrrStatus(int handle, int32_t* status); KYTY_SYSV_ABI int VideoOutInitializeOutputOptions(VideoOutOutputOptions* options); KYTY_SYSV_ABI int VideoOutIsOutputSupported(int handle, uint64_t mode, const VideoOutOutputOptions* options, diff --git a/src/kernel/fileSystem.cpp b/src/kernel/fileSystem.cpp index 3a7158f1..196c4c8e 100644 --- a/src/kernel/fileSystem.cpp +++ b/src/kernel/fileSystem.cpp @@ -64,6 +64,7 @@ struct File { std::filesystem::path real_name; std::atomic_bool opened; std::atomic_bool directory; + std::atomic_bool readable; std::atomic_bool writable; std::atomic_bool append; std::atomic_bool sync_writes; @@ -175,6 +176,7 @@ int FileDescriptors::CreateDescriptor() { auto* file = new File {}; file->opened = false; file->directory = false; + file->readable = false; file->writable = false; file->append = false; file->sync_writes = false; @@ -442,6 +444,7 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode) { EXIT_IF(file == nullptr || file->opened || file->directory); file->name = path; + file->readable = rw_mode != Common::File::Mode::Write; file->writable = rw_mode != Common::File::Mode::Read; file->append = append; file->sync_writes = fsync || sync || dsync; @@ -761,6 +764,106 @@ int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offse return bytes_read; } +static int ValidateIovecs(const KernelIovec* iov, int iovcnt, int64_t offset, + std::vector* buffers, size_t* total) { + constexpr int MaxIov = 1024; + if (iovcnt < 0 || iovcnt > MaxIov || offset < 0) { + return KERNEL_ERROR_EINVAL; + } + if (iov == nullptr && iovcnt != 0) { + return KERNEL_ERROR_EFAULT; + } + + if (iovcnt != 0) { + buffers->assign(iov, iov + iovcnt); + } + *total = 0; + for (const auto& buffer: *buffers) { + if (buffer.iov_len > INT_MAX - *total) { + return KERNEL_ERROR_EINVAL; + } + if (buffer.iov_base == nullptr && buffer.iov_len != 0) { + return KERNEL_ERROR_EFAULT; + } + *total += buffer.iov_len; + } + return OK; +} + +int64_t KYTY_SYSV_ABI KernelPreadv(int d, const KernelIovec* iov, int iovcnt, int64_t offset) { + PRINT_NAME(); + + std::vector buffers; + size_t total = 0; + const int error = ValidateIovecs(iov, iovcnt, offset, &buffers, &total); + if (error != OK) { + return error; + } + + if (d < DESCRIPTOR_MIN) { + return KERNEL_ERROR_EBADF; + } + if (::Libs::Network::Net::IsSocket(d)) { + return KERNEL_ERROR_ESPIPE; + } + auto* file = g_files->GetFile(d); + if (file == nullptr || !file->opened || !file->readable) { + return KERNEL_ERROR_EBADF; + } + + Common::LockGuard lock(file->mutex); + if (file->directory) { + return KERNEL_ERROR_EISDIR; + } + if (total == 0) { + return 0; + } + if (file->special == SpecialFile::Random) { + for (const auto& buffer: buffers) { + if (buffer.iov_len != 0) { + Memory::InvalidateMemory(reinterpret_cast(buffer.iov_base), + buffer.iov_len); + FillRandomBuffer(buffer.iov_base, buffer.iov_len); + } + } + return static_cast(total); + } + if (file->f.IsInvalid()) { + return KERNEL_ERROR_EIO; + } + + const auto position = file->f.Tell(); + const auto file_size = file->f.Size(); + if (!file->f.Seek(static_cast(offset))) { + return KERNEL_ERROR_EIO; + } + auto remaining = static_cast(offset) < file_size ? file_size - offset : 0; + int64_t bytes_read = 0; + for (const auto& buffer: buffers) { + if (remaining == 0) { + break; + } + if (buffer.iov_len == 0) { + continue; + } + const auto count = static_cast(std::min(buffer.iov_len, remaining)); + Memory::InvalidateMemory(reinterpret_cast(buffer.iov_base), count); + uint32_t bytes = 0; + file->f.Read(buffer.iov_base, count, &bytes); + bytes_read += bytes; + remaining -= bytes; + if (bytes < count) { + break; + } + } + if (!file->f.Seek(position)) { + return KERNEL_ERROR_EIO; + } + LOGF("\tReadv %" PRId64 " bytes (pos = %" PRId64 ", iovcnt = %d) from: %s\n", bytes_read, + offset, iovcnt, Common::PathToString(file->real_name).c_str()); + return bytes_read; +} + int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset) { PRINT_NAME(); @@ -814,6 +917,72 @@ int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_ return bytes_written; } +int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, int64_t offset) { + PRINT_NAME(); + + std::vector buffers; + size_t total = 0; + const int error = ValidateIovecs(iov, iovcnt, offset, &buffers, &total); + if (error != OK) { + return error; + } + + if (d < DESCRIPTOR_MIN) { + return KERNEL_ERROR_EBADF; + } + if (::Libs::Network::Net::IsSocket(d)) { + return KERNEL_ERROR_ESPIPE; + } + auto* file = g_files->GetFile(d); + if (file == nullptr || !file->opened || !file->writable) { + return KERNEL_ERROR_EBADF; + } + Common::LockGuard lock(file->mutex); + if (file->directory) { + return KERNEL_ERROR_EISDIR; + } + if (file->special != SpecialFile::None) { + return KERNEL_ERROR_EINVAL; + } + if (total == 0) { + return 0; + } + if (file->f.IsInvalid()) { + return KERNEL_ERROR_EIO; + } + + const auto position = file->f.Tell(); + const auto target = (file->append ? file->f.Size() : static_cast(offset)); + if (target > static_cast(INT64_MAX) - total) { + return KERNEL_ERROR_EFBIG; + } + if (!file->f.Seek(target)) { + return KERNEL_ERROR_EIO; + } + + int64_t bytes_written = 0; + for (const auto& buffer: buffers) { + if (buffer.iov_len == 0) { + continue; + } + uint32_t bytes = 0; + file->f.Write(buffer.iov_base, static_cast(buffer.iov_len), &bytes); + bytes_written += bytes; + if (bytes < buffer.iov_len) { + break; + } + } + const bool flushed = !file->sync_writes || file->f.Flush(); + const bool restored = file->f.Seek(position); + if (!flushed || !restored) { + return KERNEL_ERROR_EIO; + } + + LOGF("\tWritev %" PRId64 " bytes (pos = %" PRId64 ", iovcnt = %d) to: %s\n", bytes_written, + offset, iovcnt, Common::PathToString(file->real_name).c_str()); + return bytes_written; +} + int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) { PRINT_NAME(); diff --git a/src/kernel/fileSystem.h b/src/kernel/fileSystem.h index 17b22fe1..6c079010 100644 --- a/src/kernel/fileSystem.h +++ b/src/kernel/fileSystem.h @@ -6,6 +6,7 @@ #include "common/stringUtils.h" #include "kernel/pthread.h" +#include #include namespace Libs::LibKernel::FileSystem { @@ -32,6 +33,14 @@ struct FileStat { unsigned int: (8 / 2) * (16 - static_cast(sizeof(KernelTimespec))); }; +struct KernelIovec { + void* iov_base; + size_t iov_len; +}; + +static_assert(sizeof(KernelIovec) == 16); +static_assert(offsetof(KernelIovec, iov_len) == 8); + void Initialize(); void Shutdown(); void EmergencyShutdown(); @@ -51,8 +60,10 @@ int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode); int KYTY_SYSV_ABI KernelClose(int d); int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes); int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset); +int64_t KYTY_SYSV_ABI KernelPreadv(int d, const KernelIovec* iov, int iovcnt, int64_t offset); int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes); int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset); +int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, int64_t offset); int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence); int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb); int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb); diff --git a/src/libs/libKernel.cpp b/src/libs/libKernel.cpp index 19460cb7..03398738 100644 --- a/src/libs/libKernel.cpp +++ b/src/libs/libKernel.cpp @@ -3021,7 +3021,9 @@ LIB_DEFINE(InitLibKernel_1_FS) { LIB_FUNC("Cg4srZ6TKbU", FileSystem::KernelRead); LIB_FUNC("4wSze92BhLI", FileSystem::KernelWrite); LIB_FUNC("+r3rMFwItV4", FileSystem::KernelPread); + LIB_FUNC("yTj62I7kw4s", FileSystem::KernelPreadv); LIB_FUNC("nKWi-N2HBV4", FileSystem::KernelPwrite); + LIB_FUNC("mBd4AfLP+u8", FileSystem::KernelPwritev); LIB_FUNC("eV9wAD2riIA", FileSystem::KernelStat); LIB_FUNC("kBwCPsYX-m4", FileSystem::KernelFstat); LIB_FUNC("AUXVxWeJU-A", FileSystem::KernelUnlink); diff --git a/src/libs/libNet.cpp b/src/libs/libNet.cpp index 0e0b2279..570ccf78 100644 --- a/src/libs/libNet.cpp +++ b/src/libs/libNet.cpp @@ -1528,6 +1528,10 @@ struct NpEntitlementAccessAddcontEntitlementInfo { uint32_t download_status; }; +struct NpEntitlementAccessEntitlementKey { + uint8_t data[16]; +}; + static constexpr NpEntitlementAccessAddcontEntitlementInfo NP_ENTITLEMENT_ACCESS_ADDON_LIST[] = { {{{"85y-je"}, {}}, 3, 4}, // GTA V hash 0xf4315381 {{{"5d5c48"}, {}}, 3, 4}, // GTA V hash 0x961c34b0 @@ -1614,12 +1618,31 @@ static int KYTY_SYSV_ABI NpEntitlementAccessGetAddcontEntitlementInfo( return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; } +static int KYTY_SYSV_ABI NpEntitlementAccessGetEntitlementKey( + uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, + NpEntitlementAccessEntitlementKey* key) { + PRINT_NAME(); + + LOGF("\t service_label = %" PRIu32 "\n" + "\t entitlement_label = 0x%016" PRIx64 "\n" + "\t key = 0x%016" PRIx64 "\n", + service_label, reinterpret_cast(entitlement_label), + reinterpret_cast(key)); + + if (entitlement_label == nullptr || key == nullptr) { + return NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER; + } + + return NP_ENTITLEMENT_ACCESS_ERROR_NO_ENTITLEMENT; +} + LIB_DEFINE(InitNet_1_NpEntitlementAccess) { LIB_FUNC("jO8DM8oyego", LibNpEntitlementAccess::NpEntitlementAccessInitialize); LIB_FUNC("lPDO62PpJIA", LibNpEntitlementAccess::NpEntitlementAccessGetSkuFlag); LIB_FUNC("TFyU+KFBv54", LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfoList); LIB_FUNC("xddD23+8TfQ", LibNpEntitlementAccess::NpEntitlementAccessGetAddcontEntitlementInfo); + LIB_FUNC("5LiMEPuW0DQ", LibNpEntitlementAccess::NpEntitlementAccessGetEntitlementKey); } } // namespace LibNpEntitlementAccess diff --git a/src/libs/libVideoOut.cpp b/src/libs/libVideoOut.cpp index 3854bcee..b6a22680 100644 --- a/src/libs/libVideoOut.cpp +++ b/src/libs/libVideoOut.cpp @@ -47,4 +47,14 @@ LIB_DEFINE(InitVideoOut_1) { } // namespace LibGen5 +namespace LibGen5::VrrStatus { + +LIB_VERSION("VideoOutVrrStatus", 1, "VideoOut", 1, 1); + +LIB_DEFINE(InitVideoOutVrrStatus_1) { + LIB_FUNC("kP2L8t3j-aM", VideoOut::VideoOutGetVrrStatus); +} + +} // namespace LibGen5::VrrStatus + } // namespace Libs diff --git a/src/libs/libs.cpp b/src/libs/libs.cpp index f98a82b7..c177745d 100644 --- a/src/libs/libs.cpp +++ b/src/libs/libs.cpp @@ -72,6 +72,9 @@ LIB_DEFINE(InitRtc_1); namespace LibGen5 { LIB_DEFINE(InitVideoOut_1); +namespace VrrStatus { +LIB_DEFINE(InitVideoOutVrrStatus_1); +} // namespace VrrStatus } // namespace LibGen5 LIB_DEFINE(InitAppContent_1); @@ -138,6 +141,7 @@ void InitAll(Loader::SymbolDatabase* s) { InitUserService_1(s); VideoDec2::InitVideoDec2_1(s); LibGen5::InitVideoOut_1(s); + LibGen5::VrrStatus::InitVideoOutVrrStatus_1(s); InitWebBrowserDialog_1(s); }