diff --git a/src/common/bitutils.h b/src/common/bitutils.h index 3461e06e5..dd253412f 100644 --- a/src/common/bitutils.h +++ b/src/common/bitutils.h @@ -2,22 +2,26 @@ // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once + #include "types.h" +#include <cstdint> +#include <type_traits> + #ifdef _MSC_VER -#include <intrin.h> +#include <stdlib.h> #endif // Zero-extending helper template<typename TReturn, typename TValue> -ALWAYS_INLINE constexpr TReturn ZeroExtend(TValue value) +ALWAYS_INLINE static constexpr TReturn ZeroExtend(TValue value) { return static_cast<TReturn>(static_cast<typename std::make_unsigned<TReturn>::type>( static_cast<typename std::make_unsigned<TValue>::type>(value))); } // Sign-extending helper template<typename TReturn, typename TValue> -ALWAYS_INLINE constexpr TReturn SignExtend(TValue value) +ALWAYS_INLINE static constexpr TReturn SignExtend(TValue value) { return static_cast<TReturn>( static_cast<typename std::make_signed<TReturn>::type>(static_cast<typename std::make_signed<TValue>::type>(value))); @@ -25,97 +29,97 @@ ALWAYS_INLINE constexpr TReturn SignExtend(TValue value) // Type-specific helpers template<typename TValue> -ALWAYS_INLINE constexpr u16 ZeroExtend16(TValue value) +ALWAYS_INLINE static constexpr u16 ZeroExtend16(TValue value) { return ZeroExtend<u16, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u32 ZeroExtend32(TValue value) +ALWAYS_INLINE static constexpr u32 ZeroExtend32(TValue value) { return ZeroExtend<u32, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u64 ZeroExtend64(TValue value) +ALWAYS_INLINE static constexpr u64 ZeroExtend64(TValue value) { return ZeroExtend<u64, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u16 SignExtend16(TValue value) +ALWAYS_INLINE static constexpr u16 SignExtend16(TValue value) { return SignExtend<u16, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u32 SignExtend32(TValue value) +ALWAYS_INLINE static constexpr u32 SignExtend32(TValue value) { return SignExtend<u32, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u64 SignExtend64(TValue value) +ALWAYS_INLINE static constexpr u64 SignExtend64(TValue value) { return SignExtend<u64, TValue>(value); } template<typename TValue> -ALWAYS_INLINE constexpr u8 Truncate8(TValue value) +ALWAYS_INLINE static constexpr u8 Truncate8(TValue value) { return static_cast<u8>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value)); } template<typename TValue> -ALWAYS_INLINE constexpr u16 Truncate16(TValue value) +ALWAYS_INLINE static constexpr u16 Truncate16(TValue value) { return static_cast<u16>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value)); } template<typename TValue> -ALWAYS_INLINE constexpr u32 Truncate32(TValue value) +ALWAYS_INLINE static constexpr u32 Truncate32(TValue value) { return static_cast<u32>(static_cast<typename std::make_unsigned<decltype(value)>::type>(value)); } // BCD helpers -ALWAYS_INLINE constexpr u8 BinaryToBCD(u8 value) +ALWAYS_INLINE static constexpr u8 BinaryToBCD(u8 value) { return ((value / 10) << 4) + (value % 10); } -ALWAYS_INLINE constexpr u8 PackedBCDToBinary(u8 value) +ALWAYS_INLINE static constexpr u8 PackedBCDToBinary(u8 value) { return ((value >> 4) * 10) + (value % 16); } -ALWAYS_INLINE constexpr u8 IsValidBCDDigit(u8 digit) +ALWAYS_INLINE static constexpr u8 IsValidBCDDigit(u8 digit) { return (digit <= 9); } -ALWAYS_INLINE constexpr u8 IsValidPackedBCD(u8 value) +ALWAYS_INLINE static constexpr u8 IsValidPackedBCD(u8 value) { return IsValidBCDDigit(value & 0x0F) && IsValidBCDDigit(value >> 4); } // Boolean to integer -ALWAYS_INLINE constexpr u8 BoolToUInt8(bool value) +ALWAYS_INLINE static constexpr u8 BoolToUInt8(bool value) { return static_cast<u8>(value); } -ALWAYS_INLINE constexpr u16 BoolToUInt16(bool value) +ALWAYS_INLINE static constexpr u16 BoolToUInt16(bool value) { return static_cast<u16>(value); } -ALWAYS_INLINE constexpr u32 BoolToUInt32(bool value) +ALWAYS_INLINE static constexpr u32 BoolToUInt32(bool value) { return static_cast<u32>(value); } -ALWAYS_INLINE constexpr u64 BoolToUInt64(bool value) +ALWAYS_INLINE static constexpr u64 BoolToUInt64(bool value) { return static_cast<u64>(value); } // Integer to boolean template<typename TValue> -ALWAYS_INLINE constexpr bool ConvertToBool(TValue value) +ALWAYS_INLINE static constexpr bool ConvertToBool(TValue value) { return static_cast<bool>(value); } // Unsafe integer to boolean template<typename TValue> -ALWAYS_INLINE bool ConvertToBoolUnchecked(TValue value) +ALWAYS_INLINE static bool ConvertToBoolUnchecked(TValue value) { // static_assert(sizeof(uint8) == sizeof(bool)); bool ret; @@ -125,7 +129,7 @@ ALWAYS_INLINE bool ConvertToBoolUnchecked(TValue value) // Generic sign extension template<int NBITS, typename T> -ALWAYS_INLINE constexpr T SignExtendN(T value) +ALWAYS_INLINE static constexpr T SignExtendN(T value) { // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend constexpr int shift = 8 * sizeof(T) - NBITS; @@ -134,7 +138,7 @@ ALWAYS_INLINE constexpr T SignExtendN(T value) /// Returns the number of zero bits before the first set bit, going MSB->LSB. template<typename T> -ALWAYS_INLINE unsigned CountLeadingZeros(T value) +ALWAYS_INLINE static unsigned CountLeadingZeros(T value) { #ifdef _MSC_VER if constexpr (sizeof(value) >= sizeof(u64)) @@ -161,7 +165,7 @@ ALWAYS_INLINE unsigned CountLeadingZeros(T value) /// Returns the number of zero bits before the first set bit, going LSB->MSB. template<typename T> -ALWAYS_INLINE unsigned CountTrailingZeros(T value) +ALWAYS_INLINE static unsigned CountTrailingZeros(T value) { #ifdef _MSC_VER if constexpr (sizeof(value) >= sizeof(u64)) @@ -183,3 +187,37 @@ ALWAYS_INLINE unsigned CountTrailingZeros(T value) return static_cast<unsigned>(__builtin_ctz(ZeroExtend32(value))); #endif } + +// C++23-like std::byteswap() +template<typename T> +ALWAYS_INLINE static T ByteSwap(T value) +{ + if constexpr (std::is_signed_v<T>) + { + return static_cast<T>(ByteSwap(std::make_unsigned_t<T>(value))); + } + else if constexpr (std::is_same_v<T, std::uint16_t>) + { +#ifdef _MSC_VER + return _byteswap_ushort(value); +#else + return __builtin_bswap16(value); +#endif + } + else if constexpr (std::is_same_v<T, std::uint32_t>) + { +#ifdef _MSC_VER + return _byteswap_ulong(value); +#else + return __builtin_bswap32(value); +#endif + } + else if constexpr (std::is_same_v<T, std::uint64_t>) + { +#ifdef _MSC_VER + return _byteswap_uint64(value); +#else + return __builtin_bswap64(value); +#endif + } +} diff --git a/src/util/cd_image.cpp b/src/util/cd_image.cpp index 12cc1f50e..aa9c3a7c6 100644 --- a/src/util/cd_image.cpp +++ b/src/util/cd_image.cpp @@ -3,6 +3,7 @@ #include "cd_image.h" #include "common/assert.h" +#include "common/bitutils.h" #include "common/error.h" #include "common/file_system.h" #include "common/log.h" @@ -536,8 +537,8 @@ u16 CDImage::SubChannelQ::ComputeCRC(const Data& data) for (u32 i = 0; i < 10; i++) value = crc16_table[(value >> 8) ^ data[i]] ^ (value << 8); - value = ~value; - return (value >> 8) | (value << 8); + // Invert and swap + return ByteSwap(static_cast<u16>(~value)); } bool CDImage::SubChannelQ::IsCRCValid() const