mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-06 07:12:28 +01:00
Merge branch 'master' into windows-clang
This commit is contained in:
commit
24a94f2dbe
3
.github/workflows/rpcs3.yml
vendored
3
.github/workflows/rpcs3.yml
vendored
|
|
@ -387,8 +387,9 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
export CCACHE_DIR=$(cygpath -u "$CCACHE_DIR")
|
export CCACHE_DIR=$(cygpath -u "$CCACHE_DIR")
|
||||||
echo "CCACHE_DIR=$CCACHE_DIR"
|
echo "CCACHE_DIR=$CCACHE_DIR"
|
||||||
.ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }}
|
|
||||||
.ci/build-windows-clang.sh
|
.ci/build-windows-clang.sh
|
||||||
|
.ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }}
|
||||||
|
.ci/deploy-windows-${{ matrix.compiler }}.sh
|
||||||
|
|
||||||
- name: Save build Ccache
|
- name: Save build Ccache
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,8 @@ if(BUILD_RPCS3_TESTS)
|
||||||
PRIVATE
|
PRIVATE
|
||||||
tests/test.cpp
|
tests/test.cpp
|
||||||
tests/test_fmt.cpp
|
tests/test_fmt.cpp
|
||||||
|
tests/test_pair.cpp
|
||||||
|
tests/test_tuple.cpp
|
||||||
tests/test_simple_array.cpp
|
tests/test_simple_array.cpp
|
||||||
tests/test_address_range.cpp
|
tests/test_address_range.cpp
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::str
|
||||||
version_end = ver_end;
|
version_end = ver_end;
|
||||||
revision = rev;
|
revision = rev;
|
||||||
self_type = type;
|
self_type = type;
|
||||||
hex_to_bytes(erk, e.c_str(), 0);
|
hex_to_bytes(erk, e, 0);
|
||||||
hex_to_bytes(riv, r.c_str(), 0);
|
hex_to_bytes(riv, r, 0);
|
||||||
hex_to_bytes(pub, pb.c_str(), 0);
|
hex_to_bytes(pub, pb, 0);
|
||||||
hex_to_bytes(priv, pr.c_str(), 0);
|
hex_to_bytes(priv, pr, 0);
|
||||||
curve_type = ct;
|
curve_type = ct;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@
|
||||||
#include "sha1.h"
|
#include "sha1.h"
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
#include "key_vault.h"
|
#include "key_vault.h"
|
||||||
|
#include <charconv>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
#include <time.h>
|
#include <ctime>
|
||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
#include "Utilities/File.h"
|
#include "Utilities/File.h"
|
||||||
|
|
||||||
|
|
@ -21,50 +23,24 @@
|
||||||
// Auxiliary functions (endian swap, xor).
|
// Auxiliary functions (endian swap, xor).
|
||||||
|
|
||||||
// Hex string conversion auxiliary functions.
|
// Hex string conversion auxiliary functions.
|
||||||
u64 hex_to_u64(const char* hex_str)
|
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length)
|
||||||
{
|
{
|
||||||
auto length = std::strlen(hex_str);
|
const auto strn_length = (str_length > 0) ? str_length : hex_str.size();
|
||||||
u64 tmp = 0;
|
|
||||||
u64 result = 0;
|
|
||||||
char c;
|
|
||||||
|
|
||||||
while (length--)
|
|
||||||
{
|
|
||||||
c = *hex_str++;
|
|
||||||
if((c >= '0') && (c <= '9'))
|
|
||||||
tmp = c - '0';
|
|
||||||
else if((c >= 'a') && (c <= 'f'))
|
|
||||||
tmp = c - 'a' + 10;
|
|
||||||
else if((c >= 'A') && (c <= 'F'))
|
|
||||||
tmp = c - 'A' + 10;
|
|
||||||
else
|
|
||||||
tmp = 0;
|
|
||||||
result |= (tmp << (length * 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hex_to_bytes(unsigned char* data, const char* hex_str, unsigned int str_length)
|
|
||||||
{
|
|
||||||
const auto strn_length = (str_length > 0) ? str_length : std::strlen(hex_str);
|
|
||||||
auto data_length = strn_length / 2;
|
|
||||||
char tmp_buf[3] = {0, 0, 0};
|
|
||||||
|
|
||||||
// Don't convert if the string length is odd.
|
// Don't convert if the string length is odd.
|
||||||
if ((strn_length % 2) == 0)
|
if ((strn_length % 2) == 0)
|
||||||
{
|
{
|
||||||
while (data_length--)
|
for (size_t i = 0; i < strn_length; i += 2)
|
||||||
{
|
{
|
||||||
tmp_buf[0] = *hex_str++;
|
const auto [ptr, err] = std::from_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16);
|
||||||
tmp_buf[1] = *hex_str++;
|
if (err != std::errc())
|
||||||
|
{
|
||||||
*data++ = static_cast<u8>(hex_to_u64(tmp_buf) & 0xFF);
|
fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
||||||
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len)
|
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
#include "util/types.hpp"
|
#include "util/types.hpp"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
enum { CRYPTO_MAX_PATH = 4096 };
|
enum { CRYPTO_MAX_PATH = 4096 };
|
||||||
|
|
||||||
|
|
@ -15,8 +16,7 @@ char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PA
|
||||||
std::string sha256_get_hash(const char* data, usz size, bool lower_case);
|
std::string sha256_get_hash(const char* data, usz size, bool lower_case);
|
||||||
|
|
||||||
// Hex string conversion auxiliary functions.
|
// Hex string conversion auxiliary functions.
|
||||||
u64 hex_to_u64(const char* hex_str);
|
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length);
|
||||||
void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_length);
|
|
||||||
|
|
||||||
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
||||||
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len);
|
void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len);
|
||||||
|
|
|
||||||
|
|
@ -1230,7 +1230,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||||
if (flags & SAF_UNKNOWN_FLAG_9) spuTgAttr->type |= 0x0800;
|
if (flags & SAF_UNKNOWN_FLAG_9) spuTgAttr->type |= 0x0800;
|
||||||
if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) spuTgAttr->type |= SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM;
|
if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) spuTgAttr->type |= SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM;
|
||||||
|
|
||||||
if (s32 rc = sys_spu_thread_group_create(ppu, spurs.ptr(&CellSpurs::spuTG), nSpus, spuPriority, spuTgAttr))
|
if (s32 rc = sys_spu_thread_group_create(ppu, spurs.ptr(&CellSpurs::spuTG), nSpus, spuPriority, vm::unsafe_ptr_cast<reduced_sys_spu_thread_group_attribute>(spuTgAttr)))
|
||||||
{
|
{
|
||||||
ppu_execute<&sys_spu_image_close>(ppu, spurs.ptr(&CellSpurs::spuImg));
|
ppu_execute<&sys_spu_image_close>(ppu, spurs.ptr(&CellSpurs::spuImg));
|
||||||
return rollback(), rc;
|
return rollback(), rc;
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,33 @@ static FORCE_INLINE void mov_rdata_avx(__m256i* dst, const __m256i* src)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Check if only a single 16-bytes block has changed
|
||||||
|
// Returning its position, or -1 if that is not the situation
|
||||||
|
static inline usz scan16_rdata(const decltype(spu_thread::rdata)& _lhs, const decltype(spu_thread::rdata)& _rhs)
|
||||||
|
{
|
||||||
|
const auto lhs = reinterpret_cast<const v128*>(_lhs);
|
||||||
|
const auto rhs = reinterpret_cast<const v128*>(_rhs);
|
||||||
|
|
||||||
|
u32 mask = 0;
|
||||||
|
|
||||||
|
for (usz i = 0; i < 8; i += 4)
|
||||||
|
{
|
||||||
|
const u32 a = (lhs[i + 0] != rhs[i + 0]) ? 1 : 0;
|
||||||
|
const u32 b = (lhs[i + 1] != rhs[i + 1]) ? 1 : 0;
|
||||||
|
const u32 c = (lhs[i + 2] != rhs[i + 2]) ? 1 : 0;
|
||||||
|
const u32 d = (lhs[i + 3] != rhs[i + 3]) ? 1 : 0;
|
||||||
|
|
||||||
|
mask |= ((a << 0) + (b << 1) + (c << 2) + (d << 3)) << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask && (mask & (mask - 1)) == 0)
|
||||||
|
{
|
||||||
|
return std::countr_zero(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return umax;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
__forceinline
|
__forceinline
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3854,6 +3881,11 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const auto cast_as = [](void* ptr, usz pos){ return reinterpret_cast<u128*>(ptr) + pos; };
|
||||||
|
static const auto cast_as_const = [](const void* ptr, usz pos){ return reinterpret_cast<const u128*>(ptr) + pos; };
|
||||||
|
|
||||||
|
const usz diff16_pos = scan16_rdata(to_write, rdata);
|
||||||
|
|
||||||
auto [_oldd, _ok] = res.fetch_op([&](u64& r)
|
auto [_oldd, _ok] = res.fetch_op([&](u64& r)
|
||||||
{
|
{
|
||||||
if ((r & -128) != rtime || (r & 127))
|
if ((r & -128) != rtime || (r & 127))
|
||||||
|
|
@ -3975,8 +4007,19 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
|
||||||
|
|
||||||
if (cmp_rdata(rdata, super_data))
|
if (cmp_rdata(rdata, super_data))
|
||||||
{
|
{
|
||||||
mov_rdata(super_data, to_write);
|
if (diff16_pos != umax)
|
||||||
return true;
|
{
|
||||||
|
// Do it with CMPXCHG16B if possible, this allows to improve accuracy whenever "RSX Accurate Reservations" is off
|
||||||
|
if (atomic_storage<u128>::compare_exchange(*cast_as(super_data, diff16_pos), *cast_as(rdata, diff16_pos), *cast_as_const(to_write, diff16_pos)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mov_rdata(super_data, to_write);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -468,11 +468,11 @@ error_code sys_ppu_thread_restart(ppu_thread& ppu)
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 unk, s32 prio, u32 _stacksz, u64 flags, vm::cptr<char> threadname)
|
error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 unk, s32 prio, u64 _stacksz, u64 flags, vm::cptr<char> threadname)
|
||||||
{
|
{
|
||||||
ppu.state += cpu_flag::wait;
|
ppu.state += cpu_flag::wait;
|
||||||
|
|
||||||
sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=*0x%x)",
|
sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%llx, flags=0x%llx, threadname=*0x%x)",
|
||||||
thread_id, param, arg, unk, prio, _stacksz, flags, threadname);
|
thread_id, param, arg, unk, prio, _stacksz, flags, threadname);
|
||||||
|
|
||||||
// thread_id is checked for null in stub -> CELL_ENOMEM
|
// thread_id is checked for null in stub -> CELL_ENOMEM
|
||||||
|
|
@ -497,7 +497,8 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::p
|
||||||
const u32 tls = param->tls;
|
const u32 tls = param->tls;
|
||||||
|
|
||||||
// Compute actual stack size and allocate
|
// Compute actual stack size and allocate
|
||||||
const u32 stack_size = utils::align<u32>(std::max<u32>(_stacksz, 4096), 4096);
|
// 0 and UINT64_MAX both convert to 4096
|
||||||
|
const u64 stack_size = FN(x ? x : 4096)(utils::align<u64>(_stacksz, 4096));
|
||||||
|
|
||||||
auto& dct = g_fxo->get<lv2_memory_container>();
|
auto& dct = g_fxo->get<lv2_memory_container>();
|
||||||
|
|
||||||
|
|
@ -507,7 +508,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::p
|
||||||
return {CELL_ENOMEM, dct.size - dct.used};
|
return {CELL_ENOMEM, dct.size - dct.used};
|
||||||
}
|
}
|
||||||
|
|
||||||
const vm::addr_t stack_base{vm::alloc(stack_size, vm::stack, 4096)};
|
const vm::addr_t stack_base{vm::alloc(static_cast<u32>(stack_size), vm::stack, 4096)};
|
||||||
|
|
||||||
if (!stack_base)
|
if (!stack_base)
|
||||||
{
|
{
|
||||||
|
|
@ -532,7 +533,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::p
|
||||||
{
|
{
|
||||||
ppu_thread_params p;
|
ppu_thread_params p;
|
||||||
p.stack_addr = stack_base;
|
p.stack_addr = stack_base;
|
||||||
p.stack_size = stack_size;
|
p.stack_size = static_cast<u32>(stack_size);
|
||||||
p.tls_addr = tls;
|
p.tls_addr = tls;
|
||||||
p.entry = entry;
|
p.entry = entry;
|
||||||
p.arg0 = arg;
|
p.arg0 = arg;
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ error_code sys_ppu_thread_get_priority(ppu_thread& ppu, u32 thread_id, vm::ptr<s
|
||||||
error_code sys_ppu_thread_get_stack_information(ppu_thread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp);
|
error_code sys_ppu_thread_get_stack_information(ppu_thread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp);
|
||||||
error_code sys_ppu_thread_stop(ppu_thread& ppu, u32 thread_id);
|
error_code sys_ppu_thread_stop(ppu_thread& ppu, u32 thread_id);
|
||||||
error_code sys_ppu_thread_restart(ppu_thread& ppu);
|
error_code sys_ppu_thread_restart(ppu_thread& ppu);
|
||||||
error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 arg4, s32 prio, u32 stacksize, u64 flags, vm::cptr<char> threadname);
|
error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 arg4, s32 prio, u64 stacksize, u64 flags, vm::cptr<char> threadname);
|
||||||
error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id);
|
error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id);
|
||||||
error_code sys_ppu_thread_rename(ppu_thread& ppu, u32 thread_id, vm::cptr<char> name);
|
error_code sys_ppu_thread_rename(ppu_thread& ppu, u32 thread_id, vm::cptr<char> name);
|
||||||
error_code sys_ppu_thread_recover_page_fault(ppu_thread& ppu, u32 thread_id);
|
error_code sys_ppu_thread_recover_page_fault(ppu_thread& ppu, u32 thread_id);
|
||||||
|
|
|
||||||
|
|
@ -767,7 +767,12 @@ error_code sys_spu_thread_initialize(ppu_thread& ppu, vm::ptr<u32> thread, u32 g
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read thread name
|
// Read thread name
|
||||||
const std::string thread_name(attr_data.name.get_ptr(), std::max<u32>(attr_data.name_len, 1) - 1);
|
std::string thread_name;
|
||||||
|
|
||||||
|
if (attr_data.name_len && !vm::read_string(attr_data.name.addr(), attr_data.name_len - 1, thread_name, true))
|
||||||
|
{
|
||||||
|
return { CELL_EFAULT, attr_data.name.addr() };
|
||||||
|
}
|
||||||
|
|
||||||
const auto group = idm::get_unlocked<lv2_spu_group>(group_id);
|
const auto group = idm::get_unlocked<lv2_spu_group>(group_id);
|
||||||
|
|
||||||
|
|
@ -906,7 +911,7 @@ error_code sys_spu_thread_get_exit_status(ppu_thread& ppu, u32 id, vm::ptr<s32>
|
||||||
return CELL_ESTAT;
|
return CELL_ESTAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num, s32 prio, vm::ptr<sys_spu_thread_group_attribute> attr)
|
error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num, s32 prio, vm::ptr<reduced_sys_spu_thread_group_attribute> attr)
|
||||||
{
|
{
|
||||||
ppu.state += cpu_flag::wait;
|
ppu.state += cpu_flag::wait;
|
||||||
|
|
||||||
|
|
@ -914,13 +919,32 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num
|
||||||
|
|
||||||
const s32 min_prio = g_ps3_process_info.has_root_perm() ? 0 : 16;
|
const s32 min_prio = g_ps3_process_info.has_root_perm() ? 0 : 16;
|
||||||
|
|
||||||
const sys_spu_thread_group_attribute attr_data = *attr;
|
sys_spu_thread_group_attribute attr_data{};
|
||||||
|
{
|
||||||
|
const reduced_sys_spu_thread_group_attribute attr_reduced = *attr;
|
||||||
|
attr_data.name = attr_reduced.name;
|
||||||
|
attr_data.nsize = attr_reduced.nsize;
|
||||||
|
attr_data.type = attr_reduced.type;
|
||||||
|
|
||||||
|
// Read container-id member at offset 12 bytes conditionally (that's what LV2 does)
|
||||||
|
if (attr_data.type & SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER)
|
||||||
|
{
|
||||||
|
attr_data.ct = vm::unsafe_ptr_cast<sys_spu_thread_group_attribute>(attr)->ct;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attr_data.nsize > 0x80 || !num)
|
if (attr_data.nsize > 0x80 || !num)
|
||||||
{
|
{
|
||||||
return CELL_EINVAL;
|
return CELL_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string group_name;
|
||||||
|
|
||||||
|
if (attr_data.nsize && !vm::read_string(attr_data.name.addr(), attr_data.nsize - 1, group_name, true))
|
||||||
|
{
|
||||||
|
return { CELL_EFAULT, attr_data.name.addr() };
|
||||||
|
}
|
||||||
|
|
||||||
const s32 type = attr_data.type;
|
const s32 type = attr_data.type;
|
||||||
|
|
||||||
bool use_scheduler = true;
|
bool use_scheduler = true;
|
||||||
|
|
@ -1075,7 +1099,7 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr<u32> id, u32 num
|
||||||
return CELL_EBUSY;
|
return CELL_EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto group = idm::make_ptr<lv2_spu_group>(std::string(attr_data.name.get_ptr(), std::max<u32>(attr_data.nsize, 1) - 1), num, prio, type, ct, use_scheduler, mem_size);
|
const auto group = idm::make_ptr<lv2_spu_group>(std::move(group_name), num, prio, type, ct, use_scheduler, mem_size);
|
||||||
|
|
||||||
if (!group)
|
if (!group)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,18 @@ enum spu_stop_syscall : u32
|
||||||
SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE = 0x0120,
|
SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE = 0x0120,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sys_spu_thread_group_attribute
|
struct reduced_sys_spu_thread_group_attribute
|
||||||
{
|
{
|
||||||
be_t<u32> nsize; // name length including NULL terminator
|
be_t<u32> nsize; // name length including NULL terminator
|
||||||
vm::bcptr<char> name;
|
vm::bcptr<char> name;
|
||||||
be_t<s32> type;
|
be_t<s32> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sys_spu_thread_group_attribute
|
||||||
|
{
|
||||||
|
be_t<u32> nsize;
|
||||||
|
vm::bcptr<char> name;
|
||||||
|
be_t<s32> type;
|
||||||
be_t<u32> ct; // memory container id
|
be_t<u32> ct; // memory container id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -360,7 +367,7 @@ error_code _sys_spu_image_close(ppu_thread&, vm::ptr<sys_spu_image> img);
|
||||||
error_code _sys_spu_image_get_segments(ppu_thread&, vm::ptr<sys_spu_image> img, vm::ptr<sys_spu_segment> segments, s32 nseg);
|
error_code _sys_spu_image_get_segments(ppu_thread&, vm::ptr<sys_spu_image> img, vm::ptr<sys_spu_segment> segments, s32 nseg);
|
||||||
error_code sys_spu_thread_initialize(ppu_thread&, vm::ptr<u32> thread, u32 group, u32 spu_num, vm::ptr<sys_spu_image>, vm::ptr<sys_spu_thread_attribute>, vm::ptr<sys_spu_thread_argument>);
|
error_code sys_spu_thread_initialize(ppu_thread&, vm::ptr<u32> thread, u32 group, u32 spu_num, vm::ptr<sys_spu_image>, vm::ptr<sys_spu_thread_attribute>, vm::ptr<sys_spu_thread_argument>);
|
||||||
error_code sys_spu_thread_set_argument(ppu_thread&, u32 id, vm::ptr<sys_spu_thread_argument> arg);
|
error_code sys_spu_thread_set_argument(ppu_thread&, u32 id, vm::ptr<sys_spu_thread_argument> arg);
|
||||||
error_code sys_spu_thread_group_create(ppu_thread&, vm::ptr<u32> id, u32 num, s32 prio, vm::ptr<sys_spu_thread_group_attribute> attr);
|
error_code sys_spu_thread_group_create(ppu_thread&, vm::ptr<u32> id, u32 num, s32 prio, vm::ptr<reduced_sys_spu_thread_group_attribute> attr);
|
||||||
error_code sys_spu_thread_group_destroy(ppu_thread&, u32 id);
|
error_code sys_spu_thread_group_destroy(ppu_thread&, u32 id);
|
||||||
error_code sys_spu_thread_group_start(ppu_thread&, u32 id);
|
error_code sys_spu_thread_group_start(ppu_thread&, u32 id);
|
||||||
error_code sys_spu_thread_group_suspend(ppu_thread&, u32 id);
|
error_code sys_spu_thread_group_suspend(ppu_thread&, u32 id);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace rsx
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Ty>
|
template <typename Ty>
|
||||||
requires std::is_trivially_destructible_v<Ty>
|
requires std::is_trivially_destructible_v<Ty> && std::is_trivially_copyable_v<Ty>
|
||||||
struct simple_array
|
struct simple_array
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -196,7 +196,7 @@ namespace rsx
|
||||||
if (is_local_storage())
|
if (is_local_storage())
|
||||||
{
|
{
|
||||||
// Switch to heap storage
|
// Switch to heap storage
|
||||||
_data = static_cast<Ty*>(std::malloc(sizeof(Ty) * size));
|
ensure(_data = static_cast<Ty*>(std::malloc(sizeof(Ty) * size)));
|
||||||
std::memcpy(static_cast<void*>(_data), _local_storage, size_bytes());
|
std::memcpy(static_cast<void*>(_data), _local_storage, size_bytes());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "util/asm.hpp"
|
#include "util/asm.hpp"
|
||||||
|
#include "util/pair.hpp"
|
||||||
|
|
||||||
namespace rsx
|
namespace rsx
|
||||||
{
|
{
|
||||||
|
|
@ -244,10 +245,9 @@ namespace rsx
|
||||||
template <bool is_depth_surface>
|
template <bool is_depth_surface>
|
||||||
void intersect_surface_region(command_list_type cmd, u32 address, surface_type new_surface, surface_type prev_surface)
|
void intersect_surface_region(command_list_type cmd, u32 address, surface_type new_surface, surface_type prev_surface)
|
||||||
{
|
{
|
||||||
auto scan_list = [&new_surface, address](const rsx::address_range32& mem_range,
|
auto scan_list = [&new_surface, address](const rsx::address_range32& mem_range, surface_ranged_map& data)
|
||||||
surface_ranged_map& data) -> rsx::simple_array<std::pair<u32, surface_type>>
|
|
||||||
{
|
{
|
||||||
rsx::simple_array<std::pair<u32, surface_type>> result;
|
rsx::simple_array<utils::pair<u32, surface_type>> result;
|
||||||
for (auto it = data.begin_range(mem_range); it != data.end(); ++it)
|
for (auto it = data.begin_range(mem_range); it != data.end(); ++it)
|
||||||
{
|
{
|
||||||
auto surface = Traits::get(it->second);
|
auto surface = Traits::get(it->second);
|
||||||
|
|
@ -314,7 +314,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx::simple_array<std::pair<u32, surface_type>> surface_info;
|
rsx::simple_array<utils::pair<u32, surface_type>> surface_info;
|
||||||
if (list1.empty())
|
if (list1.empty())
|
||||||
{
|
{
|
||||||
surface_info = std::move(list2);
|
surface_info = std::move(list2);
|
||||||
|
|
@ -1091,7 +1091,7 @@ namespace rsx
|
||||||
rsx::simple_array<surface_overlap_info> get_merged_texture_memory_region(commandbuffer_type& cmd, u32 texaddr, u32 required_width, u32 required_height, u32 required_pitch, u8 required_bpp, rsx::surface_access access)
|
rsx::simple_array<surface_overlap_info> get_merged_texture_memory_region(commandbuffer_type& cmd, u32 texaddr, u32 required_width, u32 required_height, u32 required_pitch, u8 required_bpp, rsx::surface_access access)
|
||||||
{
|
{
|
||||||
rsx::simple_array<surface_overlap_info> result;
|
rsx::simple_array<surface_overlap_info> result;
|
||||||
rsx::simple_array<std::pair<u32, bool>> dirty;
|
rsx::simple_array<utils::pair<u32, bool>> dirty;
|
||||||
|
|
||||||
const auto surface_internal_pitch = (required_width * required_bpp);
|
const auto surface_internal_pitch = (required_width * required_bpp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,9 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
|
|
||||||
if (image_to_flip)
|
if (image_to_flip)
|
||||||
{
|
{
|
||||||
if (g_user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
|
const bool user_asked_for_screenshot = g_user_asked_for_screenshot.exchange(false);
|
||||||
|
|
||||||
|
if (user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
|
||||||
{
|
{
|
||||||
static const gl::pixel_pack_settings pack_settings{};
|
static const gl::pixel_pack_settings pack_settings{};
|
||||||
|
|
||||||
|
|
@ -374,7 +376,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
{
|
{
|
||||||
screenshot_log.error("Failed to capture image: 0x%x", err);
|
screenshot_log.error("Failed to capture image: 0x%x", err);
|
||||||
}
|
}
|
||||||
else if (g_user_asked_for_screenshot.exchange(false))
|
else if (user_asked_for_screenshot)
|
||||||
{
|
{
|
||||||
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, false);
|
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,7 +387,7 @@ namespace gl
|
||||||
allocator.pools[i].flags = 0;
|
allocator.pools[i].flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx::simple_array<std::pair<int, int>> replacement_map;
|
rsx::simple_array<utils::pair<int, int>> replacement_map;
|
||||||
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
||||||
{
|
{
|
||||||
if (reference_mask & (1 << i))
|
if (reference_mask & (1 << i))
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Emu/system_config.h"
|
#include "Emu/system_config.h"
|
||||||
#include "Utilities/date_time.h"
|
#include "Utilities/date_time.h"
|
||||||
|
|
||||||
|
extern atomic_t<bool> g_user_asked_for_screenshot;
|
||||||
|
|
||||||
namespace rsx
|
namespace rsx
|
||||||
{
|
{
|
||||||
namespace overlays
|
namespace overlays
|
||||||
|
|
@ -94,12 +96,13 @@ namespace rsx
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case page_navigation::exit:
|
case page_navigation::exit:
|
||||||
|
case page_navigation::exit_for_screenshot:
|
||||||
{
|
{
|
||||||
fade_animation.current = color4f(1.f);
|
fade_animation.current = color4f(1.f);
|
||||||
fade_animation.end = color4f(0.f);
|
fade_animation.end = color4f(0.f);
|
||||||
fade_animation.active = true;
|
fade_animation.active = true;
|
||||||
|
|
||||||
fade_animation.on_finish = [this]
|
fade_animation.on_finish = [this, navigation]
|
||||||
{
|
{
|
||||||
close(true, true);
|
close(true, true);
|
||||||
|
|
||||||
|
|
@ -110,6 +113,12 @@ namespace rsx
|
||||||
Emu.Resume();
|
Emu.Resume();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (navigation == page_navigation::exit_for_screenshot)
|
||||||
|
{
|
||||||
|
rsx_log.notice("Taking screenshot after exiting home menu");
|
||||||
|
g_user_asked_for_screenshot = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ namespace rsx
|
||||||
stay,
|
stay,
|
||||||
back,
|
back,
|
||||||
next,
|
next,
|
||||||
exit
|
exit,
|
||||||
|
exit_for_screenshot
|
||||||
};
|
};
|
||||||
|
|
||||||
struct home_menu_entry : horizontal_layout
|
struct home_menu_entry : horizontal_layout
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include "Emu/Cell/Modules/sceNpTrophy.h"
|
#include "Emu/Cell/Modules/sceNpTrophy.h"
|
||||||
|
|
||||||
extern atomic_t<bool> g_user_asked_for_recording;
|
extern atomic_t<bool> g_user_asked_for_recording;
|
||||||
extern atomic_t<bool> g_user_asked_for_screenshot;
|
|
||||||
|
|
||||||
namespace rsx
|
namespace rsx
|
||||||
{
|
{
|
||||||
|
|
@ -98,8 +97,7 @@ namespace rsx
|
||||||
if (btn != pad_button::cross) return page_navigation::stay;
|
if (btn != pad_button::cross) return page_navigation::stay;
|
||||||
|
|
||||||
rsx_log.notice("User selected screenshot in home menu");
|
rsx_log.notice("User selected screenshot in home menu");
|
||||||
g_user_asked_for_screenshot = true;
|
return page_navigation::exit_for_screenshot;
|
||||||
return page_navigation::exit;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
std::unique_ptr<overlay_element> recording = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_RECORDING));
|
std::unique_ptr<overlay_element> recording = std::make_unique<home_menu_entry>(get_localized_string(localized_string_id::HOME_MENU_RECORDING));
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ std::u32string utf16_to_u32string(const std::u16string& utf16_string)
|
||||||
|
|
||||||
for (const auto& code : utf16_string)
|
for (const auto& code : utf16_string)
|
||||||
{
|
{
|
||||||
result.push_back(code);
|
result.push_back(static_cast<char32_t>(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -694,7 +694,9 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
m_upscaler->scale_output(*m_current_command_buffer, image_to_flip, target_image, target_layout, rgn, UPSCALE_AND_COMMIT | UPSCALE_DEFAULT_VIEW);
|
m_upscaler->scale_output(*m_current_command_buffer, image_to_flip, target_image, target_layout, rgn, UPSCALE_AND_COMMIT | UPSCALE_DEFAULT_VIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
|
const bool user_asked_for_screenshot = g_user_asked_for_screenshot.exchange(false);
|
||||||
|
|
||||||
|
if (user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
|
||||||
{
|
{
|
||||||
const usz sshot_size = buffer_height * buffer_width * 4;
|
const usz sshot_size = buffer_height * buffer_width * 4;
|
||||||
|
|
||||||
|
|
@ -766,7 +768,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
|
|
||||||
const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM;
|
const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
if (g_user_asked_for_screenshot.exchange(false))
|
if (user_asked_for_screenshot)
|
||||||
{
|
{
|
||||||
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, is_bgra);
|
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, is_bgra);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ namespace vk
|
||||||
}
|
}
|
||||||
|
|
||||||
m_descriptor_slots.resize(bind_slots_count);
|
m_descriptor_slots.resize(bind_slots_count);
|
||||||
std::memset(m_descriptor_slots.data(), 0, sizeof(descriptor_slot_t) * bind_slots_count);
|
std::fill(m_descriptor_slots.begin(), m_descriptor_slots.end(), descriptor_slot_t{});
|
||||||
|
|
||||||
m_descriptors_dirty.resize(bind_slots_count);
|
m_descriptors_dirty.resize(bind_slots_count);
|
||||||
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
|
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
|
||||||
|
|
@ -534,7 +534,7 @@ namespace vk
|
||||||
auto update_descriptor_slot = [this](unsigned idx)
|
auto update_descriptor_slot = [this](unsigned idx)
|
||||||
{
|
{
|
||||||
const auto& slot = m_descriptor_slots[idx];
|
const auto& slot = m_descriptor_slots[idx];
|
||||||
const VkDescriptorType type = m_descriptor_types[idx];
|
|
||||||
if (auto ptr = std::get_if<VkDescriptorImageInfoEx>(&slot))
|
if (auto ptr = std::get_if<VkDescriptorImageInfoEx>(&slot))
|
||||||
{
|
{
|
||||||
m_descriptor_template[idx].pImageInfo = m_descriptor_set.store(*ptr);
|
m_descriptor_template[idx].pImageInfo = m_descriptor_set.store(*ptr);
|
||||||
|
|
|
||||||
|
|
@ -736,6 +736,8 @@
|
||||||
<ClInclude Include="Loader\mself.hpp" />
|
<ClInclude Include="Loader\mself.hpp" />
|
||||||
<ClInclude Include="util\atomic.hpp" />
|
<ClInclude Include="util\atomic.hpp" />
|
||||||
<ClInclude Include="util\bless.hpp" />
|
<ClInclude Include="util\bless.hpp" />
|
||||||
|
<ClInclude Include="util\pair.hpp" />
|
||||||
|
<ClInclude Include="util\tuple.hpp" />
|
||||||
<ClInclude Include="util\video_sink.h" />
|
<ClInclude Include="util\video_sink.h" />
|
||||||
<ClInclude Include="util\video_provider.h" />
|
<ClInclude Include="util\video_provider.h" />
|
||||||
<ClInclude Include="util\media_utils.h" />
|
<ClInclude Include="util\media_utils.h" />
|
||||||
|
|
|
||||||
|
|
@ -2758,6 +2758,12 @@
|
||||||
<ClInclude Include="..\Utilities\deferred_op.hpp">
|
<ClInclude Include="..\Utilities\deferred_op.hpp">
|
||||||
<Filter>Utilities</Filter>
|
<Filter>Utilities</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="util\tuple.hpp">
|
||||||
|
<Filter>Utilities</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="util\pair.hpp">
|
||||||
|
<Filter>Utilities</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||||
|
|
|
||||||
|
|
@ -769,7 +769,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: better way to do this?
|
// TODO: better way to do this?
|
||||||
switch (static_cast<cheat_type>(cbx_cheat_search_type->currentIndex()))
|
switch (cheat->type)
|
||||||
{
|
{
|
||||||
case cheat_type::unsigned_8_cheat: results = convert_and_set<u8>(final_offset); break;
|
case cheat_type::unsigned_8_cheat: results = convert_and_set<u8>(final_offset); break;
|
||||||
case cheat_type::unsigned_16_cheat: results = convert_and_set<u16>(final_offset); break;
|
case cheat_type::unsigned_16_cheat: results = convert_and_set<u16>(final_offset); break;
|
||||||
|
|
@ -1062,7 +1062,5 @@ QString cheat_manager_dialog::get_localized_cheat_type(cheat_type type)
|
||||||
case cheat_type::float_32_cheat: return tr("Float 32 bits");
|
case cheat_type::float_32_cheat: return tr("Float 32 bits");
|
||||||
case cheat_type::max: break;
|
case cheat_type::max: break;
|
||||||
}
|
}
|
||||||
std::string type_formatted;
|
return QString::fromStdString(fmt::format("%s", type));
|
||||||
fmt::append(type_formatted, "%s", type);
|
|
||||||
return QString::fromStdString(type_formatted);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "Emu/System.h"
|
#include "Emu/System.h"
|
||||||
#include "Emu/system_utils.hpp"
|
#include "Emu/system_utils.hpp"
|
||||||
|
#include "Utilities/File.h"
|
||||||
|
|
||||||
#include "Input/pad_thread.h"
|
#include "Input/pad_thread.h"
|
||||||
#include "Input/gui_pad_thread.h"
|
#include "Input/gui_pad_thread.h"
|
||||||
|
|
@ -86,21 +87,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||||
|
|
||||||
if (m_title_id.empty())
|
if (m_title_id.empty())
|
||||||
{
|
{
|
||||||
const QString input_config_dir = QString::fromStdString(rpcs3::utils::get_input_config_dir(m_title_id));
|
const auto [config_files, active_config_file] = get_config_files();
|
||||||
QStringList config_files = gui::utils::get_dir_entries(QDir(input_config_dir), QStringList() << "*.yml");
|
|
||||||
QString active_config_file = QString::fromStdString(g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key));
|
|
||||||
|
|
||||||
if (!config_files.contains(active_config_file))
|
|
||||||
{
|
|
||||||
const QString default_config_file = QString::fromStdString(g_cfg_input_configs.default_config);
|
|
||||||
|
|
||||||
if (!config_files.contains(default_config_file) && CreateConfigFile(input_config_dir, default_config_file))
|
|
||||||
{
|
|
||||||
config_files.prepend(default_config_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
active_config_file = default_config_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const QString& profile : config_files)
|
for (const QString& profile : config_files)
|
||||||
{
|
{
|
||||||
|
|
@ -145,6 +132,9 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr<gui_settings> gui_setti
|
||||||
// Pushbutton: Add config file
|
// Pushbutton: Add config file
|
||||||
connect(ui->b_addConfig, &QAbstractButton::clicked, this, &pad_settings_dialog::AddConfigFile);
|
connect(ui->b_addConfig, &QAbstractButton::clicked, this, &pad_settings_dialog::AddConfigFile);
|
||||||
|
|
||||||
|
// Pushbutton: Remove config file
|
||||||
|
connect(ui->b_remConfig, &QAbstractButton::clicked, this, &pad_settings_dialog::RemoveConfigFile);
|
||||||
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Filter Noise"));
|
ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Filter Noise"));
|
||||||
|
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button)
|
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button)
|
||||||
|
|
@ -272,6 +262,27 @@ void pad_settings_dialog::showEvent(QShowEvent* event)
|
||||||
QDialog::showEvent(event);
|
QDialog::showEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<QStringList, QString> pad_settings_dialog::get_config_files()
|
||||||
|
{
|
||||||
|
const QString input_config_dir = QString::fromStdString(rpcs3::utils::get_input_config_dir(m_title_id));
|
||||||
|
QStringList config_files = gui::utils::get_dir_entries(QDir(input_config_dir), QStringList() << "*.yml");
|
||||||
|
QString active_config_file = QString::fromStdString(g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key));
|
||||||
|
|
||||||
|
if (!config_files.contains(active_config_file))
|
||||||
|
{
|
||||||
|
const QString default_config_file = QString::fromStdString(g_cfg_input_configs.default_config);
|
||||||
|
|
||||||
|
if (!config_files.contains(default_config_file) && CreateConfigFile(input_config_dir, default_config_file))
|
||||||
|
{
|
||||||
|
config_files.prepend(default_config_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
active_config_file = default_config_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair<QStringList, QString>(std::move(config_files), std::move(active_config_file));
|
||||||
|
}
|
||||||
|
|
||||||
void pad_settings_dialog::InitButtons()
|
void pad_settings_dialog::InitButtons()
|
||||||
{
|
{
|
||||||
m_pad_buttons = new QButtonGroup(this);
|
m_pad_buttons = new QButtonGroup(this);
|
||||||
|
|
@ -321,6 +332,7 @@ void pad_settings_dialog::InitButtons()
|
||||||
|
|
||||||
m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh);
|
m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh);
|
||||||
m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file);
|
m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file);
|
||||||
|
m_pad_buttons->addButton(ui->b_remConfig, button_ids::id_remove_config_file);
|
||||||
|
|
||||||
connect(m_pad_buttons, &QButtonGroup::idClicked, this, &pad_settings_dialog::OnPadButtonClicked);
|
connect(m_pad_buttons, &QButtonGroup::idClicked, this, &pad_settings_dialog::OnPadButtonClicked);
|
||||||
|
|
||||||
|
|
@ -1320,6 +1332,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id)
|
||||||
case button_ids::id_pad_begin:
|
case button_ids::id_pad_begin:
|
||||||
case button_ids::id_pad_end:
|
case button_ids::id_pad_end:
|
||||||
case button_ids::id_add_config_file:
|
case button_ids::id_add_config_file:
|
||||||
|
case button_ids::id_remove_config_file:
|
||||||
case button_ids::id_refresh:
|
case button_ids::id_refresh:
|
||||||
return;
|
return;
|
||||||
case button_ids::id_reset_parameters:
|
case button_ids::id_reset_parameters:
|
||||||
|
|
@ -1636,6 +1649,8 @@ void pad_settings_dialog::ChangeConfig(const QString& config_file)
|
||||||
|
|
||||||
m_config_file = config_file.toStdString();
|
m_config_file = config_file.toStdString();
|
||||||
|
|
||||||
|
ui->b_remConfig->setEnabled(m_title_id.empty() && m_config_file != g_cfg_input_configs.default_config);
|
||||||
|
|
||||||
// Load in order to get the pad handlers
|
// Load in order to get the pad handlers
|
||||||
if (!g_cfg_input.load(m_title_id, m_config_file, true))
|
if (!g_cfg_input.load(m_title_id, m_config_file, true))
|
||||||
{
|
{
|
||||||
|
|
@ -1810,6 +1825,44 @@ void pad_settings_dialog::AddConfigFile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pad_settings_dialog::RemoveConfigFile()
|
||||||
|
{
|
||||||
|
const std::string config_to_remove = m_config_file;
|
||||||
|
const QString q_config_to_remove = QString::fromStdString(config_to_remove);
|
||||||
|
|
||||||
|
if (config_to_remove == g_cfg_input_configs.default_config)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Warning!"), tr("Can't remove default configuration '%0'.").arg(q_config_to_remove));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QMessageBox::question(this, tr("Remove Configuration?"), tr("Do you really want to remove the configuration '%0'?").arg(q_config_to_remove)) != QMessageBox::StandardButton::Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string filepath = fmt::format("%s%s.yml", rpcs3::utils::get_input_config_dir(m_title_id), config_to_remove);
|
||||||
|
|
||||||
|
if (!fs::remove_file(filepath))
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove '%0'.").arg(QString::fromStdString(filepath)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto [config_files, active_config_file] = get_config_files();
|
||||||
|
|
||||||
|
ui->chooseConfig->setCurrentText(active_config_file);
|
||||||
|
ui->chooseConfig->removeItem(ui->chooseConfig->findText(q_config_to_remove));
|
||||||
|
|
||||||
|
// Save new config if we removed the currently saved config
|
||||||
|
if (active_config_file == q_config_to_remove)
|
||||||
|
{
|
||||||
|
save(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::information(this, tr("Removed Configuration"), tr("Removed configuration '%0'.\nThe selected configuration is now '%1'.").arg(q_config_to_remove).arg(active_config_file));
|
||||||
|
}
|
||||||
|
|
||||||
void pad_settings_dialog::RefreshHandlers()
|
void pad_settings_dialog::RefreshHandlers()
|
||||||
{
|
{
|
||||||
const u32 player_id = GetPlayerIndex();
|
const u32 player_id = GetPlayerIndex();
|
||||||
|
|
@ -1949,27 +2002,30 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id)
|
||||||
cfg.product_id.set(info.product_id);
|
cfg.product_id.set(info.product_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pad_settings_dialog::SaveExit()
|
void pad_settings_dialog::save(bool check_duplicates)
|
||||||
{
|
{
|
||||||
ApplyCurrentPlayerConfig(m_last_player_id);
|
ApplyCurrentPlayerConfig(m_last_player_id);
|
||||||
|
|
||||||
for (const auto& [player_id, key] : m_duplicate_buttons)
|
if (check_duplicates)
|
||||||
{
|
{
|
||||||
if (!key.empty())
|
for (const auto& [player_id, key] : m_duplicate_buttons)
|
||||||
{
|
{
|
||||||
int result = QMessageBox::Yes;
|
if (!key.empty())
|
||||||
m_gui_settings->ShowConfirmationBox(
|
{
|
||||||
tr("Warning!"),
|
int result = QMessageBox::Yes;
|
||||||
tr("The %0 button <b>%1</b> of <b>Player %2</b> was assigned at least twice.<br>Please consider adjusting the configuration.<br><br>Continue anyway?<br>")
|
m_gui_settings->ShowConfirmationBox(
|
||||||
.arg(QString::fromStdString(g_cfg_input.player[player_id]->handler.to_string()))
|
tr("Warning!"),
|
||||||
.arg(QString::fromStdString(key))
|
tr("The %0 button <b>%1</b> of <b>Player %2</b> was assigned at least twice.<br>Please consider adjusting the configuration.<br><br>Continue anyway?<br>")
|
||||||
.arg(player_id + 1),
|
.arg(QString::fromStdString(g_cfg_input.player[player_id]->handler.to_string()))
|
||||||
gui::ib_same_buttons, &result, this);
|
.arg(QString::fromStdString(key))
|
||||||
|
.arg(player_id + 1),
|
||||||
|
gui::ib_same_buttons, &result, this);
|
||||||
|
|
||||||
if (result == QMessageBox::No)
|
if (result == QMessageBox::No)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1979,6 +2035,11 @@ void pad_settings_dialog::SaveExit()
|
||||||
g_cfg_input_configs.save();
|
g_cfg_input_configs.save();
|
||||||
|
|
||||||
g_cfg_input.save(m_title_id, m_config_file);
|
g_cfg_input.save(m_title_id, m_config_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pad_settings_dialog::SaveExit()
|
||||||
|
{
|
||||||
|
save(true);
|
||||||
|
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,8 @@ class pad_settings_dialog : public QDialog
|
||||||
id_reset_parameters,
|
id_reset_parameters,
|
||||||
id_blacklist,
|
id_blacklist,
|
||||||
id_refresh,
|
id_refresh,
|
||||||
id_add_config_file
|
id_add_config_file,
|
||||||
|
id_remove_config_file
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pad_button
|
struct pad_button
|
||||||
|
|
@ -101,6 +102,7 @@ private Q_SLOTS:
|
||||||
void ChangeDevice(int index);
|
void ChangeDevice(int index);
|
||||||
void HandleDeviceClassChange(u32 class_id) const;
|
void HandleDeviceClassChange(u32 class_id) const;
|
||||||
void AddConfigFile();
|
void AddConfigFile();
|
||||||
|
void RemoveConfigFile();
|
||||||
/** Update the current player config with the GUI values. */
|
/** Update the current player config with the GUI values. */
|
||||||
void ApplyCurrentPlayerConfig(int new_player_id);
|
void ApplyCurrentPlayerConfig(int new_player_id);
|
||||||
void RefreshPads();
|
void RefreshPads();
|
||||||
|
|
@ -192,6 +194,9 @@ private:
|
||||||
void start_input_thread();
|
void start_input_thread();
|
||||||
void pause_input_thread();
|
void pause_input_thread();
|
||||||
|
|
||||||
|
std::pair<QStringList, QString> get_config_files();
|
||||||
|
|
||||||
|
void save(bool check_duplicates);
|
||||||
void SaveExit();
|
void SaveExit();
|
||||||
void CancelExit();
|
void CancelExit();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,17 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="b_addConfig">
|
<widget class="QPushButton" name="b_addConfig">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add Configuration</string>
|
<string>Add New</string>
|
||||||
|
</property>
|
||||||
|
<property name="autoDefault">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="b_remConfig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoDefault">
|
<property name="autoDefault">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@
|
||||||
<ClCompile Include="test_fmt.cpp" />
|
<ClCompile Include="test_fmt.cpp" />
|
||||||
<ClCompile Include="test_simple_array.cpp" />
|
<ClCompile Include="test_simple_array.cpp" />
|
||||||
<ClCompile Include="test_address_range.cpp" />
|
<ClCompile Include="test_address_range.cpp" />
|
||||||
|
<ClCompile Include="test_tuple.cpp" />
|
||||||
|
<ClCompile Include="test_pair.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets" Condition="'$(GTestInstalled)' == 'true'">
|
<ImportGroup Label="ExtensionTargets" Condition="'$(GTestInstalled)' == 'true'">
|
||||||
|
|
|
||||||
46
rpcs3/tests/test_pair.cpp
Normal file
46
rpcs3/tests/test_pair.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "util/types.hpp"
|
||||||
|
#include "util/pair.hpp"
|
||||||
|
|
||||||
|
struct some_struct
|
||||||
|
{
|
||||||
|
u64 v {};
|
||||||
|
char s[12] = "Hello World";
|
||||||
|
|
||||||
|
bool operator == (const some_struct& r) const
|
||||||
|
{
|
||||||
|
return v == r.v && std::memcmp(s, r.s, sizeof(s)) == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Utils, Pair)
|
||||||
|
{
|
||||||
|
some_struct s {};
|
||||||
|
s.v = 1234;
|
||||||
|
|
||||||
|
utils::pair<int, some_struct> p;
|
||||||
|
EXPECT_EQ(sizeof(p), 32);
|
||||||
|
EXPECT_EQ(p.first, 0);
|
||||||
|
EXPECT_EQ(p.second, some_struct{});
|
||||||
|
|
||||||
|
p = { 666, s };
|
||||||
|
EXPECT_EQ(p.first, 666);
|
||||||
|
EXPECT_EQ(p.second, s);
|
||||||
|
|
||||||
|
const utils::pair<int, some_struct> p1 = p;
|
||||||
|
EXPECT_EQ(p.first, 666);
|
||||||
|
EXPECT_EQ(p.second, s);
|
||||||
|
EXPECT_EQ(p1.first, 666);
|
||||||
|
EXPECT_EQ(p1.second, s);
|
||||||
|
|
||||||
|
utils::pair<int, some_struct> p2 = p1;
|
||||||
|
EXPECT_EQ(p1.first, 666);
|
||||||
|
EXPECT_EQ(p1.second, s);
|
||||||
|
EXPECT_EQ(p2.first, 666);
|
||||||
|
EXPECT_EQ(p2.second, s);
|
||||||
|
|
||||||
|
utils::pair<int, some_struct> p3 = std::move(p);
|
||||||
|
EXPECT_EQ(p3.first, 666);
|
||||||
|
EXPECT_EQ(p3.second, s);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "util/pair.hpp"
|
||||||
|
|
||||||
#define private public
|
#define private public
|
||||||
#include "Emu/RSX/Common/simple_array.hpp"
|
#include "Emu/RSX/Common/simple_array.hpp"
|
||||||
#undef private
|
#undef private
|
||||||
|
|
@ -240,4 +242,29 @@ namespace rsx
|
||||||
|
|
||||||
EXPECT_EQ(sum, 15);
|
EXPECT_EQ(sum, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SimpleArray, SimplePair)
|
||||||
|
{
|
||||||
|
struct some_struct
|
||||||
|
{
|
||||||
|
u64 v {};
|
||||||
|
char s[12] = "Hello World";
|
||||||
|
};
|
||||||
|
some_struct s {};
|
||||||
|
|
||||||
|
rsx::simple_array<utils::pair<int, some_struct>> arr;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
s.v = i;
|
||||||
|
arr.push_back(utils::pair(i, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(arr.size(), 5);
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(arr[i].first, i);
|
||||||
|
EXPECT_EQ(arr[i].second.v, i);
|
||||||
|
EXPECT_EQ(std::memcmp(arr[i].second.s, "Hello World", sizeof(arr[i].second.s)), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
114
rpcs3/tests/test_tuple.cpp
Normal file
114
rpcs3/tests/test_tuple.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "util/tuple.hpp"
|
||||||
|
|
||||||
|
struct some_struct
|
||||||
|
{
|
||||||
|
u64 v {};
|
||||||
|
char s[12] = "Hello World";
|
||||||
|
|
||||||
|
bool operator == (const some_struct& r) const
|
||||||
|
{
|
||||||
|
return v == r.v && std::memcmp(s, r.s, sizeof(s)) == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Utils, Tuple)
|
||||||
|
{
|
||||||
|
some_struct s {};
|
||||||
|
s.v = 1234;
|
||||||
|
|
||||||
|
utils::tuple t0 = {};
|
||||||
|
EXPECT_EQ(t0.size(), 0);
|
||||||
|
|
||||||
|
utils::tuple<int> t;
|
||||||
|
EXPECT_EQ(sizeof(t), sizeof(int));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t.get<0>()), int&>));
|
||||||
|
EXPECT_EQ(t.size(), 1);
|
||||||
|
EXPECT_EQ(t.get<0>(), 0);
|
||||||
|
|
||||||
|
utils::tuple<int> t1 = 2;
|
||||||
|
EXPECT_EQ(sizeof(t1), sizeof(int));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t1.get<0>()), int&>));
|
||||||
|
EXPECT_EQ(t1.size(), 1);
|
||||||
|
EXPECT_EQ(t1.get<0>(), 2);
|
||||||
|
t1 = {};
|
||||||
|
EXPECT_EQ(t1.size(), 1);
|
||||||
|
EXPECT_EQ(t1.get<0>(), 0);
|
||||||
|
|
||||||
|
utils::tuple<int, some_struct> t2 = { 2, s };
|
||||||
|
EXPECT_EQ(sizeof(t2), 32);
|
||||||
|
EXPECT_EQ(t2.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t2.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t2.get<1>()), some_struct&>));
|
||||||
|
EXPECT_EQ(t2.get<0>(), 2);
|
||||||
|
EXPECT_EQ(t2.get<1>(), s);
|
||||||
|
t2 = {};
|
||||||
|
EXPECT_EQ(t2.size(), 2);
|
||||||
|
EXPECT_EQ(t2.get<0>(), 0);
|
||||||
|
EXPECT_EQ(t2.get<1>(), some_struct{});
|
||||||
|
|
||||||
|
t2.get<0>() = 666;
|
||||||
|
t2.get<1>() = s;
|
||||||
|
EXPECT_EQ(t2.get<0>(), 666);
|
||||||
|
EXPECT_EQ(t2.get<1>(), s);
|
||||||
|
|
||||||
|
utils::tuple<int, some_struct, double> t3 = { 2, s, 1234.0 };
|
||||||
|
EXPECT_EQ(sizeof(t3), 40);
|
||||||
|
EXPECT_EQ(t3.size(), 3);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t3.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t3.get<1>()), some_struct&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(t3.get<2>()), double&>));
|
||||||
|
EXPECT_EQ(t3.get<0>(), 2);
|
||||||
|
EXPECT_EQ(t3.get<1>(), s);
|
||||||
|
EXPECT_EQ(t3.get<2>(), 1234.0);
|
||||||
|
t3 = {};
|
||||||
|
EXPECT_EQ(t3.size(), 3);
|
||||||
|
EXPECT_EQ(t3.get<0>(), 0);
|
||||||
|
EXPECT_EQ(t3.get<1>(), some_struct{});
|
||||||
|
EXPECT_EQ(t3.get<2>(), 0.0);
|
||||||
|
|
||||||
|
t3.get<0>() = 666;
|
||||||
|
t3.get<1>() = s;
|
||||||
|
t3.get<2>() = 7.0;
|
||||||
|
EXPECT_EQ(t3.get<0>(), 666);
|
||||||
|
EXPECT_EQ(t3.get<1>(), s);
|
||||||
|
EXPECT_EQ(t3.get<2>(), 7.0);
|
||||||
|
|
||||||
|
// const
|
||||||
|
const utils::tuple<int, some_struct> tc = { 2, s };
|
||||||
|
EXPECT_EQ(tc.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(tc.get<0>()), const int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(tc.get<1>()), const some_struct&>));
|
||||||
|
EXPECT_EQ(tc.get<0>(), 2);
|
||||||
|
EXPECT_EQ(tc.get<1>(), s);
|
||||||
|
|
||||||
|
// assignment
|
||||||
|
const utils::tuple<int, some_struct> ta1 = { 2, s };
|
||||||
|
utils::tuple<int, some_struct> ta = ta1;
|
||||||
|
EXPECT_EQ(ta.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<1>()), some_struct&>));
|
||||||
|
EXPECT_EQ(ta.get<0>(), 2);
|
||||||
|
EXPECT_EQ(ta.get<1>(), s);
|
||||||
|
|
||||||
|
utils::tuple<int, some_struct> ta2 = { 2, s };
|
||||||
|
ta = ta2;
|
||||||
|
EXPECT_EQ(ta.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<1>()), some_struct&>));
|
||||||
|
EXPECT_EQ(ta.get<0>(), 2);
|
||||||
|
EXPECT_EQ(ta.get<1>(), s);
|
||||||
|
EXPECT_EQ(ta2.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta2.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta2.get<1>()), some_struct&>));
|
||||||
|
EXPECT_EQ(ta2.get<0>(), 2);
|
||||||
|
EXPECT_EQ(ta2.get<1>(), s);
|
||||||
|
|
||||||
|
ta = std::move(ta2);
|
||||||
|
EXPECT_EQ(ta.size(), 2);
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<0>()), int&>));
|
||||||
|
EXPECT_TRUE((std::is_same_v<decltype(ta.get<1>()), some_struct&>));
|
||||||
|
EXPECT_EQ(ta.get<0>(), 2);
|
||||||
|
EXPECT_EQ(ta.get<1>(), s);
|
||||||
|
}
|
||||||
15
rpcs3/util/pair.hpp
Normal file
15
rpcs3/util/pair.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
requires std::is_trivially_copyable_v<T1> && std::is_trivially_destructible_v<T1> &&
|
||||||
|
std::is_trivially_copyable_v<T2> && std::is_trivially_destructible_v<T2>
|
||||||
|
struct pair
|
||||||
|
{
|
||||||
|
T1 first {};
|
||||||
|
T2 second {};
|
||||||
|
};
|
||||||
|
}
|
||||||
63
rpcs3/util/tuple.hpp
Normal file
63
rpcs3/util/tuple.hpp
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
template <typename... Ts>
|
||||||
|
requires ((std::is_trivially_copyable_v<Ts> && std::is_trivially_destructible_v<Ts>) && ...)
|
||||||
|
struct tuple;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct tuple<>
|
||||||
|
{
|
||||||
|
constexpr tuple() = default;
|
||||||
|
|
||||||
|
static constexpr usz size() noexcept { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Head, typename... Tail>
|
||||||
|
struct tuple<Head, Tail...> : tuple<Tail...>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Head head;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr tuple()
|
||||||
|
: tuple<Tail...>()
|
||||||
|
, head{}
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr tuple(Head h, Tail... t)
|
||||||
|
: tuple<Tail...>(std::forward<Tail>(t)...)
|
||||||
|
, head(std::move(h))
|
||||||
|
{}
|
||||||
|
|
||||||
|
static constexpr usz size() noexcept
|
||||||
|
{
|
||||||
|
return 1 + sizeof...(Tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <usz N>
|
||||||
|
requires (N < size())
|
||||||
|
constexpr auto& get()
|
||||||
|
{
|
||||||
|
if constexpr (N == 0)
|
||||||
|
return head;
|
||||||
|
else
|
||||||
|
return tuple<Tail...>::template get<N - 1>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <usz N>
|
||||||
|
requires (N < size())
|
||||||
|
constexpr const auto& get() const
|
||||||
|
{
|
||||||
|
if constexpr (N == 0)
|
||||||
|
return head;
|
||||||
|
else
|
||||||
|
return tuple<Tail...>::template get<N - 1>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue