From ba41413396ed16cb8d6a24ebe78edf7dec941a39 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 3 Oct 2025 00:56:28 +0200 Subject: [PATCH 1/6] Original (beta) demo does not have high resolution data (#497) --- src/DETHRACE/pc-all/allsys.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DETHRACE/pc-all/allsys.c b/src/DETHRACE/pc-all/allsys.c index b4572939..2b9a83b2 100644 --- a/src/DETHRACE/pc-all/allsys.c +++ b/src/DETHRACE/pc-all/allsys.c @@ -843,6 +843,11 @@ int original_main(int pArgc, char** pArgv) { for (i = 1; i < pArgc; i++) { if (strcasecmp(pArgv[i], "-hires") == 0) { +#ifdef DETHRACE_FIX_BUGS + if (!PDCheckDriveExists("DATA/64X48X8/HEADUP.TXT")) { + PDFatalError("No high resolution data (\"DATA/64X48X8\") is available. Run game without -hires."); + } +#endif gGraf_spec_index = 1; } else if (strcasecmp(pArgv[i], "-yon") == 0 && i < pArgc - 1) { i++; From 9c82a405bd89cf3085d223ba9217c435b1de4b3f Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 3 Oct 2025 00:56:47 +0200 Subject: [PATCH 2/6] Add SDL3 platform driver (#454) --- .github/workflows/workflow.yaml | 4 +- CMakeLists.txt | 8 + src/DETHRACE/CMakeLists.txt | 6 + src/harness/CMakeLists.txt | 17 +- src/harness/harness.c | 20 +- src/harness/include/harness/trace.h | 9 +- src/harness/platforms/sdl1.c | 3 +- src/harness/platforms/sdl2.c | 7 +- ...sdl_scancode_map.h => sdl2_scancode_map.h} | 4 +- src/harness/platforms/sdl2_syms.h | 2 - src/harness/platforms/sdl3.c | 426 ++++++++++++++++++ src/harness/platforms/sdl3_scancode_map.h | 113 +++++ src/harness/platforms/sdl3_syms.h | 49 ++ src/harness/platforms/sdl_dyn_common.h | 22 +- 14 files changed, 670 insertions(+), 20 deletions(-) rename src/harness/platforms/{sdl_scancode_map.h => sdl2_scancode_map.h} (96%) create mode 100644 src/harness/platforms/sdl3.c create mode 100644 src/harness/platforms/sdl3_scancode_map.h create mode 100644 src/harness/platforms/sdl3_syms.h diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index 83a1f1a0..0bebae5d 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -51,7 +51,8 @@ jobs: cmake-generator: Ninja cmake-toolchain-file: ${{ matrix.platform.cmake-toolchain-file }} discriminator: ${{ matrix.platform.arch }} - version: 2-latest + version: 3-latest + version-sdl2-compat: 2-head version-sdl12-compat: 1-head - name: 'Prepare sources for release' if: ${{ startsWith(github.ref, 'refs/tags/') }} @@ -71,6 +72,7 @@ jobs: -DCMAKE_TOOLCHAIN_FILE=${{ matrix.platform.cmake-toolchain-file }} \ -DDETHRACE_PLATFORM_SDL1=ON \ -DDETHRACE_PLATFORM_SDL2=ON \ + -DDETHRACE_PLATFORM_SDL3=ON \ -DDETHRACE_PLATFORM_SDL_DYNAMIC=ON \ ${{ matrix.platform.cmake-args }} - name: 'Build (CMake)' diff --git a/CMakeLists.txt b/CMakeLists.txt index ed33e4fd..5e86658c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ option(DETHRACE_SOUND_ENABLED "Include audio support" ON) option(DETHRACE_NET_ENABLED "Include net support" ON) option(DETHRACE_PLATFORM_SDL1 "Support SDL 1.2 platform driver" OFF) option(DETHRACE_PLATFORM_SDL2 "Support SDL 2 platform driver" ON) +option(DETHRACE_PLATFORM_SDL3 "Support SDL 3 platform driver" OFF) option(MSVC_42_FOR_RECCMP "Build with MSVC 4.2 to match assembly" OFF) @@ -54,6 +55,7 @@ if(MSVC_42_FOR_RECCMP) set(DETHRACE_NET_ENABLED OFF) set(DETHRACE_PLATFORM_SDL1 OFF) set(DETHRACE_PLATFORM_SDL2 OFF) + set(DETHRACE_PLATFORM_SDL3 OFF) set(BRENDER_BUILD_DRIVERS OFF) set(CMAKE_C_FLAGS_DEBUG "/Od /Oi /Zi /MLd") @@ -107,6 +109,12 @@ if(DETHRACE_PLATFORM_SDL2) math(EXPR count_sdl_platforms "${count_sdl_platforms} + 1") endif() +if(DETHRACE_PLATFORM_SDL3) + find_package(SDL3 CONFIG REQUIRED) + list(APPEND DETHRACE_PLATFORMS SDL3) + 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) diff --git a/src/DETHRACE/CMakeLists.txt b/src/DETHRACE/CMakeLists.txt index 19777345..4961e5e0 100644 --- a/src/DETHRACE/CMakeLists.txt +++ b/src/DETHRACE/CMakeLists.txt @@ -295,6 +295,12 @@ if(DETHRACE_INSTALL) OPTIONAL ) endif() + if(DETHRACE_PLATFORM_SDL3) + install(FILES "$" + DESTINATION "." + OPTIONAL + ) + endif() endif() endif() endif() diff --git a/src/harness/CMakeLists.txt b/src/harness/CMakeLists.txt index 52819007..52d11057 100644 --- a/src/harness/CMakeLists.txt +++ b/src/harness/CMakeLists.txt @@ -99,7 +99,7 @@ endif() if(DETHRACE_PLATFORM_SDL2) target_sources(harness PRIVATE platforms/sdl2.c - platforms/sdl_scancode_map.h + platforms/sdl2_scancode_map.h platforms/sdl2_syms.h ) target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL2) @@ -112,6 +112,21 @@ if(DETHRACE_PLATFORM_SDL2) endif() endif() +if(DETHRACE_PLATFORM_SDL3) + target_sources(harness PRIVATE + platforms/sdl3.c + platforms/sdl2_scancode_map.h + platforms/sdl3_syms.h + ) + target_compile_definitions(harness PRIVATE DETHRACE_PLATFORM_SDL3) + if(DETHRACE_PLATFORM_SDL_DYNAMIC) + set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/platforms/sdl3.c" APPEND PROPERTY INCLUDE_DIRECTORIES "$") + set_property(GLOBAL APPEND PROPERTY DETHRACE_BUILD_RPATHS "$") + else() + target_link_libraries(harness PRIVATE SDL3::SDL3) + endif() +endif() + if(DETHRACE_PLATFORM_SDL_DYNAMIC) target_compile_definitions(harness PRIVATE DETHRACE_SDL_DYNAMIC) target_link_libraries(harness PRIVATE ${CMAKE_DL_LIBS}) diff --git a/src/harness/harness.c b/src/harness/harness.c index 3aa38de6..3ea92512 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -22,16 +22,22 @@ extern void Harness_Platform_Init(tHarness_platform* platform); extern const tPlatform_bootstrap SDL1_bootstrap; extern const tPlatform_bootstrap SDL2_bootstrap; +extern const tPlatform_bootstrap SDL3_bootstrap; static const tPlatform_bootstrap* platform_bootstraps[] = { -#if defined(DETHRACE_PLATFORM_SDL2) && defined(DETHRACE_PLATFORM_SDL1) +#if defined(DETHRACE_PLATFORM_SDL2) &SDL2_bootstrap, - &SDL1_bootstrap -#elif defined(DETHRACE_PLATFORM_SDL2) - &SDL2_bootstrap -#elif defined(DETHRACE_PLATFORM_SDL1) - &SDL1_bootstrap -#else +#define HAS_PLATFORM_BOOTSTRAP +#endif +#if defined(DETHRACE_PLATFORM_SDL3) + &SDL3_bootstrap, +#define HAS_PLATFORM_BOOTSTRAP +#endif +#if defined(DETHRACE_PLATFORM_SDL1) + &SDL1_bootstrap, +#define HAS_PLATFORM_BOOTSTRAP +#endif +#ifndef HAS_PLATFORM_BOOTSTRAP // This is the case for MSVC 4.20 builds NULL #endif diff --git a/src/harness/include/harness/trace.h b/src/harness/include/harness/trace.h index 1a61bea7..73458f43 100644 --- a/src/harness/include/harness/trace.h +++ b/src/harness/include/harness/trace.h @@ -14,7 +14,7 @@ void debug_print_matrix4(const char* fmt, const char* fn, char* name, br_matrix4 #define BLUE -#if 1 // _MSC_VER == 1020 +#if _MSC_VER == 1020 #define LOG_TRACE() #define LOG_TRACE8() @@ -86,6 +86,13 @@ void debug_print_matrix4(const char* fmt, const char* fn, char* name, br_matrix4 stub_printed = 1; \ } +#define LOG_INFO2(a, b) LOG_INFO(a, b) +#define LOG_INFO3(a, b, c) LOG_INFO(a, b, c) +#define LOG_WARN2(a, b) LOG_WARN(a, b) +#define LOG_WARN3(a, b, c) LOG_WARN(a, b, c) +#define LOG_PANIC2(a, b) LOG_PANIC(a, b) +#define LOG_DEBUG2(a, b) LOG_DEBUG(a, b) + #endif #endif // ifdef diff --git a/src/harness/platforms/sdl1.c b/src/harness/platforms/sdl1.c index f730968d..4e3c491e 100644 --- a/src/harness/platforms/sdl1.c +++ b/src/harness/platforms/sdl1.c @@ -49,12 +49,11 @@ static const char* const possible_locations[] = { "libSDL-1.2.so", }; #endif -#endif -#ifdef DETHRACE_SDL_DYNAMIC static void* sdl1_so; #endif +#define SDL_NAME "SDL1" #define OBJECT_NAME sdl1_so #define SYMBOL_PREFIX SDL1_ #define FOREACH_SDLX_SYM FOREACH_SDL1_SYM diff --git a/src/harness/platforms/sdl2.c b/src/harness/platforms/sdl2.c index 4d8cc700..55c5df81 100644 --- a/src/harness/platforms/sdl2.c +++ b/src/harness/platforms/sdl2.c @@ -4,8 +4,8 @@ #include "harness/config.h" #include "harness/hooks.h" #include "harness/trace.h" +#include "sdl2_scancode_map.h" #include "sdl2_syms.h" -#include "sdl_scancode_map.h" SDL_COMPILE_TIME_ASSERT(sdl2_platform_requires_SDL2, SDL_MAJOR_VERSION == 2); @@ -21,7 +21,7 @@ static int render_width, render_height; static Uint32 last_frame_time; -void (*gKeyHandler_func)(void); +static void (*gKeyHandler_func)(void); // 32 bytes, 1 bit per key. Matches dos executable behavior static br_uint_32 key_state[8]; @@ -59,12 +59,11 @@ static const char* const possible_locations[] = { "libSDL2-2.0.so", }; #endif -#endif -#ifdef DETHRACE_SDL_DYNAMIC static void* sdl2_so; #endif +#define SDL_NAME "SDL2" #define OBJECT_NAME sdl2_so #define SYMBOL_PREFIX SDL2_ #define FOREACH_SDLX_SYM FOREACH_SDL2_SYM diff --git a/src/harness/platforms/sdl_scancode_map.h b/src/harness/platforms/sdl2_scancode_map.h similarity index 96% rename from src/harness/platforms/sdl_scancode_map.h rename to src/harness/platforms/sdl2_scancode_map.h index eae855c9..f8e1d087 100644 --- a/src/harness/platforms/sdl_scancode_map.h +++ b/src/harness/platforms/sdl2_scancode_map.h @@ -4,7 +4,9 @@ #include "dethrace_scancodes.h" #include -int sdl_scancode_map[SDL_NUM_SCANCODES] = { +SDL_COMPILE_TIME_ASSERT(sdl2_scancode_map_requires_SDL2, SDL_MAJOR_VERSION == 2); + +static int sdl_scancode_map[SDL_NUM_SCANCODES] = { [SDL_SCANCODE_ESCAPE] = SCANCODE_ESCAPE, [SDL_SCANCODE_1] = SCANCODE_1, [SDL_SCANCODE_2] = SCANCODE_2, diff --git a/src/harness/platforms/sdl2_syms.h b/src/harness/platforms/sdl2_syms.h index f6d8bc2b..1b056100 100644 --- a/src/harness/platforms/sdl2_syms.h +++ b/src/harness/platforms/sdl2_syms.h @@ -42,6 +42,4 @@ X(GetPrefPath, char*, (const char* org, const char* app)) \ X(free, void, (void*)) -#undef SDL2_SYM - #endif /* sdl2_syms_h */ diff --git a/src/harness/platforms/sdl3.c b/src/harness/platforms/sdl3.c new file mode 100644 index 00000000..9da79ccb --- /dev/null +++ b/src/harness/platforms/sdl3.c @@ -0,0 +1,426 @@ +#include + +#include "harness.h" +#include "harness/config.h" +#include "harness/hooks.h" +#include "harness/trace.h" +#include "sdl3_scancode_map.h" +#include "sdl3_syms.h" + +SDL_COMPILE_TIME_ASSERT(sdl3_platform_requires_SDL3, SDL_MAJOR_VERSION == 3); + +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; + +static SDL_GLContext gl_context; + +static int render_width, render_height; + +static Uint32 last_frame_time; + +static void (*gKeyHandler_func)(void); + +// 32 bytes, 1 bit per key. Matches dos executable behavior +static br_uint_32 key_state[8]; + +static struct { + int x, y; + float scale_x, scale_y; +} viewport; + +// Callbacks back into original game code +extern void QuitGame(void); +extern br_pixelmap* gBack_screen; + +#ifdef DETHRACE_SDL_DYNAMIC +#ifdef _WIN32 +static const char * const possible_locations[] = { + "SDL3.dll", +}; +#elif defined(__APPLE__) +#define SHARED_OBJECT_NAME "libSDL3" +#define SDL3_LIBNAME "libSDL3.dylib" +#define SDL3_FRAMEWORK "SDL3.framework/Versions/A/SDL3" +static const char * const possible_locations[] = { + "@loader_path/" SDL3_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL3_dylib */ + "@loader_path/../Frameworks/" SDL3_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL3_framework */ + "@executable_path/" SDL3_LIBNAME, /* MyApp.app/Contents/MacOS/libSDL3_dylib */ + "@executable_path/../Frameworks/" SDL3_FRAMEWORK, /* MyApp.app/Contents/Frameworks/SDL3_framework */ + NULL, /* /Users/username/Library/Frameworks/SDL3_framework */ + "/Library/Frameworks" SDL3_FRAMEWORK, /* /Library/Frameworks/SDL3_framework */ + SDL3_LIBNAME /* oh well, anywhere the system can see the .dylib (/usr/local/lib or whatever) */ +}; +#else +static const char * const possible_locations[] = { + "libSDL3.so.0", + "libSDL3.so", +}; +#endif + +static void *sdl3_so; +#endif + +#define SDL_NAME "SDL3" +#define OBJECT_NAME sdl3_so +#define SYMBOL_PREFIX SDL3_ +#define FOREACH_SDLX_SYM FOREACH_SDL3_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 int SDL3_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; + } + SDL3_SetWindowSize(hWnd, nWidth, nHeight); + return 0; +} + +static void SDL3_Harness_DestroyWindow(void) { + // SDL3_GL_DeleteContext(context); + if (window != NULL) { + SDL3_DestroyWindow(window); + } + SDL3_Quit(); + window = 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(SDL_Keymod modifier_flags, SDL_Keymod flag_check) { + return (modifier_flags & flag_check) && (modifier_flags & (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_ALT | SDL_KMOD_GUI)) == (modifier_flags & flag_check); +} + +static void SDL3_Harness_ProcessWindowMessages(void) { + SDL_Event event; + + while (SDL3_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + if (event.key.windowID != SDL3_GetWindowID(window)) { + continue; + } + if (event.key.key == SDLK_RETURN) { + if (event.key.type == SDL_EVENT_KEY_DOWN) { + if ((event.key.mod & (SDL_KMOD_CTRL | SDL_KMOD_SHIFT | SDL_KMOD_ALT | SDL_KMOD_GUI))) { + // Ignore keydown of RETURN when used together with some modifier + return; + } + } else if (event.key.type == SDL_EVENT_KEY_UP) { + if (is_only_key_modifier(event.key.mod, SDL_KMOD_ALT)) { + SDL3_SetWindowFullscreen(window, (SDL3_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) ? 0 : SDL_WINDOW_FULLSCREEN); + } + } + } + + // Map incoming SDL scancode to PC scan code as used by game code + int dethrace_scancode = sdl_scancode_map[event.key.scancode]; + if (dethrace_scancode == 0) { + LOG_WARN3("unexpected scan code %s (%d)", SDL3_GetScancodeName(event.key.scancode), event.key.scancode); + return; + } + + if (event.type == SDL_EVENT_KEY_DOWN) { + key_state[dethrace_scancode >> 5] |= (1 << (dethrace_scancode & 0x1F)); + } else { + key_state[dethrace_scancode >> 5] &= ~(1 << (dethrace_scancode & 0x1F)); + } + gKeyHandler_func(); + break; + + case SDL_EVENT_WINDOW_RESIZED: + calculate_viewport(event.window.data1, event.window.data2); + break; + + case SDL_EVENT_QUIT: + QuitGame(); + } + } +} + +static void SDL3_Harness_SetKeyHandler(void (*handler_func)(void)) { + gKeyHandler_func = handler_func; +} + +static void SDL3_Harness_GetKeyboardState(br_uint_32* buffer) { + memcpy(buffer, key_state, sizeof(key_state)); +} + +static int SDL3_Harness_GetMouseButtons(int* pButton1, int* pButton2) { + if (SDL3_GetMouseFocus() != window) { + *pButton1 = 0; + *pButton2 = 0; + return 0; + } + int state = SDL3_GetMouseState(NULL, NULL); + *pButton1 = state & SDL_BUTTON_LMASK; + *pButton2 = state & SDL_BUTTON_RMASK; + return 0; +} + +static int SDL3_Harness_GetMousePosition(int* pX, int* pY) { + int window_width, window_height; + float fWX, fWY; + float fX, fY; + + if (SDL3_GetMouseFocus() != window) { + return 0; + } + SDL3_GetWindowSize(window, &window_width, &window_height); + + SDL3_GetMouseState(&fWX, &fWY); + if (renderer != NULL) { + // software renderer + SDL3_RenderCoordinatesFromWindow(renderer, fWX, fWY, &fX, &fY); + } else { + // hardware renderer + // handle case where window is stretched larger than the pixel size + fX = fWX * (640.0f / window_width); + fY = fWY * (480.0f / window_height); + } + *pX = (int)fX; + *pY = (int)fY; + return 0; +} + +static void limit_fps(void) { + Uint32 now = SDL3_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 = SDL3_GetTicks(); +} + +static int SDL3_Harness_ShowErrorMessage(char* text, char* caption) { + fprintf(stderr, "%s", text); + SDL3_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window); + return 0; +} + +static void SDL3_Harness_CreateWindow(const char* title, int width, int height, tHarness_window_type window_type) { + int window_width, window_height; + + render_width = width; + render_height = height; + + window_width = width; + window_height = height; + + // special case lores and make a bigger window + if (width == 320 && height == 200) { + window_width = 640; + window_height = 480; + } + + if (!SDL3_Init(SDL_INIT_VIDEO)) { + LOG_PANIC2("SDL_INIT_VIDEO error: %s", SDL3_GetError()); + } + + if (window_type == eWindow_type_opengl) { + + window = SDL3_CreateWindow(title, + window_width, window_height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + if (window == NULL) { + LOG_PANIC2("Failed to create window: %s", SDL3_GetError()); + } + + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + gl_context = SDL3_GL_CreateContext(window); + + if (gl_context == NULL) { + LOG_WARN2("Failed to create OpenGL core profile: %s. Trying OpenGLES...", SDL3_GetError()); + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL3_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + gl_context = SDL3_GL_CreateContext(window); + } + if (gl_context == NULL) { + LOG_PANIC2("Failed to create OpenGL context: %s", SDL3_GetError()); + } + SDL3_GL_SetSwapInterval(1); + + } else { + window = SDL3_CreateWindow(title, + window_width, window_height, + SDL_WINDOW_RESIZABLE); + if (window == NULL) { + LOG_PANIC2("Failed to create window: %s", SDL3_GetError()); + } + + renderer = SDL3_CreateRenderer(window, NULL); + if (renderer == NULL) { + LOG_PANIC2("Failed to create renderer: %s", SDL3_GetError()); + } + SDL3_SetRenderVSync(renderer, 1); + SDL3_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + SDL3_SetRenderLogicalPresentation(renderer, render_width, render_height, SDL_LOGICAL_PRESENTATION_LETTERBOX); + + screen_texture = SDL3_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + if (screen_texture == NULL) { + const SDL_PixelFormat *renderer_formats = NULL; + SDL_PropertiesID renderer_props = SDL3_GetRendererProperties(renderer); + if (renderer_props) { + renderer_formats = SDL3_GetPointerProperty(renderer_props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL); + if (renderer_formats) { + for (Uint32 i = 0; renderer_formats[i] != SDL_PIXELFORMAT_UNKNOWN; i++) { + LOG_INFO2("%s\n", SDL3_GetPixelFormatName(renderer_formats[i])); + } + } + } + LOG_PANIC2("Failed to create renderer texture (%s)", SDL3_GetError()); + } + } + + SDL3_ShowCursor(); + + viewport.x = 0; + viewport.y = 0; + viewport.scale_x = 1; + viewport.scale_y = 1; + + if (harness_game_config.start_full_screen) { + SDL3_SetWindowFullscreen(window, true); + } +} + +static void SDL3_Harness_Swap(br_pixelmap* back_buffer) { + + SDL3_Harness_ProcessWindowMessages(); + + if (gl_context != NULL) { + SDL3_GL_SwapWindow(window); + } else { + uint8_t* src_pixels = back_buffer->pixels; + uint32_t* dest_pixels; + int dest_pitch; + + SDL3_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++; + } + SDL3_UnlockTexture(screen_texture); + SDL3_RenderClear(renderer); + SDL3_RenderTexture(renderer, screen_texture, NULL, NULL); + SDL3_RenderPresent(renderer); + last_screen_src = back_buffer; + } + + if (harness_game_config.fps != 0) { + limit_fps(); + } +} + +static void SDL3_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) { + SDL3_Harness_Swap(last_screen_src); + } +} + +static void SDL3_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 void SDL3_Harness_GetPrefPath(char* path, char* app_name) { + char* sdl_path = SDL3_GetPrefPath(NULL, app_name); + if (sdl_path == NULL) { + LOG_PANIC("Failed to get preferences path (%s)", SDL3_GetError()); + } + strcpy(path, sdl_path); + SDL3_free(sdl_path); +} + +static uint32_t SDL3_Harness_GetTicks(void) { + return SDL3_GetTicks(); +} + +static int SDL3_Harness_ShowCursor(int show) { + if (show) { + SDL3_ShowCursor(); + } else { + SDL3_HideCursor(); + } + return 0; +} + +static void* SDL3_Harness_GL_GetProcAddress(const char* name) { + return SDL3_GL_GetProcAddress(name); +} + +static int SDL3_Harness_Platform_Init(tHarness_platform* platform) { + if (SDL3_LoadSymbols() != 0) { + return 1; + } + platform->ProcessWindowMessages = SDL3_Harness_ProcessWindowMessages; + platform->Sleep = SDL3_Delay; + platform->GetTicks = SDL3_Harness_GetTicks; + platform->ShowCursor = SDL3_Harness_ShowCursor; + platform->SetWindowPos = SDL3_Harness_SetWindowPos; + platform->DestroyWindow = SDL3_Harness_DestroyWindow; + platform->SetKeyHandler = SDL3_Harness_SetKeyHandler; + platform->GetKeyboardState = SDL3_Harness_GetKeyboardState; + platform->GetMousePosition = SDL3_Harness_GetMousePosition; + platform->GetMouseButtons = SDL3_Harness_GetMouseButtons; + platform->ShowErrorMessage = SDL3_Harness_ShowErrorMessage; + + platform->CreateWindow_ = SDL3_Harness_CreateWindow; + platform->Swap = SDL3_Harness_Swap; + platform->PaletteChanged = SDL3_Harness_PaletteChanged; + platform->GL_GetProcAddress = SDL3_Harness_GL_GetProcAddress; + platform->GetViewport = SDL3_Harness_GetViewport; + platform->GetPrefPath = SDL3_Harness_GetPrefPath; + return 0; +}; + +const tPlatform_bootstrap SDL3_bootstrap = { + "sdl3", + "SDL3 video backend (libsdl.org)", + ePlatform_cap_software | ePlatform_cap_opengl, + SDL3_Harness_Platform_Init, +}; diff --git a/src/harness/platforms/sdl3_scancode_map.h b/src/harness/platforms/sdl3_scancode_map.h new file mode 100644 index 00000000..47358437 --- /dev/null +++ b/src/harness/platforms/sdl3_scancode_map.h @@ -0,0 +1,113 @@ +#ifndef SDL_SCANCODE_MAP_H +#define SDL_SCANCODE_MAP_H + +#include "dethrace_scancodes.h" +#include + +SDL_COMPILE_TIME_ASSERT(sdl2_scancode_map_requires_SDL3, SDL_MAJOR_VERSION == 3); + +static int sdl_scancode_map[SDL_SCANCODE_COUNT] = { + [SDL_SCANCODE_ESCAPE] = SCANCODE_ESCAPE, + [SDL_SCANCODE_1] = SCANCODE_1, + [SDL_SCANCODE_2] = SCANCODE_2, + [SDL_SCANCODE_3] = SCANCODE_3, + [SDL_SCANCODE_4] = SCANCODE_4, + [SDL_SCANCODE_5] = SCANCODE_5, + [SDL_SCANCODE_6] = SCANCODE_6, + [SDL_SCANCODE_7] = SCANCODE_7, + [SDL_SCANCODE_8] = SCANCODE_8, + [SDL_SCANCODE_9] = SCANCODE_9, + [SDL_SCANCODE_0] = SCANCODE_0, + [SDL_SCANCODE_MINUS] = SCANCODE_MINUS, + [SDL_SCANCODE_EQUALS] = SCANCODE_EQUALS, + [SDL_SCANCODE_BACKSPACE] = SCANCODE_BACK, + [SDL_SCANCODE_TAB] = SCANCODE_TAB, + [SDL_SCANCODE_Q] = SCANCODE_Q, + [SDL_SCANCODE_W] = SCANCODE_W, + [SDL_SCANCODE_E] = SCANCODE_E, + [SDL_SCANCODE_R] = SCANCODE_R, + [SDL_SCANCODE_T] = SCANCODE_T, + [SDL_SCANCODE_Y] = SCANCODE_Y, + [SDL_SCANCODE_U] = SCANCODE_U, + [SDL_SCANCODE_I] = SCANCODE_I, + [SDL_SCANCODE_O] = SCANCODE_O, + [SDL_SCANCODE_P] = SCANCODE_P, + [SDL_SCANCODE_LEFTBRACKET] = SCANCODE_LBRACKET, + [SDL_SCANCODE_RIGHTBRACKET] = SCANCODE_RBRACKET, + [SDL_SCANCODE_RETURN] = SCANCODE_RETURN, + [SDL_SCANCODE_LCTRL] = SCANCODE_LCONTROL, + [SDL_SCANCODE_A] = SCANCODE_A, + [SDL_SCANCODE_S] = SCANCODE_S, + [SDL_SCANCODE_D] = SCANCODE_D, + [SDL_SCANCODE_F] = SCANCODE_F, + [SDL_SCANCODE_G] = SCANCODE_G, + [SDL_SCANCODE_H] = SCANCODE_H, + [SDL_SCANCODE_J] = SCANCODE_J, + [SDL_SCANCODE_K] = SCANCODE_K, + [SDL_SCANCODE_L] = SCANCODE_L, + [SDL_SCANCODE_SEMICOLON] = SCANCODE_SEMICOLON, + [SDL_SCANCODE_APOSTROPHE] = SCANCODE_APOSTROPHE, + [SDL_SCANCODE_GRAVE] = SCANCODE_GRAVE, + [SDL_SCANCODE_LSHIFT] = SCANCODE_LSHIFT, + [SDL_SCANCODE_BACKSLASH] = SCANCODE_BACKSLASH, + [SDL_SCANCODE_Z] = SCANCODE_Z, + [SDL_SCANCODE_X] = SCANCODE_X, + [SDL_SCANCODE_C] = SCANCODE_C, + [SDL_SCANCODE_V] = SCANCODE_V, + [SDL_SCANCODE_B] = SCANCODE_B, + [SDL_SCANCODE_N] = SCANCODE_N, + [SDL_SCANCODE_M] = SCANCODE_M, + [SDL_SCANCODE_COMMA] = SCANCODE_COMMA, + [SDL_SCANCODE_PERIOD] = SCANCODE_PERIOD, + [SDL_SCANCODE_SLASH] = SCANCODE_SLASH, + [SDL_SCANCODE_RSHIFT] = SCANCODE_RSHIFT, + [SDL_SCANCODE_KP_MULTIPLY] = SCANCODE_MULTIPLY, + [SDL_SCANCODE_LALT] = SCANCODE_LALT, + [SDL_SCANCODE_SPACE] = SCANCODE_SPACE, + [SDL_SCANCODE_CAPSLOCK] = SCANCODE_CAPITAL, + [SDL_SCANCODE_F1] = SCANCODE_F1, + [SDL_SCANCODE_F2] = SCANCODE_F2, + [SDL_SCANCODE_F3] = SCANCODE_F3, + [SDL_SCANCODE_F4] = SCANCODE_F4, + [SDL_SCANCODE_F5] = SCANCODE_F5, + [SDL_SCANCODE_F6] = SCANCODE_F6, + [SDL_SCANCODE_F7] = SCANCODE_F7, + [SDL_SCANCODE_F8] = SCANCODE_F8, + [SDL_SCANCODE_F9] = SCANCODE_F9, + [SDL_SCANCODE_F10] = SCANCODE_F10, + [SDL_SCANCODE_NUMLOCKCLEAR] = SCANCODE_NUMLOCK, + [SDL_SCANCODE_SCROLLLOCK] = SCANCODE_SCROLL, + [SDL_SCANCODE_KP_7] = SCANCODE_NUMPAD7, + [SDL_SCANCODE_KP_8] = SCANCODE_NUMPAD8, + [SDL_SCANCODE_KP_9] = SCANCODE_NUMPAD9, + [SDL_SCANCODE_KP_MINUS] = SCANCODE_SUBTRACT, + [SDL_SCANCODE_KP_4] = SCANCODE_NUMPAD4, + [SDL_SCANCODE_KP_5] = SCANCODE_NUMPAD5, + [SDL_SCANCODE_KP_6] = SCANCODE_NUMPAD6, + [SDL_SCANCODE_KP_PLUS] = SCANCODE_ADD, + [SDL_SCANCODE_KP_1] = SCANCODE_NUMPAD1, + [SDL_SCANCODE_KP_2] = SCANCODE_NUMPAD2, + [SDL_SCANCODE_KP_3] = SCANCODE_NUMPAD3, + [SDL_SCANCODE_KP_0] = SCANCODE_NUMPAD0, + [SDL_SCANCODE_KP_PERIOD] = SCANCODE_DECIMAL, + [SDL_SCANCODE_NONUSBACKSLASH] = SCANCODE_OEM_102, + [SDL_SCANCODE_F11] = SCANCODE_F11, + [SDL_SCANCODE_F12] = SCANCODE_F12, + [SDL_SCANCODE_KP_ENTER] = SCANCODE_NUMPADENTER, + [SDL_SCANCODE_RCTRL] = SCANCODE_RCONTROL, + [SDL_SCANCODE_KP_DIVIDE] = SCANCODE_DIVIDE, + [SDL_SCANCODE_RALT] = SCANCODE_RALT, + [SDL_SCANCODE_PAUSE] = SCANCODE_PAUSE, + [SDL_SCANCODE_HOME] = SCANCODE_HOME, + [SDL_SCANCODE_UP] = SCANCODE_UP, + [SDL_SCANCODE_PAGEUP] = SCANCODE_PGUP, + [SDL_SCANCODE_LEFT] = SCANCODE_LEFT, + [SDL_SCANCODE_RIGHT] = SCANCODE_RIGHT, + [SDL_SCANCODE_END] = SCANCODE_END, + [SDL_SCANCODE_DOWN] = SCANCODE_DOWN, + [SDL_SCANCODE_PAGEDOWN] = SCANCODE_PGDN, + [SDL_SCANCODE_INSERT] = SCANCODE_INSERT, + [SDL_SCANCODE_DELETE] = SCANCODE_DELETE, +}; + +#endif /* _SDL2_SCANCODE_TO_DINPUT_H_ */ diff --git a/src/harness/platforms/sdl3_syms.h b/src/harness/platforms/sdl3_syms.h new file mode 100644 index 00000000..523a38a2 --- /dev/null +++ b/src/harness/platforms/sdl3_syms.h @@ -0,0 +1,49 @@ +#ifndef sdl3_syms_h +#define sdl3_syms_h + +#include + +#define FOREACH_SDL3_SYM(X) \ + X(free, void, (void *)) \ + X(Init, bool, (Uint32)) \ + X(Quit, void, (void)) \ + X(Delay, void, (Uint32)) \ + X(GetTicks, Uint64, (void)) \ + X(GetError, const char*, (void)) \ + X(GetPointerProperty, void*, (SDL_PropertiesID, const char*, void*)) \ + X(PollEvent, bool, (SDL_Event*)) \ + X(ShowSimpleMessageBox, bool, (SDL_MessageBoxFlags flags, const char*, const char *, SDL_Window*)) \ + X(CreateWindow, SDL_Window*, (const char*, int, int, SDL_WindowFlags)) \ + X(DestroyWindow, void, (SDL_Window*)) \ + X(GetWindowFlags, SDL_WindowFlags, (SDL_Window*)) \ + X(GetPrefPath, char*, (const char *, const char *)) \ + X(GetWindowID, SDL_WindowID, (SDL_Window*)) \ + X(GetWindowSize, bool, (SDL_Window*, int*, int*)) \ + X(HideCursor, bool, (void)) \ + X(SetWindowFullscreen, bool, (SDL_Window*, bool)) \ + X(SetWindowSize, bool, (SDL_Window*, int, int)) \ + X(CreateRenderer, SDL_Renderer*, (SDL_Window*, const char*)) \ + X(RenderClear, bool, (SDL_Renderer*)) \ + X(RenderTexture, bool, (SDL_Renderer*, SDL_Texture*, const SDL_FRect*, const SDL_FRect*)) \ + X(RenderPresent, bool, (SDL_Renderer*)) \ + X(RenderCoordinatesFromWindow, bool, (SDL_Renderer*, float, float, float*, float*)) \ + X(GetRendererName, const char*, (SDL_Renderer*)) \ + X(GetRendererProperties, SDL_PropertiesID, (SDL_Renderer*)) \ + X(SetRenderLogicalPresentation, bool, (SDL_Renderer*, int, int, SDL_RendererLogicalPresentation)) \ + X(SetRenderDrawBlendMode, bool, (SDL_Renderer*, SDL_BlendMode)) \ + X(SetRenderVSync, bool, (SDL_Renderer*, int)) \ + X(CreateTexture, SDL_Texture*, (SDL_Renderer*, SDL_PixelFormat, SDL_TextureAccess, int, int)) \ + X(LockTexture, bool, (SDL_Texture*, const SDL_Rect*, void**, int*)) \ + X(UnlockTexture, void, (SDL_Texture*)) \ + X(GetMouseFocus, SDL_Window*, (void)) \ + X(GetMouseState, SDL_MouseButtonFlags, (float*, float*)) \ + X(ShowCursor, bool, (void)) \ + X(GetPixelFormatName, const char*, (SDL_PixelFormat)) \ + X(GetScancodeName, const char *, (SDL_Scancode)) \ + X(GL_CreateContext, SDL_GLContext, (SDL_Window*)) \ + X(GL_GetProcAddress, SDL_FunctionPointer, (const char*)) \ + X(GL_SetAttribute, bool, (SDL_GLAttr, int)) \ + X(GL_SetSwapInterval, bool, (int)) \ + X(GL_SwapWindow, bool, (SDL_Window*)) + +#endif /* sdl3_syms_h */ diff --git a/src/harness/platforms/sdl_dyn_common.h b/src/harness/platforms/sdl_dyn_common.h index e5a77b8d..8586fd46 100644 --- a/src/harness/platforms/sdl_dyn_common.h +++ b/src/harness/platforms/sdl_dyn_common.h @@ -16,6 +16,21 @@ static void Harness_UnloadObject(void *obj) { static void *Harness_LoadFunction(void *obj, const char *name) { return GetProcAddress(obj, name); } +static const char *Harness_LoadError(void) { + static char buffer[512]; + DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buffer, + sizeof(buffer)-1, + NULL); + if (cchMsg == 0) { + strncpy(buffer, "GetProcAddress failed", sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + } + return buffer; +} #else #include static void *Harness_LoadObject(const char *name) { @@ -27,6 +42,9 @@ static void Harness_UnloadObject(void *obj) { static void *Harness_LoadFunction(void *obj, const char *name) { return dlsym(obj, name); } +static const char *Harness_LoadError(void) { + return dlerror(); +} #endif #endif @@ -38,7 +56,8 @@ static void *Harness_LoadFunction(void *obj, const char *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) { \ + if (STR_JOIN(SYMBOL_PREFIX, name) == NULL) { \ + fprintf(stderr, "Failed to load %s function: %s (%s)\n", SDL_NAME, "SDL_" #name, Harness_LoadError()); \ goto failure; \ } #else @@ -57,6 +76,7 @@ static int STR_JOIN(SYMBOL_PREFIX,LoadSymbols)(void) { } } if (OBJECT_NAME == NULL) { + fputs("Could not find " SDL_NAME " library\n", stderr); return 1; } #endif From 12a48ffddf489e964bbea916c0df99a93e808900 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 3 Oct 2025 03:23:11 +0200 Subject: [PATCH 3/6] Fix demo compatibility (#494) * Fix demo compatibility Regression introduced in 81116169cfefa549a70a57f3c7e616c57b697973 * Decode gDecode_string (use macro's to show its definition) * Fix IWANTTOFIDDLE cheat for demo * Add demo support to tools/decode_datatxt.py --- src/DETHRACE/common/loading.c | 23 +++- src/DETHRACE/common/racestrt.c | 2 +- src/DETHRACE/common/utility.c | 69 ++++++++++ src/DETHRACE/common/utility.h | 6 + tools/decode_datatxt.py | 235 ++++++++++++++++++++++++--------- 5 files changed, 269 insertions(+), 66 deletions(-) diff --git a/src/DETHRACE/common/loading.c b/src/DETHRACE/common/loading.c index a2b5e3b3..cfbef261 100644 --- a/src/DETHRACE/common/loading.c +++ b/src/DETHRACE/common/loading.c @@ -140,8 +140,25 @@ int gAllow_open_to_fail = 1; // GLOBAL: CARM95 0x0050a5c8 int gDecode_thing = '@'; +#define DECODE_STRING_SECRET 50 + // GLOBAL: CARM95 0x0050a5d0 -char gDecode_string[] = { 0x9B, 0x52, 0x93, 0x9F, 0x52, 0x98, 0x9B, 0x96, 0x96, 0x9E, 0x9B, 0xA0, 0x99, 0x0 }; +char gDecode_string[] = { + 'i' + DECODE_STRING_SECRET, + ' ' + DECODE_STRING_SECRET, + 'a' + DECODE_STRING_SECRET, + 'm' + DECODE_STRING_SECRET, + ' ' + DECODE_STRING_SECRET, + 'f' + DECODE_STRING_SECRET, + 'i' + DECODE_STRING_SECRET, + 'd' + DECODE_STRING_SECRET, + 'd' + DECODE_STRING_SECRET, + 'l' + DECODE_STRING_SECRET, + 'i' + DECODE_STRING_SECRET, + 'n' + DECODE_STRING_SECRET, + 'g' + DECODE_STRING_SECRET, + '\0' +}; // GLOBAL: CARM95 0x00531f00 int gFunk_groove_flags[30]; @@ -400,7 +417,7 @@ void LoadGeneralParameters(void) { fgets(s, sizeof(s) - 1, f); fclose(f); for (i = 0; i < strlen(gDecode_string); i++) { - gDecode_string[i] -= 50; + gDecode_string[i] -= DECODE_STRING_SECRET; } // trim trailing CRLF etc @@ -413,7 +430,7 @@ void LoadGeneralParameters(void) { } for (i = 0; i < strlen(gDecode_string); i++) { - gDecode_string[i] += 50; + gDecode_string[i] += DECODE_STRING_SECRET; } } PathCat(the_path, gApplication_path, "GENERAL.TXT"); diff --git a/src/DETHRACE/common/racestrt.c b/src/DETHRACE/common/racestrt.c index 607c25c6..7b48afdb 100644 --- a/src/DETHRACE/common/racestrt.c +++ b/src/DETHRACE/common/racestrt.c @@ -1595,7 +1595,7 @@ void SelectRaceDraw(int pCurrent_choice, int pCurrent_mode) { fputs("*************", f); } } - gDecode_thing ^= 0x40u; + gDecode_thing ^= '@'; fclose(f); EncodeAllFilesInDirectory(""); EncodeAllFilesInDirectory("CARS"); diff --git a/src/DETHRACE/common/utility.c b/src/DETHRACE/common/utility.c index 73432018..8a2d7390 100644 --- a/src/DETHRACE/common/utility.c +++ b/src/DETHRACE/common/utility.c @@ -98,6 +98,14 @@ void EncodeLine(char* pS) { FILE* test; unsigned char c; +#ifdef DETHRACE_FIX_BUGS + // Demo has its own decryption key + behavior + if (harness_game_info.mode == eGame_carmageddon_demo) { + EncodeLine_DEMO(pS); + return; + } +#endif + len = strlen(pS); key = (char*)gLong_key; if (gEncryption_method == 0) { @@ -1441,6 +1449,14 @@ void DecodeLine2(char* pS) { unsigned char c; char* key; +#ifdef DETHRACE_FIX_BUGS + // Demo has its own decryption key + behavior + if (harness_game_info.mode == eGame_carmageddon_demo) { + DecodeLine2_DEMO(pS); + return; + } +#endif + len = strlen(pS); key = (char*)gLong_key; #ifdef DETHRACE_FIX_BUGS @@ -1494,6 +1510,14 @@ void EncodeLine2(char* pS) { unsigned char c; char* key; +#ifdef DETHRACE_FIX_BUGS + // Demo has its own decryption key + behavior + if (harness_game_info.mode == eGame_carmageddon_demo) { + EncodeLine2_DEMO(pS); + return; + } +#endif + len = strlen(pS); count = 0; key = (char*)gLong_key; @@ -1913,3 +1937,48 @@ void EncodeLine_DEMO(char* pS) { pS[i] = c; } } + +void EncodeLine2_DEMO(char* pS) { + int len; + int seed; + int i; + const char* key; + unsigned char c; +#if BR_ENDIAN_BIG + const tU32 gLong_key_DEMO[] = { 0x58503A76, 0xCBB68565, 0x15CD5B07, 0xB168DE3A }; +#else + const tU32 gLong_key_DEMO[] = { 0x763A5058, 0x6585B6CB, 0x75BCD15, 0x3ADE68B1 }; +#endif + + len = strlen(pS); + key = (char*)gLong_key_DEMO; + + while (len != 0 && (pS[len - 1] == '\r' || pS[len - 1] == '\n')) { + pS[len - 1] = 0; + len--; + } + seed = len % 16; + for (i = 0; i < len; i++) { + c = pS[i]; + if (c == '\t') { + c = 0x9F; + } + + c -= 32; + c ^= key[seed]; + c &= 0x7f; + c += 32; + if (c == 0x9F) { + c = '\t'; + } + if (c == '\n' || c == '\r') { + c |= 0x80; + } + seed = (seed + 7) % 16; + pS[i] = c; + } +} + +void DecodeLine2_DEMO(char* pS) { + EncodeLine_DEMO(pS); +} diff --git a/src/DETHRACE/common/utility.h b/src/DETHRACE/common/utility.h index 0d409670..937a86bd 100644 --- a/src/DETHRACE/common/utility.h +++ b/src/DETHRACE/common/utility.h @@ -186,4 +186,10 @@ void BlendifyMaterialPrimitively(br_material* pMaterial, int pPercent); void BlendifyMaterial(br_material* pMaterial, int pPercent); +void EncodeLine_DEMO(char* pS); + +void EncodeLine2_DEMO(char* pS); + +void DecodeLine2_DEMO(char* pS); + #endif diff --git a/tools/decode_datatxt.py b/tools/decode_datatxt.py index f195a14b..42cc56d5 100755 --- a/tools/decode_datatxt.py +++ b/tools/decode_datatxt.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse +import enum import sys LONG_KEY = ( @@ -11,7 +12,15 @@ OTHER_LONG_KEY = ( 0x67, 0xa8, 0xd6, 0x26, 0xb6, 0xdd, 0x45, 0x1b, 0x32, 0x7e, 0x22, 0x13, 0x15, 0xc2, 0x94, 0x37, ) +DEMO_KEY = ( + 0x58, 0x50, 0x3A, 0x76, 0xCB, 0xB6, 0x85, 0x65, + 0x15, 0xCD, 0x5B, 0x07, 0xB1, 0x68, 0xDE, 0x3A, +) +class Method(enum.Enum): + Method1 = "1" + Method2 = "2" + Demo = "demo" class Byte: def __init__(self, v: int): @@ -52,6 +61,13 @@ class Byte: self.v = (self.v & v) & 0xff return self + def __or__(self, v: int): + return Byte(self.v | v) + + def __ior__(self, v: int): + self.v = (self.v | v) & 0xff + return self + def __eq__(self, other): if isinstance(other, Byte): return self.v == other.v @@ -63,61 +79,20 @@ class Byte: return f"(byte 0x{self.v:02x})" -def decode_line(line: bytes, method: int) -> bytes: - line = line.rstrip(b"\r\n") - key = LONG_KEY - seed = len(line) % len(key) - dline = bytearray(len(line)) - for i, c in enumerate(line): - b = Byte(c) - if dline[i - 2:i] == b'//': - key = OTHER_LONG_KEY - if method == 1: - if b == ord(b'\t'): - b = Byte(0x9f) - - b -= 0x20 - b ^= key[seed] - b &= 0x7f - b += 0x20 - - seed += 7 - seed %= len(key) - - if b == 0x9f: - b = Byte(ord(b'\t')) - else: - if b == ord(b'\t'): - b = Byte(0x80) - - b -= 0x20 - if (b & 0x80) == 0: - b ^= key[seed] & 0x7f - b += 0x20 - - seed += 7 - seed %= len(key) - - if b == 0x80: - b = Byte(ord(b'\t')) - dline[i] = b.v - return dline - - -def encode_line(line: bytes, method: int) -> bytes: - line = line.rstrip(b"\r\n") - key = LONG_KEY - seed = len(line) % len(key) - count = 0 - eline = bytearray(len(line)) - for i, c in enumerate(line): - if count == 2: - key = OTHER_LONG_KEY - if c == ord('/'): - count += 1 - else: - count = 0 - if method == 1: +class Codec1: + def encode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = LONG_KEY + seed = len(line) % len(key) + count = 0 + eline = bytearray(len(line)) + for i, c in enumerate(line): + if count == 2: + key = OTHER_LONG_KEY + if c == ord('/'): + count += 1 + else: + count = 0 if c == ord('\t'): c = 0x9f @@ -132,7 +107,51 @@ def encode_line(line: bytes, method: int) -> bytes: if b == 0x9f: b = Byte(ord('\t')) - else: + eline[i] = b.v + return bytes(eline) + + def decode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = LONG_KEY + seed = len(line) % len(key) + dline = bytearray(len(line)) + for i, c in enumerate(line): + b = Byte(c) + if dline[i - 2:i] == b'//': + key = OTHER_LONG_KEY + + if b == ord(b'\t'): + b = Byte(0x9f) + + b -= 0x20 + b ^= key[seed] + b &= 0x7f + b += 0x20 + + seed += 7 + seed %= len(key) + + if b == 0x9f: + b = Byte(ord(b'\t')) + dline[i] = b.v + return dline + + +class Codec2: + def encode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = LONG_KEY + seed = len(line) % len(key) + count = 0 + eline = bytearray(len(line)) + for i, c in enumerate(line): + if count == 2: + key = OTHER_LONG_KEY + if c == ord('/'): + count += 1 + else: + count = 0 + if c == ord('\t'): c = 0x80 b = Byte(c - 0x20) @@ -145,24 +164,116 @@ def encode_line(line: bytes, method: int) -> bytes: if b == 0x80: b = Byte(ord('\t')) - eline[i] = b.v - return bytes(eline) + eline[i] = b.v + return bytes(eline) + + def decode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = LONG_KEY + seed = len(line) % len(key) + dline = bytearray(len(line)) + for i, c in enumerate(line): + b = Byte(c) + if dline[i - 2:i] == b'//': + key = OTHER_LONG_KEY + + if b == ord(b'\t'): + b = Byte(0x80) + + b -= 0x20 + if (b & 0x80) == 0: + b ^= key[seed] & 0x7f + b += 0x20 + + seed += 7 + seed %= len(key) + + if b == 0x80: + b = Byte(ord(b'\t')) + + dline[i] = b.v + return dline + + +class CodecDemo: + def encode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = DEMO_KEY + seed = len(line) % len(key) + dline = bytearray(len(line)) + for i, c in enumerate(line): + b = Byte(c) + + if b == ord('\t'): + b = Byte(0x9f) + + b -= 0x20 + b ^= key[seed] + b &= 0x7f + b += 0x20 + if b == 0x9f: + b = Byte(ord('\t')) + if b == ord('\n') or b == ord('\r'): + b |= 0x80 + + seed += 7 + seed %= len(key) + + dline[i] = b.v + return dline + + def decode_line(self, line: bytes) -> bytes: + line = line.rstrip(b"\r\n") + key = DEMO_KEY + seed = len(line) % len(key) + dline = bytearray(len(line)) + for i, c in enumerate(line): + b = Byte(c) + + if b == ord('\t'): + b = Byte(0x9f) + + b -= 0x20 + b ^= key[seed] + b &= 0x7f + b += 0x20 + + seed += 7 + seed %= len(key) + + if b == 0x9f: + b = Byte(ord('\t')) + + dline[i] = b.v + return dline + + +CODECS = { + Method.Method1: Codec1, + Method.Method2: Codec2, + Method.Demo: CodecDemo, +} def main(): + method_choices = tuple(e.value for e in Method.__members__.values()) parser = argparse.ArgumentParser(allow_abbrev=False, description="Decode/encode a Carmageddon text file") parser.add_argument("file", metavar="FILE", nargs="?", help="input file (default=stdin)") - parser.add_argument("--method", choices=[1, 2], type=int, default=2, help="encryption method to use (default=2)") + parser.add_argument("--method", choices=method_choices, default=Method.Method2.value, + help=f"encryption method to use (default={Method.Method2.value}, choices={','.join(method_choices)})") args = parser.parse_args() + method = Method(args.method) + codec = CODECS[method]() + istream = open(args.file, "rb") if args.file else sys.stdin.buffer for line in istream.readlines(): - if line[0] == ord(b'@'): - dline = decode_line(line[1:], args.method) + if line[0] == ord(b"@"): + dline = codec.decode_line(line[1:]) sys.stdout.buffer.write(dline) else: - eline = b"@" + encode_line(line, args.method) + eline = b"@" + codec.encode_line(line) sys.stdout.buffer.write(eline) sys.stdout.buffer.write(b'\n') From a9f82ef9750690f39ed1fe87daa275abccd32841 Mon Sep 17 00:00:00 2001 From: BSzili Date: Tue, 23 Sep 2025 17:59:06 +0200 Subject: [PATCH 4/6] French localization support --- src/harness/harness.c | 11 +++++++++++ src/harness/include/harness/config.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/harness/harness.c b/src/harness/harness.c index 3ea92512..4e150a43 100644 --- a/src/harness/harness.c +++ b/src/harness/harness.c @@ -186,6 +186,9 @@ static void Harness_DetectGameMode(void) { } else if (strstr(buffer, "NOWA GRA") != NULL) { harness_game_info.localization = eGameLocalization_polish; LOG_INFO2("Language: \"%s\"", "Polish"); + } else if (strstr(buffer, "NOUVELLE PARTIE") != NULL) { + harness_game_info.localization = eGameLocalization_french; + LOG_INFO2("Language: \"%s\"", "French"); } else { LOG_INFO("Language: unrecognized"); } @@ -547,6 +550,14 @@ int Harness_Hook_isalnum(int c) { return 1; } } + } if (harness_game_info.localization == eGameLocalization_french) { + // French diacritic letters in Windows-1252 + unsigned char letters[] = { 140, 156, 159, 192, 194, 198, 199, 200, 201, 202, 203, 206, 207, 212, 217, 219, 220, 224, 226, 230, 231, 232, 233, 234, 235, 238, 239, 244, 249, 251, 252, 255 }; + for (i = 0; i < (int)sizeof(letters); i++) { + if ((unsigned char)c == letters[i]) { + return 1; + } + } } return isalnum(c); diff --git a/src/harness/include/harness/config.h b/src/harness/include/harness/config.h index 98b7e144..71ee8e3a 100644 --- a/src/harness/include/harness/config.h +++ b/src/harness/include/harness/config.h @@ -16,6 +16,7 @@ typedef enum { eGameLocalization_none, eGameLocalization_german, eGameLocalization_polish, + eGameLocalization_french, } tHarness_game_localization; typedef struct tHarness_game_info { From 5787ec29b040d8388c025ed36cb5b6570e36549c Mon Sep 17 00:00:00 2001 From: Carlo Bramini Date: Wed, 1 Oct 2025 13:18:10 +0200 Subject: [PATCH 5/6] WIN32: replace LOG_DEBUG() with LOG_DEBUG2() Compiling dethrace rises this error when it is compiled with MinGW: dethrace/src/harness/os/windows.c: In function 'OS_GetAdapterAddress': dethrace/src/harness/os/windows.c:440:47: error: macro 'LOG_DEBUG' passed 2 arguments, but takes just 1 440 | LOG_DEBUG("name: %s", aa->FriendlyName); // Skip if name is provided and doesn't match FriendlyName | ^ According to the content of dethrace/src/harness/include/harness/trace.h, I replaced with LOG_DEBUG2() macro and the problem has been solved. --- src/harness/os/windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/os/windows.c b/src/harness/os/windows.c index f431448c..64d749bb 100644 --- a/src/harness/os/windows.c +++ b/src/harness/os/windows.c @@ -437,7 +437,7 @@ int OS_GetAdapterAddress(char* name, void* pSockaddr_in) { } for (IP_ADAPTER_ADDRESSES* aa = adapter_addrs; aa != NULL; aa = aa->Next) { - LOG_DEBUG("name: %s", aa->FriendlyName); // Skip if name is provided and doesn't match FriendlyName + LOG_DEBUG2("name: %s", aa->FriendlyName); // Skip if name is provided and doesn't match FriendlyName if (wcslen(wideName) > 0 && wcscmp(aa->FriendlyName, wideName) != 0) continue; From f03832e8c8ca02addf89bf5ed8cb76bd7454fe3c Mon Sep 17 00:00:00 2001 From: Carlo Bramini Date: Wed, 1 Oct 2025 21:30:57 +0200 Subject: [PATCH 6/6] NET: fix type mismatch for sa_len setsockopt() and recvfrom() accept socklen_t and not an unsigned int. See also PR #493 --- src/DETHRACE/pc-all/allnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DETHRACE/pc-all/allnet.c b/src/DETHRACE/pc-all/allnet.c index 8a980a9f..ae327f7b 100644 --- a/src/DETHRACE/pc-all/allnet.c +++ b/src/DETHRACE/pc-all/allnet.c @@ -175,7 +175,7 @@ int ReceiveHostResponses(void) { int already_registered; char addr_string[32]; - unsigned int sa_len; + socklen_t sa_len; int error; sa_len = sizeof(gRemote_addr); @@ -519,7 +519,7 @@ tNet_message* PDNetGetNextMessage(tNet_game_details* pDetails, void** pSender_ad int msg_type; char addr_str[32]; - unsigned int sa_len; + socklen_t sa_len; int res; tNet_message* msg;