diff --git a/CMakeLists.txt b/CMakeLists.txt index 78f276f5..381f710d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.20) # For using CMAKE__BYTE_ORDER project(Descent3 VERSION 1.5.500) @@ -11,6 +11,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_CXX_EXTENSIONS OFF) +if(CMAKE_CXX_BYTE_ORDER STREQUAL "BIG_ENDIAN") + message(STATUS "Big Endian system detected.") + add_definitions("-DOUTRAGE_BIG_ENDIAN") +endif() + +if(BUILD_TESTING) + find_package(GTest REQUIRED) + enable_testing() + include(GoogleTest) + add_subdirectory(tests) +endif() + if(NOT MSVC) # check if this is some kind of clang (Clang, AppleClang, whatever) # (convert compiler ID to lowercase so we match Clang, clang, AppleClang etc, regardless of case) diff --git a/lib/byteswap.h b/lib/byteswap.h index 8c6b2d3a..7ee39fe9 100644 --- a/lib/byteswap.h +++ b/lib/byteswap.h @@ -1,20 +1,21 @@ /* -* Descent 3 -* Copyright (C) 2024 Parallax Software -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * Descent 3 + * Copyright (C) 2024 Parallax Software + * Copyright (C) 2024 Descent Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ /* * $Logfile: /DescentIII/Main/lib/BYTESWAP.H $ @@ -61,72 +62,53 @@ * $NoKeywords: $ */ -#ifndef _BYTESWAP_H -#define _BYTESWAP_H +#ifndef BYTESWAP_H +#define BYTESWAP_H -#include "pstypes.h" +#include +#include -/* -#include "psendian.h" +namespace D3 { -#define SWAPSHORT(x) ( ((x) << 8) | (((ushort)(x)) >> 8) ) -#define SWAPINT(x) ( ((x) << 24) | (((ulong)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> -8) ) - -//Stupid function to trick the compiler into letting me byteswap a float -inline float SWAPFLOAT(float x) -{ - int i = SWAPINT(*((int *) &(x))); - return *((float *) &(i)); +// std::byteswap from C++23 +template constexpr T byteswap(T n) { + T m; + for (size_t i = 0; i < sizeof(T); i++) + reinterpret_cast(&m)[i] = reinterpret_cast(&n)[sizeof(T) - 1 - i]; + return m; } -// INTEL_ assumes the returned value will be in "Little Endian Format" -#define INTEL_INT(x) Endian_SwapInt(x) -#define INTEL_SHORT(x) Endian_SwapShort(x) -#define INTEL_FLOAT(x) Endian_SwapFloat(x) - -// MOTOROLA_ assumes the returned value will be in "Big Endian Format" -#define MOTOROLA_INT(x) SWAPINT(Endian_SwapInt(x)) -#define MOTOROLA_SHORT(x) SWAPSHORT(Endian_SwapShort(x)) -#define MOTOROLA_FLOAT(x) SWAPFLOAT(Endian_SwapFloat(x)) -*/ -#define SWAPSHORT(x) (short)(0xFFFF & (((x) << 8) | (((ushort)(x)) >> 8))) -#define SWAPINT(x) (int)(((x) << 24) | (((ulong)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> 8)) - -// Stupid function to trick the compiler into letting me byteswap a float -inline float SWAPFLOAT(float x) { - int i = SWAPINT(*((int *)&(x))); - return *((float *)&(i)); -} - -// Default is little endian, so change for Macintosh -#if (MACOSX && MACOSXPPC) -#define OUTRAGE_BIG_ENDIAN -#endif - -#if (defined __LINUX__) && (!defined(MACOSX)) -#include - -#if BYTE_ORDER == BIG_ENDIAN -#define OUTRAGE_BIG_ENDIAN -#endif -#endif - +/** + * Convert integer to/from BE order + */ +template constexpr T convert_be(T val) { #ifndef OUTRAGE_BIG_ENDIAN - -#define INTEL_INT(x) x -#define INTEL_SHORT(x) x -#define INTEL_FLOAT(x) x -#define MOTOROLA_INT(x) SWAPINT(x) -#define MOTOROLA_SHORT(x) SWAPSHORT(x) -#define MOTOROLA_FLOAT(x) SWAPFLOAT(x) + return byteswap(val); #else -#define INTEL_INT(x) SWAPINT(x) -#define INTEL_SHORT(x) SWAPSHORT(x) -#define INTEL_FLOAT(x) SWAPFLOAT(x) -#define MOTOROLA_INT(x) x -#define MOTOROLA_SHORT(x) x -#define MOTOROLA_FLOAT(x) x + return (val); #endif +} + +/** + * Convert integer to/from LE order + */ +template constexpr T convert_le(T val) { +#ifndef OUTRAGE_BIG_ENDIAN + return (val); +#else + return byteswap(val); +#endif +} + +} // namespace D3 + +// Compatibility macros. Use D3::convert_le / D3::convert_be when possible + +#define INTEL_INT(x) D3::convert_le(x) +#define INTEL_SHORT(x) D3::convert_le(x) +#define INTEL_FLOAT(x) D3::convert_le(x) +#define MOTOROLA_INT(x) D3::convert_be(x) +#define MOTOROLA_SHORT(x) D3::convert_be(x) +#define MOTOROLA_FLOAT(x) D3::convert_be(x) #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..5c2c08aa --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable( + byteswap_tests + byteswap_tests.cpp +) +target_link_libraries( + byteswap_tests + GTest::gtest_main +) +target_include_directories(byteswap_tests PRIVATE ${PROJECT_SOURCE_DIR}/lib) + +gtest_discover_tests(byteswap_tests) diff --git a/tests/byteswap_tests.cpp b/tests/byteswap_tests.cpp new file mode 100644 index 00000000..33c7d9fa --- /dev/null +++ b/tests/byteswap_tests.cpp @@ -0,0 +1,90 @@ +/* +* Descent 3 +* Copyright (C) 2024 Descent Developers +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#include + +#include "byteswap.h" + +// This code taken from original byteswap.h for testing float conversion +// It cannot convert negative float numbers in 64-bit systems, so testing only non-negative numbers + +#define SWAPINT(x) (int)(((x) << 24) | (((ulong)(x)) >> 24) | (((x) & 0x0000ff00) << 8) | (((x) & 0x00ff0000) >> 8)) + +// Stupid function to trick the compiler into letting me byteswap a float +inline float SWAPFLOAT(float x) { + int i = SWAPINT(*((int *)&(x))); + return *((float *)&(i)); +} + +// Taken from CFILE.cpp +inline double SWAPDOUBLE(double x) { + double t; + int *sp = (int *)&x; + int *dp = (int *)&t; + dp[0] = SWAPINT(sp[1]); + dp[1] = SWAPINT(sp[0]); + x = t; + + return x; +} + + +TEST(D3, ByteSwap) { + EXPECT_EQ(D3::byteswap((uint8_t)0x01), (uint8_t)0x01); + EXPECT_EQ(D3::byteswap((int8_t)0x01), (int16_t)0x01); + EXPECT_EQ(D3::byteswap((uint16_t)0x0123), (uint16_t)0x2301); + EXPECT_EQ(D3::byteswap((int16_t)0x0123), (int16_t)0x2301); + EXPECT_EQ(D3::byteswap((int32_t)0x01234567), (int32_t)0x67452301); + EXPECT_EQ(D3::byteswap((int64_t)0x0123456789ABCDEF), (int64_t)0xEFCDAB8967452301); + + EXPECT_EQ(D3::byteswap((int32_t)(0xFF000000)), (int32_t)0xFF); +} + +TEST(D3, Converting) { +#ifndef OUTRAGE_BIG_ENDIAN + EXPECT_EQ(D3::convert_le((int16_t)0x10), 0x10); + EXPECT_EQ(D3::convert_le((int32_t)0x10), 0x10); + EXPECT_EQ(D3::convert_le((int64_t)0x10), 0x10); + + EXPECT_EQ(D3::convert_be((int16_t)0x10), 0x1000); + EXPECT_EQ(D3::convert_be((int32_t)0x10), 0x10000000); + EXPECT_EQ(D3::convert_be((int64_t)0x10), 0x1000000000000000); +#else + EXPECT_EQ(D3::convert_be((int16_t)0x10), 0x10); + EXPECT_EQ(D3::convert_be((int32_t)0x10), 0x10); + EXPECT_EQ(D3::convert_be((int64_t)0x10), 0x10); + + EXPECT_EQ(D3::convert_le((int16_t)0x10), 0x1000); + EXPECT_EQ(D3::convert_le((int16_t)0x10), 0x10000000); + EXPECT_EQ(D3::convert_le((int16_t)0x10), 0x1000000000000000); +#endif +} + +TEST(D3, FloatDoubleOperations) { + // float/double sanity checks + EXPECT_EQ(sizeof(float), 4); + EXPECT_EQ(sizeof(double), 8); + + EXPECT_EQ(D3::byteswap(0.0f), SWAPFLOAT(0.0f)); + EXPECT_EQ(D3::byteswap(10.0f), SWAPFLOAT(10.0f)); + EXPECT_EQ(D3::byteswap(10000.0f), SWAPFLOAT(10000.0f)); + + EXPECT_EQ(D3::byteswap(0.0), SWAPDOUBLE(0.0)); + EXPECT_EQ(D3::byteswap(10.0), SWAPDOUBLE(10.0)); + EXPECT_EQ(D3::byteswap(10000.0), SWAPDOUBLE(10000.0)); +} diff --git a/tools/HogMaker/IOOps.h b/tools/HogMaker/IOOps.h index c254e2e3..3222fdf4 100644 --- a/tools/HogMaker/IOOps.h +++ b/tools/HogMaker/IOOps.h @@ -24,36 +24,6 @@ namespace D3 { -// std::byteswap from C++23 -template constexpr T byteswap(T n) { - T m; - for (size_t i = 0; i < sizeof(T); i++) - reinterpret_cast(&m)[i] = reinterpret_cast(&n)[sizeof(T) - 1 - i]; - return m; -} - -/** - * Convert integer to/from BE order - */ -template constexpr T convert_be(T val) { -#ifndef OUTRAGE_BIG_ENDIAN - return byteswap(val); -#else - return (val); -#endif -} - -/** - * Convert integer to/from LE order - */ -template constexpr T convert_le(T val) { -#ifndef OUTRAGE_BIG_ENDIAN - return (val); -#else - return byteswap(val); -#endif -} - template inline std::ostream &bin_write(std::ostream &output, T value, bool is_little_endian = true, size_t n = sizeof(T)) { value = is_little_endian ? convert_le(value) : convert_be(value);