mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
264 lines
9.0 KiB
C++
264 lines
9.0 KiB
C++
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <stdexcept>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <utility>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
|
|
namespace {
|
|
|
|
constexpr std::int32_t VOICE_QOS_APP_TYPE_GAME = 0x20000000;
|
|
constexpr std::int32_t VOICE_QOS_APP_TYPE_10000000 = 0x10000000;
|
|
constexpr std::uint32_t VOICE_QOS_MEMORY_SIZE = 0x40000;
|
|
constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_NOT_INIT = static_cast<int>(0x804E0901);
|
|
constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID = static_cast<int>(0x804E0902);
|
|
constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT = static_cast<int>(0x804E0907);
|
|
constexpr int SCE_VOICE_ERROR_LIBVOICEQOS_INITIALIZED = static_cast<int>(0x804E0905);
|
|
|
|
struct Endpoint {};
|
|
|
|
struct Connection {
|
|
std::int32_t local;
|
|
std::int32_t remote;
|
|
};
|
|
|
|
struct QoS {
|
|
std::mutex mutex;
|
|
bool initialized = false;
|
|
std::int32_t endpointLimit = 0;
|
|
std::map<std::int32_t, Endpoint> locals;
|
|
std::map<std::int32_t, Endpoint> remotes;
|
|
std::map<std::int32_t, Connection> connections;
|
|
};
|
|
|
|
QoS& State() {
|
|
static QoS qos;
|
|
return qos;
|
|
}
|
|
|
|
int CreateEndpoint(std::int32_t* id, std::map<std::int32_t, Endpoint>& endpoints, std::int32_t limit) {
|
|
if (!id) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
for (std::int32_t candidate = 0; candidate < limit; ++candidate) {
|
|
if (!endpoints.contains(candidate)) {
|
|
endpoints.emplace(candidate, Endpoint{});
|
|
*id = candidate;
|
|
return 0;
|
|
}
|
|
}
|
|
*id = -1;
|
|
return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT;
|
|
}
|
|
|
|
int DeleteEndpoint(std::int32_t id, std::map<std::int32_t, Endpoint>& endpoints) {
|
|
return endpoints.erase(id) != 0 ? 0 : SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
}
|
|
|
|
template<typename TBody>
|
|
int Locked(TBody&& body) {
|
|
auto& qos = State();
|
|
std::lock_guard lock(qos.mutex);
|
|
if (!qos.initialized) return SCE_VOICE_ERROR_LIBVOICEQOS_NOT_INIT;
|
|
return body(qos);
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
int APS5_VABI sceVoiceQoSInit(void* memBlock, uint32_t memSize, int32_t appType) {
|
|
auto& qos = State();
|
|
std::lock_guard lock(qos.mutex);
|
|
if (qos.initialized) return SCE_VOICE_ERROR_LIBVOICEQOS_INITIALIZED;
|
|
if (!memBlock || memSize != VOICE_QOS_MEMORY_SIZE || (appType != VOICE_QOS_APP_TYPE_GAME && appType != VOICE_QOS_APP_TYPE_10000000)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
qos.endpointLimit = appType == VOICE_QOS_APP_TYPE_10000000 ? 15 : 7;
|
|
qos.initialized = true;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSEnd() {
|
|
return Locked([](QoS& qos) {
|
|
qos.connections.clear();
|
|
qos.locals.clear();
|
|
qos.remotes.clear();
|
|
qos.initialized = false;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSCreateLocalEndpoint(int32_t* localId, int32_t userId, int32_t deviceInId, int32_t deviceOutId) {
|
|
(void)deviceInId;
|
|
(void)deviceOutId;
|
|
return Locked([&](QoS& qos) {
|
|
if (localId && userId == -1) {
|
|
*localId = -1;
|
|
return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT;
|
|
}
|
|
return CreateEndpoint(localId, qos.locals, 1);
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSDeleteLocalEndpoint(int32_t localId) {
|
|
return Locked([&](QoS& qos) { return DeleteEndpoint(localId, qos.locals); });
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSCreateRemoteEndpoint(int32_t* remoteId) {
|
|
return Locked([&](QoS& qos) { return CreateEndpoint(remoteId, qos.remotes, qos.endpointLimit); });
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSDeleteRemoteEndpoint(int32_t remoteId) {
|
|
return Locked([&](QoS& qos) {
|
|
const int result = DeleteEndpoint(remoteId, qos.remotes);
|
|
if (result != 0) return result;
|
|
for (auto it = qos.connections.begin(); it != qos.connections.end(); ++it) {
|
|
if (it->second.remote == remoteId) {
|
|
qos.connections.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSConnect(int32_t* connectionId, int32_t localId, int32_t remoteId) {
|
|
return Locked([&](QoS& qos) {
|
|
if (!connectionId || localId != 0 || remoteId < 0 || remoteId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
if (!qos.locals.contains(localId) || !qos.remotes.contains(remoteId)) throw std::runtime_error("sceVoiceQoSConnect: missing voice port error behavior is not implemented");
|
|
for (std::int32_t candidate = 0; candidate < qos.endpointLimit; ++candidate) {
|
|
if (!qos.connections.contains(candidate)) {
|
|
qos.connections.emplace(candidate, Connection{localId, remoteId});
|
|
*connectionId = candidate;
|
|
return 0;
|
|
}
|
|
}
|
|
*connectionId = -1;
|
|
return SCE_VOICE_ERROR_LIBVOICEQOS_LIMIT;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSDisconnect(int32_t connectionId) {
|
|
return Locked([&](QoS& qos) {
|
|
if (connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
qos.connections.erase(connectionId);
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSSetConnectionAttribute(std::int32_t connectionId, std::int32_t attributeId, const void* value, std::int32_t size) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSSetLocalEndpointAttribute(int32_t localId, int32_t attributeId, const void* value, int32_t size) {
|
|
return Locked([&](QoS&) -> int {
|
|
if (localId != 0 || !value || attributeId < 0 || attributeId > 3 || size != (attributeId % 2 == 0 ? 4 : 1)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
throw std::runtime_error("sceVoiceQoSSetLocalEndpointAttribute: voice port attributes are not implemented");
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSSetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, const void* value, int32_t size) {
|
|
return Locked([&](QoS& qos) -> int {
|
|
if (remoteId < 0 || remoteId >= qos.endpointLimit || !value || attributeId != 7 || size != 1) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
throw std::runtime_error("sceVoiceQoSSetRemoteEndpointAttribute: voice port attributes are not implemented");
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSReadPacket(int32_t connectionId, void* buffer, uint32_t* size) {
|
|
return Locked([&](QoS& qos) {
|
|
if (!buffer || !size || !qos.connections.contains(connectionId)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
*size = 0;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSWritePacket(int32_t connectionId, const void* buffer, uint32_t* size) {
|
|
return Locked([&](QoS& qos) {
|
|
if (!buffer || !size || !qos.connections.contains(connectionId)) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetLocalEndpointAttribute(int32_t localId, int32_t attributeId, void* value, int32_t size) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetRemoteEndpointAttribute(int32_t remoteId, int32_t attributeId, void* value, int32_t size) {
|
|
(void)remoteId;
|
|
(void)attributeId;
|
|
(void)value;
|
|
(void)size;
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetConnectionAttribute(int32_t connectionId, int32_t attributeId, void* value, int32_t size) {
|
|
(void)connectionId;
|
|
(void)attributeId;
|
|
(void)value;
|
|
(void)size;
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetLocalEndpoint(int32_t connectionId, int32_t* localId) {
|
|
return Locked([&](QoS& qos) {
|
|
if (!localId || connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
const auto it = qos.connections.find(connectionId);
|
|
*localId = it == qos.connections.end() ? -1 : it->second.local;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetRemoteEndpoint(int32_t connectionId, int32_t* remoteId) {
|
|
return Locked([&](QoS& qos) {
|
|
if (!remoteId || connectionId < 0 || connectionId >= qos.endpointLimit) return SCE_VOICE_ERROR_LIBVOICEQOS_ARGUMENT_INVALID;
|
|
const auto it = qos.connections.find(connectionId);
|
|
*remoteId = it == qos.connections.end() ? -1 : it->second.remote;
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSEnableChat() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSDisableChat() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSGetStatus(int32_t connectionId, int32_t statusId, void* value, int32_t size) {
|
|
(void)connectionId;
|
|
(void)statusId;
|
|
(void)value;
|
|
(void)size;
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSDebugGetStatus(std::int32_t connectionId, std::int32_t statusId, void* value, std::int32_t size) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSInitHQ(void* memory, std::uint32_t memorySize, std::int32_t appType) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSSetMode() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVoiceQoSTerminate() {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
}
|