mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-07-10 01:34:41 -06:00
Merge branch 'master' into macos-dj-hero-turntable
This commit is contained in:
commit
e98bd8aa7e
@ -9,7 +9,7 @@ The world's first free and open-source PlayStation 3 emulator/debugger, written
|
|||||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
|
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
|
||||||
|
|
||||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
[**Support the Lead Developers on Patreon**](https://rpcs3.net/patreon)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@ -630,6 +630,13 @@ namespace fmt
|
|||||||
thread_ctrl::emergency_exit(out);
|
thread_ctrl::emergency_exit(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void raw_verify_error(std::source_location loc, std::source_location propagated_loc, const char8_t* msg, usz object)
|
||||||
|
{
|
||||||
|
std::string out;
|
||||||
|
fmt::append(out, "%s (object: 0x%x)%s%s", msg ? msg : u8"Verification failed", object, loc, propagated_loc);
|
||||||
|
thread_ctrl::emergency_exit(out);
|
||||||
|
}
|
||||||
|
|
||||||
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size)
|
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size)
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
|
|||||||
@ -193,6 +193,7 @@ if(BUILD_RPCS3_TESTS)
|
|||||||
tests/test_rsx_fp_asm.cpp
|
tests/test_rsx_fp_asm.cpp
|
||||||
tests/test_dmux_pamf.cpp
|
tests/test_dmux_pamf.cpp
|
||||||
tests/test_spu_analyser.cpp
|
tests/test_spu_analyser.cpp
|
||||||
|
tests/test_types_util.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(rpcs3_test
|
target_link_libraries(rpcs3_test
|
||||||
|
|||||||
@ -289,8 +289,8 @@ error_code cellGifDecReadHeader(vm::ptr<GifDecoder> mainHandle, vm::ptr<GifStrea
|
|||||||
default: break; // TODO
|
default: break; // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_from_ptr<be_t<u32>>(buffer + 0) != 0x47494638u ||
|
if (read_from_ptr<be_t<u32>>(buffer, 0) != 0x47494638u ||
|
||||||
(read_from_ptr<le_t<u16>>(buffer + 4) != 0x6139u && read_from_ptr<le_t<u16>>(buffer + 4) != 0x6137u)) // Error: The first 6 bytes are not a valid GIF signature
|
(read_from_ptr<le_t<u16>>(buffer, 4) != 0x6139u && read_from_ptr<le_t<u16>>(buffer, 4) != 0x6137u)) // Error: The first 6 bytes are not a valid GIF signature
|
||||||
{
|
{
|
||||||
return CELL_GIFDEC_ERROR_STREAM_FORMAT; // Surprisingly there is no error code related with headerss
|
return CELL_GIFDEC_ERROR_STREAM_FORMAT; // Surprisingly there is no error code related with headerss
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,26 +125,26 @@ error_code cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDe
|
|||||||
CellJpgDecInfo& current_info = subHandle_data->info;
|
CellJpgDecInfo& current_info = subHandle_data->info;
|
||||||
|
|
||||||
// Write the header to buffer
|
// Write the header to buffer
|
||||||
std::unique_ptr<u8[]> buffer(new u8[fileSize]);
|
std::vector<u8> buffer(fileSize);
|
||||||
|
|
||||||
switch (subHandle_data->src.srcSelect)
|
switch (subHandle_data->src.srcSelect)
|
||||||
{
|
{
|
||||||
case CELL_JPGDEC_BUFFER:
|
case CELL_JPGDEC_BUFFER:
|
||||||
std::memcpy(buffer.get(), vm::base(subHandle_data->src.streamPtr), fileSize);
|
std::memcpy(buffer.data(), vm::base(subHandle_data->src.streamPtr), fileSize);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CELL_JPGDEC_FILE:
|
case CELL_JPGDEC_FILE:
|
||||||
{
|
{
|
||||||
auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
|
auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
|
||||||
file->file.seek(0);
|
file->file.seek(0);
|
||||||
file->file.read(buffer.get(), fileSize);
|
file->file.read(buffer.data(), fileSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break; // TODO
|
default: break; // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_from_ptr<le_t<u32>>(buffer.get() + 0) != 0xE0FFD8FF || // Error: Not a valid SOI header
|
if (read_from_ptr<le_t<u32>>(buffer, 0) != 0xE0FFD8FF || // Error: Not a valid SOI header
|
||||||
read_from_ptr<u32>(buffer.get() + 6) != "JFIF"_u32) // Error: Not a valid JFIF string
|
read_from_ptr<u32>(buffer, 6) != "JFIF"_u32) // Error: Not a valid JFIF string
|
||||||
{
|
{
|
||||||
return CELL_JPGDEC_ERROR_HEADER;
|
return CELL_JPGDEC_ERROR_HEADER;
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ error_code cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDe
|
|||||||
if (i >= fileSize)
|
if (i >= fileSize)
|
||||||
return CELL_JPGDEC_ERROR_HEADER;
|
return CELL_JPGDEC_ERROR_HEADER;
|
||||||
|
|
||||||
u16 block_length = buffer[i] * 0xFF + buffer[i + 1];
|
u16 block_length = ::at32(buffer, i) * 0xFF + ::at32(buffer, i + 1);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -165,15 +165,16 @@ error_code cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDe
|
|||||||
return CELL_JPGDEC_ERROR_HEADER;
|
return CELL_JPGDEC_ERROR_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer[i + 1] == 0xC0)
|
const u8 next = ::at32(buffer, i + 1);
|
||||||
|
if (next == 0xC0)
|
||||||
break; // 0xFFC0 is the "Start of frame" marker which contains the file size
|
break; // 0xFFC0 is the "Start of frame" marker which contains the file size
|
||||||
|
|
||||||
i += 2; // Skip the block marker
|
i += 2; // Skip the block marker
|
||||||
block_length = buffer[i] * 0xFF + buffer[i + 1]; // Go to the next block
|
block_length = ::at32(buffer, i) * 0xFF + next; // Go to the next block
|
||||||
}
|
}
|
||||||
|
|
||||||
current_info.imageWidth = buffer[i + 7] * 0x100 + buffer[i + 8];
|
current_info.imageWidth = ::at32(buffer, i + 7) * 0x100 + ::at32(buffer, i + 8);
|
||||||
current_info.imageHeight = buffer[i + 5] * 0x100 + buffer[i + 6];
|
current_info.imageHeight = ::at32(buffer, i + 5) * 0x100 + ::at32(buffer, i + 6);
|
||||||
current_info.numComponents = 3; // Unimplemented
|
current_info.numComponents = 3; // Unimplemented
|
||||||
current_info.colorSpace = CELL_JPG_RGB;
|
current_info.colorSpace = CELL_JPG_RGB;
|
||||||
|
|
||||||
|
|||||||
@ -589,7 +589,7 @@ u32 ppu_read_mmio_aware_u32(u8* vm_base, u32 eal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Value is assumed to be swapped
|
// Value is assumed to be swapped
|
||||||
return read_from_ptr<u32>(vm_base + eal);
|
return read_from_ptr<u32>(vm_base, eal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_write_mmio_aware_u32(u8* vm_base, u32 eal, u32 value)
|
void ppu_write_mmio_aware_u32(u8* vm_base, u32 eal, u32 value)
|
||||||
|
|||||||
@ -646,18 +646,32 @@ spu_cache::~spu_cache()
|
|||||||
|
|
||||||
extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size)
|
extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size)
|
||||||
{
|
{
|
||||||
if (vaddr % 4)
|
if (u32 rem = vaddr % 4)
|
||||||
{
|
{
|
||||||
return;
|
// The remainder that it needs to be aligned up to the next DWORD
|
||||||
|
rem = 4 - rem;
|
||||||
|
|
||||||
|
if (size < rem)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vaddr += rem;
|
||||||
|
ls_data_vaddr = reinterpret_cast<const char*>(ls_data_vaddr) + rem;
|
||||||
|
size -= rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
size &= -4;
|
size &= -4;
|
||||||
|
|
||||||
if (!size || vaddr + size > SPU_LS_SIZE)
|
if (!size || vaddr >= SPU_LS_SIZE)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let SPU block search mistakes pass through
|
||||||
|
// SPU code mining is too important
|
||||||
|
size = std::min<u32>(SPU_LS_SIZE - vaddr, size);
|
||||||
|
|
||||||
if (!g_cfg.core.llvm_precompilation)
|
if (!g_cfg.core.llvm_precompilation)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -2364,11 +2378,22 @@ std::vector<u32> spu_thread::discover_functions(u32 base_addr, std::span<const u
|
|||||||
// TODO: Does not detect jumptables or fixed-addr indirect calls
|
// TODO: Does not detect jumptables or fixed-addr indirect calls
|
||||||
const v128 brasl_mask = is_known_addr ? v128::from32p(0x62u << 23) : v128::from32p(umax);
|
const v128 brasl_mask = is_known_addr ? v128::from32p(0x62u << 23) : v128::from32p(umax);
|
||||||
|
|
||||||
for (u32 i = utils::align<u32>(base_addr, 0x10); i < std::min<u32>(base_addr + ::size32(ls), 0x3FFF0); i += 0x10)
|
for (u32 i = base_addr, end_ls = std::min<u32>(base_addr + ::size32(ls), SPU_LS_SIZE); i < end_ls; i = utils::align<u32>(i + 1, 0x10))
|
||||||
{
|
{
|
||||||
// Search for BRSL LR and BRASL LR or BR
|
// Search for BRSL LR and BRASL LR or BR
|
||||||
// TODO: BISL
|
// TODO: BISL
|
||||||
const v128 inst = read_from_ptr<be_t<v128>>(ls.data(), i - base_addr);
|
be_t<v128> inst_be{};
|
||||||
|
|
||||||
|
if (end_ls - i < 16)
|
||||||
|
{
|
||||||
|
std::memcpy(&inst_be, ls.data() + (i - base_addr), end_ls - i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inst_be = read_from_ptr<be_t<v128>>(ls, i - base_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
const v128 inst = inst_be;
|
||||||
const v128 cleared_i16 = gv_and32(inst, v128::from32p(std::rotl<u32>(~0xffff, 7)));
|
const v128 cleared_i16 = gv_and32(inst, v128::from32p(std::rotl<u32>(~0xffff, 7)));
|
||||||
const v128 eq_brsl = gv_eq32(cleared_i16, v128::from32p(0x66u << 23));
|
const v128 eq_brsl = gv_eq32(cleared_i16, v128::from32p(0x66u << 23));
|
||||||
const v128 eq_brasl = gv_eq32(cleared_i16, brasl_mask);
|
const v128 eq_brasl = gv_eq32(cleared_i16, brasl_mask);
|
||||||
|
|||||||
@ -658,7 +658,7 @@ void lv2_file::save(utils::serial& ar)
|
|||||||
sys_fs.error("Read less than expected! (new-size=0x%x)", read_size);
|
sys_fs.error("Read less than expected! (new-size=0x%x)", read_size);
|
||||||
stats.size = read_size;
|
stats.size = read_size;
|
||||||
ar.data.resize(old_end + stats.size);
|
ar.data.resize(old_end + stats.size);
|
||||||
write_to_ptr<fs::stat_t>(&ar.data[patch_stats_pos], stats);
|
write_to_ptr<fs::stat_t>(ar.data, patch_stats_pos, stats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -214,7 +214,7 @@ u32 dimensions_toypad::scramble(const std::array<u8, 7>& uid, u8 count)
|
|||||||
}
|
}
|
||||||
::at32(to_scramble, count * 4 - 1) = 0xaa;
|
::at32(to_scramble, count * 4 - 1) = 0xaa;
|
||||||
|
|
||||||
return read_from_ptr<be_t<u32>>(dimensions_randomize(to_scramble, count).data());
|
return read_from_ptr<be_t<u32>>(dimensions_randomize(to_scramble, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<u8, 4> dimensions_toypad::dimensions_randomize(const std::vector<u8>& key, u8 count)
|
std::array<u8, 4> dimensions_toypad::dimensions_randomize(const std::vector<u8>& key, u8 count)
|
||||||
@ -522,7 +522,7 @@ bool dimensions_toypad::create_blank_character(std::array<u8, 0x2D * 0x04>& buf,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Page 38 is used as verification for blank tags
|
// Page 38 is used as verification for blank tags
|
||||||
write_to_ptr<be_t<u16>>(buf.data(), 38 * 4, 1);
|
write_to_ptr<be_t<u16>>(buf, 38 * 4, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -359,7 +359,7 @@ namespace np
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 given_size = read_from_ptr<be_t<u32>>(data() + 4);
|
u32 given_size = read_from_ptr<be_t<u32>>(data(), 4);
|
||||||
if ((given_size + 8) != size())
|
if ((given_size + 8) != size())
|
||||||
{
|
{
|
||||||
ticket_log.error("Size mismatch (gs: %d vs s: %d)", given_size, size());
|
ticket_log.error("Size mismatch (gs: %d vs s: %d)", given_size, size());
|
||||||
|
|||||||
@ -503,8 +503,8 @@ namespace rpcn
|
|||||||
{
|
{
|
||||||
if (msg.size() == 6)
|
if (msg.size() == 6)
|
||||||
{
|
{
|
||||||
const u32 new_addr_sig = read_from_ptr<le_t<u32>>(&msg[0]);
|
const u32 new_addr_sig = read_from_ptr<le_t<u32>>(msg, 0);
|
||||||
const u16 new_port_sig = read_from_ptr<be_t<u16>>(&msg[4]);
|
const u16 new_port_sig = read_from_ptr<be_t<u16>>(msg, 4);
|
||||||
const u32 old_addr_sig = addr_sig;
|
const u32 old_addr_sig = addr_sig;
|
||||||
const u32 old_port_sig = port_sig;
|
const u32 old_port_sig = port_sig;
|
||||||
|
|
||||||
@ -533,7 +533,7 @@ namespace rpcn
|
|||||||
// We don't really need ipv6 info stored so we just update the pong data
|
// We don't really need ipv6 info stored so we just update the pong data
|
||||||
// std::array<u8, 16> new_ipv6_addr;
|
// std::array<u8, 16> new_ipv6_addr;
|
||||||
// std::memcpy(new_ipv6_addr.data(), &msg[3], 16);
|
// std::memcpy(new_ipv6_addr.data(), &msg[3], 16);
|
||||||
// const u32 new_ipv6_port = read_from_ptr<be_t<u16>>(&msg[16]);
|
// const u32 new_ipv6_port = read_from_ptr<be_t<u16>>(msg, 16);
|
||||||
|
|
||||||
last_pong_time_ipv6 = now;
|
last_pong_time_ipv6 = now;
|
||||||
}
|
}
|
||||||
@ -626,9 +626,9 @@ namespace rpcn
|
|||||||
}
|
}
|
||||||
|
|
||||||
const u8 packet_type = header[0];
|
const u8 packet_type = header[0];
|
||||||
const auto command = static_cast<rpcn::CommandType>(static_cast<u16>(read_from_ptr<le_t<u16>>(&header[1])));
|
const auto command = static_cast<rpcn::CommandType>(static_cast<u16>(read_from_ptr<le_t<u16>>(header, 1)));
|
||||||
const u32 packet_size = read_from_ptr<le_t<u32>>(&header[3]);
|
const u32 packet_size = read_from_ptr<le_t<u32>>(header, 3);
|
||||||
const u64 packet_id = read_from_ptr<le_t<u64>>(&header[7]);
|
const u64 packet_id = read_from_ptr<le_t<u64>>(header, 7);
|
||||||
|
|
||||||
if (packet_size < RPCN_HEADER_SIZE)
|
if (packet_size < RPCN_HEADER_SIZE)
|
||||||
return error_and_disconnect("Invalid packet size");
|
return error_and_disconnect("Invalid packet size");
|
||||||
|
|||||||
@ -67,7 +67,7 @@ public:
|
|||||||
error = true;
|
error = true;
|
||||||
return static_cast<T>(0);
|
return static_cast<T>(0);
|
||||||
}
|
}
|
||||||
T res = read_from_ptr<le_t<T>>(&vec[i]);
|
T res = read_from_ptr<le_t<T>>(vec, i);
|
||||||
i += sizeof(T);
|
i += sizeof(T);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,11 +183,11 @@ namespace rsx
|
|||||||
const usz first_index_off = 0;
|
const usz first_index_off = 0;
|
||||||
const usz second_index_off = (((rcount / 4) - 1) / 2) * 4;
|
const usz second_index_off = (((rcount / 4) - 1) / 2) * 4;
|
||||||
|
|
||||||
const u64 src_op1_2 = read_from_ptr<be_t<u64>>(fifo_span.data() + first_index_off);
|
const u64 src_op1_2 = read_from_ptr<be_t<u64>>(fifo_span, first_index_off);
|
||||||
const u64 src_op2_2 = read_from_ptr<be_t<u64>>(fifo_span.data() + second_index_off);
|
const u64 src_op2_2 = read_from_ptr<be_t<u64>>(fifo_span, second_index_off);
|
||||||
|
|
||||||
// Fast comparison
|
// Fast comparison
|
||||||
if (src_op1_2 != read_from_ptr<u64>(out_ptr + first_index_off) || src_op2_2 != read_from_ptr<u64>(out_ptr + second_index_off))
|
if (src_op1_2 != read_from_ptr<u64>(out_ptr, first_index_off) || src_op2_2 != read_from_ptr<u64>(out_ptr, second_index_off))
|
||||||
{
|
{
|
||||||
to_set_dirty = rsx::pipeline_state::vertex_program_ucode_dirty;
|
to_set_dirty = rsx::pipeline_state::vertex_program_ucode_dirty;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
#ifdef _MSC_VER
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#else
|
#else
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|||||||
@ -126,7 +126,7 @@ struct cfg_root : cfg::node
|
|||||||
cfg::_enum<frame_limit_type> frame_limit{ this, "Frame limit", frame_limit_type::_auto, true };
|
cfg::_enum<frame_limit_type> frame_limit{ this, "Frame limit", frame_limit_type::_auto, true };
|
||||||
cfg::_float<0, 1000> second_frame_limit{ this, "Second Frame Limit", 0, true }; // 0 disables its effect
|
cfg::_float<0, 1000> second_frame_limit{ this, "Second Frame Limit", 0, true }; // 0 disables its effect
|
||||||
cfg::_enum<msaa_level> antialiasing_level{ this, "MSAA", msaa_level::_auto };
|
cfg::_enum<msaa_level> antialiasing_level{ this, "MSAA", msaa_level::_auto };
|
||||||
cfg::_enum<shader_mode> shadermode{ this, "Shader Mode", shader_mode::async_recompiler };
|
cfg::_enum<shader_mode> shadermode{ this, "Shader Mode", shader_mode::async_with_interpreter };
|
||||||
cfg::_enum<gpu_preset_level> shader_precision{ this, "Shader Precision", gpu_preset_level::high };
|
cfg::_enum<gpu_preset_level> shader_precision{ this, "Shader Precision", gpu_preset_level::high };
|
||||||
cfg::_enum<vsync_mode> vsync{ this, "VSync Mode", vsync_mode::off, true };
|
cfg::_enum<vsync_mode> vsync{ this, "VSync Mode", vsync_mode::off, true };
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ struct cfg_root : cfg::node
|
|||||||
cfg::_bool prevent_display_sleep{ this, "Prevent display sleep while running games", true, true };
|
cfg::_bool prevent_display_sleep{ this, "Prevent display sleep while running games", true, true };
|
||||||
cfg::_bool show_trophy_popups{ this, "Show trophy popups", true, true };
|
cfg::_bool show_trophy_popups{ this, "Show trophy popups", true, true };
|
||||||
cfg::_bool show_rpcn_popups{ this, "Show RPCN popups", true, true };
|
cfg::_bool show_rpcn_popups{ this, "Show RPCN popups", true, true };
|
||||||
cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true, true };
|
cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", false, true };
|
||||||
cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true };
|
cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true };
|
||||||
cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true };
|
cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true };
|
||||||
cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true };
|
cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true };
|
||||||
|
|||||||
@ -29,7 +29,7 @@ Most of this information can be found on the PlayStation 3 Developer Wiki.</p>
|
|||||||
<url type="bugtracker">https://github.com/RPCS3/rpcs3/issues</url>
|
<url type="bugtracker">https://github.com/RPCS3/rpcs3/issues</url>
|
||||||
<url type="faq">https://rpcs3.net/faq</url>
|
<url type="faq">https://rpcs3.net/faq</url>
|
||||||
<url type="help">https://rpcs3.net/quickstart</url>
|
<url type="help">https://rpcs3.net/quickstart</url>
|
||||||
<url type="donation">https://www.patreon.com/Nekotekina</url>
|
<url type="donation">https://rpcs3.net/patreon</url>
|
||||||
<url type="vcs-browser">https://github.com/RPCS3/rpcs3</url>
|
<url type="vcs-browser">https://github.com/RPCS3/rpcs3</url>
|
||||||
<url type="contribute">https://github.com/RPCS3/rpcs3#contributing</url>
|
<url type="contribute">https://github.com/RPCS3/rpcs3#contributing</url>
|
||||||
<categories>
|
<categories>
|
||||||
|
|||||||
@ -234,7 +234,7 @@ kamen_rider_creator_dialog::kamen_rider_creator_dialog(QWidget* parent)
|
|||||||
std::array<u8, 16> figure_data = {static_cast<u8>(dist(mt)), 0x03, 0x00, 0x00, 0x01, 0x0e, 0x0a, 0x0a,
|
std::array<u8, 16> figure_data = {static_cast<u8>(dist(mt)), 0x03, 0x00, 0x00, 0x01, 0x0e, 0x0a, 0x0a,
|
||||||
static_cast<u8>(fig_id & 0xff), static_cast<u8>((fig_id >> 8) & 0xff),
|
static_cast<u8>(fig_id & 0xff), static_cast<u8>((fig_id >> 8) & 0xff),
|
||||||
static_cast<u8>((fig_id >> 16) & 0xff), static_cast<u8>((fig_id >> 24) & 0xff)};
|
static_cast<u8>((fig_id >> 16) & 0xff), static_cast<u8>((fig_id >> 24) & 0xff)};
|
||||||
write_to_ptr<le_t<u32>>(figure_data.data(), 0xC, kamen_rider_crc32(figure_data));
|
write_to_ptr<le_t<u32>>(figure_data, 0xC, kamen_rider_crc32(figure_data));
|
||||||
memcpy(&buf[16], figure_data.data(), figure_data.size());
|
memcpy(&buf[16], figure_data.data(), figure_data.size());
|
||||||
fig_file.write(buf.data(), buf.size());
|
fig_file.write(buf.data(), buf.size());
|
||||||
fig_file.close();
|
fig_file.close();
|
||||||
|
|||||||
@ -638,7 +638,7 @@ void log_frame::UpdateUI()
|
|||||||
// If ANSI TTY is enabled, remove all control characters except for ESC (0x1B) for ANSI sequences
|
// If ANSI TTY is enabled, remove all control characters except for ESC (0x1B) for ANSI sequences
|
||||||
if (m_ansi_tty)
|
if (m_ansi_tty)
|
||||||
{
|
{
|
||||||
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](s8 c)
|
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](u8 c) // Use u8. Otherwise we drop valid UTF-8 characters.
|
||||||
{
|
{
|
||||||
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F && c != 0x1B);
|
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F && c != 0x1B);
|
||||||
}), buf_line.end());
|
}), buf_line.end());
|
||||||
@ -646,7 +646,7 @@ void log_frame::UpdateUI()
|
|||||||
// Otherwise, remove all control characters to keep the output clean
|
// Otherwise, remove all control characters to keep the output clean
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](s8 c)
|
buf_line.erase(std::remove_if(buf_line.begin(), buf_line.end(), [](u8 c) // Use u8. Otherwise we drop valid UTF-8 characters.
|
||||||
{
|
{
|
||||||
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F);
|
return c <= 0x8 || c == 0x7F || (c >= 0xE && c <= 0x1F);
|
||||||
}), buf_line.end());
|
}), buf_line.end());
|
||||||
|
|||||||
@ -105,6 +105,7 @@
|
|||||||
<ClCompile Include="test_sys_fs.cpp" />
|
<ClCompile Include="test_sys_fs.cpp" />
|
||||||
<ClCompile Include="test_tuple.cpp" />
|
<ClCompile Include="test_tuple.cpp" />
|
||||||
<ClCompile Include="test_pair.cpp" />
|
<ClCompile Include="test_pair.cpp" />
|
||||||
|
<ClCompile Include="test_types_util.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'">
|
||||||
|
|||||||
487
rpcs3/tests/test_types_util.cpp
Normal file
487
rpcs3/tests/test_types_util.cpp
Normal file
@ -0,0 +1,487 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
extern atomic_t<bool> g_headless;
|
||||||
|
|
||||||
|
#define CHECK_COMPILATION_ERRORS 0
|
||||||
|
#define CHECK_DEATH 0
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
TEST(Utils, read_write_to_ptr_array)
|
||||||
|
{
|
||||||
|
g_headless = true; // Disable exception popups
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
std::array<u8, sizeof(u64)> arr{};
|
||||||
|
write_to_ptr(arr, u8(umax));
|
||||||
|
EXPECT_EQ(arr.at(0), u8(umax));
|
||||||
|
write_to_ptr(arr, u16(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr.data()), u16(umax));
|
||||||
|
write_to_ptr(arr, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr.data()), u32(umax));
|
||||||
|
write_to_ptr(arr, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr.data()), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
std::array<u8, sizeof(u128)> arr2{};
|
||||||
|
|
||||||
|
for (u8 i = 0; i < arr2.size(); i++)
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(arr2.at(i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u8>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2 = {};
|
||||||
|
for (u16 i = 0; i < arr2.size(); i += sizeof(u16))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr2.data() + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u16>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2 = {};
|
||||||
|
for (u32 i = 0; i < arr2.size(); i += sizeof(u32))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr2.data() + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2 = {};
|
||||||
|
for (u64 i = 0; i < arr2.size(); i += sizeof(u64))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr2.data() + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr2 = {};
|
||||||
|
for (usz i = 0; i < arr2.size(); i += sizeof(u128))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr2.data(), &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr2, i);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
std::array<u32, sizeof(u64) / sizeof(u32)> arr32{};
|
||||||
|
write_to_ptr(arr32, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32.data()), u32(umax));
|
||||||
|
write_to_ptr(arr32, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32.data()), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
std::array<u32, sizeof(u128) / sizeof(u32)> arr32_2{};
|
||||||
|
|
||||||
|
std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32));
|
||||||
|
for (u32 i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u32))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u32);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32_2.data() + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32));
|
||||||
|
for (u64 i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u64))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u64);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32_2.data() + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2.data(), 0, arr32_2.size() * sizeof(u32));
|
||||||
|
for (usz i = 0; i < arr32_2.size() * sizeof(u32); i += sizeof(u128))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u128);
|
||||||
|
write_to_ptr(arr32_2, index, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr32_2.data(), &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr32_2, index);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These tests can take a long time and produce warnings, so let's only run them in local builds
|
||||||
|
#if CHECK_DEATH
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u8()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u16()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2.size(), u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u8>(arr2, arr2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u16>(arr2, arr2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr2, arr2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr2, arr2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr2, arr2.size()), ".*");
|
||||||
|
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_2.size(), u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr32_2, arr32_2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, arr32_2.size()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr32_2, arr32_2.size()), ".*");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, read_write_to_ptr_c_array)
|
||||||
|
{
|
||||||
|
g_headless = true; // Disable exception popups
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
u8 arr[sizeof(u64)]{};
|
||||||
|
write_to_ptr(arr, u8(umax));
|
||||||
|
EXPECT_EQ(*arr, u8(umax));
|
||||||
|
write_to_ptr(arr, u16(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr + 0), u16(umax));
|
||||||
|
write_to_ptr(arr, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr + 0), u32(umax));
|
||||||
|
write_to_ptr(arr, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr + 0), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
u8 arr2[sizeof(u128)]{};
|
||||||
|
|
||||||
|
for (u8 i = 0; i < sizeof(arr2); i++)
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(arr2[i], i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u8>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, sizeof(arr2));
|
||||||
|
for (u16 i = 0; i < sizeof(arr2); i += sizeof(u16))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u16>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, sizeof(arr2));
|
||||||
|
for (u32 i = 0; i < sizeof(arr2); i += sizeof(u32))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, sizeof(arr2));
|
||||||
|
for (u64 i = 0; i < sizeof(arr2); i += sizeof(u64))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, sizeof(arr2));
|
||||||
|
for (usz i = 0; i < sizeof(arr2); i += sizeof(u128))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr2, &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr2, i);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
u32 arr32[sizeof(u64) / sizeof(u32)]{};
|
||||||
|
write_to_ptr(arr32, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32 + 0), u32(umax));
|
||||||
|
write_to_ptr(arr32, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32 + 0), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
u32 arr32_2[sizeof(u128) / sizeof(u32)]{};
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, sizeof(arr32_2));
|
||||||
|
for (u32 i = 0; i < sizeof(arr32_2); i += sizeof(u32))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u32);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32_2 + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, sizeof(arr32_2));
|
||||||
|
for (u64 i = 0; i < sizeof(arr32_2); i += sizeof(u64))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u64);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32_2 + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, sizeof(arr32_2));
|
||||||
|
for (usz i = 0; i < sizeof(arr32_2); i += sizeof(u128))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u128);
|
||||||
|
write_to_ptr(arr32_2, index, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr32_2, &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr32_2, index);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These tests can take a long time and produce warnings, so let's only run them in local builds
|
||||||
|
#if CHECK_DEATH
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u8()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u16()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, std::size(arr2), u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u8>(arr2, std::size(arr2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u16>(arr2, std::size(arr2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr2, std::size(arr2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr2, std::size(arr2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr2, std::size(arr2)), ".*");
|
||||||
|
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, std::size(arr32_2), u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr32_2, std::size(arr32_2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, std::size(arr32_2)), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr32_2, std::size(arr32_2)), ".*");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, read_write_to_ptr_raw_array)
|
||||||
|
{
|
||||||
|
g_headless = true; // Disable exception popups
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
constexpr usz arr_size = sizeof(u64);
|
||||||
|
u8* arr = new u8[arr_size];
|
||||||
|
std::memset(arr, 0, arr_size);
|
||||||
|
|
||||||
|
write_to_ptr(arr, u8(umax));
|
||||||
|
EXPECT_EQ(*arr, u8(umax));
|
||||||
|
write_to_ptr(arr, u16(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr), u16(umax));
|
||||||
|
write_to_ptr(arr, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr), u32(umax));
|
||||||
|
write_to_ptr(arr, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
constexpr usz arr2_size = sizeof(u128);
|
||||||
|
u8* arr2 = new u8[arr2_size];
|
||||||
|
|
||||||
|
std::memset(arr2, 0, arr2_size);
|
||||||
|
for (u8 i = 0; i < arr2_size; i++)
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(arr2[i], i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u8>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, arr2_size);
|
||||||
|
for (u16 i = 0; i < arr2_size; i += sizeof(u16))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u16>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u16>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, arr2_size);
|
||||||
|
for (u32 i = 0; i < arr2_size; i += sizeof(u32))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, arr2_size);
|
||||||
|
for (usz i = 0; i < arr2_size; i += sizeof(u64))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, u64(i));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr2 + i), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr2, i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr2, 0, arr2_size);
|
||||||
|
for (usz i = 0; i < arr2_size; i += sizeof(u128))
|
||||||
|
{
|
||||||
|
write_to_ptr(arr2, i, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr2, &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr2, i);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write_to_ptr without pos
|
||||||
|
constexpr usz arr32_count = sizeof(u64) / sizeof(u32);
|
||||||
|
constexpr usz arr32_size = arr32_count * sizeof(u32);
|
||||||
|
u32* arr32 = new u32[arr32_count];
|
||||||
|
std::memset(arr32, 0, arr32_size);
|
||||||
|
|
||||||
|
write_to_ptr(arr32, u32(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32), u32(umax));
|
||||||
|
write_to_ptr(arr32, u64(umax));
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32), u64(umax));
|
||||||
|
|
||||||
|
// write_to_ptr and read_from_ptr with pos
|
||||||
|
constexpr usz arr32_2_count = sizeof(u128) / sizeof(u32);
|
||||||
|
constexpr usz arr32_2_size = arr32_2_count * sizeof(u32);
|
||||||
|
u32* arr32_2 = new u32[arr32_2_count];
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, arr32_2_size);
|
||||||
|
for (u32 i = 0; i < arr32_2_size; i += sizeof(u32))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u32);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u32>(arr32_2 + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u32>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, arr32_2_size);
|
||||||
|
for (u64 i = 0; i < arr32_2_size; i += sizeof(u64))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u64);
|
||||||
|
write_to_ptr(arr32_2, index, i);
|
||||||
|
EXPECT_EQ(*utils::bless<u64>(arr32_2 + index), i);
|
||||||
|
EXPECT_EQ(read_from_ptr<u64>(arr32_2, index), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memset(arr32_2, 0, arr32_2_size);
|
||||||
|
for (usz i = 0; i < arr32_2_size; i += sizeof(u128))
|
||||||
|
{
|
||||||
|
const usz index = i / sizeof(u128);
|
||||||
|
write_to_ptr(arr32_2, index, u128(i));
|
||||||
|
const u128 exp_u128 = i;
|
||||||
|
EXPECT_EQ(std::memcmp(arr32_2, &exp_u128, sizeof(u128)), 0);
|
||||||
|
const u128 val_u128 = read_from_ptr<u128>(arr32_2, index);
|
||||||
|
EXPECT_EQ(std::memcmp(&val_u128, &exp_u128, sizeof(u128)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are no sanity checks for raw pointers in these functions, so these will not fail.
|
||||||
|
#if 0
|
||||||
|
// These tests can take a long time and produce warnings, so let's only run them in local builds
|
||||||
|
#if CHECK_DEATH
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u8()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u16()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr2, arr2_size, u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u8>(arr2, arr2_size), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u16>(arr2, arr2_size), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr2, arr2_size), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr2, arr2_size), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr2, arr2_size), ".*");
|
||||||
|
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32, u128()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u32()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u64()), ".*");
|
||||||
|
EXPECT_DEATH(write_to_ptr(arr32_2, arr32_count, u128()), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u32>(arr32_2, arr32_count), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u64>(arr32_2, arr32_count), ".*");
|
||||||
|
EXPECT_DEATH(read_from_ptr<u128>(arr32_2, arr32_count), ".*");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
delete[] arr;
|
||||||
|
delete[] arr2;
|
||||||
|
delete[] arr32;
|
||||||
|
delete[] arr32_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, read_from_ptr_const_eval_array)
|
||||||
|
{
|
||||||
|
g_headless = true; // Disable exception popups
|
||||||
|
|
||||||
|
constexpr std::array<u8, sizeof(u64)> arr { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 0) == u8(1));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 1) == u8(2));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 2) == u8(3));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 3) == u8(4));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 4) == u8(5));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 5) == u8(6));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 6) == u8(7));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 7) == u8(8));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 0) == u16(0x0201));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 2) == u16(0x0403));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 4) == u16(0x0605));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 6) == u16(0x0807));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u32>>(arr, 0) == u32(0x04030201));
|
||||||
|
static_assert(read_from_ptr<le_t<u32>>(arr, 4) == u32(0x08070605));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u64>>(arr, 0) == u64(0x0807060504030201));
|
||||||
|
|
||||||
|
constexpr std::array<u32, sizeof(u64) / sizeof(u32)> arr32 { 0x00000000, 0xFFFFFFFF };
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<u32>(arr32, 0) == u32(0x00000000));
|
||||||
|
static_assert(read_from_ptr<u32>(arr32, 1) == u32(0xFFFFFFFF));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u64>>(arr32, 0) == u64(0xFFFFFFFF00000000));
|
||||||
|
|
||||||
|
// This should not compile
|
||||||
|
#if CHECK_COMPILATION_ERRORS
|
||||||
|
constexpr auto v8 = read_from_ptr<u8>(arr, arr.size());
|
||||||
|
constexpr auto v16 = read_from_ptr<u16>(arr, arr.size() + 1 - sizeof(u16));
|
||||||
|
constexpr auto v32 = read_from_ptr<u32>(arr, arr.size() + 1 - sizeof(u32));
|
||||||
|
constexpr auto v64 = read_from_ptr<u64>(arr, arr.size() + 1 - sizeof(u64));
|
||||||
|
constexpr auto v128 = read_from_ptr<u128>(arr, 0);
|
||||||
|
|
||||||
|
constexpr auto v32_32 = read_from_ptr<u32>(arr32, arr32.size());
|
||||||
|
constexpr auto v64_32 = read_from_ptr<u64>(arr32, arr32.size() + 1 - sizeof(u64) / sizeof(u32));
|
||||||
|
constexpr auto v128_32 = read_from_ptr<u128>(arr32, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Utils, read_from_ptr_const_eval_c_array)
|
||||||
|
{
|
||||||
|
g_headless = true; // Disable exception popups
|
||||||
|
|
||||||
|
constexpr u8 arr[sizeof(u64)] { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 0) == u8(1));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 1) == u8(2));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 2) == u8(3));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 3) == u8(4));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 4) == u8(5));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 5) == u8(6));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 6) == u8(7));
|
||||||
|
static_assert(read_from_ptr<u8>(arr, 7) == u8(8));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 0) == u16(0x0201));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 2) == u16(0x0403));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 4) == u16(0x0605));
|
||||||
|
static_assert(read_from_ptr<le_t<u16>>(arr, 6) == u16(0x0807));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u32>>(arr, 0) == u32(0x04030201));
|
||||||
|
static_assert(read_from_ptr<le_t<u32>>(arr, 4) == u32(0x08070605));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u64>>(arr, 0) == u64(0x0807060504030201));
|
||||||
|
|
||||||
|
constexpr u32 arr32[sizeof(u64) / sizeof(u32)] { 0x00000000, 0xFFFFFFFF };
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<u32>(arr32, 0) == u32(0x00000000));
|
||||||
|
static_assert(read_from_ptr<u32>(arr32, 1) == u32(0xFFFFFFFF));
|
||||||
|
|
||||||
|
static_assert(read_from_ptr<le_t<u64>>(arr32, 0) == u64(0xFFFFFFFF00000000));
|
||||||
|
|
||||||
|
// This should not compile
|
||||||
|
#if CHECK_COMPILATION_ERRORS
|
||||||
|
constexpr auto v8 = read_from_ptr<u8>(arr, std::size(arr));
|
||||||
|
constexpr auto v16 = read_from_ptr<u16>(arr, std::size(arr) + 1 - sizeof(u16));
|
||||||
|
constexpr auto v32 = read_from_ptr<u32>(arr, std::size(arr) + 1 - sizeof(u32));
|
||||||
|
constexpr auto v64 = read_from_ptr<u64>(arr, std::size(arr) + 1 - sizeof(u64));
|
||||||
|
constexpr auto v128 = read_from_ptr<u128>(arr, 0);
|
||||||
|
|
||||||
|
constexpr auto v32_32 = read_from_ptr<u32>(arr32, std::size(arr32));
|
||||||
|
constexpr auto v64_32 = read_from_ptr<u64>(arr32, std::size(arr32) + 1 - sizeof(u64) / sizeof(u32));
|
||||||
|
constexpr auto v128_32 = read_from_ptr<u128>(arr32, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -941,6 +941,7 @@ using const_str = const_str_t<>;
|
|||||||
namespace fmt
|
namespace fmt
|
||||||
{
|
{
|
||||||
[[noreturn]] void raw_verify_error(std::source_location loc, const char8_t* msg, usz object);
|
[[noreturn]] void raw_verify_error(std::source_location loc, const char8_t* msg, usz object);
|
||||||
|
[[noreturn]] void raw_verify_error(std::source_location loc, std::source_location propagated_loc, const char8_t* msg, usz object);
|
||||||
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size);
|
[[noreturn]] void raw_range_error(std::source_location loc, std::string_view index, usz container_size);
|
||||||
[[noreturn]] void raw_range_error(std::source_location loc, usz index, usz container_size);
|
[[noreturn]] void raw_range_error(std::source_location loc, usz index, usz container_size);
|
||||||
}
|
}
|
||||||
@ -976,6 +977,17 @@ constexpr decltype(auto) ensure(T&& arg, const_str msg = const_str(), std::sourc
|
|||||||
fmt::raw_verify_error(src_loc, msg, 0);
|
fmt::raw_verify_error(src_loc, msg, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr decltype(auto) ensure(T&& arg, std::source_location propagated_loc, const_str msg = const_str(), std::source_location src_loc = std::source_location::current()) noexcept
|
||||||
|
{
|
||||||
|
if (std::forward<T>(arg)) [[likely]]
|
||||||
|
{
|
||||||
|
return std::forward<T>(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::raw_verify_error(src_loc, propagated_loc, msg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename F> requires (std::is_invocable_v<F, T&&>)
|
template <typename T, typename F> requires (std::is_invocable_v<F, T&&>)
|
||||||
constexpr decltype(auto) ensure(T&& arg, F&& pred, const_str msg = const_str(), std::source_location src_loc = std::source_location::current()) noexcept
|
constexpr decltype(auto) ensure(T&& arg, F&& pred, const_str msg = const_str(), std::source_location src_loc = std::source_location::current()) noexcept
|
||||||
{
|
{
|
||||||
@ -1185,36 +1197,74 @@ namespace stx
|
|||||||
|
|
||||||
// Read object of type T from raw pointer, array, string, vector, or any contiguous container
|
// Read object of type T from raw pointer, array, string, vector, or any contiguous container
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
constexpr T read_from_ptr(U&& array, usz pos = 0)
|
constexpr T read_from_ptr(U&& array, usz pos = 0, std::source_location src_loc = std::source_location::current())
|
||||||
{
|
{
|
||||||
// TODO: ensure array element types are trivial
|
// TODO: ensure array element types are trivial
|
||||||
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
||||||
std::decay_t<decltype(array[0])> buf[sizeof(T) / sizeof(array[0])];
|
constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]);
|
||||||
|
|
||||||
|
std::decay_t<decltype(array[0])> buf[elements_per_value];
|
||||||
|
|
||||||
if (!std::is_constant_evaluated())
|
if (!std::is_constant_evaluated())
|
||||||
|
{
|
||||||
|
if constexpr (requires { std::size(array); })
|
||||||
|
{
|
||||||
|
ensure((pos + elements_per_value) <= std::size(array), src_loc);
|
||||||
|
}
|
||||||
|
|
||||||
std::memcpy(+buf, &array[pos], sizeof(buf));
|
std::memcpy(+buf, &array[pos], sizeof(buf));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
for (usz i = 0; i < pos; buf[i] = array[pos + i], i++);
|
{
|
||||||
|
// We could add an ensure or static_assert for OOB, but lucky for us, the [] operator will not compile with OOB.
|
||||||
|
for (usz i = 0; i < elements_per_value; i++)
|
||||||
|
{
|
||||||
|
buf[i] = array[pos + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
return std::bit_cast<T>(buf);
|
return std::bit_cast<T>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
constexpr void write_to_ptr(U&& array, usz pos, const T& value)
|
constexpr void write_to_ptr(U&& array, usz pos, const T& value, std::source_location src_loc = std::source_location::current())
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
||||||
|
constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]);
|
||||||
|
|
||||||
|
if constexpr (requires { std::size(array); })
|
||||||
|
{
|
||||||
|
ensure((pos + elements_per_value) <= std::size(array), src_loc);
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::is_constant_evaluated())
|
if (!std::is_constant_evaluated())
|
||||||
|
{
|
||||||
std::memcpy(static_cast<void*>(&array[pos]), &value, sizeof(value));
|
std::memcpy(static_cast<void*>(&array[pos]), &value, sizeof(value));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ensure(!"Unimplemented");
|
ensure(!"Unimplemented");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
constexpr void write_to_ptr(U&& array, const T& value)
|
constexpr void write_to_ptr(U&& array, const T& value, std::source_location src_loc = std::source_location::current())
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
static_assert(sizeof(T) % sizeof(array[0]) == 0);
|
||||||
|
constexpr usz elements_per_value = sizeof(T) / sizeof(array[0]);
|
||||||
|
|
||||||
|
if constexpr (requires { std::size(array); })
|
||||||
|
{
|
||||||
|
ensure(elements_per_value <= std::size(array), src_loc);
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::is_constant_evaluated())
|
if (!std::is_constant_evaluated())
|
||||||
|
{
|
||||||
std::memcpy(static_cast<void*>(&array[0]), &value, sizeof(value));
|
std::memcpy(static_cast<void*>(&array[0]), &value, sizeof(value));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ensure(!"Unimplemented");
|
ensure(!"Unimplemented");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr struct aref_tag_t{} aref_tag{};
|
constexpr struct aref_tag_t{} aref_tag{};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user