Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/libs/SceTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,13 @@ struct VideoOutOutputStatus {
std::uint64_t reserved[3] = {};
};

struct VideoOutResolutionStatus {
std::uint32_t fullWidth = 0;
std::uint32_t fullHeight = 0;
std::uint32_t paneWidth = 0;
std::uint32_t paneHeight = 0;
};

struct VideoOutOutputOptions { std::uint32_t internalData[16] = {}; };

struct VideoOutColorSettings {
Expand Down
10 changes: 10 additions & 0 deletions core/libs/prx/libSceLibcInternal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,14 @@ if(BUILD_TESTING)
add_test(NAME guest_force_tls_destructor COMMAND guest_force_tls_destructor_tests)
set_tests_properties(guest_force_tls_destructor PROPERTIES TIMEOUT 20)
endif()
add_test_executable(guest_syslog_tests ${LIBS_INCLUDE_DIR}/tests/GuestSyslog.cpp)
target_include_directories(guest_syslog_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_syslog_tests PRIVATE libSceLibcInternal)
configure_windows_unwind(guest_syslog_tests)
add_test(NAME guest_syslog COMMAND guest_syslog_tests)
set_tests_properties(guest_syslog PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_syslog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
endif()
endif()
25 changes: 25 additions & 0 deletions core/libs/prx/libSceLibcInternal/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/HeapDiagnostics.hpp"
#include "prx/libc/include/HostThreadLocal.hpp"
#include "prx/libc/include/WindowsFormatting.hpp"
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <mutex>
#include <sstream>
#include <stdexcept>
#include <vector>
Expand Down Expand Up @@ -130,4 +134,25 @@ int APS5_VABI _sceLibcInternalForceTlsDestructor_nid_postfix(KernelModule handle
return 0;
}

void APS5_VABI syslog_nid_postfix(int priority, const char* message, ...) {
static_cast<void>(priority);
if (message == nullptr) throw std::runtime_error("syslog: null message");
static std::mutex outputMutex;
const std::lock_guard lock(outputMutex);
#ifdef _WIN32
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, message);
std::string buffer;
LibcDetail::FormatWindows(nullptr, 0, message, args, &buffer);
__builtin_sysv_va_end(args);
std::fwrite(buffer.data(), 1, buffer.size(), stderr);
#else
std::va_list args;
va_start(args, message);
std::vfprintf(stderr, message, args);
va_end(args);
#endif
std::fflush(stderr);
}

}
1 change: 1 addition & 0 deletions core/libs/prx/libSceVideoOut/include/Output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ int APS5_VABI sceVideoOutSetFlipRate(int handle, int rate);
int APS5_VABI sceVideoOutSetWindowModeMargins(int handle, int top, int bottom);
int APS5_VABI sceVideoOutGetFlipStatus(int handle, VideoOutFlipStatus* status);
int APS5_VABI sceVideoOutGetOutputStatus(int handle, VideoOutOutputStatus* status);
int APS5_VABI sceVideoOutGetResolutionStatus(int handle, VideoOutResolutionStatus* status);
int APS5_VABI sceVideoOutGetVblankStatus(int handle, VideoOutVblankStatus* status);
int APS5_VABI sceVideoOutIsFlipPending(int handle);
int APS5_VABI sceVideoOutWaitVblank(int handle);
Expand Down
19 changes: 19 additions & 0 deletions core/libs/prx/libSceVideoOut/src/Output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ int APS5_VABI sceVideoOutGetOutputStatus(int handle, VideoOutOutputStatus* statu
LibcAwaitExit_nid_postfix();
}

int APS5_VABI sceVideoOutGetResolutionStatus(int handle, VideoOutResolutionStatus* status) try {
if (status == nullptr) {
throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_INVALID_ADDRESS");
}
auto cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_INVALID_HANDLE");
}
std::unique_lock lock(cfg->mutex);
cfg->Check();
status->fullWidth = cfg->width;
status->fullHeight = cfg->height;
status->paneWidth = cfg->width;
status->paneHeight = cfg->height;
return 0;
} catch (const ProcessShutdown&) {
LibcAwaitExit_nid_postfix();
}

