Implement missing kernel and startup library imports (#772)

* Implement missing kernel and startup library imports

* Remove chmod and test changes; guard vectored I/O size

---------

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
Louis 2026-09-23 13:57:42 +02:00 committed by GitHub
parent 9860c2bbf8
commit b5ba482fc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 235 additions and 0 deletions

View File

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

View File

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

View File

@ -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<KernelIovec>* 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<KernelIovec> 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<uint64_t>(buffer.iov_base),
buffer.iov_len);
FillRandomBuffer(buffer.iov_base, buffer.iov_len);
}
}
return static_cast<int64_t>(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<uint64_t>(offset))) {
return KERNEL_ERROR_EIO;
}
auto remaining = static_cast<uint64_t>(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<uint32_t>(std::min<uint64_t>(buffer.iov_len, remaining));
Memory::InvalidateMemory(reinterpret_cast<uint64_t>(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<KernelIovec> 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<uint64_t>(offset));
if (target > static_cast<uint64_t>(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<uint32_t>(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();

View File

@ -6,6 +6,7 @@
#include "common/stringUtils.h"
#include "kernel/pthread.h"
#include <cstddef>
#include <filesystem>
namespace Libs::LibKernel::FileSystem {
@ -32,6 +33,14 @@ struct FileStat {
unsigned int: (8 / 2) * (16 - static_cast<int>(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);

View File

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

View File

@ -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<uint64_t>(entitlement_label),
reinterpret_cast<uint64_t>(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

View File

@ -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

View File

@ -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);
}