From 33e49e8f0ff31d9de74900d58178f01b27b7cc8d Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sat, 3 May 2025 23:43:33 +0200 Subject: [PATCH 1/2] smackw32: open SMK using harness (#452) This fixes a case sensitivity issue on Linux --- src/smackw32/include/smackw32/smackw32.h | 1 + src/smackw32/smackw32.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/smackw32/include/smackw32/smackw32.h b/src/smackw32/include/smackw32/smackw32.h index 46713ac7..c64e278f 100644 --- a/src/smackw32/include/smackw32/smackw32.h +++ b/src/smackw32/include/smackw32/smackw32.h @@ -45,6 +45,7 @@ typedef struct SmackTag { // added by dethrace void* smk_handle; // opaque pointer to the libsmacker instance + void *f; // opaque file pointer tAudioBackend_stream* audio_stream; } Smack; diff --git a/src/smackw32/smackw32.c b/src/smackw32/smackw32.c index 8722deb7..b33c79c6 100644 --- a/src/smackw32/smackw32.c +++ b/src/smackw32/smackw32.c @@ -7,6 +7,7 @@ #include #include "harness/hooks.h" +#include "harness/os.h" #include "harness/trace.h" // lib/libsmacker @@ -27,9 +28,15 @@ Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf) { double microsecs_per_frame; Smack* smack; double fps; + FILE *f = NULL; - smk smk_handle = smk_open_file(name, SMK_MODE_MEMORY); + f = OS_fopen(name, "rb"); + if (f == NULL) { + return NULL; + } + smk smk_handle = smk_open_filepointer(f, SMK_MODE_MEMORY); if (smk_handle == NULL) { + fclose(f); return NULL; } @@ -41,6 +48,8 @@ Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf) { // smk_handle is added to hold a pointer to the underlying libsmacker instance smack->smk_handle = smk_handle; + smack->f = f; + smk_info_all(smk_handle, NULL, &smack->Frames, µsecs_per_frame); fps = 1000000.0 / microsecs_per_frame; smack->MSPerFrame = (unsigned long)((1 / fps) * 1000); @@ -128,5 +137,6 @@ void SmackClose(Smack* smack) { } smk_close(smack->smk_handle); + // libsmacker closes file, no need to do `fclose(smack->f)` free(smack); } From a2cdd1f061184df8d0e7022e2dd61abdbc04e498 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sat, 3 May 2025 23:44:46 +0200 Subject: [PATCH 2/2] Support multiple platform drivers (#444) * Start of supporting multiple platform drivers * Remove dependency of null platform on SDL2 * cmake: support loading SDL2 dynamically * Move CMake options to project root for SDL2::SDL2 access * Don't link to OpenGL * Move common dynamic loading to header * Add SDL 1.2 platform * Add SDL1 platform backend * Remove accidental addition * Try multiple platform drivers until we find a good one * Fix sdl1 mouse * Use my BRender fork FIXME FIXME FIXME REMOVEME REMOVEME REMOVEME * Build dethrace supporting SDL1 on ci * use dethrace-labs BRender fork --- .github/workflows/workflow.yaml | 6 +- CMakeLists.txt | 35 +- docs/PORTING.md | 68 ++-- lib/BRender-v1.3.2 | 2 +- src/DETHRACE/CMakeLists.txt | 31 +- src/DETHRACE/main.c | 9 +- src/S3/CMakeLists.txt | 2 +- src/harness/CMakeLists.txt | 37 +- src/harness/harness.c | 161 ++++++--- src/harness/include/harness/config.h | 2 - src/harness/include/harness/hooks.h | 17 +- src/harness/platforms/null.c | 29 +- src/harness/platforms/sdl1.c | 315 ++++++++++++++++++ .../platforms/sdl1_scancode_to_dinput.h | 208 ++++++++++++ src/harness/platforms/sdl1_syms.h | 27 ++ src/harness/platforms/sdl2.c | 237 +++++++------ src/harness/platforms/sdl2_syms.h | 45 +++ src/harness/platforms/sdl_dyn_common.h | 72 ++++ test/CMakeLists.txt | 2 - 19 files changed, 1115 insertions(+), 190 deletions(-) create mode 100644 src/harness/platforms/sdl1.c create mode 100644 src/harness/platforms/sdl1_scancode_to_dinput.h create mode 100644 src/harness/platforms/sdl1_syms.h create mode 100644 src/harness/platforms/sdl2_syms.h create mode 100644 src/harness/platforms/sdl_dyn_common.h diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index e8bcd024..48698437 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -41,7 +41,7 @@ jobs: if: ${{ runner.os == 'Linux' }} run: | sudo apt-get update - sudo apt-get install -y libgl-dev libxext-dev ${{ matrix.platform.apt-packages }} + sudo apt-get install -y libgl-dev libgl1-mesa-dev libglu1-mesa-dev libxext-dev - name: Set up SDL uses: libsdl-org/setup-sdl@main with: @@ -52,6 +52,7 @@ jobs: cmake-toolchain-file: ${{ matrix.platform.cmake-toolchain-file }} discriminator: ${{ matrix.platform.arch }} version: 2-latest + version-sdl12-compat: 1-head - name: 'Prepare sources for release' if: ${{ startsWith(github.ref, 'refs/tags/') }} run: | @@ -68,6 +69,9 @@ jobs: -DDETHRACE_PACKAGE_PLATFORM=${{ matrix.platform.name }} \ -DDETHRACE_PACKAGE_ARCH=${{ matrix.platform.arch }} \ -DCMAKE_TOOLCHAIN_FILE=${{ matrix.platform.cmake-toolchain-file }} \ + -DDETHRACE_PLATFORM_SDL1=ON \ + -DDETHRACE_PLATFORM_SDL2=ON \ + -DDETHRACE_PLATFORM_SDL_DYNAMIC=ON \ ${{ matrix.platform.cmake-args }} - name: 'Build (CMake)' run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 472375dd..2285b9c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ if(MSVC) endif() include(CheckCCompilerFlag) +include(CMakeDependentOption) include(TestBigEndian) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") @@ -61,11 +62,39 @@ endfunction() test_big_endian(IS_BIGENDIAN) -find_package(SDL2 CONFIG) -if(NOT SDL2_FOUND) - find_package(SDL2 MODULE REQUIRED) +option(DETHRACE_PLATFORM_SDL1 "Support SDL 1.2 platform driver" OFF) +option(DETHRACE_PLATFORM_SDL2 "Support SDL 2 platform driver" ON) +set(count_sdl_platforms 0) + +set(DETHRACE_PLATFORMS ) +if(DETHRACE_PLATFORM_SDL1) + find_package(SDL REQUIRED) + list(APPEND DETHRACE_PLATFORMS SDL1) + math(EXPR count_sdl_platforms "${count_sdl_platforms} + 1") +endif() +if(DETHRACE_PLATFORM_SDL2) + find_package(SDL2 CONFIG) + if(NOT SDL2_FOUND) + find_package(SDL2 MODULE REQUIRED) + endif() + list(APPEND DETHRACE_PLATFORMS SDL2) + math(EXPR count_sdl_platforms "${count_sdl_platforms} + 1") endif() +if(count_sdl_platforms GREATER 1) + # Force dynamic SDL when enabling 2 (or more) SDL platform backends + set(dynamic_sdl_force TRUE) +else() + # When no SDL backend is selected, disable dynamic SDL backend + # Enabling one SDL backend is a don-t-care + set(dynamic_sdl_force FALSE) +endif() + +cmake_dependent_option(DETHRACE_PLATFORM_SDL_DYNAMIC "Load SDL dynamically" FALSE "count_sdl_platforms EQUAL 1" "${dynamic_sdl_force}") + +if(NOT DETHRACE_PLATFORMS) + message(FATAL_ERROR "No platform drivers enabled") +endif() add_subdirectory(lib/BRender-v1.3.2) add_subdirectory(lib/libsmacker) diff --git a/docs/PORTING.md b/docs/PORTING.md index 32374300..bf573f0d 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -22,34 +22,58 @@ target_sources(harness PRIVATE ... ``` -## IO Platform (windowing / input / rendering) +## Platform (windowing / input) -A `Platform` in _dethrace_ implements windowing, rendering and input handling. +A `Platform` in _dethrace_ implements windowing and input handling. -The default platform is `SDL_OpenGL`, which uses SDL for windowing and input, and OpenGL for rendering. See [platforms/sdl_opengl.c](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl_opengl.c). +The default platform is `SDL2`, which uses SDL2 for windowing and input. See [platforms/sdl_opengl.c](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl2.c). To add a new `Platform`: -1. Create `platforms/my_platform.c` file and add a `Harness_Platform_Init` function. Hook up all the function pointers using the `sdl_opengl` [implementation](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl_opengl.h) as a guide. +1. Create a `src/harness/my_platform.c` file where you'll implement your platform-specific callbacks. + Define a public fully-initialized `const tPlatform_bootstrap MYPLATFORM_bootstrap ` variable in this file. -2. Add a new conditional section in `src/harness/CMakeLists.txt` for your new platform +2. Add the the following code fragments to appropriate locations in `src/harness/harness.c`: + ```c + extern const tPlatform_bootstrap MYPLATFORM_bootstrap; + ``` + ```c + #ifdef DETHRACE_PLATFORM_MYPLATFORM + &MYPLATFORM_bootstrap, + #endif + ``` -For example: -``` -if (IO_PLATFORM STREQUAL "My_Platform") - target_sources(harness PRIVATE - io_platforms/my_platform.c - ) -endif() -``` +3. Add new conditionals to `CMakeLists.txt` and `src/harness/CMakeLists.txt` for your new platform -3. Run cmake to update your build with the new platform -```sh -cd build -cmake -DIO_PLATFORM=My_Platform .. -``` + For example: + ```cmake + # CMakeLists.txt + option(DETHRACE_PLATFORM_MYPLATFORM "Enable my platform" OFF) + if(DETHRACE_PLATFORM_MYPLATFORM) + find_package(MyPlatform REQUIRED) + endif() + ``` + ```cmake + # src/harness/CMakeLists.txt + if(DETHRACE_PLATFORM_MYPLATFORM) + target_sources(harness PRIVATE + my_platform.c + ) + target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_MYPLATFORM) + target_link_libraries(harness PRIVATE MyPlatform::MyPlatform) + endif() + ``` -4. Build -``` -cmake --build . -``` +4. Hook up all the function pointers using the [sdl2 platform](https://github.com/dethrace-labs/dethrace/blob/main/src/harness/platforms/sdl2.c) as a guide. + +5. Run cmake to update your build with the new platform + ```sh + cd build + cmake -DDETHRACE_PLATFORM_MYPLATFORM=ON .. + cmake --build . + ``` + +6. Build + ``` + -cmake --build . + ``` diff --git a/lib/BRender-v1.3.2 b/lib/BRender-v1.3.2 index 89861762..fa8b5a90 160000 --- a/lib/BRender-v1.3.2 +++ b/lib/BRender-v1.3.2 @@ -1 +1 @@ -Subproject commit 89861762c3e0743eed484ef5dace8e882ecd1289 +Subproject commit fa8b5a90dff141f6e7e1f4f146ac593779fe6cea diff --git a/src/DETHRACE/CMakeLists.txt b/src/DETHRACE/CMakeLists.txt index 770f10d8..4b0b30be 100644 --- a/src/DETHRACE/CMakeLists.txt +++ b/src/DETHRACE/CMakeLists.txt @@ -15,7 +15,7 @@ if (DETHRACE_ASAN) target_link_options(dethrace_obj PUBLIC -fsanitize=address) endif() -target_link_libraries(dethrace_obj PUBLIC SDL2::SDL2 smackw32 harness BRender::Full BRender::DDI s3) +target_link_libraries(dethrace_obj PUBLIC smackw32 harness BRender::Full BRender::DDI s3) if(MSVC) target_compile_definitions(dethrace_obj PRIVATE -D_CRT_SECURE_NO_WARNINGS) @@ -177,6 +177,9 @@ add_executable(dethrace ${CMAKE_SOURCE_DIR}/packaging/windows/dethrace.rc ) +get_property(build_rpaths GLOBAL PROPERTY DETHRACE_BUILD_RPATHS) +set_property(TARGET dethrace APPEND PROPERTY BUILD_RPATH "${build_rpaths}") + target_link_libraries(dethrace PRIVATE dethrace_obj compile_with_werror) target_sources(dethrace PRIVATE main.c) @@ -240,11 +243,25 @@ if(DETHRACE_INSTALL) DESTINATION "." OPTIONAL ) - endif() - if(WIN32) - install(FILES $ - DESTINATION "." - OPTIONAL - ) + if(DETHRACE_PLATFORM_SDL_DYNAMIC) + if(DETHRACE_PLATFORM_SDL1) + get_filename_component(sdl1_lib_dir "${SDL_LIBRARY}" DIRECTORY) + get_filename_component(sdl1_root_dir "${SDL_LIBRARY}" PATH) + set(sdl1_bin_dir "${sdl1_root_dir}/bin" PATH) + find_file(SDL1DLL_PATH NAMES "SDL.dll" PATHS ${sdl1_lib_dir} ${sdl1_root_dir} PATH_SUFFIXES "bin" REQUIRED) + if(SDL1DLL_PATH) + install(FILES "${SDL1DLL_PATH}" + DESTINATION "." + OPTIONAL + ) + endif() + endif() + if(DETHRACE_PLATFORM_SDL2) + install(FILES "$" + DESTINATION "." + OPTIONAL + ) + endif() + endif() endif() endif() diff --git a/src/DETHRACE/main.c b/src/DETHRACE/main.c index df920171..9bf6650f 100644 --- a/src/DETHRACE/main.c +++ b/src/DETHRACE/main.c @@ -9,7 +9,7 @@ #include "brender.h" -extern void Harness_Init(int* argc, char* argv[]); +extern int Harness_Init(int* argc, char* argv[]); extern int original_main(int pArgc, char* pArgv[]); void BR_CALLBACK _BrBeginHook(void) { @@ -28,6 +28,8 @@ void BR_CALLBACK _BrEndHook(void) { } int main(int argc, char* argv[]) { + int result; + #ifdef _WIN32 /* Attach to the console that started us if any */ if (AttachConsole(ATTACH_PARENT_PROCESS)) { @@ -46,7 +48,10 @@ int main(int argc, char* argv[]) { } #endif - Harness_Init(&argc, argv); + result = Harness_Init(&argc, argv); + if (result != 0) { + return result; + } return original_main(argc, argv); } diff --git a/src/S3/CMakeLists.txt b/src/S3/CMakeLists.txt index be8843af..03cd583b 100644 --- a/src/S3/CMakeLists.txt +++ b/src/S3/CMakeLists.txt @@ -5,7 +5,7 @@ target_include_directories(s3 include ) -target_link_libraries(s3 PRIVATE brender SDL2::SDL2 harness compile_with_werror) +target_link_libraries(s3 PRIVATE brender harness compile_with_werror) if(NOT MSVC) target_link_libraries(s3 PUBLIC pthread m) diff --git a/src/harness/CMakeLists.txt b/src/harness/CMakeLists.txt index 9349d4b7..ad011488 100644 --- a/src/harness/CMakeLists.txt +++ b/src/harness/CMakeLists.txt @@ -2,10 +2,6 @@ configure_file(version.h.in version.h @ONLY) add_library(harness STATIC) -if (NOT DEFINED IO_PLATFORM) - set(IO_PLATFORM "SDL2") -endif() - target_include_directories(harness PRIVATE . @@ -62,12 +58,41 @@ target_sources(harness PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/version.h" ) -if (IO_PLATFORM STREQUAL "SDL2") +if(DETHRACE_PLATFORM_SDL1) + target_sources(harness PRIVATE + platforms/sdl1.c + platforms/sdl1_scancode_to_dinput.h + platforms/sdl1_syms.h + ) + target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL1) + if(DETHRACE_PLATFORM_SDL_DYNAMIC) + set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/platforms/sdl1.c" APPEND PROPERTY INCLUDE_DIRECTORIES "$") + get_filename_component(sdl_library_directory "${SDL_LIBRARY}" DIRECTORY) + set_property(GLOBAL APPEND PROPERTY DETHRACE_BUILD_RPATHS "${sdl_library_directory}") + else() + target_include_directories(harness PRIVATE ${SDL_INCLUDE_DIR}) + target_link_libraries(harness PRIVATE ${SDL_LIBRARIES}) + endif() +endif() + +if(DETHRACE_PLATFORM_SDL2) target_sources(harness PRIVATE platforms/sdl2.c platforms/sdl2_scancode_to_dinput.h + platforms/sdl2_syms.h ) - target_link_libraries(harness PRIVATE SDL2::SDL2) + target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL2) + if(DETHRACE_PLATFORM_SDL_DYNAMIC) + set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/platforms/sdl2.c" APPEND PROPERTY INCLUDE_DIRECTORIES "$") + set_property(GLOBAL APPEND PROPERTY DETHRACE_BUILD_RPATHS "$") + else() + target_link_libraries(harness PRIVATE SDL2::SDL2) + endif() +endif() + +if(DETHRACE_PLATFORM_SDL_DYNAMIC) + target_compile_definitions(harness PRIVATE DETHRACE_SDL_DYNAMIC) + target_link_libraries(harness PRIVATE ${CMAKE_DL_LIBS}) endif() if(WIN32) diff --git a/src/harness/harness.c b/src/harness/harness.c index b223f201..7c70cc51 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -12,16 +12,22 @@ #include #include -br_pixelmap* palette; -uint32_t* screen_buffer; - -br_pixelmap* last_dst = NULL; -br_pixelmap* last_src = NULL; - -int force_null_platform = 0; - extern uint32_t gI_am_cheating; +extern void Harness_Platform_Init(tHarness_platform* platform); + +extern const tPlatform_bootstrap SDL1_bootstrap; +extern const tPlatform_bootstrap SDL2_bootstrap; + +static const tPlatform_bootstrap *platform_bootstraps[] = { +#ifdef DETHRACE_PLATFORM_SDL2 + &SDL2_bootstrap, +#endif +#ifdef DETHRACE_PLATFORM_SDL1 + &SDL1_bootstrap, +#endif +}; + // SplatPack or Carmageddon. This is where we represent the code differences between the two. For example, the intro smack file. tHarness_game_info harness_game_info; @@ -31,9 +37,15 @@ tHarness_game_config harness_game_config; // Platform hooks tHarness_platform gHarness_platform; -extern void Harness_Platform_Init(tHarness_platform* platform); +static int force_null_platform = 0; -int Harness_ProcessCommandLine(int* argc, char* argv[]); +typedef struct { + const char *platform_name; + uint32_t platform_capabilityies; + int install_signalhandler; +} tArgument_config; + +static int Harness_ProcessCommandLine(tArgument_config* argument_config, int* argc, char* argv[]); static void Harness_DetectGameMode(void) { if (access("DATA/RACES/CASTLE.TXT", F_OK) != -1) { @@ -138,7 +150,7 @@ static void Harness_DetectGameMode(void) { } } -void Harness_Init(int* argc, char* argv[]) { +int Harness_Init(int* argc, char* argv[]) { int result; printf("Dethrace version: %s\n", DETHRACE_VERSION); @@ -169,12 +181,21 @@ void Harness_Init(int* argc, char* argv[]) { harness_game_config.no_bind = 0; // Disable verbose logging harness_game_config.verbose = 0; - // install signal handler by default - harness_game_config.install_signalhandler = 1; - Harness_ProcessCommandLine(argc, argv); + tArgument_config argument_config; + // don't require a particular platform + argument_config.platform_name = NULL; + // request software renderer capability + argument_config.platform_capabilityies = ePlatform_cap_software; + // install signal handler + argument_config.install_signalhandler = 1; - if (harness_game_config.install_signalhandler) { + if (Harness_ProcessCommandLine(&argument_config, argc, argv) != 0) { + fprintf(stderr, "Failed to parse harness command line\n"); + return 1; + } + + if (argument_config.install_signalhandler) { OS_InstallSignalHandler(argv[0]); } @@ -205,8 +226,54 @@ void Harness_Init(int* argc, char* argv[]) { if (force_null_platform) { Null_Platform_Init(&gHarness_platform); } else { - Harness_Platform_Init(&gHarness_platform); + const tPlatform_bootstrap* selected_bootstrap = NULL; + + if (argument_config.platform_name != NULL) { + size_t i; + for (i = 0; i < BR_ASIZE(platform_bootstraps); i++) { + if (strcasecmp(platform_bootstraps[i]->name, argument_config.platform_name) == 0) { + if ((platform_bootstraps[i]->capabilities & argument_config.platform_capabilityies) != argument_config.platform_capabilityies) { + fprintf(stderr, "Platform \"%s\" does not support requested capabilities. Try another video driver and/or add/remove --opengl\n", selected_bootstrap->name); + return 1; + } + selected_bootstrap = platform_bootstraps[i]; + break; + } + } + if (selected_bootstrap == NULL) { + fprintf(stderr, "Could not find a platform named \"%s\"\n", argument_config.platform_name); + return 1; + } + if (selected_bootstrap->init(&gHarness_platform) != 0) { + fprintf(stderr, "%s initialization failed\n", selected_bootstrap->name); + return 1; + } + } else { + size_t i; + for (i = 0; i < BR_ASIZE(platform_bootstraps); i++) { + LOG_TRACE10("Attempting video driver \"%s\"", platform_bootstraps[i]->name); + if ((platform_bootstraps[i]->capabilities & argument_config.platform_capabilityies) != argument_config.platform_capabilityies) { + fprintf(stderr, "Skipping platform \"%s\". Does not support required capabilities.\n", platform_bootstraps[i]->name); + continue; + } + LOG_TRACE10("Try platform \"%s\"..."); + if (platform_bootstraps[i]->init(&gHarness_platform) == 0) { + selected_bootstrap = platform_bootstraps[i]; + break; + } + } + if (selected_bootstrap == NULL) { + fprintf(stderr, "Could not find a supported platform\n"); + return 1; + } + } + if (selected_bootstrap == NULL) { + fprintf(stderr, "Could not find a supported platform\n"); + return 1; + } + LOG_INFO("Platform: %s (%s)", selected_bootstrap->name, selected_bootstrap->description); } + return 0; } // used by unit tests @@ -214,86 +281,94 @@ void Harness_ForceNullPlatform(void) { force_null_platform = 1; } -int Harness_ProcessCommandLine(int* argc, char* argv[]) { - for (int i = 1; i < *argc; i++) { - int handled = 0; +int Harness_ProcessCommandLine(tArgument_config* config, int* argc, char* argv[]) { + for (int i = 1; i < *argc;) { + int consumed = -1; if (strcasecmp(argv[i], "--cdcheck") == 0) { harness_game_config.enable_cd_check = 1; - handled = 1; + consumed = 1; } else if (strstr(argv[i], "--debug=") != NULL) { char* s = strstr(argv[i], "="); harness_debug_level = atoi(s + 1); LOG_INFO("debug level set to %d", harness_debug_level); - handled = 1; + consumed = 1; } else if (strstr(argv[i], "--physics-step-time=") != NULL) { char* s = strstr(argv[i], "="); harness_game_config.physics_step_time = atoi(s + 1); LOG_INFO("Physics step time set to %d", harness_game_config.physics_step_time); - handled = 1; + consumed = 1; } else if (strstr(argv[i], "--fps=") != NULL) { char* s = strstr(argv[i], "="); harness_game_config.fps = atoi(s + 1); LOG_INFO("FPS limiter set to %f", harness_game_config.fps); - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--freeze-timer") == 0) { LOG_INFO("Timer frozen"); harness_game_config.freeze_timer = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--no-signal-handler") == 0) { LOG_INFO("Don't install the signal handler"); - harness_game_config.install_signalhandler = 0; - handled = 1; + config->install_signalhandler = 0; + consumed = 1; } else if (strstr(argv[i], "--demo-timeout=") != NULL) { char* s = strstr(argv[i], "="); harness_game_config.demo_timeout = atoi(s + 1) * 1000; LOG_INFO("Demo timeout set to %d milliseconds", harness_game_config.demo_timeout); - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--i-am-cheating") == 0) { gI_am_cheating = 0xa11ee75d; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--enable-diagnostics") == 0) { harness_game_config.enable_diagnostics = 1; - handled = 1; + consumed = 1; } else if (strstr(argv[i], "--volume-multiplier=") != NULL) { char* s = strstr(argv[i], "="); harness_game_config.volume_multiplier = atof(s + 1); LOG_INFO("Volume multiplier set to %f", harness_game_config.volume_multiplier); - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--full-screen") == 0) { // option left for backwards compatibility harness_game_config.start_full_screen = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--window") == 0) { harness_game_config.start_full_screen = 0; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--gore-check") == 0) { harness_game_config.gore_check = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--sound-options") == 0) { harness_game_config.sound_options = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--no-bind") == 0) { harness_game_config.no_bind = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--opengl") == 0) { + config->platform_capabilityies &= ~ePlatform_cap_video_mask; + config->platform_capabilityies |= ePlatform_cap_opengl; harness_game_config.opengl_3dfx_mode = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--no-music") == 0) { harness_game_config.no_music = 1; - handled = 1; + consumed = 1; } else if (strcasecmp(argv[i], "--game-completed") == 0) { harness_game_config.game_completed = 1; - handled = 1; + consumed = 1; + } else if (strcmp(argv[i], "--platform") == 0) { + if (i < *argc + 1) { + config->platform_name = argv[i + 1]; + consumed = 2; + } } - if (handled) { + if (consumed > 0) { // shift args downwards - for (int j = i; j < *argc - 1; j++) { - argv[j] = argv[j + 1]; + for (int j = i; j < *argc - consumed; j++) { + argv[j] = argv[j + consumed]; } - (*argc)--; - i--; + *argc -= consumed; + } else { + i += 1; } } diff --git a/src/harness/include/harness/config.h b/src/harness/include/harness/config.h index 97bad493..7e62cff1 100644 --- a/src/harness/include/harness/config.h +++ b/src/harness/include/harness/config.h @@ -50,8 +50,6 @@ typedef struct tHarness_game_config { int verbose; int opengl_3dfx_mode; int game_completed; - - int install_signalhandler; } tHarness_game_config; extern tHarness_game_info harness_game_info; diff --git a/src/harness/include/harness/hooks.h b/src/harness/include/harness/hooks.h index e529a58b..8f921221 100644 --- a/src/harness/include/harness/hooks.h +++ b/src/harness/include/harness/hooks.h @@ -38,7 +38,7 @@ typedef struct tHarness_platform { int (*ShowErrorMessage)(void* window, char* text, char* caption); // Create a window. Uses an underscore to avoid name collisions with windows.h `CreateWindow` macro - void (*CreateWindow_)(char* title, int nWidth, int nHeight, tHarness_window_type window_type); + void (*CreateWindow_)(const char* title, int nWidth, int nHeight, tHarness_window_type window_type); void (*Swap)(br_pixelmap* back_buffer); void (*PaletteChanged)(br_colour entries[256]); // If this platform supports OpenGL @@ -47,9 +47,22 @@ typedef struct tHarness_platform { } tHarness_platform; +enum { + ePlatform_cap_software = 0x1, + ePlatform_cap_opengl = 0x2, + ePlatform_cap_video_mask = ePlatform_cap_software | ePlatform_cap_opengl, +}; + +typedef struct tPlatform_bootstrap { + const char *name; + const char *description; + uint32_t capabilities; + int (*init)(tHarness_platform* platform); +} tPlatform_bootstrap; + extern tHarness_platform gHarness_platform; -void Harness_Init(int* argc, char* argv[]); +int Harness_Init(int* argc, char* argv[]); // Hooks are called from original game code. diff --git a/src/harness/platforms/null.c b/src/harness/platforms/null.c index 62254672..f8e2d858 100644 --- a/src/harness/platforms/null.c +++ b/src/harness/platforms/null.c @@ -1,45 +1,64 @@ #include "null.h" -// todo: shouldnt depend on sdl... -#include +static uint32_t null_time; static int null_set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) { + null_time += 1; return 0; } static void null_destroy_window(void* hWnd) { + null_time += 1; } static int null_show_error_message(void* window, char* text, char* caption) { + null_time += 1; return 0; } static void null_get_and_handle_message(MSG_* msg) { + null_time += 1; } static void null_get_keyboard_state(unsigned int count, uint8_t* buffer) { + null_time += 1; } static int null_get_mouse_buttons(int* pButton1, int* pButton2) { + null_time += 1; return 0; } static int null_get_mouse_position(int* pX, int* pY) { + null_time += 1; return 0; } static int null_show_cursor(int show) { + null_time += 1; return 0; } static void null_set_palette(PALETTEENTRY_* palette) { + null_time += 1; +} + +static void null_sleep(uint32_t milliseconds) { + null_time += 1; + null_time += milliseconds; +} + +static uint32_t null_getticks(void) { + null_time += 1; + return null_time; } void Null_Platform_Init(tHarness_platform* platform) { + null_time = 0; platform->ProcessWindowMessages = null_get_and_handle_message; - // todo: shouldnt depend on sdl... - platform->Sleep = SDL_Delay; - platform->GetTicks = SDL_GetTicks; + + platform->Sleep = null_sleep; + platform->GetTicks = null_getticks; platform->ShowCursor = null_show_cursor; platform->SetWindowPos = null_set_window_pos; platform->DestroyWindow = null_destroy_window; diff --git a/src/harness/platforms/sdl1.c b/src/harness/platforms/sdl1.c new file mode 100644 index 00000000..d73101f8 --- /dev/null +++ b/src/harness/platforms/sdl1.c @@ -0,0 +1,315 @@ +#include + +#include "harness.h" +#include "harness/config.h" +#include "harness/hooks.h" +#include "harness/trace.h" +#include "sdl1_scancode_to_dinput.h" +#include "sdl1_syms.h" + +SDL_COMPILE_TIME_ASSERT(sdl1_platform_requires_SDL1, SDL_MAJOR_VERSION == 1); + +static SDL_Surface* screen; +static uint32_t converted_palette[256]; +static br_pixelmap* last_screen_src; + +static Uint32 last_frame_time; + +static uint8_t directinput_key_state[SDLK_LAST]; + +static struct { + int x, y; + float scale_x, scale_y; +} viewport; + +// Callbacks back into original game code +extern void QuitGame(void); +extern uint32_t gKeyboard_bits[8]; +extern br_pixelmap* gBack_screen; +static int window_width, window_height; + +#ifdef DETHRACE_SDL_DYNAMIC +#ifdef _WIN32 +static const char * const possible_locations[] = { + "SDL.dll", +}; +#elif defined(__APPLE__) +#define SHARED_OBJECT_NAME "libSDL" +#define SDL1_LIBNAME "libSDL.dylib" +static const char * const possible_locations[] = { + "@loader_path/" SDL1_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */ + "@executable_path/" SDL1_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */ + SDL1_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */ +}; +#else +static const char * const possible_locations[] = { + "libSDL-1.2.so.0", + "libSDL-1.2.so", +}; +#endif +#endif + +#ifdef DETHRACE_SDL_DYNAMIC +static void *sdl1_so; +#endif + +#define OBJECT_NAME sdl1_so +#define SYMBOL_PREFIX SDL1_ +#define FOREACH_SDLX_SYM FOREACH_SDL1_SYM + +#include "sdl_dyn_common.h" + +static void calculate_viewport(int window_width, int window_height) { + int vp_width, vp_height; + float target_aspect_ratio; + float aspect_ratio; + + aspect_ratio = (float)window_width / window_height; + target_aspect_ratio = (float)gBack_screen->width / gBack_screen->height; + + vp_width = window_width; + vp_height = window_height; + if (aspect_ratio != target_aspect_ratio) { + if (aspect_ratio > target_aspect_ratio) { + vp_width = window_height * target_aspect_ratio + .5f; + } else { + vp_height = window_width / target_aspect_ratio + .5f; + } + } + viewport.x = (window_width - vp_width) / 2; + viewport.y = (window_height - vp_height) / 2; + viewport.scale_x = (float)vp_width / gBack_screen->width; + viewport.scale_y = (float)vp_height / gBack_screen->height; +} + +static void recreate_screen(void) { + int video_flags = SDL_RESIZABLE | SDL_HWSURFACE | SDL_DOUBLEBUF; + + if (harness_game_config.start_full_screen) { + video_flags |= SDL_FULLSCREEN; + } + screen = SDL1_SetVideoMode(window_width, window_height, 32, video_flags); + if (screen == NULL) { + LOG_PANIC("SDL_SetVideoMode failed (%s)", SDL1_GetError()); + } +} + +static void SDL1_Harness_CreateWindow(const char* title, int width, int height, tHarness_window_type window_type) { + window_width = width; + window_height = height; + + initializeSDL1KeyNums(); + + if (SDL1_Init(SDL_INIT_VIDEO) != 0) { + LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL1_GetError()); + } + + if (window_type == eWindow_type_software) { + recreate_screen(); + } else { + LOG_PANIC("Unsupported window type (%d)", window_type); + } + + SDL1_WM_SetCaption("Carmageddon", NULL); + + SDL1_ShowCursor(SDL_DISABLE); +} + +static int SDL1_Harness_SetWindowPos(void* hWnd, int x, int y, int nWidth, int nHeight) { + // SDL_SetWindowPosition(hWnd, x, y); + if (nWidth == 320 && nHeight == 200) { + nWidth = 640; + nHeight = 400; + } + window_width = nWidth; + window_height = nHeight; + recreate_screen(); + SDL1_UpdateRect(hWnd, x, y, nWidth, nHeight); + return 0; +} + +static void SDL1_Harness_DestroyWindow(void* hWnd) { + SDL1_FreeSurface(screen); + SDL1_Quit(); + screen = NULL; +} + +// Checks whether the `flag_check` is the only modifier applied. +// e.g. is_only_modifier(event.key.keysym.mod, KMOD_ALT) returns true when only the ALT key was pressed +static int is_only_key_modifier(int modifier_flags, int flag_check) { + return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_META)) == (modifier_flags & flag_check); +} + +static void SDL1_Harness_ProcessWindowMessages(MSG_* msg) { + SDL_Event event; + int dinput_key; + + while (SDL1_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (event.key.keysym.sym == SDLK_RETURN) { + if (event.key.type == SDL_KEYDOWN) { + if ((event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_META))) { + // Ignore keydown of RETURN when used together with some modifier + return; + } + } else if (event.key.type == SDL_KEYUP) { + if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) { + SDL1_WM_ToggleFullScreen(screen); + } + } + } + + // Map incoming SDL scancode to DirectInput DIK_* key code. + // https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h + dinput_key = sdl1KeyToDirectInputKeyNum[event.key.keysym.sym]; + if (dinput_key == 0) { + LOG_WARN("unexpected key \"%s\" (0x%x)", SDL1_GetKeyName(event.key.keysym.sym), event.key.keysym.sym); + return; + } + // DInput expects high bit to be set if key is down + // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418261(v=vs.85) + directinput_key_state[dinput_key] = (event.type == SDL_KEYDOWN ? 0x80 : 0); + if (event.type == SDL_KEYDOWN) { + gKeyboard_bits[dinput_key >> 5] |= (1 << (dinput_key & 0x1F)); + } else { + gKeyboard_bits[dinput_key >> 5] &= ~(1 << (dinput_key & 0x1F)); + } + break; + + case SDL_VIDEORESIZE: + calculate_viewport(event.resize.w, event.resize.h); + break; + + case SDL_QUIT: + QuitGame(); + return; + } + } + return; +} + +static void get_keyboard_state(unsigned int count, uint8_t* buffer) { + memcpy(buffer, directinput_key_state, count); +} + +static int get_mouse_buttons(int* pButton1, int* pButton2) { + int state = SDL1_GetMouseState(NULL, NULL); + *pButton1 = state & SDL_BUTTON_LMASK; + *pButton2 = state & SDL_BUTTON_RMASK; + return 0; +} + +static int get_mouse_position(int* pX, int* pY) { + SDL1_GetMouseState(pX, pY); + return 0; +} + +static void limit_fps(void) { + Uint32 now = SDL1_GetTicks(); + if (last_frame_time != 0) { + unsigned int frame_time = now - last_frame_time; + last_frame_time = now; + if (frame_time < 100) { + int sleep_time = (1000 / harness_game_config.fps) - frame_time; + if (sleep_time > 5) { + gHarness_platform.Sleep(sleep_time); + } + } + } + last_frame_time = SDL1_GetTicks(); +} + +static void SDL1_Renderer_Present(br_pixelmap* src) { + // fastest way to convert 8 bit indexed to 32 bit + uint8_t* src_pixels = src->pixels; + uint32_t* dest_pixels; + + SDL1_LockSurface(screen); + dest_pixels = screen->pixels; + for (int i = 0; i < src->height * src->width; i++) { + *dest_pixels = converted_palette[*src_pixels]; + dest_pixels++; + src_pixels++; + } + SDL1_UnlockSurface(screen); + + SDL1_Flip(screen); + + last_screen_src = src; + + if (harness_game_config.fps != 0) { + limit_fps(); + } +} + +static void SDL1_Harness_Swap(br_pixelmap* back_buffer) { + + SDL1_Harness_ProcessWindowMessages(NULL); + + if (0) { + SDL1_GL_SwapBuffers(); + } else { + SDL1_Renderer_Present(back_buffer); + + if (harness_game_config.fps != 0) { + limit_fps(); + } + } +} + +static void SDL1_Harness_PaletteChanged(br_colour entries[256]) { + for (int i = 0; i < 256; i++) { + converted_palette[i] = (0xff << 24 | BR_RED(entries[i]) << 16 | BR_GRN(entries[i]) << 8 | BR_BLU(entries[i])); + } + if (last_screen_src != NULL) { + SDL1_Harness_Swap(last_screen_src); + } +} + +static int SDL1_Harness_ShowErrorMessage(void* window, char* text, char* caption) { + fprintf(stderr, "%s", text); +#ifdef _WIN32 + MessageBoxA(NULL, text, caption, MB_ICONERROR); +#endif + return 0; +} + +static void SDL1_Harness_GetViewport(int* x, int* y, float* width_multipler, float* height_multiplier) { + *x = viewport.x; + *y = viewport.y; + *width_multipler = viewport.scale_x; + *height_multiplier = viewport.scale_y; +} + +static int SDL1_Harness_Platform_Init(tHarness_platform* platform) { + if (SDL1_LoadSymbols() != 0) { + return 1; + } + platform->ProcessWindowMessages = SDL1_Harness_ProcessWindowMessages; + platform->Sleep = SDL1_Delay; + platform->GetTicks = SDL1_GetTicks; + platform->ShowCursor = SDL1_ShowCursor; + platform->SetWindowPos = SDL1_Harness_SetWindowPos; + platform->DestroyWindow = SDL1_Harness_DestroyWindow; + platform->GetKeyboardState = get_keyboard_state; + platform->GetMousePosition = get_mouse_position; + platform->GetMouseButtons = get_mouse_buttons; + platform->ShowErrorMessage = SDL1_Harness_ShowErrorMessage; + + platform->CreateWindow_ = SDL1_Harness_CreateWindow; + platform->Swap = SDL1_Harness_Swap; + platform->PaletteChanged = SDL1_Harness_PaletteChanged; + platform->GL_GetProcAddress = SDL1_GL_GetProcAddress; + platform->GetViewport = SDL1_Harness_GetViewport; + return 0; +} + +const tPlatform_bootstrap SDL1_bootstrap = { + "sdl1", + "SDL1 video backend (libsdl.org)", + ePlatform_cap_software, + SDL1_Harness_Platform_Init, +}; + diff --git a/src/harness/platforms/sdl1_scancode_to_dinput.h b/src/harness/platforms/sdl1_scancode_to_dinput.h new file mode 100644 index 00000000..676bc251 --- /dev/null +++ b/src/harness/platforms/sdl1_scancode_to_dinput.h @@ -0,0 +1,208 @@ +/* + * Maps SDL1 scancodes to directinput keynums/scancodes. + * Useful if you're porting a game that uses dinput scancodes internally + * (for key bindings etc) or any other lib (like CEGUI) that uses them. + * + * (C) 2015 Daniel Gibson + * + * Homepage: https://github.com/DanielGibson/Snippets/ + * + * License: + * This software is dual-licensed to the public domain and under the following + * license: you are granted a perpetual, irrevocable license to copy, modify, + * publish, and distribute this file as you see fit. + * No warranty implied; use at your own risk. + * + * So you can do whatever you want with this code, including copying it + * (or parts of it) into your own source. + * No need to mention me or this "license" in your code or docs, even though + * it would be appreciated, of course. + * + */ + +#include + +#if 0 // Usage Example: +#include "sdl2_scancode_to_dinput.h" + static int SDLScanCodeToKeyNum(SDL_Scancode sc) + { + int idx = (int)sc; + assert(idx >= 0 && idx < SDL_NUM_SCANCODES); + return scanCodeToKeyNum[idx]; + } + + static SDL_Scancode KeyNumToSDLScanCode( int keyNum ) + { + if( keyNum >= 0 && keyNum < 0xEF ) + { + for(int i = 0; i < SDL_NUM_SCANCODES; ++i) + { + if(scanCodeToKeyNum[i] == keyNum) return (SDL_Scancode)i; + } + } + return SDL_SCANCODE_UNKNOWN; + } +#endif // 0 + +#ifndef _SDL1_SCANCODE_TO_DINPUT_H_ +#define _SDL1_SCANCODE_TO_DINPUT_H_ + +// TODO: map the following keys, if possible: +// #define DIK_UNDERLINE 0x93 /* (NEC PC98) */ +// #define DIK_KANJI 0x94 /* (Japanese keyboard) */ +// #define DIK_AX 0x96 /* (Japan AX) */ +// #define DIK_UNLABELED 0x97 /* (J3100) */ +// +// #define DIK_WAKE 0xE3 /* System Wake */ +// +// (#define DIK_ABNT_C2 0x7E /* Numpad . on Brazilian keyboard */ - system should map this to KP_COMMA or something, +// according to USB doc, so probably it doesn't need mapping here) + +// maps SDL1 keysyms to directinput keynums/scancodes - dinput_key = sdlScanCodeToDirectInputKeyNum[(int)your_sdl2_scancode]; +static int sdl1KeyToDirectInputKeyNum[SDLK_LAST]; + +static void initializeSDL1KeyNums(void) { + memset(sdl1KeyToDirectInputKeyNum, 0, sizeof(sdl1KeyToDirectInputKeyNum)); + sdl1KeyToDirectInputKeyNum[SDLK_a] = 0x1E; /* DIK_A */ + sdl1KeyToDirectInputKeyNum[SDLK_b] = 0x30; /* DIK_B */ + sdl1KeyToDirectInputKeyNum[SDLK_c] = 0x2E; /* DIK_C */ + sdl1KeyToDirectInputKeyNum[SDLK_d] = 0x20; /* DIK_D */ + sdl1KeyToDirectInputKeyNum[SDLK_e] = 0x12; /* DIK_E */ + sdl1KeyToDirectInputKeyNum[SDLK_f] = 0x21; /* DIK_F */ + sdl1KeyToDirectInputKeyNum[SDLK_g] = 0x22; /* DIK_G */ + sdl1KeyToDirectInputKeyNum[SDLK_h] = 0x23; /* DIK_H */ + sdl1KeyToDirectInputKeyNum[SDLK_i] = 0x17; /* DIK_I */ + sdl1KeyToDirectInputKeyNum[SDLK_j] = 0x24; /* DIK_J */ + sdl1KeyToDirectInputKeyNum[SDLK_k] = 0x25; /* DIK_K */ + sdl1KeyToDirectInputKeyNum[SDLK_l] = 0x26; /* DIK_L */ + sdl1KeyToDirectInputKeyNum[SDLK_m] = 0x32; /* DIK_M */ + sdl1KeyToDirectInputKeyNum[SDLK_n] = 0x31; /* DIK_N */ + sdl1KeyToDirectInputKeyNum[SDLK_o] = 0x18; /* DIK_O */ + sdl1KeyToDirectInputKeyNum[SDLK_p] = 0x19; /* DIK_P */ + sdl1KeyToDirectInputKeyNum[SDLK_q] = 0x10; /* DIK_Q */ + sdl1KeyToDirectInputKeyNum[SDLK_r] = 0x13; /* DIK_R */ + sdl1KeyToDirectInputKeyNum[SDLK_s] = 0x1F; /* DIK_S */ + sdl1KeyToDirectInputKeyNum[SDLK_t] = 0x14; /* DIK_T */ + sdl1KeyToDirectInputKeyNum[SDLK_u] = 0x16; /* DIK_U */ + sdl1KeyToDirectInputKeyNum[SDLK_v] = 0x2F; /* DIK_V */ + sdl1KeyToDirectInputKeyNum[SDLK_w] = 0x11; /* DIK_W */ + sdl1KeyToDirectInputKeyNum[SDLK_x] = 0x2D; /* DIK_X */ + sdl1KeyToDirectInputKeyNum[SDLK_y] = 0x15; /* DIK_Y */ + sdl1KeyToDirectInputKeyNum[SDLK_z] = 0x2C; /* DIK_Z */ + sdl1KeyToDirectInputKeyNum[SDLK_0] = 0x2C; /* DIK_Z */ + sdl1KeyToDirectInputKeyNum[SDLK_1] = 0x02; /* DIK_1 */ + sdl1KeyToDirectInputKeyNum[SDLK_2] = 0x03; /* DIK_2 */ + sdl1KeyToDirectInputKeyNum[SDLK_3] = 0x04; /* DIK_3 */ + sdl1KeyToDirectInputKeyNum[SDLK_4] = 0x05; /* DIK_4 */ + sdl1KeyToDirectInputKeyNum[SDLK_5] = 0x06; /* DIK_5 */ + sdl1KeyToDirectInputKeyNum[SDLK_6] = 0x07; /* DIK_6 */ + sdl1KeyToDirectInputKeyNum[SDLK_7] = 0x08; /* DIK_7 */ + sdl1KeyToDirectInputKeyNum[SDLK_8] = 0x09; /* DIK_8 */ + sdl1KeyToDirectInputKeyNum[SDLK_9] = 0x0A; /* DIK_9 */ + sdl1KeyToDirectInputKeyNum[SDLK_0] = 0x0B; /* DIK_0 */ + + sdl1KeyToDirectInputKeyNum[SDLK_KP1] = 0x4F; /* DIK_NUMPAD1 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP2] = 0x50; /* DIK_NUMPAD2 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP3] = 0x51; /* DIK_NUMPAD3 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP4] = 0x4B; /* DIK_NUMPAD4 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP5] = 0x4C; /* DIK_NUMPAD5 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP6] = 0x4D; /* DIK_NUMPAD6 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP7] = 0x47; /* DIK_NUMPAD7 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP8] = 0x48; /* DIK_NUMPAD8 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP9] = 0x49; /* DIK_NUMPAD9 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP0] = 0x52; /* DIK_NUMPAD0 */ + + sdl1KeyToDirectInputKeyNum[SDLK_RETURN] = 0x1C; /* DIK_RETURN */ + sdl1KeyToDirectInputKeyNum[SDLK_ESCAPE] = 0x01; /* DIK_ESCAPE */ + sdl1KeyToDirectInputKeyNum[SDLK_BACKSPACE] = 0x0E; /* DIK_BACK */ + sdl1KeyToDirectInputKeyNum[SDLK_TAB] = 0x0F; /* DIK_TAB */ + sdl1KeyToDirectInputKeyNum[SDLK_SPACE] = 0x39; /* DIK_SPACE */ + + sdl1KeyToDirectInputKeyNum[SDLK_MINUS] = 0x0C; /* DIK_MINUS */ + sdl1KeyToDirectInputKeyNum[SDLK_EQUALS] = 0x0D; /* DIK_EQUALS */ + sdl1KeyToDirectInputKeyNum[SDLK_LEFTBRACKET] = 0x1A; /* DIK_LBRACKET */ + sdl1KeyToDirectInputKeyNum[SDLK_RIGHTBRACKET] = 0x1B; /* DIK_RBRACKET */ + sdl1KeyToDirectInputKeyNum[SDLK_BACKSLASH] = 0x2B; /* DIK_BACKSLASH */ + sdl1KeyToDirectInputKeyNum[SDLK_HASH] = 0x2B; /* DIK_BACKSLASH */ + sdl1KeyToDirectInputKeyNum[SDLK_SEMICOLON] = 0x27; /* DIK_SEMICOLON */ + sdl1KeyToDirectInputKeyNum[SDLK_QUOTE] = 0x28; /* DIK_APOSTROPHE */ + sdl1KeyToDirectInputKeyNum[SDLK_BACKQUOTE] = 0x29; /* DIK_GRAVE */ + sdl1KeyToDirectInputKeyNum[SDLK_COMMA] = 0x33; /* DIK_COMMA */ + sdl1KeyToDirectInputKeyNum[SDLK_PERIOD] = 0x34; /* DIK_PERIOD */ + sdl1KeyToDirectInputKeyNum[SDLK_SLASH] = 0x35; /* DIK_SLASH */ + + sdl1KeyToDirectInputKeyNum[SDLK_CAPSLOCK] = 0x3A; /* DIK_CAPITAL */ + + sdl1KeyToDirectInputKeyNum[SDLK_F1] = 0x3A; /* DIK_CAPITAL */ + + sdl1KeyToDirectInputKeyNum[SDLK_F1] = 0x3B; /* DIK_F1 */ + sdl1KeyToDirectInputKeyNum[SDLK_F2] = 0x3C; /* DIK_F2 */ + sdl1KeyToDirectInputKeyNum[SDLK_F3] = 0x3D; /* DIK_F3 */ + sdl1KeyToDirectInputKeyNum[SDLK_F4] = 0x3E; /* DIK_F4 */ + sdl1KeyToDirectInputKeyNum[SDLK_F5] = 0x3F; /* DIK_F5 */ + sdl1KeyToDirectInputKeyNum[SDLK_F6] = 0x40; /* DIK_F6 */ + sdl1KeyToDirectInputKeyNum[SDLK_F7] = 0x41; /* DIK_F7 */ + sdl1KeyToDirectInputKeyNum[SDLK_F8] = 0x42; /* DIK_F8 */ + sdl1KeyToDirectInputKeyNum[SDLK_F9] = 0x43; /* DIK_F9 */ + sdl1KeyToDirectInputKeyNum[SDLK_F10] = 0x44; /* DIK_F10 */ + sdl1KeyToDirectInputKeyNum[SDLK_F11] = 0x57; /* DIK_F11 */ + sdl1KeyToDirectInputKeyNum[SDLK_F12] = 0x58; /* DIK_F12 */ + + sdl1KeyToDirectInputKeyNum[SDLK_PRINT] = 0xB7; /* DIK_F12 */ + + sdl1KeyToDirectInputKeyNum[SDLK_SCROLLOCK] = 0x46; /* DIK_SCROLL */ + sdl1KeyToDirectInputKeyNum[SDLK_PAUSE] = 0xC5; /* DIK_PAUSE */ + sdl1KeyToDirectInputKeyNum[SDLK_INSERT] = 0xD2; /* DIK_INSERT */ + sdl1KeyToDirectInputKeyNum[SDLK_HOME] = 0xC7; /* DIK_HOME */ + sdl1KeyToDirectInputKeyNum[SDLK_PAGEUP] = 0xC9; /* DIK_PRIOR */ + sdl1KeyToDirectInputKeyNum[SDLK_DELETE] = 0xD3; /* DIK_DELETE */ + sdl1KeyToDirectInputKeyNum[SDLK_END] = 0xCF; /* DIK_END */ + sdl1KeyToDirectInputKeyNum[SDLK_PAGEDOWN] = 0xD1; /* DIK_NEXT */ + sdl1KeyToDirectInputKeyNum[SDLK_RIGHT] = 0xCD; /* DIK_RIGHT */ + sdl1KeyToDirectInputKeyNum[SDLK_LEFT] = 0xCB; /* DIK_LEFT */ + sdl1KeyToDirectInputKeyNum[SDLK_DOWN] = 0xD0; /* DIK_DOWN */ + sdl1KeyToDirectInputKeyNum[SDLK_UP] = 0xC8; /* DIK_UP */ + + + sdl1KeyToDirectInputKeyNum[SDLK_LCTRL] = 0x1D; /* DIK_LCONTROL */ + sdl1KeyToDirectInputKeyNum[SDLK_LSHIFT] = 0x2A; /* DIK_LSHIFT */ + sdl1KeyToDirectInputKeyNum[SDLK_LALT] = 0x38; /* DIK_LMENU */ + sdl1KeyToDirectInputKeyNum[SDLK_LMETA] = 0xDB; /* DIK_LWIN */ + sdl1KeyToDirectInputKeyNum[SDLK_RCTRL] = 0x9D; /* DIK_RCONTROL */ + sdl1KeyToDirectInputKeyNum[SDLK_RSHIFT] = 0x36; /* DIK_RSHIFT */ + sdl1KeyToDirectInputKeyNum[SDLK_RALT] = 0xB8; /* DIK_RMENU */ + sdl1KeyToDirectInputKeyNum[SDLK_RMETA] = 0xDC; /* DIK_RWIN */ + + sdl1KeyToDirectInputKeyNum[SDLK_NUMLOCK] = 0x45; /* DIK_NUMLOCK */ + + sdl1KeyToDirectInputKeyNum[SDLK_KP_DIVIDE] = 0xB5; /* DIK_DIVIDE */ + sdl1KeyToDirectInputKeyNum[SDLK_KP_MULTIPLY] = 0x37; /* DIK_MULTIPLY */ + sdl1KeyToDirectInputKeyNum[SDLK_KP_MINUS] = 0x4A; /* DIK_SUBTRACT */ + sdl1KeyToDirectInputKeyNum[SDLK_KP_PLUS] = 0x4E; /* DIK_ADD */ + sdl1KeyToDirectInputKeyNum[SDLK_KP_ENTER] = 0x9C; /* DIK_NUMPADENTER */ + sdl1KeyToDirectInputKeyNum[SDLK_KP1] = 0x4F; /* DIK_NUMPAD1 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP2] = 0x50; /* DIK_NUMPAD2 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP3] = 0x51; /* DIK_NUMPAD3 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP4] = 0x4B; /* DIK_NUMPAD4 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP5] = 0x4C; /* DIK_NUMPAD5 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP6] = 0x4D; /* DIK_NUMPAD6 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP7] = 0x47; /* DIK_NUMPAD7 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP8] = 0x48; /* DIK_NUMPAD8 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP9] = 0x49; /* DIK_NUMPAD9 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP0] = 0x52; /* DIK_NUMPAD0 */ + sdl1KeyToDirectInputKeyNum[SDLK_KP_PERIOD] = 0x53; /* DIK_DECIMAL */ + + /* 0x56 - DIK_OEM_102 */ + /* 0xDD - DIK_APPS */ + sdl1KeyToDirectInputKeyNum[SDLK_POWER] = 0xDE; /* DIK_POWER */ + + sdl1KeyToDirectInputKeyNum[SDLK_KP_EQUALS] = 0x8D; /* DIK_NUMPADEQUALS */ + + sdl1KeyToDirectInputKeyNum[SDLK_F13] = 0x8D; /* DIK_F13 */ + sdl1KeyToDirectInputKeyNum[SDLK_F14] = 0x64; /* DIK_F14 */ + sdl1KeyToDirectInputKeyNum[SDLK_F15] = 0x65; /* DIK_F15 */ + + sdl1KeyToDirectInputKeyNum[SDLK_SYSREQ] = 0xB7; /* SDL_SCANCODE_SYSREQ */ +} + +#endif /* _SDL1_SCANCODE_TO_DINPUT_H_ */ diff --git a/src/harness/platforms/sdl1_syms.h b/src/harness/platforms/sdl1_syms.h new file mode 100644 index 00000000..f69a1ff4 --- /dev/null +++ b/src/harness/platforms/sdl1_syms.h @@ -0,0 +1,27 @@ +#ifndef sdl1_syms_h +#define sdl1_syms_h + +#include + +#define FOREACH_SDL1_SYM(X) \ + X(Init, int, (Uint32)) \ + X(Quit, void, (void)) \ + X(Delay, void, (Uint32)) \ + X(GetTicks, Uint32, (void)) \ + X(GetError, char*, (void)) \ + X(PollEvent, int, (SDL_Event*)) \ + X(SetVideoMode, SDL_Surface*, (int, int, int, Uint32)) \ + X(FreeSurface, void, (SDL_Surface*)) \ + X(LockSurface, int, (SDL_Surface*)) \ + X(UnlockSurface, void, (SDL_Surface*)) \ + X(UpdateRect, void, (SDL_Surface*, Sint32, Sint32, Sint32, Sint32)) \ + X(WM_SetCaption, void, (const char*, const char*)) \ + X(WM_ToggleFullScreen, int, (SDL_Surface*)) \ + X(Flip, int, (SDL_Surface*)) \ + X(ShowCursor, int, (int)) \ + X(GetMouseState, Uint8, (int*, int*)) \ + X(GetKeyName, char*, (SDLKey)) \ + X(GL_GetProcAddress, void*, (const char*)) \ + X(GL_SwapBuffers, void, (void)) + +#endif /* sdl1_syms_h */ diff --git a/src/harness/platforms/sdl2.c b/src/harness/platforms/sdl2.c index 01468cd1..cb73f652 100644 --- a/src/harness/platforms/sdl2.c +++ b/src/harness/platforms/sdl2.c @@ -5,22 +5,25 @@ #include "harness/hooks.h" #include "harness/trace.h" #include "sdl2_scancode_to_dinput.h" +#include "sdl2_syms.h" -SDL_Window* window; -SDL_Renderer* renderer; -SDL_Texture* screen_texture; -uint32_t converted_palette[256]; -br_pixelmap* last_screen_src; +SDL_COMPILE_TIME_ASSERT(sdl2_platform_requires_SDL2, SDL_MAJOR_VERSION == 2); -SDL_GLContext* gl_context; +static SDL_Window* window; +static SDL_Renderer* renderer; +static SDL_Texture* screen_texture; +static uint32_t converted_palette[256]; +static br_pixelmap* last_screen_src; -int render_width, render_height; +static SDL_GLContext* gl_context; -Uint32 last_frame_time; +static int render_width, render_height; -uint8_t directinput_key_state[SDL_NUM_SCANCODES]; +static Uint32 last_frame_time; -struct { +static uint8_t directinput_key_state[SDL_NUM_SCANCODES]; + +static struct { int x, y; float scale_x, scale_y; } viewport; @@ -30,6 +33,42 @@ extern void QuitGame(void); extern uint32_t gKeyboard_bits[8]; extern br_pixelmap* gBack_screen; +#ifdef DETHRACE_SDL_DYNAMIC +#ifdef _WIN32 +static const char * const possible_locations[] = { + "SDL2.dll", +}; +#elif defined(__APPLE__) +#define SHARED_OBJECT_NAME "libSDL2" +#define SDL2_LIBNAME "libSDL2.dylib" +#define SDL2_FRAMEWORK "SDL2.framework/Versions/A/SDL2" +static const char * const possible_locations[] = { + "@loader_path/" SDL2_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */ + "@loader_path/../Frameworks/" SDL2_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2_framework */ + "@executable_path/" SDL2_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL2_dylib */ + "@executable_path/../Frameworks/" SDL2_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL2_framework */ + NULL, /* /Users/username/Library/Frameworks/SDL2_framework */ + "/Library/Frameworks" SDL2_FRAMEWORK, /* /Library/Frameworks/SDL2_framework */ + SDL2_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */ +}; +#else +static const char * const possible_locations[] = { + "libSDL2-2.0.so.0", + "libSDL2-2.0.so", +}; +#endif +#endif + +#ifdef DETHRACE_SDL_DYNAMIC +static void *sdl2_so; +#endif + +#define OBJECT_NAME sdl2_so +#define SYMBOL_PREFIX SDL2_ +#define FOREACH_SDLX_SYM FOREACH_SDL2_SYM + +#include "sdl_dyn_common.h" + static void calculate_viewport(int window_width, int window_height) { int vp_width, vp_height; float target_aspect_ratio; @@ -53,20 +92,20 @@ static void calculate_viewport(int window_width, int window_height) { viewport.scale_y = (float)vp_height / gBack_screen->height; } -static int set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) { +static int SDL2_Harness_SetWindowPos(void* hWnd, int x, int y, int nWidth, int nHeight) { // SDL_SetWindowPosition(hWnd, x, y); if (nWidth == 320 && nHeight == 200) { nWidth = 640; nHeight = 400; } - SDL_SetWindowSize(hWnd, nWidth, nHeight); + SDL2_SetWindowSize(hWnd, nWidth, nHeight); return 0; } -static void destroy_window(void* hWnd) { - // SDL_GL_DeleteContext(context); - SDL_DestroyWindow(window); - SDL_Quit(); +static void SDL2_Harness_DestroyWindow(void* hWnd) { + // SDL2_GL_DeleteContext(context); + SDL2_DestroyWindow(window); + SDL2_Quit(); window = NULL; } @@ -76,15 +115,15 @@ static int is_only_key_modifier(int modifier_flags, int flag_check) { return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == (modifier_flags & flag_check); } -static void get_and_handle_message(MSG_* msg) { +static void SDL2_Harness_ProcessWindowMessages(MSG_* msg) { SDL_Event event; int dinput_key; - while (SDL_PollEvent(&event)) { + while (SDL2_PollEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: case SDL_KEYUP: - if (event.key.windowID != SDL_GetWindowID(window)) { + if (event.key.windowID != SDL2_GetWindowID(window)) { continue; } if (event.key.keysym.sym == SDLK_RETURN) { @@ -95,7 +134,7 @@ static void get_and_handle_message(MSG_* msg) { } } else if (event.key.type == SDL_KEYUP) { if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) { - SDL_SetWindowFullscreen(window, (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); + SDL2_SetWindowFullscreen(window, (SDL2_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); } } } @@ -104,7 +143,7 @@ static void get_and_handle_message(MSG_* msg) { // https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h dinput_key = sdlScanCodeToDirectInputKeyNum[event.key.keysym.scancode]; if (dinput_key == 0) { - LOG_WARN("unexpected scan code %s (%d)", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode); + LOG_WARN("unexpected scan code %s (%d)", SDL2_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode); return; } // DInput expects high bit to be set if key is down @@ -129,35 +168,35 @@ static void get_and_handle_message(MSG_* msg) { } } -static void get_keyboard_state(unsigned int count, uint8_t* buffer) { +static void SDL2_Harness_GetKeyboardState(unsigned int count, uint8_t* buffer) { memcpy(buffer, directinput_key_state, count); } -static int get_mouse_buttons(int* pButton1, int* pButton2) { - if (SDL_GetMouseFocus() != window) { +static int SDL2_Harness_GetMouseButtons(int* pButton1, int* pButton2) { + if (SDL2_GetMouseFocus() != window) { *pButton1 = 0; *pButton2 = 0; return 0; } - int state = SDL_GetMouseState(NULL, NULL); + int state = SDL2_GetMouseState(NULL, NULL); *pButton1 = state & SDL_BUTTON_LMASK; *pButton2 = state & SDL_BUTTON_RMASK; return 0; } -static int get_mouse_position(int* pX, int* pY) { +static int SDL2_Harness_GetMousePosition(int* pX, int* pY) { int window_width, window_height; float lX, lY; - if (SDL_GetMouseFocus() != window) { + if (SDL2_GetMouseFocus() != window) { return 0; } - SDL_GetWindowSize(window, &window_width, &window_height); + SDL2_GetWindowSize(window, &window_width, &window_height); - SDL_GetMouseState(pX, pY); + SDL2_GetMouseState(pX, pY); if (renderer != NULL) { // software renderer - SDL_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY); + SDL2_RenderWindowToLogical(renderer, *pX, *pY, &lX, &lY); } else { // hardware renderer // handle case where window is stretched larger than the pixel size @@ -170,7 +209,7 @@ static int get_mouse_position(int* pX, int* pY) { } static void limit_fps(void) { - Uint32 now = SDL_GetTicks(); + Uint32 now = SDL2_GetTicks(); if (last_frame_time != 0) { unsigned int frame_time = now - last_frame_time; last_frame_time = now; @@ -181,16 +220,16 @@ static void limit_fps(void) { } } } - last_frame_time = SDL_GetTicks(); + last_frame_time = SDL2_GetTicks(); } -int show_error_message(void* window, char* text, char* caption) { +static int SDL2_Harness_ShowErrorMessage(void* window, char* text, char* caption) { fprintf(stderr, "%s", text); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window); + SDL2_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window); return 0; } -static void create_window(char* title, int width, int height, tHarness_window_type window_type) { +static void SDL2_Harness_CreateWindow(const char* title, int width, int height, tHarness_window_type window_type) { int window_width, window_height; render_width = width; @@ -205,68 +244,68 @@ static void create_window(char* title, int width, int height, tHarness_window_ty window_height = 480; } - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError()); + if (SDL2_Init(SDL_INIT_VIDEO) != 0) { + LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL2_GetError()); } if (window_type == eWindow_type_opengl) { - window = SDL_CreateWindow(title, + window = SDL2_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (window == NULL) { - LOG_PANIC("Failed to create window: %s", SDL_GetError()); + LOG_PANIC("Failed to create window: %s", SDL2_GetError()); } - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - gl_context = SDL_GL_CreateContext(window); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + gl_context = SDL2_GL_CreateContext(window); if (gl_context == NULL) { - LOG_WARN("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL_GetError()); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - gl_context = SDL_GL_CreateContext(window); + LOG_WARN("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL2_GetError()); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL2_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + gl_context = SDL2_GL_CreateContext(window); } if (gl_context == NULL) { - LOG_PANIC("Failed to create OpenGL context: %s", SDL_GetError()); + LOG_PANIC("Failed to create OpenGL context: %s", SDL2_GetError()); } - SDL_GL_SetSwapInterval(1); + SDL2_GL_SetSwapInterval(1); } else { - window = SDL_CreateWindow(title, + window = SDL2_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, SDL_WINDOW_RESIZABLE); if (window == NULL) { - LOG_PANIC("Failed to create window: %s", SDL_GetError()); + LOG_PANIC("Failed to create window: %s", SDL2_GetError()); } - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); + renderer = SDL2_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); if (renderer == NULL) { - LOG_PANIC("Failed to create renderer: %s", SDL_GetError()); + LOG_PANIC("Failed to create renderer: %s", SDL2_GetError()); } - SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); - SDL_RenderSetLogicalSize(renderer, render_width, render_height); + SDL2_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + SDL2_RenderSetLogicalSize(renderer, render_width, render_height); - screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + screen_texture = SDL2_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); if (screen_texture == NULL) { SDL_RendererInfo info; - SDL_GetRendererInfo(renderer, &info); + SDL2_GetRendererInfo(renderer, &info); for (Uint32 i = 0; i < info.num_texture_formats; i++) { - LOG_INFO("%s\n", SDL_GetPixelFormatName(info.texture_formats[i])); + LOG_INFO("%s\n", SDL2_GetPixelFormatName(info.texture_formats[i])); } - LOG_PANIC("Failed to create screen_texture: %s", SDL_GetError()); + LOG_PANIC("Failed to create screen_texture: %s", SDL2_GetError()); } } - SDL_ShowCursor(SDL_DISABLE); + SDL2_ShowCursor(SDL_DISABLE); viewport.x = 0; viewport.y = 0; @@ -274,30 +313,31 @@ static void create_window(char* title, int width, int height, tHarness_window_ty viewport.scale_y = 1; if (harness_game_config.start_full_screen) { - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + SDL2_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } } -static void swap(br_pixelmap* back_buffer) { - uint8_t* src_pixels = back_buffer->pixels; - uint32_t* dest_pixels; - int dest_pitch; +static void SDL2_Harness_Swap(br_pixelmap* back_buffer) { - get_and_handle_message(NULL); + SDL2_Harness_ProcessWindowMessages(NULL); if (gl_context != NULL) { - SDL_GL_SwapWindow(window); + SDL2_GL_SwapWindow(window); } else { - SDL_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch); + uint8_t* src_pixels = back_buffer->pixels; + uint32_t* dest_pixels; + int dest_pitch; + + SDL2_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch); for (int i = 0; i < back_buffer->height * back_buffer->width; i++) { *dest_pixels = converted_palette[*src_pixels]; dest_pixels++; src_pixels++; } - SDL_UnlockTexture(screen_texture); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, screen_texture, NULL, NULL); - SDL_RenderPresent(renderer); + SDL2_UnlockTexture(screen_texture); + SDL2_RenderClear(renderer); + SDL2_RenderCopy(renderer, screen_texture, NULL, NULL); + SDL2_RenderPresent(renderer); last_screen_src = back_buffer; } @@ -306,37 +346,48 @@ static void swap(br_pixelmap* back_buffer) { } } -static void palette_changed(br_colour entries[256]) { +static void SDL2_Harness_PaletteChanged(br_colour entries[256]) { for (int i = 0; i < 256; i++) { converted_palette[i] = (0xff << 24 | BR_RED(entries[i]) << 16 | BR_GRN(entries[i]) << 8 | BR_BLU(entries[i])); } if (last_screen_src != NULL) { - swap(last_screen_src); + SDL2_Harness_Swap(last_screen_src); } } -static void get_viewport(int* x, int* y, float* width_multipler, float* height_multiplier) { +static void SDL2_Harness_GetViewport(int* x, int* y, float* width_multipler, float* height_multiplier) { *x = viewport.x; *y = viewport.y; *width_multipler = viewport.scale_x; *height_multiplier = viewport.scale_y; } -void Harness_Platform_Init(tHarness_platform* platform) { - platform->ProcessWindowMessages = get_and_handle_message; - platform->Sleep = SDL_Delay; - platform->GetTicks = SDL_GetTicks; - platform->ShowCursor = SDL_ShowCursor; - platform->SetWindowPos = set_window_pos; - platform->DestroyWindow = destroy_window; - platform->GetKeyboardState = get_keyboard_state; - platform->GetMousePosition = get_mouse_position; - platform->GetMouseButtons = get_mouse_buttons; - platform->ShowErrorMessage = show_error_message; +static int SDL2_Harness_Platform_Init(tHarness_platform* platform) { + if (SDL2_LoadSymbols() != 0) { + return 1; + } + platform->ProcessWindowMessages = SDL2_Harness_ProcessWindowMessages; + platform->Sleep = SDL2_Delay; + platform->GetTicks = SDL2_GetTicks; + platform->ShowCursor = SDL2_ShowCursor; + platform->SetWindowPos = SDL2_Harness_SetWindowPos; + platform->DestroyWindow = SDL2_Harness_DestroyWindow; + platform->GetKeyboardState = SDL2_Harness_GetKeyboardState; + platform->GetMousePosition = SDL2_Harness_GetMousePosition; + platform->GetMouseButtons = SDL2_Harness_GetMouseButtons; + platform->ShowErrorMessage = SDL2_Harness_ShowErrorMessage; - platform->CreateWindow_ = create_window; - platform->Swap = swap; - platform->PaletteChanged = palette_changed; - platform->GL_GetProcAddress = SDL_GL_GetProcAddress; - platform->GetViewport = get_viewport; -} + platform->CreateWindow_ = SDL2_Harness_CreateWindow; + platform->Swap = SDL2_Harness_Swap; + platform->PaletteChanged = SDL2_Harness_PaletteChanged; + platform->GL_GetProcAddress = SDL2_GL_GetProcAddress; + platform->GetViewport = SDL2_Harness_GetViewport; + return 0; +}; + +const tPlatform_bootstrap SDL2_bootstrap = { + "sdl2", + "SDL2 video backend (libsdl.org)", + ePlatform_cap_software | ePlatform_cap_opengl, + SDL2_Harness_Platform_Init, +}; diff --git a/src/harness/platforms/sdl2_syms.h b/src/harness/platforms/sdl2_syms.h new file mode 100644 index 00000000..96fd7d15 --- /dev/null +++ b/src/harness/platforms/sdl2_syms.h @@ -0,0 +1,45 @@ +#ifndef sdl2_syms_h +#define sdl2_syms_h + +#include + +#define FOREACH_SDL2_SYM(X) \ + X(Init, int, (Uint32)) \ + X(Quit, void, (void)) \ + X(Delay, void, (Uint32)) \ + X(GetTicks, Uint32, (void)) \ + X(GetError, const char*, (void)) \ + X(PollEvent, int, (SDL_Event*)) \ + X(ShowSimpleMessageBox, int, (Uint32, const char*, const char *, SDL_Window*)) \ + X(CreateWindow, SDL_Window*, (const char*, int, int, int, int, Uint32)) \ + X(DestroyWindow, void, (SDL_Window*)) \ + X(GetWindowFlags, Uint32, (SDL_Window*)) \ + X(GetWindowID, Uint32, (SDL_Window*)) \ + X(GetWindowSize, void, (SDL_Window*, int*, int*)) \ + X(SetWindowFullscreen, int, (SDL_Window*, Uint32)) \ + X(SetWindowSize, void, (SDL_Window*, int, int)) \ + X(CreateRenderer, SDL_Renderer*, (SDL_Window*, int, Uint32)) \ + X(RenderClear, int, (SDL_Renderer*)) \ + X(RenderCopy, int, (SDL_Renderer*,SDL_Texture*, const SDL_Rect*, const SDL_Rect*)) \ + X(RenderPresent, void, (SDL_Renderer*)) \ + X(RenderWindowToLogical, void, (SDL_Renderer*, int, int, float*, float*)) \ + X(GetRendererInfo, int, (SDL_Renderer*, SDL_RendererInfo*)) \ + X(RenderSetLogicalSize, int, (SDL_Renderer*, int, int)) \ + X(SetRenderDrawBlendMode, int, (SDL_Renderer*, SDL_BlendMode)) \ + X(CreateTexture, SDL_Texture*, (SDL_Renderer*, Uint32, int, int, int)) \ + X(LockTexture, int, (SDL_Texture*, const SDL_Rect*, void**, int*)) \ + X(UnlockTexture, void, (SDL_Texture*)) \ + X(GetMouseFocus, SDL_Window*, (void)) \ + X(GetMouseState, Uint32, (int*, int*)) \ + X(ShowCursor, int, (int)) \ + X(GetPixelFormatName, const char*, (Uint32)) \ + X(GetScancodeName, const char *, (SDL_Scancode)) \ + X(GL_CreateContext, SDL_GLContext, (SDL_Window*)) \ + X(GL_GetProcAddress, void*, (const char*)) \ + X(GL_SetAttribute, int, (SDL_GLattr, int)) \ + X(GL_SetSwapInterval, int, (int)) \ + X(GL_SwapWindow, void, (SDL_Window*)) + +#undef SDL2_SYM + +#endif /* sdl2_syms_h */ diff --git a/src/harness/platforms/sdl_dyn_common.h b/src/harness/platforms/sdl_dyn_common.h new file mode 100644 index 00000000..e5a77b8d --- /dev/null +++ b/src/harness/platforms/sdl_dyn_common.h @@ -0,0 +1,72 @@ +#ifndef sdl_dyn_common_h +#define sdl_dyn_common_h + +#ifdef DETHRACE_SDL_DYNAMIC +#ifdef _WIN32 +#include +#ifdef CreateWindow +#undef CreateWindow +#endif +static void *Harness_LoadObject(const char *name) { + return LoadLibraryA(name); +} +static void Harness_UnloadObject(void *obj) { + FreeLibrary(obj); +} +static void *Harness_LoadFunction(void *obj, const char *name) { + return GetProcAddress(obj, name); +} +#else +#include +static void *Harness_LoadObject(const char *name) { + return dlopen(name, RTLD_NOW | RTLD_LOCAL); +} +static void Harness_UnloadObject(void *obj) { + dlclose(obj); +} +static void *Harness_LoadFunction(void *obj, const char *name) { + return dlsym(obj, name); +} +#endif +#endif + +#define STR2_JOIN(A,B) A##B +#define STR_JOIN(A,B) STR2_JOIN(A, B) + +#define X_TYPEDEF(name, ret, args) typedef ret SDLCALL t##SDL_##name##_fn args; +#define X_STATIC_SYMBOL(name, ret, args) static t##SDL_##name##_fn* STR_JOIN(SYMBOL_PREFIX, name); +#ifdef DETHRACE_SDL_DYNAMIC +#define X_LOAD_FUNCTION(name, ret, args) \ + STR_JOIN(SYMBOL_PREFIX, name) = Harness_LoadFunction(OBJECT_NAME, "SDL_" #name); \ + if (STR_JOIN(SYMBOL_PREFIX, name) == NULL) { \ + goto failure; \ + } +#else +#define X_LOAD_FUNCTION(name, ret, args) STR_JOIN(SYMBOL_PREFIX, name) = SDL_##name; +#endif + +FOREACH_SDLX_SYM(X_TYPEDEF) +FOREACH_SDLX_SYM(X_STATIC_SYMBOL) + +static int STR_JOIN(SYMBOL_PREFIX,LoadSymbols)(void) { +#ifdef DETHRACE_SDL_DYNAMIC + for (size_t i = 0; i < BR_ASIZE(possible_locations); i++) { + OBJECT_NAME = Harness_LoadObject(possible_locations[i]); + if (OBJECT_NAME != NULL) { + break; + } + } + if (OBJECT_NAME == NULL) { + return 1; + } +#endif + FOREACH_SDLX_SYM(X_LOAD_FUNCTION) + return 0; +#ifdef DETHRACE_SDL_DYNAMIC +failure: + Harness_UnloadObject(OBJECT_NAME); + return 1; +#endif +} + +#endif /* sdl_dyn_common_h */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e6632ce3..d6c77be2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,6 @@ add_executable(dethrace_test) add_test(NAME test_dethrace COMMAND dethrace_test) -set(IO_PLATFORM "SDL2") - target_link_libraries(dethrace_test PRIVATE dethrace_obj) target_include_directories(dethrace_test PRIVATE