int APS5_VABI sceVideoOutIsFlipPending(int handle) try {
auto cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
Expand Down
231 changes: 231 additions & 0 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <afunix.h>
#else
#include <sys/socket.h>
#include <sys/ioctl.h>
Expand All @@ -13,9 +14,11 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <new>
#include <map>
Expand Down Expand Up @@ -168,6 +171,193 @@ void GuestAddress(const sockaddr_storage& native, void* output, std::uint32_t* l
std::memcpy(output, bytes, std::min<std::uint32_t>(*length, bytes[0]));
*length = bytes[0];
}
void CloseNative(NativeSocket value) {
if (value == Invalid) return;
#ifdef _WIN32
closesocket(value);
#else
::close(value);
#endif
}
bool NativeGetsockname(NativeSocket value, sockaddr_storage& address, socklen_t& length) {
length = sizeof(address);
return ::getsockname(value, reinterpret_cast<sockaddr*>(&address), &length) == 0;
}
bool LoopbackAddress(int nativeFamily, sockaddr_storage& address, socklen_t& length) {
std::memset(&address, 0, sizeof(address));
if (nativeFamily == AF_INET) {
auto& v4 = reinterpret_cast<sockaddr_in&>(address);
v4.sin_family = AF_INET;
v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
length = sizeof(v4);
return true;
}
if (nativeFamily == AF_INET6) {
auto& v6 = reinterpret_cast<sockaddr_in6&>(address);
v6.sin6_family = AF_INET6;
v6.sin6_addr = in6addr_loopback;
length = sizeof(v6);
return true;
}
return false;
}
bool MakeStreamPair(int nativeFamily, NativeSocket pair[2]) {
sockaddr_storage loopback{};
socklen_t loopbackLength = 0;
if (!LoopbackAddress(nativeFamily, loopback, loopbackLength)) return false;
const NativeSocket listener = ::socket(nativeFamily, SOCK_STREAM, 0);
if (listener == Invalid) return false;
bool ok = false;
if (::bind(listener, reinterpret_cast<const sockaddr*>(&loopback), loopbackLength) == 0 &&
::listen(listener, 1) == 0) {
sockaddr_storage bound{};
socklen_t boundLength = 0;
if (NativeGetsockname(listener, bound, boundLength)) {
const NativeSocket connector = ::socket(nativeFamily, SOCK_STREAM, 0);
if (connector != Invalid) {
if (::connect(connector, reinterpret_cast<const sockaddr*>(&bound), boundLength) == 0) {
const NativeSocket acceptor = ::accept(listener, nullptr, nullptr);
if (acceptor != Invalid) {
pair[0] = connector;
pair[1] = acceptor;
ok = true;
} else {
CloseNative(connector);
}
} else {
CloseNative(connector);
}
}
}
}
CloseNative(listener);
return ok;
}
bool MakeDatagramPair(int nativeFamily, NativeSocket pair[2]) {
sockaddr_storage loopback{};
socklen_t loopbackLength = 0;
if (!LoopbackAddress(nativeFamily, loopback, loopbackLength)) return false;
const NativeSocket first = ::socket(nativeFamily, SOCK_DGRAM, 0);
if (first == Invalid) return false;
bool ok = false;
if (::bind(first, reinterpret_cast<const sockaddr*>(&loopback), loopbackLength) == 0) {
sockaddr_storage firstBound{};
socklen_t firstLength = 0;
if (NativeGetsockname(first, firstBound, firstLength)) {
const NativeSocket second = ::socket(nativeFamily, SOCK_DGRAM, 0);
if (second != Invalid) {
if (::bind(second, reinterpret_cast<const sockaddr*>(&loopback), loopbackLength) == 0) {
sockaddr_storage secondBound{};
socklen_t secondLength = 0;
if (NativeGetsockname(second, secondBound, secondLength) &&
::connect(first, reinterpret_cast<const sockaddr*>(&secondBound), secondLength) == 0 &&
::connect(second, reinterpret_cast<const sockaddr*>(&firstBound), firstLength) == 0) {
pair[0] = first;
pair[1] = second;
ok = true;
}
}
if (!ok) CloseNative(second);
}
}
}
if (!ok) CloseNative(first);
return ok;
}
#ifdef _WIN32
bool UnixSocketAddress(const char* path, sockaddr_storage& address, socklen_t& length) {
const std::size_t pathLength = std::strlen(path);
if (pathLength >= sizeof(sockaddr_un{}.sun_path)) return false;
std::memset(&address, 0, sizeof(address));
auto& unix = reinterpret_cast<sockaddr_un&>(address);
unix.sun_family = AF_UNIX;
std::memcpy(unix.sun_path, path, pathLength + 1);
length = static_cast<socklen_t>(offsetof(sockaddr_un, sun_path) + pathLength + 1);
return true;
}
bool MakeUnixPair(int nativeType, NativeSocket pair[2]) {
char directory[MAX_PATH];
const DWORD directoryLength = GetTempPathA(sizeof(directory), directory);
if (directoryLength == 0 || directoryLength > sizeof(directory) - 1) return false;
static std::atomic<unsigned long> sequence{0};
const unsigned long tag = sequence.fetch_add(1);
const unsigned long process = static_cast<unsigned long>(GetCurrentProcessId());
char firstPath[MAX_PATH];
char secondPath[MAX_PATH];
const int firstLength = std::snprintf(firstPath, sizeof(firstPath), "%sanyps5-sp-%lu-%lu-a", directory, process, tag);
const int secondLength = std::snprintf(secondPath, sizeof(secondPath), "%sanyps5-sp-%lu-%lu-b", directory, process, tag);
if (firstLength <= 0 || secondLength <= 0 ||
static_cast<std::size_t>(firstLength) >= sizeof(firstPath) ||
static_cast<std::size_t>(secondLength) >= sizeof(secondPath))
return false;
DeleteFileA(firstPath);
DeleteFileA(secondPath);
if (nativeType == SOCK_STREAM) {
sockaddr_storage address{};
socklen_t addressLength = 0;
if (!UnixSocketAddress(firstPath, address, addressLength)) return false;
const NativeSocket listener = ::socket(AF_UNIX, SOCK_STREAM, 0);
if (listener == Invalid) return false;
bool ok = false;
if (::bind(listener, reinterpret_cast<const sockaddr*>(&address), addressLength) == 0 &&
::listen(listener, 1) == 0) {
const NativeSocket connector = ::socket(AF_UNIX, SOCK_STREAM, 0);
if (connector != Invalid) {
if (::connect(connector, reinterpret_cast<const sockaddr*>(&address), addressLength) == 0) {
const NativeSocket acceptor = ::accept(listener, nullptr, nullptr);
if (acceptor != Invalid) {
pair[0] = connector;
pair[1] = acceptor;
ok = true;
} else {
CloseNative(connector);
}
} else {
CloseNative(connector);
}
}
}
CloseNative(listener);
DeleteFileA(firstPath);
return ok;
}
const NativeSocket first = ::socket(AF_UNIX, SOCK_DGRAM, 0);
if (first == Invalid) return false;
bool ok = false;
sockaddr_storage firstAddress{};
socklen_t firstAddressLength = 0;
sockaddr_storage secondAddress{};
socklen_t secondAddressLength = 0;
if (UnixSocketAddress(firstPath, firstAddress, firstAddressLength) &&
UnixSocketAddress(secondPath, secondAddress, secondAddressLength) &&
::bind(first, reinterpret_cast<const sockaddr*>(&firstAddress), firstAddressLength) == 0) {
const NativeSocket second = ::socket(AF_UNIX, SOCK_DGRAM, 0);
if (second != Invalid) {
if (::bind(second, reinterpret_cast<const sockaddr*>(&secondAddress), secondAddressLength) == 0 &&
::connect(first, reinterpret_cast<const sockaddr*>(&secondAddress), secondAddressLength) == 0 &&
::connect(second, reinterpret_cast<const sockaddr*>(&firstAddress), firstAddressLength) == 0) {
pair[0] = first;
pair[1] = second;
ok = true;
}
if (!ok) CloseNative(second);
}
}
if (!ok) CloseNative(first);
DeleteFileA(firstPath);
DeleteFileA(secondPath);
return ok;
}
#endif
bool MakeConnectedPair(int nativeFamily, int nativeType, NativeSocket pair[2]) {
#ifndef _WIN32
if (nativeFamily == AF_UNIX) return ::socketpair(AF_UNIX, nativeType, 0, pair) == 0;
#else
if (nativeFamily == AF_UNIX) return MakeUnixPair(nativeType, pair);
#endif
if (nativeType == SOCK_STREAM) return MakeStreamPair(nativeFamily, pair);
return MakeDatagramPair(nativeFamily, pair);
}
}

