From aa9a6e91407635208c703f310ce0e70aa8b00664 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Sat, 7 Nov 2026 14:04:19 +0100 Subject: [PATCH] Triforce, MagCard, Bluetooth: bytes lent as pointer and size, no spans or maps Serial devices lend their Rx bytes in place and take Tx as pointer plus size; JVS frames unescape onto the stack and are consumed without a scope guard. IO port inputs and outputs are plain pointers with fixed counts. Mediaboard addresses resolve to a lent pointer and size; the game table is a flat array. Bluetooth passthrough keeps its link keys in a fixed sorted array, links live transfers through the header that already fronts each buffer, and lends ACL packets as pointer and size. AsU8Span/AsWritableU8Span are gone from BitUtils. --- Source/Core/Common/BitUtils.h | 33 --- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 237 ++++++++++-------- Source/Core/Core/HW/DVD/AMMediaboard.h | 3 +- .../Core/HW/MagCard/MagneticCardReader.cpp | 77 +++--- .../Core/Core/HW/MagCard/MagneticCardReader.h | 3 +- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 37 ++- Source/Core/Core/HW/Triforce/DeckReader.cpp | 90 ++++--- Source/Core/Core/HW/Triforce/DeckReader.h | 2 + Source/Core/Core/HW/Triforce/FZeroAX.cpp | 28 +-- Source/Core/Core/HW/Triforce/FZeroAX.h | 7 +- Source/Core/Core/HW/Triforce/ICCardReader.cpp | 208 +++++++-------- Source/Core/Core/HW/Triforce/ICCardReader.h | 9 +- Source/Core/Core/HW/Triforce/IOPorts.cpp | 47 ++-- Source/Core/Core/HW/Triforce/IOPorts.h | 51 ++-- Source/Core/Core/HW/Triforce/JVSIO.cpp | 184 +++++++------- Source/Core/Core/HW/Triforce/JVSIO.h | 3 +- Source/Core/Core/HW/Triforce/MarioKartGP.cpp | 31 ++- Source/Core/Core/HW/Triforce/MarioKartGP.h | 7 +- Source/Core/Core/HW/Triforce/SerialDevice.cpp | 34 +-- Source/Core/Core/HW/Triforce/SerialDevice.h | 13 +- Source/Core/Core/HW/Triforce/Touchscreen.cpp | 17 +- Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp | 34 ++- Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp | 118 ++++++--- Source/Core/Core/IOS/USB/Bluetooth/BTReal.h | 17 +- .../USB/Bluetooth/LibUSBBluetoothAdapter.cpp | 115 +++++---- .../USB/Bluetooth/LibUSBBluetoothAdapter.h | 27 +- .../USB/Bluetooth/RealtekFirmwareLoader.cpp | 124 +++++---- .../Core/IOS/USB/Bluetooth/WiimoteDevice.cpp | 2 +- 28 files changed, 843 insertions(+), 715 deletions(-) diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h index f9e0879326..e997d9a0ab 100644 --- a/Source/Core/Common/BitUtils.h +++ b/Source/Core/Common/BitUtils.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -255,36 +254,4 @@ T ExpandValue(T value, size_t left_shift_amount) (T(-ExtractBit<0>(value)) >> (BitSize() - left_shift_amount)); } -// Convert a contiguous range of a trivially-copyable type to a `span` -template -requires(std::is_trivially_copyable_v>) -constexpr auto AsU8Span(const T& range) -{ - return std::span{reinterpret_cast(range.data()), range.size() * sizeof(*range.data())}; -} - -// Convert a contiguous range of a non-const trivially-copyable type to a `span` -template -requires(std::is_trivially_copyable_v>) -constexpr auto AsWritableU8Span(T& range) -{ - return std::span{reinterpret_cast(range.data()), range.size() * sizeof(*range.data())}; -} - -// Convert a trivially-copyable object to a `span` -template -requires(!std::ranges::contiguous_range && std::is_trivially_copyable_v) -constexpr auto AsU8Span(const T& obj) -{ - return std::span{reinterpret_cast(std::addressof(obj)), sizeof(obj)}; -} - -// Convert a non-const trivially-copyable object to a `span` -template -requires(!std::ranges::contiguous_range && std::is_trivially_copyable_v) -constexpr auto AsWritableU8Span(T& obj) -{ - return std::span{reinterpret_cast(std::addressof(obj)), sizeof(obj)}; -} - } // namespace Common diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 6b502f3d78..a87adbefb0 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -5,11 +5,9 @@ #include -#include #include #include #include -#include #include @@ -81,15 +79,15 @@ namespace AMMediaboard static constexpr u32 TEST_OK_WORD0 = 0x54455354; // "TEST" static constexpr u32 TEST_OK_WORD1 = 0x204F4B00; // " OK\0" -MediaBoardRange::MediaBoardRange(u32 start_, u32 size_, std::span buffer_) - : start{start_}, end{start_ + std::min(size_, u32(buffer_.size()))}, buffer{buffer_.data()}, - buffer_size{buffer_.size()} +MediaBoardRange::MediaBoardRange(u32 start_, u32 size_, u8* buffer_, std::size_t buffer_size_) + : start{start_}, end{start_ + (size_ < buffer_size_ ? size_ : u32(buffer_size_))}, + buffer{buffer_}, buffer_size{buffer_size_} { - if (size_ <= buffer_.size()) + if (size_ <= buffer_size_) return; WARN_LOG_FMT(AMMEDIABOARD, "Invalid MediaBoardRange: start=0x{:08x}, size=0x{:06x}, buffer_size=0x{:06x}", - start, size_, buffer_.size()); + start, size_, buffer_size_); } using Common::SEND_FLAGS; @@ -189,32 +187,43 @@ constexpr char s_allnet_reply[] = { "name2=asia®ion_name3=export&end"}; static const MediaBoardRange s_mediaboard_ranges[] = { - {DIMMCommandVersion1, 0x40, Common::AsWritableU8Span(s_media_buffer_32)}, - {DIMMCommandVersion2, 0x60, Common::AsWritableU8Span(s_media_buffer_32)}, - {DIMMCommandVersion2_2, 0x220, Common::AsWritableU8Span(s_media_buffer_32)}, - {NetworkCommandAddress1, 0x1040, s_network_command_buffer}, - {NetworkCommandAddress2, 0x40000, s_network_command_buffer}, // TODO: Guesswork, verify this. - {NetworkBufferAddress1, 0x10000, s_network_buffer}, - {NetworkBufferAddress2, 0x10000, s_network_buffer}, - {NetworkBufferAddress3, 0x50000, s_network_buffer}, - {NetworkBufferAddress4, 0xc0000, s_network_buffer}, // TODO: size bigger than buffer's? - {NetworkBufferAddress5, 0x10000, s_network_buffer}, - {AllNetSettings, 0x8000, s_allnet_settings}, - {AllNetBuffer, 0x1000, s_allnet_buffer}, + {DIMMCommandVersion1, 0x40, s_media_buffer, sizeof(s_media_buffer_32)}, + {DIMMCommandVersion2, 0x60, s_media_buffer, sizeof(s_media_buffer_32)}, + {DIMMCommandVersion2_2, 0x220, s_media_buffer, sizeof(s_media_buffer_32)}, + {NetworkCommandAddress1, 0x1040, s_network_command_buffer.data(), s_network_command_buffer.size()}, + {NetworkCommandAddress2, 0x40000, s_network_command_buffer.data(), s_network_command_buffer.size()}, // TODO: Guesswork, verify this. + {NetworkBufferAddress1, 0x10000, s_network_buffer.data(), s_network_buffer.size()}, + {NetworkBufferAddress2, 0x10000, s_network_buffer.data(), s_network_buffer.size()}, + {NetworkBufferAddress3, 0x50000, s_network_buffer.data(), s_network_buffer.size()}, + {NetworkBufferAddress4, 0xc0000, s_network_buffer.data(), s_network_buffer.size()}, // TODO: size bigger than buffer's? + {NetworkBufferAddress5, 0x10000, s_network_buffer.data(), s_network_buffer.size()}, + {AllNetSettings, 0x8000, s_allnet_settings.data(), s_allnet_settings.size()}, + {AllNetBuffer, 0x1000, s_allnet_buffer.data(), s_allnet_buffer.size()}, }; -static std::span GetSpanForMediaboardAddress(u32 address) +// The host bytes behind a mediaboard address, lent; nullptr when unmapped. +static u8* GetMediaboardPointer(u32 address, std::size_t* size) { for (const auto& range : s_mediaboard_ranges) { if (address >= range.start && address < range.end) - return std::span{range.buffer, range.buffer_size}.subspan(address - range.start); + { + *size = range.buffer_size - (address - range.start); + return range.buffer + (address - range.start); + } } - return {}; + *size = 0; + return nullptr; } -static const std::unordered_map s_game_map = { +struct GameMapEntry +{ + u16 triforce_id; + GameType game_type; +}; + +static const GameMapEntry s_game_map[] = { {0x4747, FZeroAX}, {0x4841, FZeroAXMonster}, {0x4B50, MarioKartGP}, @@ -287,29 +296,29 @@ static SOCKET GetHostSocket(GuestSocket x) static GuestSocket GetGuestSocket(SOCKET x) { - const auto it = std::find(std::begin(s_sockets) + FIRST_VALID_FD, std::end(s_sockets), x); - - if (it == std::end(s_sockets)) + for (std::size_t i = FIRST_VALID_FD; i < std::size(s_sockets); i++) { - ERROR_LOG_FMT(AMMEDIABOARD, "GuestSocket not found. This should not happen."); - return INVALID_GUEST_SOCKET; + if (s_sockets[i] == x) + return GuestSocket(i); } - return GuestSocket(it - std::begin(s_sockets)); + ERROR_LOG_FMT(AMMEDIABOARD, "GuestSocket not found. This should not happen."); + return INVALID_GUEST_SOCKET; } static std::string_view GetSafeString(u32 offset, u32 max_length) { - const auto str_span = GetSpanForMediaboardAddress(offset); + std::size_t str_size; + u8* const str_ptr = GetMediaboardPointer(offset, &str_size); - if (str_span.empty()) + if (!str_size) return {}; // Don't exceed max_length or end of buffer. - const auto adjusted_length = std::min(max_length, str_span.size()); - const auto length = strnlen(reinterpret_cast(str_span.data()), adjusted_length); + const std::size_t adjusted_length = max_length < str_size ? max_length : str_size; + const auto length = strnlen(reinterpret_cast(str_ptr), adjusted_length); - return {reinterpret_cast(str_span.data()), length}; + return {reinterpret_cast(str_ptr), length}; } static bool SafeCopyToEmu(Memory::MemoryManager& memory, u32 address, const u8* source, @@ -511,7 +520,8 @@ void Init() return; } - const u64 length = std::min(sega_boot.GetSize(), sizeof(s_firmware)); + const u64 boot_size = sega_boot.GetSize(); + const u64 length = boot_size < sizeof(s_firmware) ? boot_size : u64(sizeof(s_firmware)); sega_boot.ReadBytes(s_firmware.data(), length); s_test_menu = true; @@ -523,19 +533,19 @@ void InitDIMM(const DiscIO::Volume& volume) s_dimm_disc = DiscIO::CreateCachedBlobReader(volume.GetBlobReader().CopyReader()); } -static int PlatformPoll(std::span pfds, std::chrono::milliseconds timeout) +static int PlatformPoll(WSAPOLLFD* pfds, std::size_t count, std::chrono::milliseconds timeout) { #if defined(_WIN32) - return WSAPoll(pfds.data(), ULONG(pfds.size()), INT(timeout.count())); + return WSAPoll(pfds, ULONG(count), INT(timeout.count())); #else - return UnixUtil::RetryOnEINTR(poll, pfds.data(), pfds.size(), timeout.count()); + return UnixUtil::RetryOnEINTR(poll, pfds, count, timeout.count()); #endif } std::optional ParseIPRedirection(std::string_view str) { // Everything after a space is the description. - const auto ip_pair_str = std::string_view{str.begin(), std::ranges::find(str, ' ')}; + const auto ip_pair_str = str.substr(0, str.find(' ')); const auto parts = SplitStringIntoArray<2>(ip_pair_str, '='); if (!parts.has_value()) @@ -779,7 +789,7 @@ static s32 NetDIMMConnect(GuestSocket guest_socket, const GuestSocketAddress& gu duration_cast(std::chrono::microseconds{s_timeouts[0]}); // TODO: Might block if timeout is too big - const int poll_result = PlatformPoll(pfds, timeout); + const int poll_result = PlatformPoll(pfds, 1, timeout); if (poll_result < 0) [[unlikely]] { @@ -842,7 +852,7 @@ static GuestSocket NetDIMMAccept(GuestSocket guest_socket, u8* guest_addr_ptr, DEBUG_LOG_FMT(AMMEDIABOARD, "NetDIMMAccept: {}({})", host_socket, int(guest_socket)); - const int poll_result = PlatformPoll(pfds, timeout); + const int poll_result = PlatformPoll(pfds, 1, timeout); if (poll_result < 0) [[unlikely]] { @@ -899,8 +909,9 @@ static GuestSocket NetDIMMAccept(GuestSocket guest_socket, u8* guest_addr_ptr, Common::IPAddressToString(guest_addr.ip_address), ntohs(guest_addr.port)); } - const auto write_size = - std::min(Common::BitCastPtr(guest_addrlen_ptr), sizeof(guest_addr)); + const u32 guest_addrlen = Common::BitCastPtr(guest_addrlen_ptr); + const u32 write_size = + guest_addrlen < sizeof(guest_addr) ? guest_addrlen : u32(sizeof(guest_addr)); // Write out the addr. std::memcpy(guest_addr_ptr, &guest_addr, write_size); @@ -978,8 +989,9 @@ static void AMMBCommandRecv(u32 parameter_offset) const u32 off = s_media_buffer_32[parameter_offset + 1]; u32 len = s_media_buffer_32[parameter_offset + 2]; - const auto data_span = GetSpanForMediaboardAddress(off); - if (data_span.size() < len) + std::size_t data_size; + u8* const data_ptr = GetMediaboardPointer(off, &data_size); + if (data_size < len) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandRecv: Bad data offset or length: off={:08x} len={}", off, len); @@ -987,7 +999,7 @@ static void AMMBCommandRecv(u32 parameter_offset) } // TODO: Might be blocking depending on the timeout (see SetTimeouts command). - const int ret = recv(fd, reinterpret_cast(data_span.data()), len, 0); + const int ret = recv(fd, reinterpret_cast(data_ptr), len, 0); const int err = WSAGetLastError(); if (ret < 0 && err != WSAEWOULDBLOCK) @@ -1017,8 +1029,9 @@ static void AMMBCommandSend(u32 parameter_offset) const u32 off = s_media_buffer_32[parameter_offset + 1]; u32 len = s_media_buffer_32[parameter_offset + 2]; - const auto data_span = GetSpanForMediaboardAddress(off); - if (data_span.size() < len) + std::size_t data_size; + u8* const data_ptr = GetMediaboardPointer(off, &data_size); + if (data_size < len) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandSend: Bad data offset or length: off={:08x} len={}", off, len); @@ -1026,7 +1039,7 @@ static void AMMBCommandSend(u32 parameter_offset) } // TODO: Might be blocking depending on the timeout (see SetTimeouts command). - const int ret = send(fd, reinterpret_cast(data_span.data()), len, SEND_FLAGS); + const int ret = send(fd, reinterpret_cast(data_ptr), len, SEND_FLAGS); const int err = WSAGetLastError(); if (ret < 0 && err != WSAEWOULDBLOCK) @@ -1088,14 +1101,15 @@ static void AMMBCommandConnect(u32 parameter_offset) return; } - const auto addr_span = GetSpanForMediaboardAddress(addr_offset); - if (addr_span.size() < sizeof(addr)) + std::size_t addr_size; + u8* const addr_ptr = GetMediaboardPointer(addr_offset, &addr_size); + if (addr_size < sizeof(addr)) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandConnect: Bad address offset: {:08x}", addr_offset); return; } - memcpy(&addr, addr_span.data(), sizeof(addr)); + memcpy(&addr, addr_ptr, sizeof(addr)); const int ret = NetDIMMConnect(guest_socket, addr); @@ -1109,20 +1123,22 @@ static void AMMBCommandAccept(u32 parameter_offset) const u32 addr_off = s_media_buffer_32[parameter_offset + 1]; const u32 addrlen_off = s_media_buffer_32[parameter_offset + 2]; - const auto addrlen_span = GetSpanForMediaboardAddress(addrlen_off); + std::size_t addrlen_size; + u8* const addrlen_view = GetMediaboardPointer(addrlen_off, &addrlen_size); u8* addr_ptr{}; u8* addrlen_ptr{}; - if (addrlen_span.size() >= sizeof(u32)) + if (addrlen_size >= sizeof(u32)) { - addrlen_ptr = addrlen_span.data(); + addrlen_ptr = addrlen_view; const u32 addrlen = Common::BitCastPtr(addrlen_ptr); - const auto addr_span = GetSpanForMediaboardAddress(addr_off); - if (addr_span.size() >= addrlen) + std::size_t addr_size; + u8* const addr_view = GetMediaboardPointer(addr_off, &addr_size); + if (addr_size >= addrlen) { - addr_ptr = addr_span.data(); + addr_ptr = addr_view; } else { @@ -1151,14 +1167,15 @@ static void AMMBCommandBind() return; } - const auto addr_span = GetSpanForMediaboardAddress(addr_offset); - if (addr_span.size() < sizeof(guest_addr)) + std::size_t addr_size; + u8* const addr_ptr = GetMediaboardPointer(addr_offset, &addr_size); + if (addr_size < sizeof(guest_addr)) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandBind: Bad address offset: {:08x}", addr_offset); return; } - memcpy(&guest_addr, addr_span.data(), sizeof(guest_addr)); + memcpy(&guest_addr, addr_ptr, sizeof(guest_addr)); const auto bind_result = NetDIMMBind(guest_socket, guest_addr); @@ -1166,54 +1183,55 @@ static void AMMBCommandBind() s_last_error = SSC_SUCCESS; } -static void FillPollFdsFromGuestFdSet(std::span pfds, u32 guest_fds_offset, - short requested_events) +static void FillPollFdsFromGuestFdSet(WSAPOLLFD* pfds, std::size_t pfd_count, + u32 guest_fds_offset, short requested_events) { if (guest_fds_offset == 0) return; GuestFdSet guest_fds; - const auto guest_fds_span = GetSpanForMediaboardAddress(guest_fds_offset); - if (guest_fds_span.size() < sizeof(guest_fds)) + std::size_t guest_fds_size; + u8* const guest_fds_ptr = GetMediaboardPointer(guest_fds_offset, &guest_fds_size); + if (guest_fds_size < sizeof(guest_fds)) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "Bad FDSET offset: {:08x}", guest_fds_offset); return; } - std::memcpy(&guest_fds, guest_fds_span.data(), sizeof(guest_fds)); + std::memcpy(&guest_fds, guest_fds_ptr, sizeof(guest_fds)); - u32 index = 0; - for (auto& pfd : pfds) + for (u32 index = 0; index < pfd_count; ++index) { + WSAPOLLFD& pfd = pfds[index]; const auto guest_socket = GuestSocket(index); if (guest_fds.IsFdSet(guest_socket)) { pfd.fd = GetHostSocket(guest_socket); pfd.events |= requested_events; } - - ++index; } } -static void WriteGuestFdSetFromPollFds(u32 guest_fds_offset, std::span fds, - short returned_events) +static void WriteGuestFdSetFromPollFds(u32 guest_fds_offset, const WSAPOLLFD* fds, + std::size_t fd_count, short returned_events) { if (guest_fds_offset == 0) return; GuestFdSet guest_fds; - const auto guest_fds_span = GetSpanForMediaboardAddress(guest_fds_offset); - if (guest_fds_span.size() < sizeof(guest_fds)) + std::size_t guest_fds_size; + u8* const guest_fds_ptr = GetMediaboardPointer(guest_fds_offset, &guest_fds_size); + if (guest_fds_size < sizeof(guest_fds)) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "Bad FDSET offset: {:08x}", guest_fds_offset); return; } - for (const auto& fd : fds) + for (std::size_t i = 0; i < fd_count; i++) { + const WSAPOLLFD& fd = fds[i]; if ((fd.revents & returned_events) == 0) continue; @@ -1224,7 +1242,7 @@ static void WriteGuestFdSetFromPollFds(u32 guest_fds_offset, std::span(nfds, std::size(s_sockets)); + if (nfds > std::size(s_sockets)) + nfds = u32(std::size(s_sockets)); std::chrono::milliseconds timeout{-1}; if (timeout_offset != 0) { - const auto guest_timeout_span = GetSpanForMediaboardAddress(timeout_offset); + std::size_t guest_timeout_size; + u8* const guest_timeout_ptr = GetMediaboardPointer(timeout_offset, &guest_timeout_size); TimeVal guest_timeout; - if (guest_timeout_span.size() < sizeof(guest_timeout)) + if (guest_timeout_size < sizeof(guest_timeout)) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandSelect: Bad timeout offset: {:08x}", timeout_offset); } else { - std::memcpy(&guest_timeout, guest_timeout_span.data(), sizeof(guest_timeout)); + std::memcpy(&guest_timeout, guest_timeout_ptr, sizeof(guest_timeout)); timeout = duration_cast( std::chrono::seconds(guest_timeout.seconds) + @@ -1274,25 +1294,33 @@ static void AMMBCommandSelect(u32 parameter_offset) // Fill with the host sockets for each guest socket less-than `nfds` in each GuestFdSet. std::vector pollfds(nfds, WSAPOLLFD{.fd = INVALID_SOCKET}); - FillPollFdsFromGuestFdSet(pollfds, readfds_offset, POLLIN); - FillPollFdsFromGuestFdSet(pollfds, writefds_offset, POLLOUT); - FillPollFdsFromGuestFdSet(pollfds, exceptfds_offset, POLLPRI); + FillPollFdsFromGuestFdSet(pollfds.data(), pollfds.size(), readfds_offset, POLLIN); + FillPollFdsFromGuestFdSet(pollfds.data(), pollfds.size(), writefds_offset, POLLOUT); + FillPollFdsFromGuestFdSet(pollfds.data(), pollfds.size(), exceptfds_offset, POLLPRI); // Erase "INVALID" entries and also entries that weren't in any GuestFdSet. - std::erase_if(pollfds, [](const WSAPOLLFD& fd) { return fd.fd == INVALID_SOCKET; }); + { + std::size_t kept = 0; + for (std::size_t i = 0; i < pollfds.size(); i++) + { + if (pollfds[i].fd != INVALID_SOCKET) + pollfds[kept++] = pollfds[i]; + } + pollfds.resize(kept); + } // TODO: There may be some edge cases where // poll's (POLLIN,POLLOUT,POLLPRI) don't map 1:1 with select's (readfds,writefds,exceptfds). DEBUG_LOG_FMT(AMMEDIABOARD, "AMMBCommandSelect: Polling with socket count: {}", pollfds.size()); - const int ret = PlatformPoll(pollfds, timeout); + const int ret = PlatformPoll(pollfds.data(), pollfds.size(), timeout); if (ret >= 0) { - WriteGuestFdSetFromPollFds(readfds_offset, pollfds, POLLIN); - WriteGuestFdSetFromPollFds(writefds_offset, pollfds, POLLOUT); - WriteGuestFdSetFromPollFds(exceptfds_offset, pollfds, POLLPRI); + WriteGuestFdSetFromPollFds(readfds_offset, pollfds.data(), pollfds.size(), POLLIN); + WriteGuestFdSetFromPollFds(writefds_offset, pollfds.data(), pollfds.size(), POLLOUT); + WriteGuestFdSetFromPollFds(exceptfds_offset, pollfds.data(), pollfds.size(), POLLPRI); DEBUG_LOG_FMT(AMMEDIABOARD_NET, "GC-AM: select result: {}", ret); } else @@ -1313,8 +1341,9 @@ static void AMMBCommandSetSockOpt(u32 parameter_offset) const u32 optval_offset = s_media_buffer_32[parameter_offset + 3]; const u32 optlen = s_media_buffer_32[parameter_offset + 4]; - const auto optval_span = GetSpanForMediaboardAddress(optval_offset); - if (optval_span.size() < optlen) + std::size_t optval_size; + u8* const optval_ptr = GetMediaboardPointer(optval_offset, &optval_size); + if (optval_size < optlen) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "AMMBCommandSetSockOpt: Bad optval offset or length: off={:08x} len={}", @@ -1322,7 +1351,7 @@ static void AMMBCommandSetSockOpt(u32 parameter_offset) return; } - const char* optval = reinterpret_cast(optval_span.data()); + const char* optval = reinterpret_cast(optval_ptr); // TODO: Ensure parameters are compatible with host's setsockopt const int ret = setsockopt(fd, level, optname, optval, optlen); @@ -1545,7 +1574,8 @@ u32 ExecuteCommand(std::array& dicmd_buf, u32* diimm_buf, u32 address, u // Intercept 128-byte read after GetNetworkConfig: serve network config from trinetcfg.bin if (s_netconfig_read_pending && length == 0x80) { - if (!GetSpanForMediaboardAddress(offset).empty()) + std::size_t mediaboard_size; + if (GetMediaboardPointer(offset, &mediaboard_size)) { s_netconfig_read_pending = false; @@ -1585,10 +1615,11 @@ u32 ExecuteCommand(std::array& dicmd_buf, u32* diimm_buf, u32 address, u return 0; } - if (const auto mediaboard_span = GetSpanForMediaboardAddress(offset); !mediaboard_span.empty()) + std::size_t mediaboard_size; + if (u8* const mediaboard_ptr = GetMediaboardPointer(offset, &mediaboard_size)) { DEBUG_LOG_FMT(AMMEDIABOARD, "GC-AM: Read MediaBoard ({:08x},{:08x})", offset, length); - SafeCopyToEmu(memory, address, mediaboard_span.data(), mediaboard_span.size(), 0, length); + SafeCopyToEmu(memory, address, mediaboard_ptr, mediaboard_size, 0, length); PrintMBBuffer(address, length); return 0; } @@ -2049,7 +2080,8 @@ u32 ExecuteCommand(std::array& dicmd_buf, u32* diimm_buf, u32 address, u } } - if (const auto mediaboard_span = GetSpanForMediaboardAddress(offset); !mediaboard_span.empty()) + std::size_t write_size; + if (u8* const write_ptr = GetMediaboardPointer(offset, &write_size)) { // Persist network config to trinetcfg.bin for SET IP ADDRESS. // The DMA Write (0x80 bytes) arrives before the corresponding command (0x0204), @@ -2062,7 +2094,7 @@ u32 ExecuteCommand(std::array& dicmd_buf, u32* diimm_buf, u32 address, u } DEBUG_LOG_FMT(AMMEDIABOARD, "GC-AM: Write MediaBoard ({:08x},{:08x})", offset, length); - SafeCopyFromEmu(memory, mediaboard_span.data(), address, mediaboard_span.size(), 0, length); + SafeCopyFromEmu(memory, write_ptr, address, write_size, 0, length); PrintMBBuffer(address, length); return 0; } @@ -2222,14 +2254,15 @@ u32 ExecuteCommand(std::array& dicmd_buf, u32* diimm_buf, u32 address, u NOTICE_LOG_FMT(AMMEDIABOARD_NET, "GC-AM: Offset: ({:04x})", off); NOTICE_LOG_FMT(AMMEDIABOARD_NET, "GC-AM: ({:08x})", addr); - const auto data_span = GetSpanForMediaboardAddress(off + addr); - if (data_span.size() < 0x20) + std::size_t data_size; + u8* const data_ptr = GetMediaboardPointer(off + addr, &data_size); + if (data_size < 0x20) { ERROR_LOG_FMT(AMMEDIABOARD_NET, "SearchDevices: Bad data offset: {:08x}", off + addr); break; } - const u8* const data = data_span.data(); + const u8* const data = data_ptr; for (u32 i = 0; i < 0x20; i += 0x10) { @@ -2315,10 +2348,10 @@ u32 GetGameType() triforce_id = 0x454A; // Fallback (VirtuaStriker3) } - auto it = s_game_map.find(triforce_id); - if (it != s_game_map.end()) + for (const GameMapEntry& entry : s_game_map) { - return it->second; + if (entry.triforce_id == triforce_id) + return entry.game_type; } PanicAlertFmtT("Unknown game ID:{0:08x}, using default controls.", triforce_id); diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h index 77cf9bce70..efd9af204b 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.h +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -5,7 +5,6 @@ #include #include -#include #include #include "Common/ChunkFile.h" @@ -75,7 +74,7 @@ struct MediaBoardRange u8* buffer; std::size_t buffer_size; - MediaBoardRange(u32 start_, u32 size_, std::span buffer_); + MediaBoardRange(u32 start_, u32 size_, u8* buffer_, std::size_t buffer_size_); }; enum class AMMBDICommand : u16 diff --git a/Source/Core/Core/HW/MagCard/MagneticCardReader.cpp b/Source/Core/Core/HW/MagCard/MagneticCardReader.cpp index c2ae7d3c79..f7060bff2e 100644 --- a/Source/Core/Core/HW/MagCard/MagneticCardReader.cpp +++ b/Source/Core/Core/HW/MagCard/MagneticCardReader.cpp @@ -7,8 +7,7 @@ #include "Core/HW/MagCard/MagneticCardReader.h" -#include -#include +#include #include "Common/DirectIOFile.h" #include "Common/FileUtil.h" @@ -21,11 +20,10 @@ namespace { -void AppendRange(auto* container, const auto& range) +void AppendBytes(std::vector* container, const void* data, std::size_t size) { - const auto prev_size = container->size(); - container->resize(prev_size + range.size()); - std::ranges::copy(range, container->data() + prev_size); + const u8* const bytes = static_cast(data); + container->insert(container->end(), bytes, bytes + size); } } // namespace @@ -191,7 +189,7 @@ void MagneticCardReader::Command_33_ReadData() return; } - AppendRange(&m_command_payload, *track_data); + AppendBytes(&m_command_payload, track_data->data(), track_data->size()); } break; } @@ -224,7 +222,7 @@ void MagneticCardReader::Command_35_GetData() return; } - AppendRange(&m_command_payload, *track_data); + AppendBytes(&m_command_payload, track_data->data(), track_data->size()); } break; default: @@ -263,10 +261,11 @@ void MagneticCardReader::Command_53_WriteData() const auto track_indices = GetTrackIndicesForTrackType(track); const auto proper_payload_size = track_indices.size() * TRACK_SIZE; - const auto payload = std::span{m_current_packet}.subspan(3); + const u8* const payload = m_current_packet.data() + 3; + const std::size_t payload_size = m_current_packet.size() - 3; if (mode != Mode::Standard || !IsCardLoaded() || track_indices.size() == 0 || - payload.size() != proper_payload_size) + payload_size != proper_payload_size) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "Command_53_WriteData: Failed."); SetPError(P::ILLEGAL_ERR); @@ -280,7 +279,7 @@ void MagneticCardReader::Command_53_WriteData() { auto& track_data = m_card_data[track_index]; track_data.emplace(); - std::ranges::copy(payload.subspan(track_index * TRACK_SIZE, TRACK_SIZE), track_data->data()); + std::memcpy(track_data->data(), payload + track_index * TRACK_SIZE, TRACK_SIZE); } Core::DisplayMessage("Writing Magnetic Card", 4000); @@ -501,7 +500,7 @@ void MagneticCardReader::Command_D0_ControlShutter() void MagneticCardReader::Command_F0_GetVersion() { - AppendRange(&m_command_payload, VERSION_STRING); + AppendBytes(&m_command_payload, VERSION_STRING.data(), VERSION_STRING.size()); m_status.SoftReset(); FinishCommand(); @@ -531,7 +530,8 @@ void MagneticCardReader::ReadCardFile() ERROR_LOG_FMT(SERIALINTERFACE_CARD, "ReadCardFile: Incorrect card file size: {}", file_size); } - const auto tracks_to_read = std::min(complete_track_count, NUM_TRACKS); + const u32 tracks_to_read = + complete_track_count < NUM_TRACKS ? u32(complete_track_count) : u32(NUM_TRACKS); for (u32 track_num = 0; track_num != tracks_to_read; ++track_num) { auto& track_data = m_card_data[track_num]; @@ -602,11 +602,12 @@ void MagneticCardReader::Update() { while (true) { - const auto read = GetRxByteSpan(); - if (read.empty()) + const u8* const read = GetRxBytes(); + const std::size_t read_size = GetRxByteCount(); + if (!read_size) break; - const u8 first_byte = read.front(); + const u8 first_byte = read[0]; if (first_byte == ENQUIRY) { // ENQUIRY @@ -639,14 +640,14 @@ void MagneticCardReader::Update() // This is a command packet. The next byte provides the size. // Upon read they ACK or NACK and start processing of a command. - if (read.size() < 2) + if (read_size < 2) break; // Wait for more data. const std::size_t packet_size = read[1]; - if (packet_size > read.size() - 2) + if (packet_size > read_size - 2) break; // Wait for more data. - if (ReceivePacket(read.subspan(2, packet_size))) + if (ReceivePacket(read + 2, packet_size)) { DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "Writing ACK"); WriteTxByte(ACK); @@ -661,17 +662,17 @@ void MagneticCardReader::Update() } } -bool MagneticCardReader::ReceivePacket(std::span packet) +bool MagneticCardReader::ReceivePacket(const u8* packet, std::size_t size) { - DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "ReceivePacket: {}", HexDump(packet.data(), packet.size())); + DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "ReceivePacket: {}", HexDump(packet, size)); - if (packet.size() < 6) + if (size < 6) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "ReceivePacket: Bad packet size!"); return false; } - if (packet[packet.size() - 2] != END_OF_TEXT) + if (packet[size - 2] != END_OF_TEXT) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "ReceivePacket: Missing END_OF_TEXT!"); return false; @@ -679,7 +680,10 @@ bool MagneticCardReader::ReceivePacket(std::span packet) // The final byte is an XOR checksum of all previous bytes, // including the count byte that we already chopped off. - if (std::accumulate(packet.begin(), packet.end(), u8(packet.size()), std::bit_xor{}) != 0) + u8 checksum = u8(size); + for (std::size_t i = 0; i < size; i++) + checksum ^= packet[i]; + if (checksum != 0) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "ReceivePacket: Bad checksum!"); return false; @@ -708,8 +712,7 @@ bool MagneticCardReader::ReceivePacket(std::span packet) // Copy the payload. // We're skipping the command number and the host's 3 status bytes, // and dropping the END_OF_TEXT and checksum. - m_current_packet.resize(packet.size() - 6); - std::ranges::copy(packet.subspan(4, m_current_packet.size()), m_current_packet.data()); + m_current_packet.assign(packet + 4, packet + size - 2); // TODO: Is this sensible for every command ? m_status.SoftReset(); @@ -861,23 +864,21 @@ void MagneticCardReader::BuildPacket() WriteTxByte(START_OF_TEXT); // Not included in the checksum. - const auto lead_in = std::to_array({ - payload_size, - GetCurrentCommand(), - GetPositionValue(), - u8(m_status.p), - u8(m_status.s), - }); - WriteTxBytes(lead_in); + const u8 lead_in[5] = { + payload_size, GetCurrentCommand(), GetPositionValue(), u8(m_status.p), u8(m_status.s), + }; + WriteTxBytes(lead_in, sizeof(lead_in)); - WriteTxBytes(m_command_payload); + WriteTxBytes(m_command_payload.data(), m_command_payload.size()); WriteTxByte(END_OF_TEXT); // Checksum is XOR of bytes after START_OF_TEXT. - const u8 packet_checksum = std::accumulate(lead_in.begin(), lead_in.end(), u8{}, std::bit_xor{}) ^ - std::accumulate(m_command_payload.begin(), m_command_payload.end(), - END_OF_TEXT, std::bit_xor{}); + u8 packet_checksum = END_OF_TEXT; + for (const u8 b : lead_in) + packet_checksum ^= b; + for (const u8 b : m_command_payload) + packet_checksum ^= b; WriteTxByte(packet_checksum); } diff --git a/Source/Core/Core/HW/MagCard/MagneticCardReader.h b/Source/Core/Core/HW/MagCard/MagneticCardReader.h index dd301d4590..9eb8bde7c0 100644 --- a/Source/Core/Core/HW/MagCard/MagneticCardReader.h +++ b/Source/Core/Core/HW/MagCard/MagneticCardReader.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -111,7 +110,7 @@ protected: ResetSpecifications = 0x32, }; - bool ReceivePacket(std::span packet); + bool ReceivePacket(const u8* packet, std::size_t size); void BuildPacket(); void StepStateMachine(DT elapsed_time); diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index 2da22e37b3..e2fbe7b8c2 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -3,8 +3,7 @@ #include "Core/HW/SI/SI_DeviceAMBaseboard.h" -#include -#include +#include #include #include @@ -244,8 +243,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { case GCAMCommand::StatusSwitches: { - const auto status_switches = m_io_ports.GetStatusSwitches(); - const auto out_length = u8(status_switches.size()); + const u8* const status_switches = m_io_ports.GetStatusSwitches(); + const auto out_length = u8(Triforce::IOPorts::STATUS_SWITCH_BYTE_COUNT); if (!validate_data_in_out(1, 2 + out_length, "StatusSwitches")) break; @@ -257,7 +256,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) data_out[data_offset++] = gcam_command; data_out[data_offset++] = out_length; - std::ranges::copy(status_switches, data_out.data() + data_offset); + std::memcpy(data_out.data() + data_offset, status_switches, out_length); data_offset += out_length; break; @@ -415,7 +414,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (m_serial_device_a != nullptr) { - m_serial_device_a->WriteRxBytes({data_in, in_length}); + m_serial_device_a->WriteRxBytes(data_in, in_length); } data_in += in_length; @@ -432,7 +431,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (m_serial_device_b != nullptr) { - m_serial_device_b->WriteRxBytes({data_in, in_length}); + m_serial_device_b->WriteRxBytes(data_in, in_length); } data_in += in_length; @@ -448,7 +447,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (!validate_data_in_out(in_length, 0, "JVSIO")) break; - m_jvs_io_board->WriteRxBytes({data_in, in_length}); + m_jvs_io_board->WriteRxBytes(data_in, in_length); data_in += in_length; m_jvs_io_board->Update(); @@ -466,9 +465,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) data_out[data_offset++] = gcam_command; data_out[data_offset++] = u8(out_length); - const auto out_span = std::span{data_out}.subspan(data_offset, out_length); - - m_jvs_io_board->TakeTxBytes(out_span); + m_jvs_io_board->TakeTxBytes(data_out.data() + data_offset, out_length); data_offset += out_length; break; @@ -506,8 +503,9 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) serial_device->Update(); - const auto out_length = - std::min(u32(serial_device->GetTxByteCount()), SERIAL_PORT_MAX_READ_SIZE); + const u32 tx_count = u32(serial_device->GetTxByteCount()); + const u32 out_length = + tx_count < SERIAL_PORT_MAX_READ_SIZE ? tx_count : SERIAL_PORT_MAX_READ_SIZE; if (out_length == 0) return; @@ -520,9 +518,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) data_out[data_offset++] = u8(cmd); data_out[data_offset++] = u8(out_length); - const auto out_span = std::span{data_out}.subspan(data_offset, out_length); - - serial_device->TakeTxBytes(out_span); + serial_device->TakeTxBytes(data_out.data() + data_offset, out_length); data_offset += out_length; }; @@ -534,10 +530,13 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) data_out[1] = data_offset - response_header_size; // Length // Zero-fill the rest of the buffer. - std::fill(data_out.data() + data_offset, data_out.data() + usable_out_size, 0x00); + std::memset(data_out.data() + data_offset, 0x00, usable_out_size - data_offset); // Compute the checksum (bitwise-xor of additive sum). - const u8 checksum = ~std::accumulate(data_out.data(), data_out.data() + data_offset, u8{}); + u8 sum = 0; + for (u32 i = 0; i < data_offset; i++) + sum += data_out[i]; + const u8 checksum = u8(~sum); data_out.back() = checksum; DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "Command response: {}", @@ -553,7 +552,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (!buffer_has_data) return 0; - std::ranges::copy(response_buffer, buffer); + std::memcpy(buffer, response_buffer.data(), response_buffer.size()); return int(response_buffer.size()); } default: diff --git a/Source/Core/Core/HW/Triforce/DeckReader.cpp b/Source/Core/Core/HW/Triforce/DeckReader.cpp index d41afa2a3d..03ebd5173d 100644 --- a/Source/Core/Core/HW/Triforce/DeckReader.cpp +++ b/Source/Core/Core/HW/Triforce/DeckReader.cpp @@ -14,7 +14,6 @@ #include "Common/FileUtil.h" #include "Common/JsonUtil.h" #include "Common/Logging/Log.h" -#include "Common/ScopeGuard.h" #include "Common/Swap.h" #include "Core/ConfigManager.h" @@ -156,12 +155,21 @@ namespace Triforce void DeckReader::Update() { - Common::ScopeGuard update_ic_card_reader{[this] { - // Update and take any response data from the IC card reader. - m_ic_card_reader.Update(); - PassThroughTxBytes(m_ic_card_reader); - }}; + HandleCommand(); + // Update and take any response data from the IC card reader. + m_ic_card_reader.Update(); + PassThroughTxBytes(m_ic_card_reader); +} + +void DeckReader::WriteTxU32BE(u32 value) +{ + const u8 bytes[4] = {u8(value >> 24), u8(value >> 16), u8(value >> 8), u8(value)}; + WriteTxBytes(bytes, sizeof(bytes)); +} + +void DeckReader::HandleCommand() +{ // All Deck Reader commands are exactly one byte (except FirmwareUpdate). // Responses must be the correct length, which varies by command. // Only the ReadCard response actually includes some kind of length field. @@ -177,12 +185,13 @@ void DeckReader::Update() return; } - const auto input_span = GetRxByteSpan(); + const u8* const input = GetRxBytes(); + const std::size_t input_size = GetRxByteCount(); - if (input_span.empty()) + if (!input_size) return; // Wait for more data. - const u8 cd_reader_command = input_span.front(); + const u8 cd_reader_command = input[0]; if (cd_reader_command == 0x00) { @@ -191,16 +200,16 @@ void DeckReader::Update() // How does the actual hardware deal with this ? // Does the Deck Reader really parse the IC Card Reader commands ? - if (input_span.size() < 4) + if (input_size < 4) return; // Wait for more data. - const u16 input_payload_size = Common::swap16(input_span.data() + 2); + const u16 input_payload_size = Common::swap16(input + 2); const u32 total_request_size = input_payload_size + 5u; - if (input_span.size() >= total_request_size) + if (input_size >= total_request_size) { // Forward the data to the attached IC Card Reader. - m_ic_card_reader.WriteRxBytes(input_span.first(total_request_size)); + m_ic_card_reader.WriteRxBytes(input, total_request_size); ConsumeRxBytes(total_request_size); } @@ -208,7 +217,8 @@ void DeckReader::Update() } // Write header. - WriteTxBytes(std::array{0xaa, cd_reader_command}); + WriteTxByte(0xaa); + WriteTxByte(cd_reader_command); switch (CDReaderCommand(cd_reader_command)) { @@ -236,10 +246,12 @@ void DeckReader::Update() { INFO_LOG_FMT(SERIALINTERFACE_CARD, "ProgramVersion"); - WriteTxBytes(Common::AsU8Span((CDR_PROGRAM_VERSION))); + WriteTxBytes(reinterpret_cast(CDR_PROGRAM_VERSION.data()), + CDR_PROGRAM_VERSION.size()); // Pad with zeros. - WriteTxBytes(std::array{}); + for (std::size_t i = CDR_PROGRAM_VERSION.size(); i < EXPECTED_VERSION_STR_LENGTH; i++) + WriteTxByte(0x00); break; } @@ -247,10 +259,11 @@ void DeckReader::Update() { INFO_LOG_FMT(SERIALINTERFACE_CARD, "BootVersion"); - WriteTxBytes(Common::AsU8Span(CDR_BOOT_VERSION)); + WriteTxBytes(reinterpret_cast(CDR_BOOT_VERSION.data()), CDR_BOOT_VERSION.size()); // Pad with zeros. - WriteTxBytes(std::array{}); + for (std::size_t i = CDR_BOOT_VERSION.size(); i < EXPECTED_VERSION_STR_LENGTH; i++) + WriteTxByte(0x00); break; } @@ -259,8 +272,7 @@ void DeckReader::Update() INFO_LOG_FMT(SERIALINTERFACE_CARD, "ProgramChecksum"); // Avalon seems to not care about the actual value. - Common::BigEndianValue fake_checksum{0x89abcdef}; - WriteTxBytes(Common::AsU8Span(fake_checksum)); + WriteTxU32BE(0x89abcdef); break; } @@ -269,8 +281,7 @@ void DeckReader::Update() INFO_LOG_FMT(SERIALINTERFACE_CARD, "BootChecksum"); // Avalon seems to not care about the actual value. - Common::BigEndianValue fake_checksum{0x01234567}; - WriteTxBytes(Common::AsU8Span(fake_checksum)); + WriteTxU32BE(0x01234567); break; } @@ -280,8 +291,8 @@ void DeckReader::Update() // FYI: If `header[1] != 0x68` the game inspects some bits and 9 bytes. Some error state ? - std::array response{}; - WriteTxBytes(response); + for (int i = 0; i < 10; i++) + WriteTxByte(0x00); break; } @@ -289,8 +300,7 @@ void DeckReader::Update() { INFO_LOG_FMT(SERIALINTERFACE_CARD, "ShutterGet"); - Common::BigEndianValue shutter_time{SHUTTER_TIME}; - WriteTxBytes(Common::AsU8Span(shutter_time)); + WriteTxU32BE(SHUTTER_TIME); break; } @@ -298,8 +308,7 @@ void DeckReader::Update() { INFO_LOG_FMT(SERIALINTERFACE_CARD, "ShutterAuto"); - Common::BigEndianValue shutter_time{SHUTTER_TIME}; - WriteTxBytes(Common::AsU8Span(shutter_time)); + WriteTxU32BE(SHUTTER_TIME); break; } @@ -338,19 +347,22 @@ void DeckReader::Update() if (is_deck_empty) { // I think this means that there are no cards or maybe "not ready". - WriteTxBytes(std::array{0xaa, 0x52}); + WriteTxByte(0xaa); + WriteTxByte(0x52); } else { // We've had one, yes, but what about second header ? - WriteTxBytes(std::array{0xaa, u8(CDReaderCommand::ReadCard)}); + WriteTxByte(0xaa); + WriteTxByte(u8(CDReaderCommand::ReadCard)); if (const auto deck = LoadCardDeckFromFile()) { // What happens with more than 30 cards ? WriteTxByte(u8(deck->size() * sizeof(CardIdentifier))); - WriteTxBytes(Common::AsU8Span(*deck)); + WriteTxBytes(reinterpret_cast(deck->data()), + deck->size() * sizeof(CardIdentifier)); } else { @@ -402,16 +414,17 @@ void DeckReader::Update() void DeckReader::HandleFirmwareUpdate() { - const auto input_span = GetRxByteSpan(); + const u8* const input = GetRxBytes(); + const std::size_t input_size = GetRxByteCount(); // No idea how the real hardware works. // There doesn't seem to be any sort of size field so.. here's a timeout. - if (input_span.empty()) + if (!input_size) { if (--m_firmware_update_timeout == 0) { - constexpr std::array fw_update_done{0xaa, u8(CDReaderCommand::FirmwareUpdate), 0x00}; - WriteTxBytes(fw_update_done); + static const u8 fw_update_done[3] = {0xaa, u8(CDReaderCommand::FirmwareUpdate), 0x00}; + WriteTxBytes(fw_update_done, sizeof(fw_update_done)); INFO_LOG_FMT(SERIALINTERFACE_CARD, "Fake FirmwareUpdate done."); @@ -422,14 +435,13 @@ void DeckReader::HandleFirmwareUpdate() { if (m_firmware_dump_file.IsOpen()) { - if (!m_firmware_dump_file.Write(input_span.data(), input_span.size())) + if (!m_firmware_dump_file.Write(input, input_size)) { - ERROR_LOG_FMT(SERIALINTERFACE_CARD, "Failed to write {} bytes to firmware.", - input_span.size()); + ERROR_LOG_FMT(SERIALINTERFACE_CARD, "Failed to write {} bytes to firmware.", input_size); } } - ConsumeRxBytes(input_span.size()); + ConsumeRxBytes(input_size); m_firmware_update_timeout = FIRMWARE_UPDATE_TIMEOUT; } } diff --git a/Source/Core/Core/HW/Triforce/DeckReader.h b/Source/Core/Core/HW/Triforce/DeckReader.h index 1e1fb23dad..86e8958794 100644 --- a/Source/Core/Core/HW/Triforce/DeckReader.h +++ b/Source/Core/Core/HW/Triforce/DeckReader.h @@ -31,7 +31,9 @@ private: // Unless FirmwareUpdate temporarily cuts off the IC Card Reader ? ICCardReader m_ic_card_reader{0}; + void HandleCommand(); void HandleFirmwareUpdate(); + void WriteTxU32BE(u32 value); u8 m_firmware_update_timeout = 0; diff --git a/Source/Core/Core/HW/Triforce/FZeroAX.cpp b/Source/Core/Core/HW/Triforce/FZeroAX.cpp index dde106882c..9be57198eb 100644 --- a/Source/Core/Core/HW/Triforce/FZeroAX.cpp +++ b/Source/Core/Core/HW/Triforce/FZeroAX.cpp @@ -3,13 +3,13 @@ #include "Core/HW/Triforce/FZeroAX.h" -#include #include #include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/Logging/Log.h" +#include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/HW/GCPad.h" @@ -81,8 +81,7 @@ void FZeroAXCommon_IOAdapter::Update() analog_inputs[6] = IOPorts::NEUTRAL_ANALOG_VALUE; } -void FZeroAXCommon_IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void FZeroAXCommon_IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { const u8 bits_changed_0 = bits_set[0] | bits_cleared[0]; @@ -111,7 +110,7 @@ void FZeroAXDeluxe_IOAdapter::Update() const auto generic_outputs = io_ports->GetGenericOutputs(); // Not fully understood trickery to satisfy the game's initialization sequence. - const u16 seat_state = Common::swap16(generic_outputs.data() + 1) >> 2; + const u16 seat_state = Common::swap16(generic_outputs + 1) >> 2; switch (seat_state) { case 0x70: @@ -186,21 +185,20 @@ void FZeroAXDeluxe_IOAdapter::DoState(PointerWrap& p) void FZeroAXSteeringWheel::Update() { - constexpr std::size_t REQUEST_SIZE = 4; - std::size_t rx_position = 0; while (true) { - const auto rx_span = GetRxByteSpan().subspan(rx_position); - - if (rx_span.size() < REQUEST_SIZE) + if (GetRxByteCount() - rx_position < REQUEST_SIZE) break; // Wait for more data. - const auto request = rx_span.first(); + const u8* const request = GetRxBytes() + rx_position; // The first byte is XOR'd with 0x80. // The last byte is an XOR of the previous bytes. - if (std::accumulate(request.begin(), request.end(), u8{0x80}, std::bit_xor{}) != 0) + u8 checksum = 0x80; + for (std::size_t i = 0; i < REQUEST_SIZE; i++) + checksum ^= request[i]; + if (checksum != 0) { WARN_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Bad checksum!"); ++rx_position; @@ -214,9 +212,10 @@ void FZeroAXSteeringWheel::Update() ConsumeRxBytes(rx_position); } -void FZeroAXSteeringWheel::ProcessRequest(std::span request) +void FZeroAXSteeringWheel::ProcessRequest(const u8* request) { - DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Request: {:02x}", fmt::join(request, " ")); + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Request: {}", + Common::BytesToHexString(request, REQUEST_SIZE)); // The first byte is XOR'd with 0x80. const u8 cmd = request[0] ^ 0x80u; @@ -258,7 +257,8 @@ void FZeroAXSteeringWheel::ProcessRequest(std::span request) } // Simple 4 byte response. - WriteTxBytes(std::array{}); + static const u8 response[4] = {}; + WriteTxBytes(response, sizeof(response)); } bool FZeroAXSteeringWheel::IsInitializing() const diff --git a/Source/Core/Core/HW/Triforce/FZeroAX.h b/Source/Core/Core/HW/Triforce/FZeroAX.h index 18688336e2..f66973cc00 100644 --- a/Source/Core/Core/HW/Triforce/FZeroAX.h +++ b/Source/Core/Core/HW/Triforce/FZeroAX.h @@ -22,7 +22,9 @@ public: void DoState(PointerWrap&) override; private: - void ProcessRequest(std::span); + static constexpr std::size_t REQUEST_SIZE = 4; + + void ProcessRequest(const u8* request); u8 m_init_state = 0; @@ -41,8 +43,7 @@ public: void Update() override; protected: - void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) override; + void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) override; private: FZeroAXSteeringWheel* const m_steering_wheel; diff --git a/Source/Core/Core/HW/Triforce/ICCardReader.cpp b/Source/Core/Core/HW/Triforce/ICCardReader.cpp index ab1b5e32e6..4afe65f6a3 100644 --- a/Source/Core/Core/HW/Triforce/ICCardReader.cpp +++ b/Source/Core/Core/HW/Triforce/ICCardReader.cpp @@ -3,7 +3,7 @@ #include "Core/HW/Triforce/ICCardReader.h" -#include +#include #include @@ -12,7 +12,7 @@ #include "Common/DirectIOFile.h" #include "Common/FileUtil.h" #include "Common/Logging/Log.h" -#include "Common/ScopeGuard.h" +#include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/ConfigManager.h" @@ -27,12 +27,15 @@ constexpr u16 CARD_SESSION_BASE = 0x2300; // The serial number is located here. Games seem to expect it to be read-only. constexpr u32 READ_ONLY_PAGE_INDEX = 4; -constexpr u8 CheckSumXOR(std::span data) +u8 CheckSumXOR(const u8* data, std::size_t size) { - return std::accumulate(data.data(), data.data() + data.size(), u8{}, std::bit_xor{}); + u8 checksum = 0; + for (std::size_t i = 0; i < size; i++) + checksum ^= data[i]; + return checksum; } -bool LoadCardData(const std::string& filename, std::span data) +bool LoadCardData(const std::string& filename, u8* data, std::size_t size) { File::DirectIOFile file{filename, File::AccessMode::Read}; @@ -40,14 +43,14 @@ bool LoadCardData(const std::string& filename, std::span data) return false; const auto file_size = file.GetSize(); - if (file_size > data.size()) + if (file_size > size) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "Unexpected size of {} for file: {}", file_size, filename); } - const u64 read_size = file_size < data.size() ? file_size : u64(data.size()); + const u64 read_size = file_size < size ? file_size : u64(size); - if (!file.Read(data.data(), read_size)) + if (!file.Read(data, read_size)) { ERROR_LOG_FMT(SERIALINTERFACE_CARD, "Failed to read from file: {}", filename); return false; @@ -57,10 +60,9 @@ bool LoadCardData(const std::string& filename, std::span data) return true; } -void SanitizeSerialNumber(std::span data) +// data: the 8-byte serial number +void SanitizeSerialNumber(u8* data) { - DEBUG_ASSERT(data.size() == 8); - // This seems to be treated as a 16-digit BCD value. // Avalon: 9571 264_ XXXX XXXX @@ -85,8 +87,8 @@ void SanitizeSerialNumber(std::span data) data[3] = 0x40; u32 checksum = 0; - for (auto bcd_pair : data.subspan(4)) - checksum += (bcd_pair & 0x0f) + (bcd_pair >> 4); + for (int i = 4; i < 8; i++) + checksum += (data[i] & 0x0f) + (data[i] >> 4); data[3] |= (checksum % 10); break; @@ -109,16 +111,16 @@ void SanitizeSerialNumber(std::span data) } } -void InitializeCardData(std::span data) +void InitializeCardData(u8* data) { - SanitizeSerialNumber(data.subspan(READ_ONLY_PAGE_INDEX * 8, 8)); + SanitizeSerialNumber(data + READ_ONLY_PAGE_INDEX * 8); constexpr u32 use_count_offset = 0x28; constexpr u16 use_count = 0; // The use count seems to be a big endian count down from 0xffff. - Common::WriteSwap16(data.data() + use_count_offset, 0xffff - use_count); + Common::WriteSwap16(data + use_count_offset, 0xffff - use_count); } // TODO: I'm not so sure that this is really a device status. @@ -191,8 +193,9 @@ void ICCardReader::Update() if (m_eject_timer != 0) --m_eject_timer; - const auto rx_byte_span = GetRxByteSpan(); - if (rx_byte_span.size() < 4) + const u8* const rx = GetRxBytes(); + const std::size_t rx_size = GetRxByteCount(); + if (rx_size < 4) return; // Wait for more data. // For reference: @@ -205,24 +208,23 @@ void ICCardReader::Update() // u8 checksum; // XOR of all previous bytes. // }; - const u16 input_payload_size = Common::swap16(rx_byte_span.data() + 2); + const u16 input_payload_size = Common::swap16(rx + 2); // 4 header bytes + 1 checksum byte const u32 entire_request_size = input_payload_size + 5u; - if (rx_byte_span.size() < entire_request_size) + if (rx_size < entire_request_size) return; // Wait for more data. - if (CheckSumXOR(rx_byte_span.first(entire_request_size)) != 0) + if (CheckSumXOR(rx, entire_request_size) != 0) { ERROR_LOG_FMT(SERIALINTERFACE_CARD, "Bad checksum!"); ConsumeRxBytes(1); return; } - Common::ScopeGuard consume_request{[&] { ConsumeRxBytes(entire_request_size); }}; - - const u8 card_command = rx_byte_span[1]; - const auto input_payload = rx_byte_span.subspan(4, input_payload_size); + // Lent from the Rx buffer; the request is consumed after the reply is sent. + const u8 card_command = rx[1]; + const u8* const input_payload = rx + 4; u16 status_code = 0x00; @@ -265,7 +267,8 @@ void ICCardReader::Update() std::array small_reply_payload{}; // Will be later assigned to `small_reply_payload` or some region of the card data itself. - std::span reply_payload_span; + const u8* reply_payload = nullptr; + std::size_t reply_payload_size = 0; // FYI: Many of the big-endian u16 parameters may just be u8 values. // Avalon writes single bytes, but at odd addresses, so u16 seems like the intention. @@ -297,7 +300,8 @@ void ICCardReader::Update() if (!validate_input_payload_size(8)) break; - INFO_LOG_FMT(SERIALINTERFACE_CARD, "SetBaudrate: {:02x}", fmt::join(input_payload, " ")); + INFO_LOG_FMT(SERIALINTERFACE_CARD, "SetBaudrate: {}", + Common::BytesToHexString(input_payload, input_payload_size)); break; } case ICCardCommand::FieldOn: @@ -342,7 +346,7 @@ void ICCardReader::Update() // Avalon sends 0 or 1 here, not sure what the meaning is. // This is maybe the "Time Slot Number" (TSN)? 0=1-slot, 1=2-slots, etc. - const u16 unknown_parameter = Common::swap16(input_payload.data() + 0); + const u16 unknown_parameter = Common::swap16(input_payload + 0); INFO_LOG_FMT(SERIALINTERFACE_CARD, "InsertCheck: {}", unknown_parameter); @@ -367,17 +371,25 @@ void ICCardReader::Update() break; // Avalon's logic optionally sets param0=0x20 and 8 memcpy'd bytes but never seems to do it. - const u16 unknown_param0 = Common::swap16(input_payload.data() + 0); - const auto unknown_param1 = input_payload.subspan(2, 8); + const u16 unknown_param0 = Common::swap16(input_payload + 0); - INFO_LOG_FMT(SERIALINTERFACE_CARD, "AntiCollision: unk0:{:04x} unk1:{:02x}", unknown_param0, - fmt::join(unknown_param1, " ")); + INFO_LOG_FMT(SERIALINTERFACE_CARD, "AntiCollision: unk0:{:04x} unk1:{}", unknown_param0, + Common::BytesToHexString(input_payload + 2, 8)); - const auto first_non_halted_card = std::ranges::find_if_not(m_ic_cards, &ICCard::IsHalted); - - if (first_non_halted_card != m_ic_cards.end()) + const ICCard* first_non_halted_card = nullptr; + for (const auto& card : m_ic_cards) { - reply_payload_span = (*first_non_halted_card)->GetUID(); + if (!card->IsHalted()) + { + first_non_halted_card = card.get(); + break; + } + } + + if (first_non_halted_card) + { + reply_payload = first_non_halted_card->GetUID().data(); + reply_payload_size = first_non_halted_card->GetUID().size(); size_t active_cards = 0; for (const auto& card : m_ic_cards) @@ -400,19 +412,24 @@ void ICCardReader::Update() break; // FYI: The request includes the UID that we produced in `AntiCollision`. - const auto card_uid = input_payload.first(8); + const u8* const card_uid = input_payload; - INFO_LOG_FMT(SERIALINTERFACE_CARD, "SelectCard: {:02x}", fmt::join(card_uid, " ")); + INFO_LOG_FMT(SERIALINTERFACE_CARD, "SelectCard: {}", Common::BytesToHexString(card_uid, 8)); - const auto found_card = std::ranges::find_if( - m_ic_cards, std::bind_front(std::ranges::equal, card_uid), &ICCard::GetUID); - - if (found_card != m_ic_cards.end()) + std::size_t found_card = 0; + while (found_card < m_ic_cards.size() && + std::memcmp(m_ic_cards[found_card]->GetUID().data(), card_uid, 8)) { - const u16 card_session = CARD_SESSION_BASE | u16(found_card - m_ic_cards.begin()); + found_card++; + } + + if (found_card < m_ic_cards.size()) + { + const u16 card_session = CARD_SESSION_BASE | u16(found_card); Common::WriteSwap16(small_reply_payload.data(), card_session); - reply_payload_span = small_reply_payload; + reply_payload = small_reply_payload.data(); + reply_payload_size = small_reply_payload.size(); // Avalon's tests specifically want 0x00. status_code = 0x00; @@ -435,8 +452,8 @@ void ICCardReader::Update() if (!validate_input_payload_size(8)) break; - const u16 card_session = Common::swap16(input_payload.data() + 0); - const u16 page = Common::swap16(input_payload.data() + 2) & IC_PAGE_INDEX_MASK; + const u16 card_session = Common::swap16(input_payload + 0); + const u16 page = Common::swap16(input_payload + 2) & IC_PAGE_INDEX_MASK; INFO_LOG_FMT(SERIALINTERFACE_CARD, "ReadPage: session:{:04x} page:{}", card_session, page); @@ -444,15 +461,9 @@ void ICCardReader::Update() if (!ic_card) break; - const auto read_span = ic_card->ReadData(page, 1); - if (read_span.empty()) - { + reply_payload = ic_card->ReadData(page, 1, &reply_payload_size); + if (!reply_payload) status_code = HardwareUntestedErrorCode; - } - else - { - reply_payload_span = read_span; - } break; } @@ -461,10 +472,10 @@ void ICCardReader::Update() if (!validate_input_payload_size(16)) break; - const u16 card_session = Common::swap16(input_payload.data() + 0); + const u16 card_session = Common::swap16(input_payload + 0); // Avalon specifically puts a zero here. - const u16 unknown = Common::swap16(input_payload.data() + 2); - const u16 page = Common::swap16(input_payload.data() + 4) & IC_PAGE_INDEX_MASK; + const u16 unknown = Common::swap16(input_payload + 2); + const u16 page = Common::swap16(input_payload + 4) & IC_PAGE_INDEX_MASK; INFO_LOG_FMT(SERIALINTERFACE_CARD, "WritePage: session:{:04x} unk:{} page:{}", card_session, unknown, page); @@ -473,7 +484,7 @@ void ICCardReader::Update() if (!ic_card) break; - if (!ic_card->WriteData(page, input_payload.subspan(6, IC_PAGE_SIZE))) + if (!ic_card->WriteData(page, input_payload + 6, IC_PAGE_SIZE)) { status_code = HardwareUntestedErrorCode; } @@ -485,10 +496,10 @@ void ICCardReader::Update() if (!validate_input_payload_size(8)) break; - const u16 card_session = Common::swap16(input_payload.data() + 0); + const u16 card_session = Common::swap16(input_payload + 0); // Avalon seems to always sends 5 and 1. Guessing on the meaning and behavior. - const u16 page = Common::swap16(input_payload.data() + 2) & IC_PAGE_INDEX_MASK; - const u16 amount = Common::swap16(input_payload.data() + 4); + const u16 page = Common::swap16(input_payload + 2) & IC_PAGE_INDEX_MASK; + const u16 amount = Common::swap16(input_payload + 4); INFO_LOG_FMT(SERIALINTERFACE_CARD, "DecreaseUseCount: session:{:04x}, page:{} amount:{}", card_session, page, amount); @@ -500,7 +511,8 @@ void ICCardReader::Update() const u16 new_count = ic_card->DecreaseUseCount(page, amount); Common::WriteSwap16(small_reply_payload.data(), new_count); - reply_payload_span = small_reply_payload; + reply_payload = small_reply_payload.data(); + reply_payload_size = small_reply_payload.size(); break; } @@ -509,7 +521,7 @@ void ICCardReader::Update() if (!validate_input_payload_size(8)) break; - const u16 card_session = Common::swap16(input_payload.data() + 0); + const u16 card_session = Common::swap16(input_payload + 0); INFO_LOG_FMT(SERIALINTERFACE_CARD, "HaltCard: session:{:04x}", card_session); @@ -526,9 +538,9 @@ void ICCardReader::Update() if (!validate_input_payload_size(8)) break; - const u16 card_session = Common::swap16(input_payload.data() + 0); - const u16 page = Common::swap16(input_payload.data() + 2) & IC_PAGE_INDEX_MASK; - const u16 page_count = Common::swap16(input_payload.data() + 4); + const u16 card_session = Common::swap16(input_payload + 0); + const u16 page = Common::swap16(input_payload + 2) & IC_PAGE_INDEX_MASK; + const u16 page_count = Common::swap16(input_payload + 4); INFO_LOG_FMT(SERIALINTERFACE_CARD, "ReadPages: session:{:04x} page:{} page_count:{}", card_session, page, page_count); @@ -537,15 +549,9 @@ void ICCardReader::Update() if (!ic_card) break; - const auto read_span = ic_card->ReadData(page, page_count); - if (read_span.empty()) - { + reply_payload = ic_card->ReadData(page, page_count, &reply_payload_size); + if (!reply_payload) status_code = HardwareUntestedErrorCode; - } - else - { - reply_payload_span = read_span; - } break; } @@ -558,9 +564,9 @@ void ICCardReader::Update() break; } - const u16 card_session = Common::swap16(input_payload.data() + 0); - const u32 page = Common::swap16(input_payload.data() + 2) & IC_PAGE_INDEX_MASK; - const u32 page_count = Common::swap16(input_payload.data() + 4); + const u16 card_session = Common::swap16(input_payload + 0); + const u32 page = Common::swap16(input_payload + 2) & IC_PAGE_INDEX_MASK; + const u32 page_count = Common::swap16(input_payload + 4); INFO_LOG_FMT(SERIALINTERFACE_CARD, "WritePages: session:{:04x} page:{} page_count:{}", card_session, page, page_count); @@ -574,7 +580,7 @@ void ICCardReader::Update() if (!validate_input_payload_size(8 + byte_count)) break; - if (!ic_card->WriteData(page, input_payload.subspan(6, byte_count))) + if (!ic_card->WriteData(page, input_payload + 6, byte_count)) { status_code = HardwareUntestedErrorCode; } @@ -588,10 +594,12 @@ void ICCardReader::Update() } } - SendReply(card_command, status_code, reply_payload_span); + SendReply(card_command, status_code, reply_payload, reply_payload_size); + ConsumeRxBytes(entire_request_size); } -void ICCardReader::SendReply(u8 command, u16 status_code, std::span payload) +void ICCardReader::SendReply(u8 command, u16 status_code, const u8* payload, + std::size_t payload_size) { struct ICCardReplyHeader { @@ -602,37 +610,41 @@ void ICCardReader::SendReply(u8 command, u16 status_code, std::span pa }; ICCardReplyHeader header{.command = command}; - header.length = u16(sizeof(header.status) + payload.size()); + header.length = u16(sizeof(header.status) + payload_size); header.status = status_code; - const auto header_span = Common::AsU8Span(header); + const u8* const header_bytes = reinterpret_cast(&header); - const u8 checksum = CheckSumXOR(header_span) ^ CheckSumXOR(payload); + const u8 checksum = + CheckSumXOR(header_bytes, sizeof(header)) ^ CheckSumXOR(payload, payload_size); - WriteTxBytes(header_span); - WriteTxBytes(payload); + WriteTxBytes(header_bytes, sizeof(header)); + if (payload_size) + WriteTxBytes(payload, payload_size); WriteTxByte(checksum); } -std::span ICCardReader::ICCard::ReadData(u16 page, u16 page_count) +const u8* ICCardReader::ICCard::ReadData(u16 page, u16 page_count, std::size_t* size) { const u32 byte_offset = page * IC_PAGE_SIZE; const u32 byte_count = page_count * IC_PAGE_SIZE; + *size = 0; if (byte_count + byte_offset > m_data.size()) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "ReadData: Attempt to read beyond end of card."); - return {}; + return nullptr; } - const auto read_span = std::span{m_data}.subspan(byte_offset, byte_count); + const u8* const data = m_data.data() + byte_offset; - DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "\n{}", HexDump(read_span.data(), read_span.size())); + DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "\n{}", HexDump(data, byte_count)); - return read_span; + *size = byte_count; + return data; } -bool ICCardReader::ICCard::WriteData(u16 page, std::span write_span) +bool ICCardReader::ICCard::WriteData(u16 page, const u8* data, std::size_t size) { constexpr u32 read_only_area_begin = READ_ONLY_PAGE_INDEX * IC_PAGE_SIZE; constexpr u32 read_only_area_end = read_only_area_begin + IC_PAGE_SIZE; @@ -640,24 +652,24 @@ bool ICCardReader::ICCard::WriteData(u16 page, std::span write_span) const u32 byte_offset = page * IC_PAGE_SIZE; if ((byte_offset < read_only_area_end) && - (byte_offset + write_span.size() > read_only_area_begin)) + (byte_offset + size > read_only_area_begin)) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "WriteData: Read-only page."); // Read-only page, must return error. return false; } - if (write_span.size() + byte_offset > m_data.size()) + if (size + byte_offset > m_data.size()) { WARN_LOG_FMT(SERIALINTERFACE_CARD, "WriteData: Attempt to write beyond end of card."); return false; } - std::memcpy(m_data.data() + byte_offset, write_span.data(), write_span.size()); + std::memcpy(m_data.data() + byte_offset, data, size); - DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "\n{}", HexDump(write_span.data(), write_span.size())); + DEBUG_LOG_FMT(SERIALINTERFACE_CARD, "\n{}", HexDump(data, size)); - FlushData(byte_offset, u32(write_span.size())); + FlushData(byte_offset, u32(size)); return true; } @@ -729,10 +741,10 @@ ICCardReader::ICCard::ICCard(std::string filename, const UID& uid) void ICCardReader::ICCard::Initialize() { - if (!LoadCardData(m_filename, m_data)) + if (!LoadCardData(m_filename, m_data.data(), m_data.size())) { NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "Creating new IC Card data."); - InitializeCardData(m_data); + InitializeCardData(m_data.data()); FlushData(READ_ONLY_PAGE_INDEX * IC_PAGE_SIZE, IC_PAGE_SIZE * 2); } } diff --git a/Source/Core/Core/HW/Triforce/ICCardReader.h b/Source/Core/Core/HW/Triforce/ICCardReader.h index 98f89066a6..3fdbab6bf9 100644 --- a/Source/Core/Core/HW/Triforce/ICCardReader.h +++ b/Source/Core/Core/HW/Triforce/ICCardReader.h @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -39,7 +38,7 @@ public: private: void CreateCards(u8 card_count); - void SendReply(u8 command, u16 status_code, std::span payload); + void SendReply(u8 command, u16 status_code, const u8* payload, std::size_t payload_size); static constexpr std::size_t IC_PAGE_SIZE = 8; static constexpr u32 IC_PAGE_COUNT = 256; @@ -60,10 +59,10 @@ private: const UID& GetUID() const { return m_uid; } bool IsHalted() const { return m_current_state == State::Halted; } - // Returns an empty span on error. - std::span ReadData(u16 page, u16 page_count); + // The card's own bytes, lent; nullptr on error. + const u8* ReadData(u16 page, u16 page_count, std::size_t* size); - bool WriteData(u16 page, std::span write_span); + bool WriteData(u16 page, const u8* data, std::size_t size); // Returns the new value. u16 DecreaseUseCount(u16 page, u16 amount); diff --git a/Source/Core/Core/HW/Triforce/IOPorts.cpp b/Source/Core/Core/HW/Triforce/IOPorts.cpp index 55b0651be6..c9778539b0 100644 --- a/Source/Core/Core/HW/Triforce/IOPorts.cpp +++ b/Source/Core/Core/HW/Triforce/IOPorts.cpp @@ -3,7 +3,8 @@ #include "Core/HW/Triforce/IOPorts.h" -#include + +#include #include @@ -31,23 +32,22 @@ void IOPorts::Update() m_analog_inputs.fill(NEUTRAL_ANALOG_VALUE); m_coin_inputs.fill(false); - std::ranges::for_each(m_io_adapters, &IOAdapter::Update); + for (auto& adapter : m_io_adapters) + adapter->Update(); } -std::span IOPorts::GetSwitchInputs(u32 player_index) +u8* IOPorts::GetSwitchInputs(u32 player_index) { ASSERT(player_index < PLAYER_COUNT); - return std::span{m_switch_inputs}.subspan(SWITCH_INPUT_BYTES_PER_PLAYER * player_index, - SWITCH_INPUT_BYTES_PER_PLAYER); + return m_switch_inputs.data() + SWITCH_INPUT_BYTES_PER_PLAYER * player_index; } -std::span IOPorts::GetSwitchInputs(u32 player_index) const +const u8* IOPorts::GetSwitchInputs(u32 player_index) const { ASSERT(player_index < PLAYER_COUNT); - return std::span{m_switch_inputs}.subspan(SWITCH_INPUT_BYTES_PER_PLAYER * player_index, - SWITCH_INPUT_BYTES_PER_PLAYER); + return m_switch_inputs.data() + SWITCH_INPUT_BYTES_PER_PLAYER * player_index; } void IOPorts::DoState(PointerWrap& p) @@ -71,19 +71,17 @@ void IOPorts::AddIOAdapter(std::unique_ptr adapter) IOAdapter::IOAdapter() = default; IOAdapter::~IOAdapter() = default; -void IOPorts::SetGenericOutputs(std::span bytes) +void IOPorts::SetGenericOutputs(const u8* bytes, std::size_t size) { - if (bytes.size() > m_generic_outputs.size()) + if (size > m_generic_outputs.size()) { - WARN_LOG_FMT(SERIALINTERFACE_JVSIO, "SetGenericOutputs: Unexpected byte count: {}", - bytes.size()); + WARN_LOG_FMT(SERIALINTERFACE_JVSIO, "SetGenericOutputs: Unexpected byte count: {}", size); } - const auto bytes_to_copy = std::min(bytes.size(), GENERIC_OUTPUT_BYTE_COUNT); + const std::size_t bytes_to_copy = + size < GENERIC_OUTPUT_BYTE_COUNT ? size : GENERIC_OUTPUT_BYTE_COUNT; - const bool no_change = std::ranges::equal(bytes.first(bytes_to_copy), - std::span{m_generic_outputs}.first(bytes_to_copy)); - if (no_change) + if (!std::memcmp(bytes, m_generic_outputs.data(), bytes_to_copy)) return; decltype(m_generic_outputs) bits_set{}; @@ -103,13 +101,14 @@ void IOPorts::SetGenericOutputs(std::span bytes) for (auto& adapter : m_io_adapters) { - adapter->HandleGenericOutputsChanged(bits_set, bits_cleared); + adapter->HandleGenericOutputsChanged(bits_set.data(), bits_cleared.data()); } } void IOPorts::ResetGenericOutputs() { - SetGenericOutputs(std::array{}); + static const u8 cleared[GENERIC_OUTPUT_BYTE_COUNT] = {}; + SetGenericOutputs(cleared, sizeof(cleared)); } void IOAdapter::Update() @@ -120,8 +119,7 @@ void IOAdapter::DoState(PointerWrap& p) { } -void IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { } @@ -279,8 +277,7 @@ void VirtuaStriker4Common_IOAdapter::Update() } } -void VirtuaStriker4Common_IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void VirtuaStriker4Common_IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { const u8 bits_changed_0 = bits_set[0] | bits_cleared[0]; @@ -297,8 +294,7 @@ void VirtuaStriker4Common_IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void VirtuaStriker4_2006_IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { (void)bits_cleared; @@ -357,8 +353,7 @@ void GekitouProYakyuu_IOAdapter::Update() } } -void GekitouProYakyuu_IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void GekitouProYakyuu_IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { (void)bits_set; diff --git a/Source/Core/Core/HW/Triforce/IOPorts.h b/Source/Core/Core/HW/Triforce/IOPorts.h index 7fa3f7ae8c..c582b702bb 100644 --- a/Source/Core/Core/HW/Triforce/IOPorts.h +++ b/Source/Core/Core/HW/Triforce/IOPorts.h @@ -5,7 +5,6 @@ #include #include -#include #include #include "Common/CommonTypes.h" @@ -30,28 +29,38 @@ class IOPorts public: void Update(); - std::span GetStatusSwitches() { return m_status_switches; } - std::span GetStatusSwitches() const { return m_status_switches; } + // STATUS_SWITCH_BYTE_COUNT bytes + u8* GetStatusSwitches() { return m_status_switches.data(); } + const u8* GetStatusSwitches() const { return m_status_switches.data(); } u8* GetSystemInputs() { return &m_system_inputs; } const u8* GetSystemInputs() const { return &m_system_inputs; } - std::span GetSwitchInputs(u32 player_index); - std::span GetSwitchInputs(u32 player_index) const; + // SWITCH_INPUT_BYTES_PER_PLAYER bytes + u8* GetSwitchInputs(u32 player_index); + const u8* GetSwitchInputs(u32 player_index) const; - std::span GetAnalogInputs() { return m_analog_inputs; } - std::span GetAnalogInputs() const { return m_analog_inputs; } + // ANALOG_INPUT_COUNT values + u16* GetAnalogInputs() { return m_analog_inputs.data(); } + const u16* GetAnalogInputs() const { return m_analog_inputs.data(); } - std::span GetGenericOutputs() const { return m_generic_outputs; } + // GENERIC_OUTPUT_BYTE_COUNT bytes + const u8* GetGenericOutputs() const { return m_generic_outputs.data(); } - void SetGenericOutputs(std::span bytes); + void SetGenericOutputs(const u8* bytes, std::size_t size); void ResetGenericOutputs(); static constexpr std::size_t PLAYER_COUNT = 2; static constexpr std::size_t COIN_SLOT_COUNT = 2; - std::span GetCoinInputs() { return m_coin_inputs; } - std::span GetCoinInputs() const { return m_coin_inputs; } + // COIN_SLOT_COUNT flags + bool* GetCoinInputs() { return m_coin_inputs.data(); } + const bool* GetCoinInputs() const { return m_coin_inputs.data(); } + + static constexpr std::size_t STATUS_SWITCH_BYTE_COUNT = 2; + static constexpr std::size_t SWITCH_INPUT_BYTES_PER_PLAYER = 2; + static constexpr std::size_t ANALOG_INPUT_COUNT = 8; + static constexpr std::size_t GENERIC_OUTPUT_BYTE_COUNT = 4; void AddIOAdapter(std::unique_ptr adapter); @@ -62,21 +71,15 @@ public: private: std::vector> m_io_adapters; - std::array m_status_switches{0xff, 0xff}; + std::array m_status_switches{0xff, 0xff}; // The first byte in JVSIO switch input data. u8 m_system_inputs = 0x00; - static constexpr std::size_t SWITCH_INPUT_BYTES_PER_PLAYER = 2; - std::array m_switch_inputs{}; - static constexpr std::size_t ANALOG_INPUT_COUNT = 8; - std::array m_analog_inputs{}; - static constexpr std::size_t GENERIC_OUTPUT_BYTE_COUNT = 4; - std::array m_generic_outputs{}; std::array m_coin_inputs{}; @@ -103,8 +106,7 @@ protected: IOPorts* GetIOPorts() { return m_io_ports; } // Invoked with newly set and cleared bits. - virtual void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared); + virtual void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared); private: void SetIOPorts(IOPorts* io_ports) { m_io_ports = io_ports; } @@ -144,8 +146,7 @@ protected: static constexpr u8 SLOT_A_LOCK_BIT = 0x40; static constexpr u8 SLOT_B_LOCK_BIT = 0x10; - void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) override; + void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) override; private: std::array const m_card_readers; @@ -160,8 +161,7 @@ public: } protected: - void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) override; + void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) override; private: static constexpr u8 SLOT_A_EJECT_BIT = 0x80; @@ -181,8 +181,7 @@ public: void Update() override; protected: - void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) override; + void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) override; private: static constexpr u8 SLOT_A_LOCK_BIT = 0x40; diff --git a/Source/Core/Core/HW/Triforce/JVSIO.cpp b/Source/Core/Core/HW/Triforce/JVSIO.cpp index a4aad10296..71a708cdcf 100644 --- a/Source/Core/Core/HW/Triforce/JVSIO.cpp +++ b/Source/Core/Core/HW/Triforce/JVSIO.cpp @@ -3,8 +3,7 @@ #include "Core/HW/Triforce/JVSIO.h" -#include -#include +#include #include #include @@ -12,7 +11,7 @@ #include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/Logging/Log.h" -#include "Common/ScopeGuard.h" +#include "Common/StringUtil.h" #include "Common/Swap.h" namespace @@ -117,13 +116,10 @@ enum class JVSIOCommand : u8 class JVSIORequestReader { public: - explicit JVSIORequestReader(std::span data) - : m_data{data.data()}, m_data_end{data.data() + data.size()} - { - } + JVSIORequestReader(const u8* data, std::size_t size) : m_data{data}, m_data_end{data + size} {} - // Returns a span of all remaining bytes. - constexpr std::span PeekBytes() const { return {m_data, m_data_end}; } + // The remaining bytes, lent; RemainingByteCount() of them. + constexpr const u8* PeekBytes() const { return m_data; } // Returns the remaining readable byte count. constexpr std::size_t RemainingByteCount() const { return std::size_t(m_data_end - m_data); } @@ -152,13 +148,10 @@ public: void AddData(u8 value) { m_buffer.emplace_back(value); } - void AddData(std::span data) + void AddData(const void* data, std::size_t size) { -#if defined(__cpp_lib_containers_ranges) - m_buffer.append_range(data); -#else - m_buffer.insert(m_buffer.end(), data.begin(), data.end()); -#endif + const u8* const bytes = static_cast(data); + m_buffer.insert(m_buffer.end(), bytes, bytes + size); } void StartFrame(u8 destination_node) @@ -189,7 +182,9 @@ public: m_buffer[2] = u8(status_code); // Write checksum. - const u8 checksum = std::accumulate(m_buffer.begin(), m_buffer.end(), u8{}); + u8 checksum = 0; + for (const u8 byte_value : m_buffer) + checksum += byte_value; AddData(checksum); } @@ -208,29 +203,33 @@ private: std::size_t m_last_report_code_index{}; }; -// Attempts to decode exactly output.size() bytes. -// On success, returns the number of escaped bytes read. -static std::optional UnescapeData(std::span input, std::span output) +// Attempts to decode exactly output_size bytes. +// On success, stores the number of escaped bytes read. +static bool UnescapeData(const u8* input, std::size_t input_size, u8* output, + std::size_t output_size, std::size_t* escaped_size) { - auto in = input.begin(); - const auto in_end = input.end(); + const u8* in = input; + const u8* const in_end = input + input_size; - auto out = output.begin(); - const auto out_end = output.end(); + u8* out = output; + u8* const out_end = output + output_size; while (true) { if (out == out_end) - return in - input.begin(); // Success. + { + *escaped_size = std::size_t(in - input); // Success. + return true; + } if (in == in_end) - return std::nullopt; + return false; u8 byte_value = *(in++); if (byte_value == JVSIO_MARK) { if (in == in_end) - return std::nullopt; + return false; byte_value = *(in++) + 0x01; } @@ -246,8 +245,8 @@ JVSIOBoard::JVSIOBoard(IOPorts* io_ports) : m_io_ports{io_ports} void JVSIOBoard::Update() { // Update coin counters. - const auto coin_inputs = m_io_ports->GetCoinInputs(); - for (std::size_t i = 0; i != coin_inputs.size(); ++i) + const bool* const coin_inputs = m_io_ports->GetCoinInputs(); + for (std::size_t i = 0; i != IOPorts::COIN_SLOT_COUNT; ++i) { if (!std::exchange(m_coin_prev_states[i], coin_inputs[i]) && coin_inputs[i]) ++m_coin_counts[i]; @@ -255,12 +254,13 @@ void JVSIOBoard::Update() while (true) { - const auto rx_span = GetRxByteSpan(); + const u8* const rx = GetRxBytes(); + const std::size_t rx_size = GetRxByteCount(); - if (rx_span.empty()) + if (!rx_size) break; // Wait for more data. - if (rx_span[0] != JVSIO_SYNC) + if (rx[0] != JVSIO_SYNC) { ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "Expected JVSIO_SYNC"); ConsumeRxBytes(1); @@ -270,25 +270,23 @@ void JVSIOBoard::Update() std::array unescaped_data; // Read 2 header bytes. - const auto header_escaped_size = - UnescapeData(rx_span.subspan(1), std::span{unescaped_data}.first(2)); - - if (!header_escaped_size.has_value()) + std::size_t header_escaped_size; + if (!UnescapeData(rx + 1, rx_size - 1, unescaped_data.data(), 2, &header_escaped_size)) break; // Wait for more data. const u8 destination_node = unescaped_data[0]; const u8 payload_size = unescaped_data[1]; // Read remaining frame bytes. - const auto payload_escaped_size = - UnescapeData(rx_span.subspan(1 + *header_escaped_size), - std::span{unescaped_data}.subspan(2, payload_size)); - - if (!payload_escaped_size.has_value()) + std::size_t payload_escaped_size; + if (!UnescapeData(rx + 1 + header_escaped_size, rx_size - 1 - header_escaped_size, + unescaped_data.data() + 2, payload_size, &payload_escaped_size)) + { break; // Wait for more data. + } - Common::ScopeGuard consume_frame{ - [&] { ConsumeRxBytes(1 + *header_escaped_size + *payload_escaped_size); }}; + // The frame is consumed whatever happens below; the bytes stay unescaped on the stack. + ConsumeRxBytes(1 + header_escaped_size + payload_escaped_size); if (payload_size < 1) { @@ -297,9 +295,9 @@ void JVSIOBoard::Update() } // Verify checksum. - const auto range_to_checksum = std::span{unescaped_data}.first(payload_size + 1); - const u8 expected_checksum = - std::accumulate(range_to_checksum.begin(), range_to_checksum.end(), u8()); + u8 expected_checksum = 0; + for (std::size_t i = 0; i < std::size_t(payload_size) + 1; i++) + expected_checksum += unescaped_data[i]; if (expected_checksum != unescaped_data[payload_size + 1]) { ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "Bad checksum"); @@ -307,11 +305,11 @@ void JVSIOBoard::Update() JVSIOResponseWriter writer{&m_last_response}; writer.StartFrame(JVSIO_HOST_ADDRESS); writer.EndFrame(JVSIOStatusCode::ChecksumError); - WriteResponse(m_last_response); + WriteResponse(m_last_response.data(), m_last_response.size()); continue; } - JVSIORequestReader request{range_to_checksum.subspan(2)}; + JVSIORequestReader request{unescaped_data.data() + 2, std::size_t(payload_size) - 1}; if (destination_node == JVSIO_BROADCAST_ADDRESS) { @@ -382,7 +380,7 @@ void JVSIOBoard::ProcessBroadcastRequest(JVSIORequestReader* request) writer.StartFrame(JVSIO_HOST_ADDRESS); writer.AddData(u8(JVSIOReportCode::Okay)); writer.EndFrame(JVSIOStatusCode::Okay); - WriteResponse(m_last_response); + WriteResponse(m_last_response.data(), m_last_response.size()); break; } default: @@ -394,11 +392,11 @@ void JVSIOBoard::ProcessBroadcastRequest(JVSIORequestReader* request) void JVSIOBoard::ProcessUnicastRequest(JVSIORequestReader* request) { if (request->RemainingByteCount() > 0 && - JVSIOCommand(request->PeekBytes().front()) == JVSIOCommand::DataRetransmit) + JVSIOCommand(request->PeekBytes()[0]) == JVSIOCommand::DataRetransmit) { request->SkipBytes(1); INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "DataRetransmit"); - WriteResponse(m_last_response); + WriteResponse(m_last_response.data(), m_last_response.size()); return; } @@ -441,15 +439,16 @@ void JVSIOBoard::ProcessUnicastRequest(JVSIORequestReader* request) } } - WriteResponse(m_last_response); + WriteResponse(m_last_response.data(), m_last_response.size()); } -void JVSIOBoard::WriteResponse(std::span response) +void JVSIOBoard::WriteResponse(const u8* response, std::size_t size) { WriteTxByte(JVSIO_SYNC); - for (const u8 byte_value : response) + for (std::size_t i = 0; i < size; i++) { + const u8 byte_value = response[i]; const bool needs_escaping = byte_value == JVSIO_SYNC || byte_value == JVSIO_MARK; if (needs_escaping) { @@ -487,14 +486,15 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes } case JVSIOCommand::MainID: { - const auto remaining_bytes = ctx.request.PeekBytes(); - const auto null_iter = std::ranges::find(remaining_bytes, u8{}); + const u8* const remaining_bytes = ctx.request.PeekBytes(); + const std::size_t remaining_count = ctx.request.RemainingByteCount(); + const void* const null_byte = std::memchr(remaining_bytes, 0, remaining_count); - if (null_iter == remaining_bytes.end()) + if (!null_byte) return JVSIOReportCode::ParameterDataError; - std::string_view main_id_str{reinterpret_cast(remaining_bytes.data()), - reinterpret_cast(std::to_address(null_iter))}; + std::string_view main_id_str{reinterpret_cast(remaining_bytes), + std::size_t(static_cast(null_byte) - remaining_bytes)}; ctx.request.SkipBytes(main_id_str.size() + 1); INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "MainID: {}", main_id_str); @@ -515,10 +515,13 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes for (u8 player_index = 0; player_index != player_count; ++player_index) { - const auto switch_inputs = m_io_ports->GetSwitchInputs(player_index); - const auto bytes_to_use = std::min(bytes_per_player, switch_inputs.size()); + const u8* const switch_inputs = m_io_ports->GetSwitchInputs(player_index); + const std::size_t bytes_to_use = + bytes_per_player < IOPorts::SWITCH_INPUT_BYTES_PER_PLAYER ? + bytes_per_player : + IOPorts::SWITCH_INPUT_BYTES_PER_PLAYER; - ctx.response.AddData(switch_inputs.first(bytes_to_use)); + ctx.response.AddData(switch_inputs, bytes_to_use); // Pad data if the game requests more inputs than we have (unlikely). for (std::size_t i = bytes_to_use; i != bytes_per_player; ++i) @@ -536,15 +539,23 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "AnalogInput: channel_count:{}", channel_count); - const auto analog_inputs = m_io_ports->GetAnalogInputs(); - const auto inputs_to_use = std::min(channel_count, analog_inputs.size()); + const u16* const analog_inputs = m_io_ports->GetAnalogInputs(); + const std::size_t inputs_to_use = channel_count < IOPorts::ANALOG_INPUT_COUNT ? + channel_count : + IOPorts::ANALOG_INPUT_COUNT; - for (auto value : analog_inputs.first(inputs_to_use)) - ctx.response.AddData(Common::AsU8Span(Common::swap16(value))); + for (std::size_t i = 0; i != inputs_to_use; ++i) + { + ctx.response.AddData(u8(analog_inputs[i] >> 8)); + ctx.response.AddData(u8(analog_inputs[i])); + } // Pad data if the game requests more inputs than we have (unlikely). for (std::size_t i = inputs_to_use; i != channel_count; ++i) - ctx.response.AddData(Common::AsU8Span(Common::swap16(IOPorts::NEUTRAL_ANALOG_VALUE))); + { + ctx.response.AddData(u8(IOPorts::NEUTRAL_ANALOG_VALUE >> 8)); + ctx.response.AddData(u8(IOPorts::NEUTRAL_ANALOG_VALUE)); + } return JVSIOReportCode::Okay; } @@ -558,11 +569,11 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes if (ctx.request.RemainingByteCount() < byte_count) return JVSIOReportCode::ParameterSizeError; - const auto output_span = ctx.request.PeekBytes().first(byte_count); + const u8* const outputs = ctx.request.PeekBytes(); - DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GenericOutput: {}", fmt::join(output_span, " ")); + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "GenericOutput: {}", Common::BytesToHexString(outputs, byte_count)); - m_io_ports->SetGenericOutputs(output_span); + m_io_ports->SetGenericOutputs(outputs, byte_count); ctx.request.SkipBytes(byte_count); @@ -593,10 +604,10 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes // Top 2 bits contain the condition code. // Bottom 14 bits contain the coin count. - const Common::BigEndianValue data{ - u16((u32(condition_code) << 14) | (coin_count & 0x3fffu))}; + const u16 data = u16((u32(condition_code) << 14) | (coin_count & 0x3fffu)); - ctx.response.AddData(Common::AsU8Span(data)); + ctx.response.AddData(u8(data >> 8)); + ctx.response.AddData(u8(data)); } return JVSIOReportCode::Okay; @@ -617,7 +628,10 @@ auto JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> HandlerRes NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "CoinCounter: slot:{} adjustment:{}", slot, adjustment); if (slot < m_coin_counts.size()) - m_coin_counts[slot] = std::clamp(m_coin_counts[slot] + adjustment, 0, 0x3fff); + { + const s32 count = m_coin_counts[slot] + adjustment; + m_coin_counts[slot] = u16(count < 0 ? 0 : count > 0x3fff ? 0x3fff : count); + } return JVSIOReportCode::Okay; } @@ -636,8 +650,8 @@ auto Namco_FCA_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> case JVSIOCommand::IOIdentify: { INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "IOIdentify"); - ctx.response.AddData( - Common::AsU8Span(std::span{"namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"})); + static const char id[] = "namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"; + ctx.response.AddData(id, sizeof(id)); return JVSIOReportCode::Okay; } case JVSIOCommand::FeatureCheck: @@ -652,7 +666,7 @@ auto Namco_FCA_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> {JVSIOFeature::GeneralPurposeOutput, 6}, {JVSIOFeature::AnalogOutput, 4}, }); - ctx.response.AddData(Common::AsU8Span(features)); + ctx.response.AddData(features.data(), sizeof(features)); ctx.response.AddData(u8{}); // End code return JVSIOReportCode::Okay; @@ -671,10 +685,8 @@ auto Namco_FCA_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx) -> if (ctx.request.RemainingByteCount() < param_size) return JVSIOReportCode::ParameterSizeError; - const auto params = ctx.request.PeekBytes().first(param_size); - - INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "NAMCO_COMMAND({:02x}): {:02x}", namco_command, - fmt::join(params, " ")); + INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "NAMCO_COMMAND({:02x}): {}", namco_command, + Common::BytesToHexString(ctx.request.PeekBytes(), param_size)); ctx.request.SkipBytes(param_size); ctx.response.AddData(0xff); @@ -699,8 +711,8 @@ auto Sega_837_13551_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx case JVSIOCommand::IOIdentify: { INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "IOIdentify"); - ctx.response.AddData( - Common::AsU8Span(std::span{"SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"})); + static const char id[] = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; + ctx.response.AddData(id, sizeof(id)); return JVSIOReportCode::Okay; } case JVSIOCommand::FeatureCheck: @@ -712,7 +724,7 @@ auto Sega_837_13551_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx {JVSIOFeature::AnalogInput, 8}, {JVSIOFeature::GeneralPurposeOutput, 6}, }); - ctx.response.AddData(Common::AsU8Span(features)); + ctx.response.AddData(features.data(), sizeof(features)); ctx.response.AddData(u8{}); // End code return JVSIOReportCode::Okay; @@ -729,8 +741,8 @@ auto Sega_837_13844_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx case JVSIOCommand::IOIdentify: { INFO_LOG_FMT(SERIALINTERFACE_JVSIO, "IOIdentify"); - ctx.response.AddData(Common::AsU8Span( - std::span{"SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;Ver1.00;99/07"})); + static const char id[] = "SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;Ver1.00;99/07"; + ctx.response.AddData(id, sizeof(id)); return JVSIOReportCode::Okay; } case JVSIOCommand::FeatureCheck: @@ -742,7 +754,7 @@ auto Sega_837_13844_JVSIOBoard::HandleCommand(JVSIOCommand cmd, FrameContext ctx {JVSIOFeature::AnalogInput, 8}, {JVSIOFeature::GeneralPurposeOutput, 22}, }); - ctx.response.AddData(Common::AsU8Span(features)); + ctx.response.AddData(features.data(), sizeof(features)); ctx.response.AddData(u8{}); // End code return JVSIOReportCode::Okay; diff --git a/Source/Core/Core/HW/Triforce/JVSIO.h b/Source/Core/Core/HW/Triforce/JVSIO.h index 78ec5a3091..a9b12accd8 100644 --- a/Source/Core/Core/HW/Triforce/JVSIO.h +++ b/Source/Core/Core/HW/Triforce/JVSIO.h @@ -5,7 +5,6 @@ #include #include -#include #include #include "Common/CommonTypes.h" @@ -49,7 +48,7 @@ private: void ProcessBroadcastRequest(JVSIORequestReader* request); void ProcessUnicastRequest(JVSIORequestReader* request); - void WriteResponse(std::span response); + void WriteResponse(const u8* response, std::size_t size); // 0 == address not yet assigned. u8 m_client_address = 0; diff --git a/Source/Core/Core/HW/Triforce/MarioKartGP.cpp b/Source/Core/Core/HW/Triforce/MarioKartGP.cpp index 9b7d2cb0ef..da294fb268 100644 --- a/Source/Core/Core/HW/Triforce/MarioKartGP.cpp +++ b/Source/Core/Core/HW/Triforce/MarioKartGP.cpp @@ -8,6 +8,7 @@ #include "Common/BitUtils.h" #include "Common/ChunkFile.h" #include "Common/Logging/Log.h" +#include "Common/StringUtil.h" #include "Common/Swap.h" #include "Core/HW/GCPad.h" @@ -55,8 +56,7 @@ void MarioKartGPCommon_IOAdapter::Update() analog_inputs[2] = Common::ExpandValue(pad_status.triggerLeft, 8); } -void MarioKartGPCommon_IOAdapter::HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) +void MarioKartGPCommon_IOAdapter::HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) { if (bits_set[0] & 0x80u) m_steering_wheel->Reset(); @@ -80,37 +80,34 @@ void MarioKartGPCommon_IOAdapter::HandleGenericOutputsChanged(std::span()); + ProcessRequest(GetRxBytes() + rx_position); rx_position += REQUEST_SIZE; } ConsumeRxBytes(rx_position); } -void MarioKartGPSteeringWheel::ProcessRequest(std::span request) +void MarioKartGPSteeringWheel::ProcessRequest(const u8* request) { - DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Request: {:02x}", fmt::join(request, " ")); + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Request: {}", + Common::BytesToHexString(request, REQUEST_SIZE)); - const u32 cmd = Common::swap32(request.data()); + const u32 cmd = Common::swap32(request); if (cmd != 0xffffff01) { WARN_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: Unknown command: {:08x}", cmd); return; } - const u16 centering_force = Common::swap16(request.data() + 4); - const u16 friction_force = Common::swap16(request.data() + 6); - const u16 roll = Common::swap16(request.data() + 8); + const u16 centering_force = Common::swap16(request + 4); + const u16 friction_force = Common::swap16(request + 6); + const u16 roll = Common::swap16(request + 8); DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "SteeringWheel: FFB: {:04x} {:04x} {:04x}", centering_force, friction_force, roll); @@ -119,7 +116,7 @@ void MarioKartGPSteeringWheel::ProcessRequest(std::span request) { case 0: // The game seems to expect this on power up. - WriteTxBytes(ACK_NO_ERROR); + WriteTxBytes(ACK_NO_ERROR.data(), ACK_NO_ERROR.size()); ++m_init_state; break; @@ -131,12 +128,12 @@ void MarioKartGPSteeringWheel::ProcessRequest(std::span request) if (m_init_state == 1) { - WriteTxBytes(ACK_POWER_OFF); + WriteTxBytes(ACK_POWER_OFF.data(), ACK_POWER_OFF.size()); ++m_init_state; } else { - WriteTxBytes(ACK_POWER_ON); + WriteTxBytes(ACK_POWER_ON.data(), ACK_POWER_ON.size()); m_init_state = 1; } break; diff --git a/Source/Core/Core/HW/Triforce/MarioKartGP.h b/Source/Core/Core/HW/Triforce/MarioKartGP.h index 163bfbb61b..a92d2faf65 100644 --- a/Source/Core/Core/HW/Triforce/MarioKartGP.h +++ b/Source/Core/Core/HW/Triforce/MarioKartGP.h @@ -20,7 +20,9 @@ public: void DoState(PointerWrap&) override; private: - void ProcessRequest(std::span); + static constexpr std::size_t REQUEST_SIZE = 10; + + void ProcessRequest(const u8* request); u8 m_init_state = 0; }; @@ -37,8 +39,7 @@ public: void Update() override; protected: - void HandleGenericOutputsChanged(std::span bits_set, - std::span bits_cleared) override; + void HandleGenericOutputsChanged(const u8* bits_set, const u8* bits_cleared) override; private: MarioKartGPSteeringWheel* const m_steering_wheel; diff --git a/Source/Core/Core/HW/Triforce/SerialDevice.cpp b/Source/Core/Core/HW/Triforce/SerialDevice.cpp index cf9d2ca706..97e7986177 100644 --- a/Source/Core/Core/HW/Triforce/SerialDevice.cpp +++ b/Source/Core/Core/HW/Triforce/SerialDevice.cpp @@ -3,7 +3,6 @@ #include "Core/HW/Triforce/SerialDevice.h" -#include #include "Common/Assert.h" #include "Common/ChunkFile.h" @@ -11,21 +10,18 @@ namespace Triforce { -void SerialDevice::WriteRxBytes(std::span bytes) +void SerialDevice::WriteRxBytes(const u8* bytes, std::size_t size) { -#if defined(__cpp_lib_containers_ranges) - m_rx_buffer.append_range(bytes); -#else - m_rx_buffer.insert(m_rx_buffer.end(), bytes.begin(), bytes.end()); -#endif + m_rx_buffer.insert(m_rx_buffer.end(), bytes, bytes + size); } -void SerialDevice::TakeTxBytes(std::span bytes) +void SerialDevice::TakeTxBytes(u8* bytes, std::size_t size) { - DEBUG_ASSERT(m_tx_buffer.size() >= bytes.size()); + DEBUG_ASSERT(m_tx_buffer.size() >= size); - std::copy_n(m_tx_buffer.begin(), bytes.size(), bytes.data()); - m_tx_buffer.erase(m_tx_buffer.begin(), m_tx_buffer.begin() + bytes.size()); + for (std::size_t i = 0; i < size; i++) + bytes[i] = m_tx_buffer[i]; + m_tx_buffer.erase(m_tx_buffer.begin(), m_tx_buffer.begin() + size); } void SerialDevice::ConsumeRxBytes(std::size_t count) @@ -35,13 +31,9 @@ void SerialDevice::ConsumeRxBytes(std::size_t count) m_rx_buffer.erase(m_rx_buffer.begin(), m_rx_buffer.begin() + count); } -void SerialDevice::WriteTxBytes(std::span bytes) +void SerialDevice::WriteTxBytes(const u8* bytes, std::size_t size) { -#if defined(__cpp_lib_containers_ranges) - m_tx_buffer.append_range(bytes); -#else - m_tx_buffer.insert(m_tx_buffer.end(), bytes.begin(), bytes.end()); -#endif + m_tx_buffer.insert(m_tx_buffer.end(), bytes, bytes + size); } void SerialDevice::PassThroughTxBytes(SerialDevice& other) @@ -49,20 +41,12 @@ void SerialDevice::PassThroughTxBytes(SerialDevice& other) // The destination buffer will often be empty or near-empty. if (m_tx_buffer.size() < other.m_tx_buffer.size()) [[likely]] { -#if defined(__cpp_lib_containers_ranges) - other.m_tx_buffer.prepend_range(m_tx_buffer); -#else other.m_tx_buffer.insert(other.m_tx_buffer.begin(), m_tx_buffer.begin(), m_tx_buffer.end()); -#endif m_tx_buffer.swap(other.m_tx_buffer); } else { -#if defined(__cpp_lib_containers_ranges) - m_tx_buffer.append_range(other.m_tx_buffer); -#else m_tx_buffer.insert(m_tx_buffer.end(), other.m_tx_buffer.begin(), other.m_tx_buffer.end()); -#endif } other.m_tx_buffer.clear(); diff --git a/Source/Core/Core/HW/Triforce/SerialDevice.h b/Source/Core/Core/HW/Triforce/SerialDevice.h index fa3af0b954..2a205b156f 100644 --- a/Source/Core/Core/HW/Triforce/SerialDevice.h +++ b/Source/Core/Core/HW/Triforce/SerialDevice.h @@ -4,7 +4,6 @@ #pragma once #include -#include #include #include "Common/CommonTypes.h" @@ -26,24 +25,26 @@ public: SerialDevice(SerialDevice&&) = delete; SerialDevice& operator=(SerialDevice&&) = delete; - void WriteRxBytes(std::span bytes); + void WriteRxBytes(const u8* bytes, std::size_t size); std::size_t GetTxByteCount() const { return m_tx_buffer.size(); } - // Caller should ensure GetTxByteCount() >= byte.size(). - void TakeTxBytes(std::span bytes); + // Caller should ensure GetTxByteCount() >= size. + void TakeTxBytes(u8* bytes, std::size_t size); virtual void Update() = 0; virtual void DoState(PointerWrap& p); protected: - std::span GetRxByteSpan() const { return m_rx_buffer; } + // Pending received bytes, lent until the next Rx write or consume. + const u8* GetRxBytes() const { return m_rx_buffer.data(); } + std::size_t GetRxByteCount() const { return m_rx_buffer.size(); } void ConsumeRxBytes(std::size_t count); void WriteTxByte(u8 byte) { m_tx_buffer.emplace_back(byte); } - void WriteTxBytes(std::span bytes); + void WriteTxBytes(const u8* bytes, std::size_t size); // Transfer the entirety of other's tx buffer to this tx buffer. void PassThroughTxBytes(SerialDevice& other); diff --git a/Source/Core/Core/HW/Triforce/Touchscreen.cpp b/Source/Core/Core/HW/Triforce/Touchscreen.cpp index 6f59415854..01bfc9edae 100644 --- a/Source/Core/Core/HW/Triforce/Touchscreen.cpp +++ b/Source/Core/Core/HW/Triforce/Touchscreen.cpp @@ -3,7 +3,8 @@ #include "Core/HW/Triforce/Touchscreen.h" -#include + +#include #include "Common/BitUtils.h" #include "Common/Logging/Log.h" @@ -34,12 +35,12 @@ namespace Triforce void Touchscreen::Update() { - if (const auto input = GetRxByteSpan(); !input.empty()) + if (const std::size_t input_size = GetRxByteCount()) { // The Key of Avalon doesn't write to the device, it only reads. WARN_LOG_FMT(SERIALINTERFACE_AMBB, "Unexpected write of {} bytes to touchscreen.", - input.size()); - ConsumeRxBytes(input.size()); + input_size); + ConsumeRxBytes(input_size); } // Our touchscreen conveniently produces exactly one packet every Update cycle. @@ -64,9 +65,13 @@ void Touchscreen::Update() .pressure = pad_status.triggerRight, }; - packet.checksum = std::accumulate(&packet.lead_in, &packet.checksum, u8{0xaa}); + const u8* const packet_bytes = reinterpret_cast(&packet); + u8 checksum = 0xaa; + for (std::size_t i = 0; i < offsetof(SmartSetDataPacket, checksum); i++) + checksum += packet_bytes[i]; + packet.checksum = checksum; - WriteTxBytes(Common::AsU8Span(packet)); + WriteTxBytes(packet_bytes, sizeof(packet)); } } // namespace Triforce diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp index d997e1efa5..325d57ffa2 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTEmu.cpp @@ -3,7 +3,7 @@ #include "Core/IOS/USB/Bluetooth/BTEmu.h" -#include +#include #include #include #include @@ -48,8 +48,12 @@ BluetoothEmuDevice::BluetoothEmuDevice(EmulationKernel& ios, const std::string& const bdaddr_t tmp_bd = {0x11, 0x02, 0x19, 0x79, 0, i}; // Previous records can be safely overwritten, since they are backed up - std::ranges::copy(tmp_bd, std::rbegin(bt_dinf.active[i].bdaddr)); - std::ranges::copy(tmp_bd, std::rbegin(bt_dinf.registered[i].bdaddr)); + // stored reversed + for (std::size_t b = 0; b < tmp_bd.size(); b++) + { + bt_dinf.active[i].bdaddr[tmp_bd.size() - 1 - b] = tmp_bd[b]; + bt_dinf.registered[i].bdaddr[tmp_bd.size() - 1 - b] = tmp_bd[b]; + } const auto& wm_name = (i == WIIMOTE_BALANCE_BOARD) ? "Nintendo RVL-WBC-01" : "Nintendo RVL-CNT-01"; @@ -378,7 +382,7 @@ void BluetoothEmuDevice::ACLPool::Store(const u8* data, const u16 size, const u1 m_queue.push_back(Packet()); auto& packet = m_queue.back(); - std::copy_n(data, size, packet.data); + std::memcpy(packet.data, data, size); packet.size = size; packet.conn_handle = conn_handle; } @@ -403,7 +407,7 @@ void BluetoothEmuDevice::ACLPool::WriteToEndpoint(const USB::V0BulkMessage& endp header->length = size; // Write the packet to the buffer - std::copy_n(data, size, (u8*)header + sizeof(hci_acldata_hdr_t)); + std::memcpy((u8*)header + sizeof(hci_acldata_hdr_t), data, size); m_queue.pop_front(); @@ -438,16 +442,22 @@ bool BluetoothEmuDevice::SendEventInquiryResponse() static_assert( sizeof(SHCIEventInquiryResult) - 2 + (num_responses * sizeof(hci_inquiry_response)) < 256); - const auto iter = std::ranges::find_if(m_wiimotes, &WiimoteDevice::IsInquiryScanEnabled); - if (iter == m_wiimotes.end()) + const WiimoteDevice* wiimote = nullptr; + for (const auto& candidate : m_wiimotes) + { + if (candidate->IsInquiryScanEnabled()) + { + wiimote = candidate.get(); + break; + } + } + if (!wiimote) { // No remotes are discoverable. SendEventInquiryComplete(0); return false; } - const auto& wiimote = *iter; - SQueuedEvent event( u32(sizeof(SHCIEventInquiryResult) + num_responses * sizeof(hci_inquiry_response)), 0); @@ -462,7 +472,7 @@ bool BluetoothEmuDevice::SendEventInquiryResponse() response->page_scan_rep_mode = 1; response->page_scan_period_mode = 0; response->page_scan_mode = 0; - std::copy_n(wiimote->GetClass().begin(), HCI_CLASS_SIZE, response->uclass); + std::memcpy(response->uclass, wiimote->GetClass().data(), HCI_CLASS_SIZE); response->clock_offset = 0x3818; DEBUG_LOG_FMT(IOS_WIIMOTE, "Event: Send Fake Inquiry of one controller"); @@ -639,7 +649,7 @@ bool BluetoothEmuDevice::SendEventReadRemoteFeatures(u16 connection_handle) read_remote_features->PayloadLength = sizeof(SHCIEventReadRemoteFeatures) - 2; read_remote_features->EventStatus = 0x00; read_remote_features->ConnectionHandle = connection_handle; - std::copy_n(wiimote->GetFeatures().begin(), HCI_FEATURES_SIZE, read_remote_features->features); + std::memcpy(read_remote_features->features, wiimote->GetFeatures().data(), HCI_FEATURES_SIZE); DEBUG_LOG_FMT(IOS_WIIMOTE, "Event: SendEventReadRemoteFeatures"); DEBUG_LOG_FMT(IOS_WIIMOTE, " Connection_Handle: {:#06x}", @@ -844,7 +854,7 @@ bool BluetoothEmuDevice::SendEventLinkKeyNotification(const u8 num_to_send) hci_link_key_rep_cp* link_key_info = (hci_link_key_rep_cp*)((u8*)&event_link_key->bdaddr + sizeof(hci_link_key_rep_cp) * i); link_key_info->bdaddr = m_wiimotes[i]->GetBD(); - std::copy_n(m_wiimotes[i]->GetLinkKey().begin(), HCI_KEY_SIZE, link_key_info->key); + std::memcpy(link_key_info->key, m_wiimotes[i]->GetLinkKey().data(), HCI_KEY_SIZE); DEBUG_LOG_FMT(IOS_WIIMOTE, " bd: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", link_key_info->bdaddr[0], link_key_info->bdaddr[1], link_key_info->bdaddr[2], diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp index b9b2b1bf4a..c65b476bff 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.cpp @@ -5,11 +5,9 @@ #include -#include #include #include #include -#include #include #include @@ -92,9 +90,9 @@ std::optional BluetoothRealDevice::IOCtlV(const IOCtlVRequest& request memory.CopyFromEmu(&delete_cmd, cmd->data_address, sizeof(delete_cmd)); if (delete_cmd.delete_all != 0) - m_link_keys.clear(); + m_link_key_count = 0; else - m_link_keys.erase(delete_cmd.bdaddr); + EraseLinkKey(delete_cmd.bdaddr); } else if (opcode == HCI_CMD_SNIFF_MODE) { @@ -102,9 +100,13 @@ std::optional BluetoothRealDevice::IOCtlV(const IOCtlVRequest& request DEBUG_LOG_FMT(IOS_WIIMOTE, "HCI_CMD_SNIFF_MODE"); } - const auto payload = memory.GetSpanForAddress(cmd->data_address).first(cmd->length); - m_lib_usb_bt_adapter->SendControlTransfer(cmd->request_type, cmd->request, cmd->value, - cmd->index, payload); + // Lent from guest memory; the adapter copies it into the transfer. + const u8* const payload = memory.GetPointerForRange(cmd->data_address, cmd->length); + if (payload) + { + m_lib_usb_bt_adapter->SendControlTransfer(cmd->request_type, cmd->request, cmd->value, + cmd->index, payload, cmd->length); + } if (opcode == HCI_CMD_RESET) { @@ -131,8 +133,9 @@ std::optional BluetoothRealDevice::IOCtlV(const IOCtlVRequest& request } auto& memory = GetSystem().GetMemory(); - const auto payload = memory.GetSpanForAddress(cmd->data_address).first(cmd->length); - m_lib_usb_bt_adapter->SendBulkTransfer(cmd->endpoint, payload); + const u8* const payload = memory.GetPointerForRange(cmd->data_address, cmd->length); + if (payload) + m_lib_usb_bt_adapter->SendBulkTransfer(cmd->endpoint, payload, cmd->length); break; } // Incoming HCI events @@ -210,7 +213,9 @@ auto BluetoothRealDevice::ProcessHCIEvent(BufferType buffer) -> BufferType hci_link_key_notification_ep notification; std::memcpy(¬ification, buffer.data() + sizeof(hci_event_hdr_t), sizeof(notification)); - std::ranges::copy(notification.key, std::begin(m_link_keys[notification.bdaddr])); + linkkey_t key; + std::memcpy(key.data(), notification.key, key.size()); + SetLinkKey(notification.bdaddr, key); } if (m_lib_usb_bt_adapter->IsWiiBTModule()) @@ -280,7 +285,7 @@ auto BluetoothRealDevice::ProcessHCIEvent(BufferType buffer) -> BufferType payload.command.latency = 10000; payload.command.delay_variation = 0xffffffff; - m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload)); + m_lib_usb_bt_adapter->SendControlTransfer(&payload, sizeof(payload)); } else if (event == HCI_EVENT_QOS_SETUP_COMPL) { @@ -299,14 +304,15 @@ void BluetoothRealDevice::TryToFillACLEndpoint() if (!m_acl_endpoint) return; - const auto buffer = m_lib_usb_bt_adapter->PeekACLData(); - if (buffer.empty()) + std::size_t size; + const u8* const buffer = m_lib_usb_bt_adapter->PeekACLData(&size); + if (!buffer) return; - assert(buffer.size() <= m_acl_endpoint->length); + assert(size <= m_acl_endpoint->length); - m_acl_endpoint->FillBuffer(buffer.data(), buffer.size()); - GetEmulationKernel().EnqueueIPCReply(m_acl_endpoint->ios_request, s32(buffer.size())); + m_acl_endpoint->FillBuffer(buffer, size); + GetEmulationKernel().EnqueueIPCReply(m_acl_endpoint->ios_request, s32(size)); m_lib_usb_bt_adapter->PopACLData(); m_acl_endpoint.reset(); @@ -376,8 +382,8 @@ void BluetoothRealDevice::TriggerSyncButtonHeldEvent() void BluetoothRealDevice::SendHCIResetCommand() { INFO_LOG_FMT(IOS_WIIMOTE, "SendHCIResetCommand"); - m_lib_usb_bt_adapter->SendControlTransfer( - Common::AsU8Span(hci_cmd_hdr_t{.opcode = HCI_CMD_RESET, .length = 0})); + const hci_cmd_hdr_t reset{.opcode = HCI_CMD_RESET, .length = 0}; + m_lib_usb_bt_adapter->SendControlTransfer(&reset, sizeof(reset)); } void BluetoothRealDevice::SendHCIDeleteLinkKeyCommand() @@ -388,17 +394,18 @@ void BluetoothRealDevice::SendHCIDeleteLinkKeyCommand() payload.command.bdaddr = {}; payload.command.delete_all = 0x01; - m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload)); + m_lib_usb_bt_adapter->SendControlTransfer(&payload, sizeof(payload)); } bool BluetoothRealDevice::SendHCIStoreLinkKeyCommand() { - if (m_link_keys.empty()) + if (!m_link_key_count) return false; // Range: 0x01 to 0x0B per Bluetooth spec. static constexpr std::size_t MAX_LINK_KEYS = 0x0B; - const auto num_link_keys = u8(std::min(m_link_keys.size(), MAX_LINK_KEYS)); + const auto num_link_keys = + u8(m_link_key_count < MAX_LINK_KEYS ? m_link_key_count : MAX_LINK_KEYS); INFO_LOG_FMT(IOS_WIIMOTE, "SendHCIStoreLinkKeyCommand num_link_keys: {}", num_link_keys); @@ -421,11 +428,10 @@ bool BluetoothRealDevice::SendHCIStoreLinkKeyCommand() payload.header.length = payload_size - sizeof(payload.header); payload.command.num_keys_write = num_link_keys; - int index = 0; - for (auto& [bdaddr, linkkey] : m_link_keys | std::views::take(num_link_keys)) - payload.link_keys[index++] = {bdaddr, linkkey}; + for (u8 index = 0; index < num_link_keys; index++) + payload.link_keys[index] = {m_link_keys[index].bdaddr, m_link_keys[index].linkkey}; - m_lib_usb_bt_adapter->SendControlTransfer(Common::AsU8Span(payload).first(payload_size)); + m_lib_usb_bt_adapter->SendControlTransfer(&payload, payload_size); return true; } @@ -450,15 +456,51 @@ void BluetoothRealDevice::FakeVendorCommandReply(u16 opcode, USB::V0IntrMessage& GetEmulationKernel().EnqueueIPCReply(ctrl.ios_request, s32(sizeof(payload))); } -void BluetoothRealDevice::FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, std::span payload) +void BluetoothRealDevice::FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, const u8* payload, + std::size_t size) { - const hci_event_hdr_t hci_event{.event = HCI_EVENT_VENDOR, .length = u8(payload.size())}; + const hci_event_hdr_t hci_event{.event = HCI_EVENT_VENDOR, .length = u8(size)}; auto& memory = GetSystem().GetMemory(); memory.CopyToEmu(ctrl.data_address, &hci_event, sizeof(hci_event)); - memory.CopyToEmu(ctrl.data_address + sizeof(hci_event), payload.data(), payload.size()); - GetEmulationKernel().EnqueueIPCReply(ctrl.ios_request, s32(sizeof(hci_event) + payload.size())); + memory.CopyToEmu(ctrl.data_address + sizeof(hci_event), payload, size); + GetEmulationKernel().EnqueueIPCReply(ctrl.ios_request, s32(sizeof(hci_event) + size)); +} + +void BluetoothRealDevice::SetLinkKey(const bdaddr_t& bdaddr, const linkkey_t& linkkey) +{ + // sorted by address: find the slot, replace or insert + std::size_t index = 0; + while (index < m_link_key_count && m_link_keys[index].bdaddr < bdaddr) + index++; + if (index < m_link_key_count && m_link_keys[index].bdaddr == bdaddr) + { + m_link_keys[index].linkkey = linkkey; + return; + } + if (m_link_key_count == MAX_STORED_LINK_KEYS) + { + WARN_LOG_FMT(IOS_WIIMOTE, "Too many link keys; dropping one."); + return; + } + for (std::size_t i = m_link_key_count; i > index; i--) + m_link_keys[i] = m_link_keys[i - 1]; + m_link_keys[index] = {bdaddr, linkkey}; + m_link_key_count++; +} + +void BluetoothRealDevice::EraseLinkKey(const bdaddr_t& bdaddr) +{ + for (std::size_t index = 0; index < m_link_key_count; index++) + { + if (m_link_keys[index].bdaddr != bdaddr) + continue; + for (std::size_t i = index + 1; i < m_link_key_count; i++) + m_link_keys[i - 1] = m_link_keys[i]; + m_link_key_count--; + return; + } } // When the red sync button is pressed, a HCI event is generated. @@ -467,7 +509,7 @@ void BluetoothRealDevice::FakeSyncButtonPressedEvent(USB::V0IntrMessage& ctrl) { NOTICE_LOG_FMT(IOS_WIIMOTE, "Faking 'sync button pressed' (0x08) event packet"); constexpr u8 payload[1] = {0x08}; - FakeSyncButtonEvent(ctrl, payload); + FakeSyncButtonEvent(ctrl, payload, sizeof(payload)); SetSyncButtonState(SyncButtonState::Ignored); } @@ -476,7 +518,7 @@ void BluetoothRealDevice::FakeSyncButtonHeldEvent(USB::V0IntrMessage& ctrl) { NOTICE_LOG_FMT(IOS_WIIMOTE, "Faking 'sync button held' (0x09) event packet"); constexpr u8 payload[1] = {0x09}; - FakeSyncButtonEvent(ctrl, payload); + FakeSyncButtonEvent(ctrl, payload, sizeof(payload)); SetSyncButtonState(SyncButtonState::Ignored); } @@ -501,8 +543,9 @@ void BluetoothRealDevice::LoadLinkKeys() continue; } - auto& mac = *address; - std::ranges::reverse(mac); + bdaddr_t mac; + for (std::size_t i = 0; i < mac.size(); i++) + mac[i] = (*address)[mac.size() - 1 - i]; const auto key_string = pair.substr(index + 1); linkkey_t key{}; @@ -510,18 +553,21 @@ void BluetoothRealDevice::LoadLinkKeys() for (size_t i = 0; i < key_string.length() && pos != key.size(); i += 2) TryParse(std::string(key_string.substr(i, 2)), &key[pos++], 16); - m_link_keys[mac] = key; + SetLinkKey(mac, key); } } void BluetoothRealDevice::SaveLinkKeys() { std::string config_string; - for (const auto& [bdaddr, linkkey] : m_link_keys) + for (std::size_t index = 0; index < m_link_key_count; index++) { + const bdaddr_t& bdaddr = m_link_keys[index].bdaddr; + const linkkey_t& linkkey = m_link_keys[index].linkkey; bdaddr_t address; // Reverse the address so that it is stored in the correct order in the config file - std::ranges::reverse_copy(bdaddr, address.begin()); + for (std::size_t i = 0; i < address.size(); i++) + address[i] = bdaddr[address.size() - 1 - i]; config_string += Common::MacAddressToString(address); config_string += '='; diff --git a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h index 22b99ba853..546c14d6b1 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/BTReal.h @@ -6,7 +6,6 @@ #if defined(__LIBUSB__) #include #include -#include #include #include #include @@ -75,10 +74,20 @@ private: // Enqueued when we observe a command to which we need to fake a reply. std::queue> m_fake_replies; - // This stores the address of paired devices and associated link keys. + // This stores the address of paired devices and associated link keys, sorted by address. // It is needed because some adapters forget all stored link keys when they are reset, // which breaks pairings because the Wii relies on the Bluetooth module to remember them. - std::map m_link_keys; + struct LinkKeyEntry + { + bdaddr_t bdaddr; + linkkey_t linkkey; + }; + static constexpr std::size_t MAX_STORED_LINK_KEYS = 64; + LinkKeyEntry m_link_keys[MAX_STORED_LINK_KEYS]; + std::size_t m_link_key_count = 0; + + void SetLinkKey(const bdaddr_t& bdaddr, const linkkey_t& linkkey); + void EraseLinkKey(const bdaddr_t& bdaddr); // Endpoints provided by the emulated software. std::unique_ptr m_hci_endpoint; @@ -97,7 +106,7 @@ private: void FakeVendorCommandReply(u16 opcode, USB::V0IntrMessage& ctrl); - void FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, std::span payload); + void FakeSyncButtonEvent(USB::V0IntrMessage& ctrl, const u8* payload, std::size_t size); void FakeSyncButtonPressedEvent(USB::V0IntrMessage& ctrl); void FakeSyncButtonHeldEvent(USB::V0IntrMessage& ctrl); diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp index f8b9285d6f..5591e36112 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.cpp @@ -5,10 +5,8 @@ #include -#include #include #include -#include #include #include @@ -208,9 +206,10 @@ void LibUSBBluetoothAdapter::Close() // Cancel all LibUSB transfers, wait for their callbacks and clean up all the buffers. A // callback that read the run flag before it was cleared may resubmit its transfer after the // cancel, so whatever is still in flight is cancelled again on every pass. - while (!m_transfer_buffers.empty()) + while (m_live_transfers) { - std::ranges::for_each(m_transfer_buffers | std::ranges::views::keys, libusb_cancel_transfer); + for (FreeNode* node = m_live_transfers; node; node = node->next) + libusb_cancel_transfer(node->transfer); const int key = retro_eventcount_prepare_wait(&m_adapter_event.ec); if (!mpsc_stack_empty(&m_transfers_to_free)) retro_eventcount_cancel_wait(&m_adapter_event.ec); @@ -287,32 +286,33 @@ auto LibUSBBluetoothAdapter::ReceiveHCIEvent() -> BufferType const auto event = buffer[0]; if (event == HCI_EVENT_COMMAND_COMPL) { - AcknowledgeCommand(buffer); + AcknowledgeCommand(buffer.data(), buffer.size()); } else if (event == HCI_EVENT_COMMAND_STATUS) { - AcknowledgeCommand(buffer); + AcknowledgeCommand(buffer.data(), buffer.size()); } Update(); return buffer; } -std::span LibUSBBluetoothAdapter::PeekACLData() +const u8* LibUSBBluetoothAdapter::PeekACLData(std::size_t* size) { const u16 length = PeekPacketLength(&m_acl_data_queue); + *size = length; if (length == 0) - return {}; + return nullptr; m_acl_peeked = sizeof(length) + length; - const void* span; - if (retro_spsc_read_begin(&m_acl_data_queue, &span) >= m_acl_peeked) - return {static_cast(span) + sizeof(length), length}; + const void* ring; + if (retro_spsc_read_begin(&m_acl_data_queue, &ring) >= m_acl_peeked) + return static_cast(ring) + sizeof(length); // Only a ring without a mirror splits a packet at its end. m_acl_bounce.resize(m_acl_peeked); retro_spsc_peek(&m_acl_data_queue, m_acl_bounce.data(), m_acl_peeked); - return {m_acl_bounce.data() + sizeof(length), length}; + return m_acl_bounce.data() + sizeof(length); } void LibUSBBluetoothAdapter::PopACLData() @@ -326,23 +326,27 @@ bool LibUSBBluetoothAdapter::IsControllerReadyForCommand() const } template -void LibUSBBluetoothAdapter::AcknowledgeCommand(std::span buffer) +void LibUSBBluetoothAdapter::AcknowledgeCommand(const u8* buffer, std::size_t size) { - if (buffer.size() < sizeof(hci_event_hdr_t) + sizeof(EventType)) [[unlikely]] + if (size < sizeof(hci_event_hdr_t) + sizeof(EventType)) [[unlikely]] { WARN_LOG_FMT(IOS_WIIMOTE, "Undersized HCI event"); return; } EventType ev; - std::memcpy(&ev, buffer.data() + sizeof(hci_event_hdr_t), sizeof(ev)); + std::memcpy(&ev, buffer + sizeof(hci_event_hdr_t), sizeof(ev)); - const auto it = - std::ranges::find(m_unacknowledged_commands, ev.opcode, &OutstandingCommand::opcode); - if (it != m_unacknowledged_commands.end()) + std::size_t index = 0; + while (index < m_unacknowledged_commands.size() && + m_unacknowledged_commands[index].opcode != ev.opcode) + { + index++; + } + if (index < m_unacknowledged_commands.size()) { DEBUG_LOG_FMT(IOS_WIIMOTE, "HCI command acknowledged: 0x{:04x}", ev.opcode); - m_unacknowledged_commands.erase(it); + m_unacknowledged_commands.erase(m_unacknowledged_commands.begin() + index); } else if (ev.opcode != 0x0000) { @@ -354,28 +358,45 @@ void LibUSBBluetoothAdapter::AcknowledgeCommand(std::span buffer) libusb_transfer* LibUSBBluetoothAdapter::AllocateTransfer(std::size_t buffer_size) { - auto& item = *m_transfer_buffers - .emplace(libusb_alloc_transfer(0), - Common::UniqueBuffer{BUFFER_HEADER_SIZE + buffer_size}) - .first; + // One block: the free-list node and live-list links, then the transfer's bytes. + u8* const block = static_cast(std::malloc(BUFFER_HEADER_SIZE + buffer_size)); + libusb_transfer* const transfer = block ? libusb_alloc_transfer(0) : nullptr; + if (!transfer) + { + std::free(block); + return nullptr; + } - auto* const transfer = item.first; - transfer->buffer = item.second.get() + BUFFER_HEADER_SIZE; + FreeNode* const node = reinterpret_cast(block); + node->transfer = transfer; + node->prev = nullptr; + node->next = m_live_transfers; + if (m_live_transfers) + m_live_transfers->prev = node; + m_live_transfers = node; + transfer->buffer = block + BUFFER_HEADER_SIZE; return transfer; } void LibUSBBluetoothAdapter::FreeTransfer(libusb_transfer* transfer) { + FreeNode* const node = reinterpret_cast(transfer->buffer - BUFFER_HEADER_SIZE); + if (node->prev) + node->prev->next = node->next; + else + m_live_transfers = node->next; + if (node->next) + node->next->prev = node->prev; libusb_free_transfer(transfer); - m_transfer_buffers.erase(transfer); + std::free(node); } // Any thread. void LibUSBBluetoothAdapter::QueueFree(libusb_transfer* transfer) { + // node->transfer was set at allocation; only the free-list link is written here. FreeNode* const node = reinterpret_cast(transfer->buffer - BUFFER_HEADER_SIZE); - node->transfer = transfer; mpsc_stack_push(&m_transfers_to_free, &node->link); retro_eventcount_notify(&m_adapter_event.ec); } @@ -420,30 +441,34 @@ void LibUSBBluetoothAdapter::SubmitTransfer(libusb_transfer* transfer) QueueFree(transfer); } -void LibUSBBluetoothAdapter::SendBulkTransfer(u8 endpoint, std::span data) +void LibUSBBluetoothAdapter::SendBulkTransfer(u8 endpoint, const u8* data, std::size_t size) { constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleOutputTransfer>(); - auto* const transfer = AllocateTransfer(data.size()); - libusb_fill_bulk_transfer(transfer, m_handle, endpoint, transfer->buffer, int(data.size()), - callback, this, 0); + auto* const transfer = AllocateTransfer(size); + if (!transfer) + return; + libusb_fill_bulk_transfer(transfer, m_handle, endpoint, transfer->buffer, int(size), callback, + this, 0); - std::ranges::copy(data, transfer->buffer); + std::memcpy(transfer->buffer, data, size); SubmitTransfer(transfer); } void LibUSBBluetoothAdapter::SendControlTransfer(u8 type, u8 request, u8 value, u8 index, - std::span data) + const u8* data, std::size_t size) { constexpr auto callback = LibUSBMemFunCallback<&LibUSBBluetoothAdapter::HandleOutputTransfer>(); - auto* const transfer = AllocateTransfer(data.size() + LIBUSB_CONTROL_SETUP_SIZE); + auto* const transfer = AllocateTransfer(size + LIBUSB_CONTROL_SETUP_SIZE); + if (!transfer) + return; - libusb_fill_control_setup(transfer->buffer, type, request, value, index, u16(data.size())); + libusb_fill_control_setup(transfer->buffer, type, request, value, index, u16(size)); libusb_fill_control_transfer(transfer, m_handle, transfer->buffer, callback, this, 0); - std::ranges::copy(data, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE); + std::memcpy(transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, data, size); if (IsControllerReadyForCommand()) { @@ -456,16 +481,18 @@ void LibUSBBluetoothAdapter::SendControlTransfer(u8 type, u8 request, u8 value, } } -void LibUSBBluetoothAdapter::SendControlTransfer(std::span data) +void LibUSBBluetoothAdapter::SendControlTransfer(const void* data, std::size_t size) { - SendControlTransfer(REQUEST_TYPE, 0, 0, 0, data); + SendControlTransfer(REQUEST_TYPE, 0, 0, 0, static_cast(data), size); } -bool LibUSBBluetoothAdapter::SendBlockingCommand(std::span data, std::span response) +bool LibUSBBluetoothAdapter::SendBlockingCommand(const void* data, std::size_t size, + void* response, std::size_t response_size) { - SendControlTransfer(data); + SendControlTransfer(data, size); - const hci_cmd_hdr_t cmd = Common::BitCastPtr(data.data()); + hci_cmd_hdr_t cmd; + std::memcpy(&cmd, data, sizeof(cmd)); while (AreCommandsPendingResponse()) { @@ -505,14 +532,14 @@ bool LibUSBBluetoothAdapter::SendBlockingCommand(std::span data, std:: if (compl_event.opcode != cmd.opcode) continue; - if (event.size() < sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep) + response.size()) + if (event.size() < sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep) + response_size) { ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized command result"); break; } - std::copy_n(event.data() + sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep), - response.size(), response.data()); + std::memcpy(response, event.data() + sizeof(hci_event_hdr_t) + sizeof(hci_command_compl_ep), + response_size); return true; } diff --git a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h index 55b7210cf2..fe1c458061 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h +++ b/Source/Core/Core/IOS/USB/Bluetooth/LibUSBBluetoothAdapter.h @@ -3,9 +3,7 @@ #pragma once -#include #include -#include #include #include @@ -48,20 +46,22 @@ public: // ReceiveHCIEvent is expected to be called regularly to do some internal processing. [[nodiscard]] BufferType ReceiveHCIEvent(); - // The ACL packet at the front of the queue, read in place; empty when nothing is queued. + // The ACL packet at the front of the queue, read in place; nullptr when nothing is queued. // PopACLData releases it. - [[nodiscard]] std::span PeekACLData(); + [[nodiscard]] const u8* PeekACLData(std::size_t* size); void PopACLData(); // Submit a transfer now; libusb completes it asynchronously. - void SendBulkTransfer(u8 endpoint, std::span data); - void SendControlTransfer(u8 type, u8 request, u8 value, u8 index, std::span data); - void SendControlTransfer(std::span data); + void SendBulkTransfer(u8 endpoint, const u8* data, std::size_t size); + void SendControlTransfer(u8 type, u8 request, u8 value, u8 index, const u8* data, + std::size_t size); + void SendControlTransfer(const void* data, std::size_t size); // Blocks and eats events until a command complete event of the correct opcode is received. // Returns true on success or false on error or when the device stops responding. // The written response does not include the event header or command complete data. - bool SendBlockingCommand(std::span data, std::span response); + bool SendBlockingCommand(const void* data, std::size_t size, void* response, + std::size_t response_size); bool IsWiiBTModule() const; @@ -91,11 +91,14 @@ private: retro_atomic_int_t m_input_failed; // Callback and worker threads push transfers here, lock-free, when they are ready for clean up. - // The node lives in the front of the transfer's own buffer, so this allocates nothing. + // The node lives in the front of the transfer's own buffer, so this allocates nothing; the + // same header links every live transfer for Close to cancel. struct FreeNode { mpsc_stack_node_t link; libusb_transfer* transfer; + FreeNode* prev; + FreeNode* next; }; static constexpr std::size_t BUFFER_HEADER_SIZE = (sizeof(FreeNode) + 15) & ~std::size_t{15}; void QueueFree(libusb_transfer* transfer); @@ -107,8 +110,8 @@ private: std::size_t m_acl_peeked = 0; std::vector m_acl_bounce; - // All transfers are alloc'd and kept here until complete. - std::map> m_transfer_buffers; + // All transfers are alloc'd and linked here until complete. + FreeNode* m_live_transfers = nullptr; // Set to reduce OSD and log spam. bool m_showed_failed_transfer = false; @@ -144,7 +147,7 @@ private: void PushHCICommand(libusb_transfer*); template - void AcknowledgeCommand(std::span buffer); + void AcknowledgeCommand(const u8* buffer, std::size_t size); libusb_transfer* AllocateTransfer(std::size_t buffer_size); void Close(); diff --git a/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp index cd934972ff..3806014c47 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp @@ -8,7 +8,6 @@ #include "Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.h" -#include #include "Common/BitUtils.h" #include "Common/FileUtil.h" @@ -203,22 +202,27 @@ bool IsKnownRealtekBluetoothDevice(u16 vid, u16 pid) {0x0cb5, 0xc547}, }; - return std::ranges::find(realtek_ids, std::pair(vid, pid)) != std::end(realtek_ids); + for (const auto& id : realtek_ids) + { + if (id.first == vid && id.second == pid) + return true; + } + return false; } // Returns the data to be loaded, or an empty buffer on error. -static Common::UniqueBuffer ParseFirmware(std::span fw_data, - std::span cfg_data, +static Common::UniqueBuffer ParseFirmware(const char* fw_data, std::size_t fw_size, + const char* cfg_data, std::size_t cfg_size, const hci_read_local_ver_rp& local_ver, u8 rom_version) { - if (fw_data.size() < sizeof(rtl_epatch_header)) + if (fw_size < sizeof(rtl_epatch_header)) { ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); return {}; } - const rtl_epatch_header epatch_info = Common::BitCastPtr(fw_data.data()); + const rtl_epatch_header epatch_info = Common::BitCastPtr(fw_data); if (std::memcmp(epatch_info.signature.data(), RTL_EPATCH_SIGNATURE.data(), epatch_info.signature.size()) != 0) { @@ -240,8 +244,9 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, INFO_LOG_FMT(IOS_WIIMOTE, "fw_version={} num_patches={}", epatch_info.fw_version, num_patches); static constexpr u8 EXTENSION_SIGNATURE[] = {0x51, 0x04, 0xfd, 0x77}; - const auto extension = fw_data.last(std::size(EXTENSION_SIGNATURE)); - if (std::memcmp(extension.data(), EXTENSION_SIGNATURE, extension.size()) != 0) + const char* const extension = fw_data + fw_size - sizeof(EXTENSION_SIGNATURE); + if (fw_size < sizeof(rtl_epatch_header) + sizeof(EXTENSION_SIGNATURE) || + std::memcmp(extension, EXTENSION_SIGNATURE, sizeof(EXTENSION_SIGNATURE)) != 0) { ERROR_LOG_FMT(IOS_WIIMOTE, "Bad extension section signature"); return {}; @@ -249,7 +254,7 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, // Look for the project ID starting from the end of the file. int project_id = -1; - for (auto iter = extension.begin(); iter >= fw_data.begin() + (sizeof(rtl_epatch_header) + 3);) + for (const char* iter = extension; iter >= fw_data + (sizeof(rtl_epatch_header) + 3);) { const u8 opcode = *--iter; const u8 length = *--iter; @@ -289,9 +294,16 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, }; // Verify that the project ID is what we expect for our LMP subversion. - const auto* const it = - std::ranges::find(subver_project_ids, project_id, &LMPSubverProjectID::project_id); - if (it == std::end(subver_project_ids) || it->lmp_subver != local_ver.lmp_subversion) + const LMPSubverProjectID* it = nullptr; + for (const LMPSubverProjectID& entry : subver_project_ids) + { + if (entry.project_id == project_id) + { + it = &entry; + break; + } + } + if (!it || it->lmp_subver != local_ver.lmp_subversion) { ERROR_LOG_FMT(IOS_WIIMOTE, "Unexpected project_id: {}", project_id); return {}; @@ -299,13 +311,13 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, // Structure is: // u16 chip_ids[num_patches]; u16 patch_lengths[num_patches]; u32 patch_offsets[num_patches]; - if (fw_data.size() < + if (fw_size < sizeof(rtl_epatch_header) + num_patches * (sizeof(u16) + sizeof(u16) + sizeof(u32))) { ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); return {}; } - const auto* chip_id_base = fw_data.data() + sizeof(rtl_epatch_header); + const char* chip_id_base = fw_data + sizeof(rtl_epatch_header); const auto* const patch_length_base = chip_id_base + (sizeof(u16) * num_patches); const auto* const patch_offset_base = patch_length_base + (sizeof(u16) * num_patches); for (int i = 0; i != num_patches; ++i) @@ -318,21 +330,22 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, INFO_LOG_FMT(IOS_WIIMOTE, "Patch length:{} offset:{}", patch_length, patch_offset); - if (patch_offset + patch_length > fw_data.size()) + if (patch_offset + patch_length > fw_size) { ERROR_LOG_FMT(IOS_WIIMOTE, "Undersized firmware file"); return {}; } - auto result = Common::UniqueBuffer{patch_length + cfg_data.size()}; - std::copy_n(fw_data.data() + patch_offset, patch_length - sizeof(fw_version), result.data()); + auto result = Common::UniqueBuffer{patch_length + cfg_size}; + std::memcpy(result.data(), fw_data + patch_offset, patch_length - sizeof(fw_version)); // The last 4 bytes are replaced with fw_version. std::memcpy(result.data() + patch_length - sizeof(fw_version), &fw_version, sizeof(fw_version)); // Append the config binary. - std::ranges::copy(cfg_data, result.data() + patch_length); + if (cfg_size) + std::memcpy(result.data() + patch_length, cfg_data, cfg_size); return result; } @@ -343,58 +356,59 @@ static Common::UniqueBuffer ParseFirmware(std::span fw_data, } // Loads the provided data on the adapter. -static bool LoadFirmware(LibUSBBluetoothAdapter& adapter, std::span data) +static bool WriteFirmwareBlock(LibUSBBluetoothAdapter& adapter, const void* block, + std::size_t size) { - if (data.empty()) + rtl_download_response result{}; + if (!adapter.SendBlockingCommand(block, size, &result, sizeof(result))) + return false; + + if (result.status != 0x00) + { + ERROR_LOG_FMT(IOS_WIIMOTE, "Bad load firmware status: 0x{:02x}", result.status); + return false; + } + + return true; +} + +static bool LoadFirmware(LibUSBBluetoothAdapter& adapter, const char* data, std::size_t size) +{ + if (!size) { ERROR_LOG_FMT(IOS_WIIMOTE, "Firmware data is empty"); return false; } - INFO_LOG_FMT(IOS_WIIMOTE, "Loading firmware, total size: {}", data.size()); + INFO_LOG_FMT(IOS_WIIMOTE, "Loading firmware, total size: {}", size); HCICommandPayload<0xfc20, rtl_download_cmd> payload{}; - const auto write_block = [&](std::span block) { - rtl_download_response result{}; - if (!adapter.SendBlockingCommand(block, Common::AsWritableU8Span(result))) - return false; - - if (result.status != 0x00) - { - ERROR_LOG_FMT(IOS_WIIMOTE, "Bad load firmware status: 0x{:02x}", result.status); - return false; - } - - return true; - }; - // Write all blocks except the last one. - auto block_iter = data.begin(); - while (data.end() - block_iter > rtl_download_cmd::DATA_LEN) + std::size_t offset = 0; + while (size - offset > rtl_download_cmd::DATA_LEN) { - const auto block_end = block_iter + rtl_download_cmd::DATA_LEN; - std::copy(block_iter, block_end, payload.command.data.data()); + std::memcpy(payload.command.data.data(), data + offset, rtl_download_cmd::DATA_LEN); - if (!write_block(Common::AsU8Span(payload))) + if (!WriteFirmwareBlock(adapter, &payload, sizeof(payload))) return false; if (++payload.command.index == 0x80) payload.command.index = 1; - block_iter = block_end; + offset += rtl_download_cmd::DATA_LEN; } // Write the last block. - const auto last_block_size = data.end() - block_iter; - const auto bytes_short_of_full_block = rtl_download_cmd::DATA_LEN - last_block_size; + const std::size_t last_block_size = size - offset; + const std::size_t bytes_short_of_full_block = rtl_download_cmd::DATA_LEN - last_block_size; // Mark data end. payload.command.index |= 0x80u; - payload.header.length -= bytes_short_of_full_block; - std::copy(block_iter, data.end(), payload.command.data.data()); + payload.header.length -= u8(bytes_short_of_full_block); + std::memcpy(payload.command.data.data(), data + offset, last_block_size); - if (!write_block(Common::AsU8Span(payload).first(sizeof(payload) - bytes_short_of_full_block))) + if (!WriteFirmwareBlock(adapter, &payload, sizeof(payload) - bytes_short_of_full_block)) return false; NOTICE_LOG_FMT(IOS_WIIMOTE, "Loading firmware complete"); @@ -521,9 +535,9 @@ bool InitializeRealtekBluetoothDevice(LibUSBBluetoothAdapter& adapter) { // Read local version. hci_read_local_ver_rp local_ver{}; - if (!adapter.SendBlockingCommand( - Common::AsU8Span(hci_cmd_hdr_t{.opcode = HCI_CMD_READ_LOCAL_VER, .length = 0}), - Common::AsWritableU8Span(local_ver))) + const hci_cmd_hdr_t read_local_ver{.opcode = HCI_CMD_READ_LOCAL_VER, .length = 0}; + if (!adapter.SendBlockingCommand(&read_local_ver, sizeof(read_local_ver), &local_ver, + sizeof(local_ver))) { return false; } @@ -581,13 +595,14 @@ bool InitializeRealtekBluetoothDevice(LibUSBBluetoothAdapter& adapter) return false; } - return LoadFirmware(adapter, fw_data); + return LoadFirmware(adapter, fw_data.data(), fw_data.size()); } // Read ROM version. rtl_rom_version_evt read_rom_ver{}; - if (!adapter.SendBlockingCommand(Common::AsU8Span(hci_cmd_hdr_t{.opcode = 0xfc6d, .length = 0}), - Common::AsWritableU8Span(read_rom_ver))) + const hci_cmd_hdr_t read_rom{.opcode = 0xfc6d, .length = 0}; + if (!adapter.SendBlockingCommand(&read_rom, sizeof(read_rom), &read_rom_ver, + sizeof(read_rom_ver))) { return false; } @@ -604,6 +619,7 @@ bool InitializeRealtekBluetoothDevice(LibUSBBluetoothAdapter& adapter) std::string config_data; File::ReadFileToString(config_path, config_data); - const auto firmware = ParseFirmware(fw_data, config_data, local_ver, rom_version); - return LoadFirmware(adapter, firmware); + const auto firmware = ParseFirmware(fw_data.data(), fw_data.size(), config_data.data(), + config_data.size(), local_ver, rom_version); + return LoadFirmware(adapter, firmware.data(), firmware.size()); } diff --git a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp index 02a11fe9ae..58bf7a7901 100644 --- a/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp +++ b/Source/Core/Core/IOS/USB/Bluetooth/WiimoteDevice.cpp @@ -997,7 +997,7 @@ void WiimoteDevice::InterruptDataInputCallback(u8 hid_type, const u8* data, u32 data_frame.header.dcid = channel->remote_cid; data_frame.header.length = u16(sizeof(hid_type) + size); data_frame.hid_type = hid_type; - std::copy_n(data, size, data_frame.data.data()); + std::memcpy(data_frame.data.data(), data, size); const u32 data_frame_size = data_frame.header.length + sizeof(l2cap_hdr_t);