int GuestSockets::Close(int descriptor) {
Expand Down Expand Up @@ -286,6 +476,47 @@ int APS5_VABI socket_nid_postfix(int family, int type, int protocol) {
return Fail(12);
}
}
int APS5_VABI socketpair_nid_postfix(int family, int type, int protocol, int* pair) {
if (pair == nullptr) return Fail(14);
if (protocol != 0) return Fail(43);
const int nativeType = type == 1 ? SOCK_STREAM : type == 2 ? SOCK_DGRAM : -1;
if (nativeType == -1) return Fail(43);
int nativeFamily;
if (family == 1) nativeFamily = AF_UNIX;
else if (family == 2) nativeFamily = AF_INET;
else if (family == 28) nativeFamily = AF_INET6;
else return Fail(47);
#ifdef _WIN32
static const int startup = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }();
if (startup) return Fail(5);
#endif
NativeSocket native[2] = {Invalid, Invalid};
if (!MakeConnectedPair(nativeFamily, nativeType, native)) return Fail(NativeError());
Socket firstGuard(native[0], family, type);
Socket secondGuard(native[1], family, type);
try {
auto first = std::make_shared<Socket>(native[0], family, type);
auto second = std::make_shared<Socket>(native[1], family, type);
firstGuard.value = Invalid;
secondGuard.value = Invalid;
std::lock_guard lock(socketsMutex);
if (nextDescriptor >= INT_MAX - 1) return Fail(24);
const int firstDescriptor = nextDescriptor++;
const int secondDescriptor = nextDescriptor++;
sockets.emplace(firstDescriptor, std::move(first));
try {
sockets.emplace(secondDescriptor, std::move(second));
} catch (const std::bad_alloc&) {
sockets.erase(firstDescriptor);
return Fail(12);
}
pair[0] = firstDescriptor;
pair[1] = secondDescriptor;
return 0;
} catch (const std::bad_alloc&) {
return Fail(12);
}
}
int APS5_VABI connect_nid_postfix(int descriptor, const void* address, std::uint32_t length) {
const auto socket = Lookup(descriptor);
if (!socket) return -1;
Expand Down
34 changes: 34 additions & 0 deletions core/libs/tests/GuestSyslog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <string>
extern "C" {
void APS5_VABI syslog_nid_postfix(int, const char*, ...);
}
static void Require(bool value) { if (!value) std::abort(); }
int main() {
const char* path = "guest_syslog_output.txt";
Require(std::freopen(path, "w", stderr) != nullptr);
syslog_nid_postfix(6, "plain message");
syslog_nid_postfix(3, "formatted %s %d", "value", 42);
std::fflush(stderr);
std::fclose(stderr);
FILE* input = std::fopen(path, "r");
Require(input != nullptr);
std::string actual;
char chunk[256];
std::size_t count;
while ((count = std::fread(chunk, 1, sizeof(chunk), input)) > 0) actual.append(chunk, count);
std::fclose(input);
std::remove(path);
Require(actual == "plain messageformatted value 42");
bool threw = false;
try {
syslog_nid_postfix(6, nullptr);
} catch (const std::runtime_error&) {
threw = true;
}
Require(threw);
}
Loading