libsdl-org / sdl12-compat

An SDL-1.2 compatibility layer that uses SDL 2.0 behind the scenes.
Other
197 stars 40 forks source link

Misc. issues affecting Loki port of Civilization: Call to Power #176

Closed sulix closed 2 years ago

sulix commented 2 years ago

[Just dropping my notes about this here: I may split things up and/or fix them if I get some time…]

There are a large number of issues with Civilization: Call to Power v1.2a (it being an ancient SDL 1.1 game with a bunch of glibc/libstdc++ incompatibilities). Getting it to run even with real SDL 1.2 on a modern system (i.e., with modern glibc, etc, dynamically linked) is a chore, involving a lot of hex editing and patching. See: https://gist.github.com/sulix/09f059c468fcb58eefbca8ef46006ccb

That being said, while it does crash occasionally regardless, it does work considerably better with the real SDL 1.2 than with sdl12-compat. Maybe some of the issues could affect other games as well:

icculus commented 2 years ago

Stack realignment: CivCTP's stacks are 4-byte aligned. Most distros are built with 16-byte aligned stacks to use SSE. Use attribute((force_align_arg_pointer)) to realign stacks in sdl12-compat to bridge this gap.

This is going to blow up calling into glibc, too, even if it doesn't have other external dependencies.

DanielGibson commented 2 years ago

Does it really make that much sense to hack sdl12-compat to work with games that use SDL1.1 (not 1.2) and are generally incompatible with modern glibc etc?

I hate to say it, but maybe Wine is the better solution here

icculus commented 2 years ago

I don't think 1.1 had any API changes over 1.2. The hurdles to the Loki games are going to be larger than SDL, but these issues are all 1.2 issues as well, so far.

uralets commented 2 years ago

I'm trying to play Loki's port of Sid Meier's Alpha Centauri (aka smac) - it seems to be a bit less ancient than the Civ as its dynamic executable is linked against SDL1.2 at least.

But it still requires an old glibc of course... Thus I'm trying my luck with this bunch of old libraries - https://www.improbability.net/loki/ It actually seems to work here (or simply using the statically built executable works too).

But how one goes about using the compat shim with this pile of old libs? Just dropping the compiled .so in place of the libsdl1.2 provided by the above pack results in a "bad dynamic tag" from the linker... Would it be necessary to compile the shim with an equally old toolchain as well? How are you doing it?

DanielGibson commented 2 years ago

Does anyone know what causes these incompatibilies?

I first thought this was about libc5 vs libc6, but apparently it's about glibc >= 2.3 vs <= 2.2.x (which should both be libc6 and compatible, right?)

DanielGibson commented 2 years ago

urgh, https://bugzilla.redhat.com/show_bug.cgi?id=112707#c1 suggests that those old Loki binaries statically linked the dynamic linker (I think this is usually ld-linux.so which is part of glibc? or maybe libdl.so? or both?), which should only work with the glibc version it belonged to, and might work with other versions "just by luck" and apparently (in case of the version Loki statically linked) doesn't work at all with glibc versions >= 2.3.

I wonder if they also linked other parts of the glibc statically, and if that was intentional (in that case: WHY?!) or happened accidentally.

Either way, I guess that fixing this "properly" would probably require to binary-patch the original executables to not use those statically linked linker/libc functions anymore, which certainly sounds challenging.

sulix commented 2 years ago

I've not found glibc to be the main problem with the .dynamic versions of the Loki executables: it's the libstdc++ stuff which causes more problems.

That being said, there's no way you're going to get sdl12-compat working with those "Loki compat libraries": you'd need to recompile everything (sdl12-compat, your system graphics drivers, etc) against them, which probably wouldn't work.

My strategy has been to run the game against your normal system libc, but patch out the few bits of libstdc++ which are broken:

To then run in under sdl12-compat, you'll need to load it in, as well as libX11.so.6, as Alpha Centauri expects that SDL1.2 links that unconditionally:

LD_LIBRARY_PATH=.:~/Development/sdl12-compat/build32 LD_PRELOAD=libcivctp_wrapper.so:libX11.so.6  ./smac.dynamic-fixed

You can also use patchelf to add the library dependencies with:

patchelf --add-needed libcivctp_wrapper.so --add-needed libX11.so.6 --add-rpath "\$ORIGIN" smac.dynamic-fixed

That's enough to get SMAC to boot under sdl12-compat for me, though it's likely going to be a bit unstable (particularly if it does anything multithreaded with the rendering like civctp does.)

uralets commented 2 years ago

Thanks for a great peace of hack-fu.

Also seems to work without sticking libX11 in, linked in down the road as usual...

uralets commented 2 years ago

not too functional unfortunately (neither tho is the static compiled-in binary)

icculus commented 2 years ago

Multithreaded SDL_UpdateRects(): CivCTP calls SDL_UpdateRects() from two different threads, only one of which is the original thread. Under wayland (and nvidia+x11), this results in eglMakeCurrent failing in the renderer, and the texture locking/reupload failing a lot of the time (mouse cursor only visible sometimes, YUV overlays show up green, etc). It might be contributing to the random crashing in the renderer/Mesa on X11/Intel, too. Maybe we should keep a shadow surface in local memory (without needing to call into GL to lock it each call), and then defer the actual updates to present, which we can defer to the main thread? Though that might not work, as the "main" thread might not be doing any SDL calls at all during some parts of the game…

@sulix When you get a moment, can you see if the latest in revision control fixes this specific issue? I think it might.

icculus commented 2 years ago

Stack realignment: CivCTP's stacks are 4-byte aligned. Most distros are built with 16-byte aligned stacks to use SSE. Use attribute((force_align_arg_pointer)) to realign stacks in sdl12-compat to bridge this gap.

I don't mind adding this, but I can't coerce GCC to output different code, either with this attribute or -mstackrealign on the gcc command line, at least with a little test program.

This is the current patch. If it works for you, regardless of my test program, let's put it in revision control, or something like it.

EDIT: Proposed patch hidden in here EDIT: I was getting carpal tunnel scrolling past this to find bug info, so I hid it with details/summary tags. ```diff diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c index d89a5b1..cee0328 100644 --- a/src/SDL12_compat.c +++ b/src/SDL12_compat.c @@ -80,6 +80,18 @@ extern "C" { #endif +/* on x86 Linux builds, we have the public entry points force stack alignment to 16 bytes + on entry. This won't be a massive performance hit, but it might help extremely old + binaries that want to call into SDL to not crash in hard-to-diagnose ways. It's not a + panacea to the stack alignment problem, but it might help a little. */ +#if defined(__linux__) && defined(__i386__) && (defined(__GNUC__) || defined(__clang__)) +#define FORCEALIGNATTR __attribute__((force_align_arg_pointer)) +#else +#define FORCEALIGNATTR __attribute__((force_align_arg_pointer)) +#endif + +#define DECLSPEC12 DECLSPEC FORCEALIGNATTR + #define SDL20_SYM(rc,fn,params,args,ret) \ typedef rc (SDLCALL *SDL20_##fn##_t) params; \ static SDL20_##fn##_t SDL20_##fn = NULL; @@ -87,7 +99,7 @@ extern "C" { /* Things that _should_ be binary compatible pass right through... */ #define SDL20_SYM_PASSTHROUGH(rc,fn,params,args,ret) \ - DECLSPEC rc SDLCALL SDL_##fn params { ret SDL20_##fn args; } + DECLSPEC12 rc SDLCALL SDL_##fn params { ret SDL20_##fn args; } #include "SDL20_syms.h" @@ -1430,21 +1442,21 @@ unsigned _System LibMain(unsigned hmod, unsigned termination) * SDL2 doesn't define SDL_MAIN_NEEDED for _WIN32, * therefore no need to call SDL_SetMainReady(). */ -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetModuleHandle(void *handle) { (void) handle;/* handled internally by SDL2 - nothing to do.. */ } #endif -DECLSPEC const SDL_version * SDLCALL +DECLSPEC12 const SDL_version * SDLCALL SDL_Linked_Version(void) { static const SDL_version version = { 1, 2, SDL12_COMPAT_VERSION }; return &version; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_sscanf(const char *text, const char *fmt, ...) { int retval; @@ -1455,7 +1467,7 @@ SDL_sscanf(const char *text, const char *fmt, ...) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...) { int retval; @@ -1466,7 +1478,7 @@ SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...) return retval; } -DECLSPEC void * SDLCALL +DECLSPEC12 void * SDLCALL SDL_revcpy(void *_dst, const void *_src, size_t len) { if (len > 0) { @@ -1543,13 +1555,13 @@ static int get_cpu_ext_features(void) { return cpu_ext_features; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_HasMMXExt(void) { return (get_cpu_ext_features() & 0x00400000)? SDL_TRUE : SDL_FALSE; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_Has3DNowExt(void) { return (get_cpu_ext_features() & 0x40000000)? SDL_TRUE : SDL_FALSE; @@ -1687,13 +1699,13 @@ Quit12Joystick(void) NumJoysticks = 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_NumJoysticks(void) { return NumJoysticks; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickNumAxes(SDL12_Joystick *stick12) { if (BogusJoystick(stick12)) { @@ -1702,7 +1714,7 @@ SDL_JoystickNumAxes(SDL12_Joystick *stick12) return JoysticksAreGameControllers ? (SDL_CONTROLLER_AXIS_MAX + 1) : SDL20_JoystickNumAxes(stick12->dev.joystick); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickNumBalls(SDL12_Joystick *stick12) { if (BogusJoystick(stick12)) { @@ -1711,7 +1723,7 @@ SDL_JoystickNumBalls(SDL12_Joystick *stick12) return JoysticksAreGameControllers ? 0 : SDL20_JoystickNumBalls(stick12->dev.joystick); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickNumHats(SDL12_Joystick *stick12) { if (BogusJoystick(stick12)) { @@ -1720,7 +1732,7 @@ SDL_JoystickNumHats(SDL12_Joystick *stick12) return JoysticksAreGameControllers ? 0 : SDL20_JoystickNumHats(stick12->dev.joystick); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickNumButtons(SDL12_Joystick *stick12) { if (BogusJoystick(stick12)) { @@ -1729,7 +1741,7 @@ SDL_JoystickNumButtons(SDL12_Joystick *stick12) return JoysticksAreGameControllers ? (SDL_CONTROLLER_BUTTON_MAX + 1) : SDL20_JoystickNumButtons(stick12->dev.joystick); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_JoystickUpdate(void) { if (JoysticksAreGameControllers) { @@ -1739,7 +1751,7 @@ SDL_JoystickUpdate(void) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickEventState(int state) { switch (state) { @@ -1757,7 +1769,7 @@ SDL_JoystickEventState(int state) } } -DECLSPEC Sint16 SDLCALL +DECLSPEC12 Sint16 SDLCALL SDL_JoystickGetAxis(SDL12_Joystick *stick12, int axis) { if (BogusJoystick(stick12)) { @@ -1766,7 +1778,7 @@ SDL_JoystickGetAxis(SDL12_Joystick *stick12, int axis) return JoysticksAreGameControllers ? SDL20_GameControllerGetAxis(stick12->dev.controller, axis) : SDL20_JoystickGetAxis(stick12->dev.joystick, axis); } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_JoystickGetHat(SDL12_Joystick *stick12, int hat) { if (BogusJoystick(stick12)) { @@ -1775,7 +1787,7 @@ SDL_JoystickGetHat(SDL12_Joystick *stick12, int hat) return JoysticksAreGameControllers ? 0 : SDL20_JoystickGetHat(stick12->dev.joystick, hat); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickGetBall(SDL12_Joystick *stick12, int ball, int *dx, int *dy) { if (BogusJoystick(stick12)) { @@ -1788,7 +1800,7 @@ SDL_JoystickGetBall(SDL12_Joystick *stick12, int ball, int *dx, int *dy) return SDL20_JoystickGetBall(stick12->dev.joystick, ball, dx, dy); } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_JoystickGetButton(SDL12_Joystick *stick12, int button) { if (BogusJoystick(stick12)) { @@ -1797,7 +1809,7 @@ SDL_JoystickGetButton(SDL12_Joystick *stick12, int button) return JoysticksAreGameControllers ? SDL20_GameControllerGetButton(stick12->dev.controller, button) : SDL20_JoystickGetButton(stick12->dev.joystick, button); } -DECLSPEC SDL12_Joystick * SDLCALL +DECLSPEC12 SDL12_Joystick * SDLCALL SDL_JoystickOpen(int device_index) { if (BogusJoystickIndex(device_index)) { @@ -1809,7 +1821,7 @@ SDL_JoystickOpen(int device_index) return &JoystickList[device_index]; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_JoystickClose(SDL12_Joystick *stick12) { if (!BogusJoystick(stick12)) { @@ -1820,19 +1832,19 @@ SDL_JoystickClose(SDL12_Joystick *stick12) } } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_JoystickName(int device_index) { return BogusJoystickIndex(device_index) ? NULL : JoystickList[device_index].name; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickIndex(SDL12_Joystick *stick12) { return BogusJoystick(stick12) ? -1 : (int) (stick12 - JoystickList); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_JoystickOpened(int device_index) { if (BogusJoystickIndex(device_index)) { @@ -2128,7 +2140,7 @@ Init12VidModes(void) /* we should have a default cursor */ #include "default_cursor.h" -DECLSPEC void SDLCALL SDL_FreeCursor(SDL12_Cursor *); +DECLSPEC12 void SDLCALL SDL_FreeCursor(SDL12_Cursor *); static int HasWmAvailable(const char *driver) @@ -2165,7 +2177,7 @@ HasWmAvailable(const char *driver) return 0; } -DECLSPEC int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); +DECLSPEC12 int SDLCALL SDL_EnableKeyRepeat(int delay, int interval); static int Init12Video(void) @@ -2237,7 +2249,7 @@ Init12Video(void) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_VideoInit(const char *driver, Uint32 flags) { int retval; @@ -2259,7 +2271,7 @@ static void InitializeCDSubsystem(void); static void QuitCDSubsystem(void); -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_InitSubSystem(Uint32 sdl12flags) { Uint32 sdl20flags = 0; @@ -2343,7 +2355,7 @@ SDL_InitSubSystem(Uint32 sdl12flags) return rc; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_Init(Uint32 sdl12flags) { return SDL_InitSubSystem(sdl12flags); /* there's no difference betwee Init and InitSubSystem in SDL2. */ @@ -2389,7 +2401,7 @@ InitFlags20to12(const Uint32 flags20) } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_WasInit(Uint32 sdl12flags) { Uint32 sdl20flags, extraflags; @@ -2438,7 +2450,7 @@ Quit12Video(void) EventThreadEnabled = SDL_FALSE; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_QuitSubSystem(Uint32 sdl12flags) { Uint32 sdl20flags, extraflags; @@ -2478,20 +2490,20 @@ SDL_QuitSubSystem(Uint32 sdl12flags) InitializedSubsystems20 &= ~SDL_INIT_NOPARACHUTE; /* SDL2 accepts this flag but ignores it. */ } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_Quit(void) { SDL_QuitSubSystem(SDL_WasInit(0) | SDL12_INIT_CDROM); SDL_assert((InitializedSubsystems20 == 0) || (SDL12Compat_GetHintBoolean("SDL12COMPAT_NO_QUIT_VIDEO", SDL_FALSE) && (InitializedSubsystems20 == SDL_INIT_VIDEO))); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_Error(SDL_errorcode error) { SDL20_Error(error); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetError(const char *fmt, ...) { char ch; @@ -2515,7 +2527,7 @@ SDL_SetError(const char *fmt, ...) } } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_GetError(void) { if (SDL20_GetError == NULL) { @@ -2540,13 +2552,13 @@ GetDriverName(const char *name, char *namebuf, int maxlen) return NULL; } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_AudioDriverName(char *namebuf, int maxlen) { return GetDriverName(SDL20_GetCurrentAudioDriver(), namebuf, maxlen); } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_VideoDriverName(char *namebuf, int maxlen) { return GetDriverName(SDL20_GetCurrentVideoDriver(), namebuf, maxlen); @@ -2579,7 +2591,7 @@ SDL_PollEvent_locked(SDL12_Event *event12) return 1; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_PollEvent(SDL12_Event *event12) { int retval; @@ -2623,7 +2635,7 @@ SDL_PushEvent_locked(SDL12_Event *event12) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_PushEvent(SDL12_Event *event12) { int retval; @@ -2716,7 +2728,7 @@ SDL_PeepEvents_locked(SDL12_Event *events12, int numevents, SDL_eventaction acti return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_PeepEvents(SDL12_Event *events12, int numevents, SDL_eventaction action, Uint32 mask) { int retval; @@ -2732,7 +2744,7 @@ SDL_PeepEvents(SDL12_Event *events12, int numevents, SDL_eventaction action, Uin return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_WaitEvent(SDL12_Event *event12) { if (!EventQueueMutex) { @@ -2764,7 +2776,7 @@ PushEventIfNotFiltered(SDL12_Event *event12) return retval; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_EventState(Uint8 type, int state) { Uint8 retval = 0; @@ -2913,7 +2925,7 @@ static Uint8 MouseButtonState20to12(const Uint32 state20) return retval; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetMouseState(int *x, int *y) { const Uint8 buttons = MouseButtonState20to12(SDL20_GetMouseState(x, y)); @@ -2922,13 +2934,13 @@ SDL_GetMouseState(int *x, int *y) return buttons; } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetRelativeMouseState(int *x, int *y) { return MouseButtonState20to12(SDL20_GetRelativeMouseState(x, y)); } -DECLSPEC char * SDLCALL +DECLSPEC12 char * SDLCALL SDL_GetKeyName(SDL12Key key) { switch (key) { @@ -4194,7 +4206,7 @@ Scancode20to12(SDL_Scancode sc) } } -DECLSPEC Uint8 * SDLCALL +DECLSPEC12 Uint8 * SDLCALL SDL_GetKeyState(int *numkeys) { if (numkeys) { @@ -4653,14 +4665,14 @@ EventFilter20to12(void *data, SDL_Event *event20) return 1; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetEventFilter(SDL12_EventFilter filter12) { /* We always have a filter installed, but will call the app's too. */ EventFilter12 = filter12; } -DECLSPEC SDL12_EventFilter SDLCALL +DECLSPEC12 SDL12_EventFilter SDLCALL SDL_GetEventFilter(void) { return EventFilter12; @@ -4851,7 +4863,7 @@ SetPalette12ForMasks(SDL12_Surface *surface12, const Uint32 Rmask, const Uint32 } } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { SDL_Surface *surface20; @@ -4902,7 +4914,7 @@ SDL_CreateRGBSurface(Uint32 flags12, int width, int height, int depth, Uint32 Rm return surface12; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) { SDL_Surface *surface20; @@ -4932,7 +4944,7 @@ SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pit return surface12; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeSurface(SDL12_Surface *surface12) { if (surface12 && (surface12 != VideoSurface12)) { @@ -4948,7 +4960,7 @@ SDL_FreeSurface(SDL12_Surface *surface12) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetClipRect(SDL12_Surface *surface12, SDL12_Rect *rect) { if (surface12 && rect) { @@ -4956,7 +4968,7 @@ SDL_GetClipRect(SDL12_Surface *surface12, SDL12_Rect *rect) } } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_SetClipRect(SDL12_Surface *surface12, const SDL12_Rect *rect12) { SDL_bool retval = SDL_FALSE; @@ -4969,7 +4981,7 @@ SDL_SetClipRect(SDL12_Surface *surface12, const SDL12_Rect *rect12) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_FillRect(SDL12_Surface *dst, SDL12_Rect *dstrect12, Uint32 color) { SDL_Rect dstrect20; @@ -4984,7 +4996,7 @@ SDL_FillRect(SDL12_Surface *dst, SDL12_Rect *dstrect12, Uint32 color) return retval; } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_MapRGB(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b) { /* This is probably way slower than apps expect. */ @@ -4993,7 +5005,7 @@ SDL_MapRGB(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b) return SDL20_MapRGB(PixelFormat12to20(&format20, &palette20, format12), r, g, b); } -DECLSPEC Uint32 SDLCALL +DECLSPEC12 Uint32 SDLCALL SDL_MapRGBA(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b, Uint8 a) { /* This is probably way slower than apps expect. */ @@ -5002,7 +5014,7 @@ SDL_MapRGBA(const SDL12_PixelFormat *format12, Uint8 r, Uint8 g, Uint8 b, Uint8 return SDL20_MapRGBA(PixelFormat12to20(&format20, &palette20, format12), r, g, b, a); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetRGB(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, Uint8 *b) { /* This is probably way slower than apps expect. */ @@ -5011,7 +5023,7 @@ SDL_GetRGB(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, SDL20_GetRGB(pixel, PixelFormat12to20(&format20, &palette20, format12), r, g, b); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetRGBA(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) { /* This is probably way slower than apps expect. */ @@ -5020,13 +5032,13 @@ SDL_GetRGBA(Uint32 pixel, const SDL12_PixelFormat *format12, Uint8 *r, Uint8 *g, SDL20_GetRGBA(pixel, PixelFormat12to20(&format20, &palette20, format12), r, g, b, a); } -DECLSPEC const SDL12_VideoInfo * SDLCALL +DECLSPEC12 const SDL12_VideoInfo * SDLCALL SDL_GetVideoInfo(void) { return VideoInfo12.vfmt ? &VideoInfo12 : NULL; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_VideoModeOK(int width, int height, int bpp, Uint32 sdl12flags) { int i, j, actual_bpp = 0; @@ -5061,7 +5073,7 @@ SDL_VideoModeOK(int width, int height, int bpp, Uint32 sdl12flags) return (actual_bpp == 24) ? 32 : actual_bpp; } -DECLSPEC SDL12_Rect ** SDLCALL +DECLSPEC12 SDL12_Rect ** SDLCALL SDL_ListModes(const SDL12_PixelFormat *format12, Uint32 flags) { VideoModeList *best_modes = NULL; @@ -5112,7 +5124,7 @@ SDL_ListModes(const SDL12_PixelFormat *format12, Uint32 flags) return best_modes->modes12; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeCursor(SDL12_Cursor *cursor12) { if (cursor12 == CurrentCursor12) { @@ -5128,7 +5140,7 @@ SDL_FreeCursor(SDL12_Cursor *cursor12) } } -DECLSPEC SDL12_Cursor * SDLCALL +DECLSPEC12 SDL12_Cursor * SDLCALL SDL_CreateCursor(Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y) { const size_t datasize = h * (w / 8); @@ -5177,14 +5189,14 @@ failed: return NULL; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_SetCursor(SDL12_Cursor *cursor) { CurrentCursor12 = cursor; SDL20_SetCursor(cursor ? cursor->wm_cursor : NULL); } -DECLSPEC SDL12_Cursor * SDLCALL +DECLSPEC12 SDL12_Cursor * SDLCALL SDL_GetCursor(void) { if (!CurrentCursor12) { @@ -5600,7 +5612,7 @@ static void ResetVideoRendererForThreading(void) static void HandleInputGrab(SDL12_GrabMode mode); -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) { SDL_DisplayMode dmode; @@ -6035,7 +6047,7 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12) return VideoSurface12; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_GetVideoSurface(void) { return VideoSurface12; @@ -6138,7 +6150,7 @@ RestoreDestAlpha(SDL12_Surface *dst12, Uint8 *dstalpha, const SDL12_Rect *dstrec } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) { Uint8 *dstalpha; @@ -6165,7 +6177,7 @@ SDL_UpperBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LowerBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) { Uint8 *dstalpha; @@ -6194,7 +6206,7 @@ SDL_LowerBlit(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SoftStretch(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst12, SDL12_Rect *dstrect12) { SDL_Rect srcrect20, dstrect20; @@ -6204,7 +6216,7 @@ SDL_SoftStretch(SDL12_Surface *src12, SDL12_Rect *srcrect12, SDL12_Surface *dst1 dstrect12 ? Rect12to20(dstrect12, &dstrect20) : NULL); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetAlpha(SDL12_Surface *surface12, Uint32 flags12, Uint8 value) { /* note that SDL 1.2 does not check if surface12 is NULL before dereferencing it either */ @@ -6234,7 +6246,7 @@ SDL_SetAlpha(SDL12_Surface *surface12, Uint32 flags12, Uint8 value) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LockSurface(SDL12_Surface *surface12) { const int retval = SDL20_LockSurface(surface12->surface20); @@ -6243,7 +6255,7 @@ SDL_LockSurface(SDL12_Surface *surface12) return retval; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UnlockSurface(SDL12_Surface *surface12) { SDL20_UnlockSurface(surface12->surface20); @@ -6251,7 +6263,7 @@ SDL_UnlockSurface(SDL12_Surface *surface12) surface12->pitch = surface12->surface20->pitch; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_ConvertSurface(SDL12_Surface *src12, const SDL12_PixelFormat *format12, Uint32 flags12) { Uint32 flags20 = 0; @@ -6278,7 +6290,7 @@ SDL_ConvertSurface(SDL12_Surface *src12, const SDL12_PixelFormat *format12, Uint return retval; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_DisplayFormat(SDL12_Surface *surface12) { const Uint32 flags = surface12->flags & (SDL12_SRCCOLORKEY|SDL12_SRCALPHA|SDL12_RLEACCELOK); @@ -6290,7 +6302,7 @@ SDL_DisplayFormat(SDL12_Surface *surface12) return SDL_ConvertSurface(surface12, VideoSurface12->format, flags); } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_DisplayFormatAlpha(SDL12_Surface *surface12) { const Uint32 flags = surface12->flags & (SDL12_SRCALPHA|SDL12_RLEACCELOK); @@ -6393,7 +6405,7 @@ GetDesiredMillisecondsPerFrame() } /* SDL_OPENGLBLIT support APIs. https://discourse.libsdl.org/t/ogl-and-sdl/2775/3 */ -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_Lock(void) { if (!OpenGLBlitTexture) { @@ -6436,7 +6448,7 @@ SDL_GL_Lock(void) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_UpdateRects(int numrects, SDL12_Rect *rects12) { if (OpenGLBlitTexture) { @@ -6486,7 +6498,7 @@ SDL_GL_UpdateRects(int numrects, SDL12_Rect *rects12) } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_Unlock(void) { if (OpenGLBlitTexture) { @@ -6503,7 +6515,7 @@ SDL_GL_Unlock(void) } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12) { /* strangely, SDL 1.2 doesn't check if surface12 is NULL before touching it */ @@ -6583,7 +6595,7 @@ SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UpdateRect(SDL12_Surface *screen12, Sint32 x, Sint32 y, Uint32 w, Uint32 h) { if (screen12) { @@ -6596,7 +6608,7 @@ SDL_UpdateRect(SDL12_Surface *screen12, Sint32 x, Sint32 y, Uint32 w, Uint32 h) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_Flip(SDL12_Surface *surface12) { if (surface12 == VideoSurface12) { @@ -6625,7 +6637,7 @@ HandleKeyRepeat(void) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_PumpEvents(void) { SDL_Event e; @@ -6658,7 +6670,7 @@ SDL_PumpEvents(void) } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_SetCaption(const char *title, const char *icon) { if (WindowTitle) { @@ -6674,7 +6686,7 @@ SDL_WM_SetCaption(const char *title, const char *icon) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_GetCaption(const char **title, const char **icon) { if (title) { @@ -6685,7 +6697,7 @@ SDL_WM_GetCaption(const char **title, const char **icon) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) { SDL_BlendMode oldmode; @@ -6744,7 +6756,7 @@ SDL_WM_SetIcon(SDL12_Surface *icon12, Uint8 *mask) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_WM_IconifyWindow(void) { if (VideoWindow20) { @@ -6753,7 +6765,7 @@ SDL_WM_IconifyWindow(void) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_WM_ToggleFullScreen(SDL12_Surface *surface) { int retval = 0; @@ -6803,7 +6815,7 @@ UpdateRelativeMouseMode(void) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_ShowCursor(int toggle) { const int retval = VideoCursorHidden ? 0 : 1; @@ -6834,7 +6846,7 @@ HandleInputGrab(SDL12_GrabMode mode) } } -DECLSPEC SDL12_GrabMode SDLCALL +DECLSPEC12 SDL12_GrabMode SDLCALL SDL_WM_GrabInput(SDL12_GrabMode mode) { if (mode != SDL12_GRAB_QUERY) { @@ -6843,7 +6855,7 @@ SDL_WM_GrabInput(SDL12_GrabMode mode) return VideoWindowGrabbed ? SDL12_GRAB_ON : SDL12_GRAB_OFF; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_WarpMouse(Uint16 x, Uint16 y) { if (MouseInputIsRelative) { /* we have to track this ourselves, in case app calls SDL_GetMouseState(). */ @@ -6856,7 +6868,7 @@ SDL_WarpMouse(Uint16 x, Uint16 y) } } -DECLSPEC Uint8 SDLCALL +DECLSPEC12 Uint8 SDLCALL SDL_GetAppState(void) { Uint8 state12 = 0; @@ -6875,7 +6887,7 @@ SDL_GetAppState(void) return state12; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetColorKey(SDL12_Surface *surface12, Uint32 flag12, Uint32 key) { const SDL_bool addkey = (flag12 & SDL12_SRCCOLORKEY) ? SDL_TRUE : SDL_FALSE; @@ -6893,7 +6905,7 @@ SDL_SetColorKey(SDL12_Surface *surface12, Uint32 flag12, Uint32 key) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, int firstcolor, int ncolors) { @@ -6959,7 +6971,7 @@ SDL_SetPalette(SDL12_Surface *surface12, int flags, const SDL_Color *colors, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetColors(SDL12_Surface *surface12, const SDL_Color * colors, int firstcolor, int ncolors) { @@ -6973,7 +6985,7 @@ static void x11_lock_display(void) {} static void x11_unlock_display(void) {} #endif -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GetWMInfo(SDL12_SysWMinfo *info12) { SDL_SysWMinfo info20; @@ -7146,7 +7158,7 @@ SDL_GetWMInfo(SDL12_SysWMinfo *info12) return 1; } -DECLSPEC SDL12_Overlay * SDLCALL +DECLSPEC12 SDL12_Overlay * SDLCALL SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) { /* SDL 1.2 has you pass the screen surface in here, but it doesn't check that it's _actually_ the screen surface, @@ -7235,7 +7247,7 @@ SDL_CreateYUVOverlay(int w, int h, Uint32 format12, SDL12_Surface *display12) return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_LockYUVOverlay(SDL12_Overlay *overlay12) { SDL12_YUVData *hwdata; @@ -7258,7 +7270,7 @@ SDL_LockYUVOverlay(SDL12_Overlay *overlay12) return 0; /* success */ } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12) { QueuedOverlayItem *overlay; @@ -7332,7 +7344,7 @@ SDL_DisplayYUVOverlay(SDL12_Overlay *overlay12, SDL12_Rect *dstrect12) return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_UnlockYUVOverlay(SDL12_Overlay *overlay12) { if (overlay12) { @@ -7340,7 +7352,7 @@ SDL_UnlockYUVOverlay(SDL12_Overlay *overlay12) } } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeYUVOverlay(SDL12_Overlay *overlay12) { if (overlay12) { @@ -7358,7 +7370,7 @@ SDL_FreeYUVOverlay(SDL12_Overlay *overlay12) } } -DECLSPEC void * SDLCALL +DECLSPEC12 void * SDLCALL SDL_GL_GetProcAddress(const char *sym) { /* see comments on glBindFramebuffer_shim_for_scaling for explanation */ @@ -7391,7 +7403,7 @@ SDL_GL_GetProcAddress(const char *sym) return SDL20_GL_GetProcAddress(sym); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_LoadLibrary(const char *libname) { /* SDL 1.2 would unload the previous library if one was loaded. SDL2 @@ -7422,7 +7434,7 @@ SDL_GL_LoadLibrary(const char *libname) } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_SetAttribute(SDL12_GLattr attr, int value) { if (attr >= SDL12_GL_MAX_ATTRIBUTE) { @@ -7444,7 +7456,7 @@ SDL_GL_SetAttribute(SDL12_GLattr attr, int value) return SDL20_GL_SetAttribute((SDL_GLattr) attr, value); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GL_GetAttribute(SDL12_GLattr attr, int* value) { int retval; @@ -7481,7 +7493,7 @@ SDL_GL_GetAttribute(SDL12_GLattr attr, int* value) } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_SwapBuffers(void) { if (VideoWindow20) { @@ -7539,7 +7551,7 @@ SDL_GL_SwapBuffers(void) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetGamma(float red, float green, float blue) { Uint16 red_ramp[256]; @@ -7563,19 +7575,19 @@ SDL_SetGamma(float red, float green, float blue) return SDL20_SetWindowGammaRamp(VideoWindow20, red_ramp, green_ramp, blue_ramp); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetGammaRamp(const Uint16 *red, const Uint16 *green, const Uint16 *blue) { return SDL20_SetWindowGammaRamp(VideoWindow20, red, green, blue); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_GetGammaRamp(Uint16 *red, Uint16 *green, Uint16 *blue) { return SDL20_GetWindowGammaRamp(VideoWindow20, red, green, blue); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_EnableKeyRepeat(int delay, int interval) { if ((delay < 0) || (interval < 0)) { @@ -7589,7 +7601,7 @@ SDL_EnableKeyRepeat(int delay, int interval) return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GetKeyRepeat(int *delay, int *interval) { if (delay) { @@ -7600,7 +7612,7 @@ SDL_GetKeyRepeat(int *delay, int *interval) } } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_EnableUNICODE(int enable) { const int old = EnabledUnicode; @@ -7629,7 +7641,7 @@ SetTimerCallback12(Uint32 interval, void* param) return RoundTimerTo12Resolution(((SDL12_TimerCallback)param)(interval)); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SetTimer(Uint32 interval, SDL12_TimerCallback callback) { static SDL_TimerID compat_timer; @@ -7649,7 +7661,7 @@ SDL_SetTimer(Uint32 interval, SDL12_TimerCallback callback) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_putenv(const char *_var) { char *ptr = NULL; @@ -7686,20 +7698,20 @@ SDL_putenv(const char *_var) * each other. * * Therefore, we have to do the following trick below. */ -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) { return SDL20_CreateThread(fn, NULL, data, NULL, NULL); } #else -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread) { return SDL20_CreateThread(fn, NULL, data, pfnBeginThread, pfnEndThread); } #endif #else -DECLSPEC SDL_Thread * SDLCALL +DECLSPEC12 SDL_Thread * SDLCALL SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) { return SDL20_CreateThread(fn, NULL, data); @@ -7708,28 +7720,28 @@ SDL_CreateThread(int (SDLCALL *fn)(void *), void *data) /* These two will truncate the returned value on LP64 systems, * a shortcoming of SDL-1.2. */ -DECLSPEC Uint32 SDLCALL SDL_ThreadID(void) +DECLSPEC12 Uint32 SDLCALL SDL_ThreadID(void) { return SDL20_ThreadID(); } -DECLSPEC Uint32 SDLCALL SDL_GetThreadID(SDL_Thread *thread) +DECLSPEC12 Uint32 SDLCALL SDL_GetThreadID(SDL_Thread *thread) { return SDL20_GetThreadID(thread); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_mutexP(SDL_mutex *mutex) { return SDL20_LockMutex(mutex); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_mutexV(SDL_mutex *mutex) { return SDL20_UnlockMutex(mutex); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_KillThread(SDL_Thread *thread) { (void)thread; @@ -7744,7 +7756,7 @@ AddTimerCallback12(Uint32 interval, void *param) return RoundTimerTo12Resolution(data->callback(interval, data->param)); } -DECLSPEC SDL12_TimerID SDLCALL +DECLSPEC12 SDL12_TimerID SDLCALL SDL_AddTimer(Uint32 interval, SDL12_NewTimerCallback callback, void *param) { SDL12_TimerID data = (SDL12_TimerID) SDL20_malloc(sizeof (SDL12_TimerID_Data)); @@ -7781,7 +7793,7 @@ SDL_AddTimer(Uint32 interval, SDL12_NewTimerCallback callback, void *param) return data; } -DECLSPEC SDL_bool SDLCALL +DECLSPEC12 SDL_bool SDLCALL SDL_RemoveTimer(SDL12_TimerID data) { SDL_bool retval = SDL_FALSE; @@ -7837,7 +7849,7 @@ typedef struct SDL12_RWops { } SDL12_RWops; -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_AllocRW(void) { SDL12_RWops *rwops = (SDL12_RWops *) SDL20_malloc(sizeof (SDL12_RWops)); @@ -7847,7 +7859,7 @@ SDL_AllocRW(void) return rwops; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_FreeRW(SDL12_RWops *rwops12) { SDL20_free(rwops12); @@ -7909,7 +7921,7 @@ RWops20to12(SDL_RWops *rwops20) return rwops12; } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromFile(const char *file, const char *mode) { if (!file || !*file || !mode || !*mode) { @@ -7919,26 +7931,26 @@ SDL_RWFromFile(const char *file, const char *mode) return RWops20to12(SDL20_RWFromFile(file, mode)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromFP(void *io, int autoclose) { return RWops20to12(SDL20_RWFromFP(io, autoclose)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromMem(void *mem, int size) { return RWops20to12(SDL20_RWFromMem(mem, size)); } -DECLSPEC SDL12_RWops * SDLCALL +DECLSPEC12 SDL12_RWops * SDLCALL SDL_RWFromConstMem(const void *mem, int size) { return RWops20to12(SDL20_RWFromConstMem(mem, size)); } #define READ_AND_BYTESWAP(endian, bits) \ - DECLSPEC Uint##bits SDLCALL SDL_Read##endian##bits(SDL12_RWops *rwops12) { \ + DECLSPEC12 Uint##bits SDLCALL SDL_Read##endian##bits(SDL12_RWops *rwops12) { \ Uint##bits val; rwops12->read(rwops12, &val, sizeof (val), 1); \ return SDL_Swap##endian##bits(val); \ } @@ -7952,7 +7964,7 @@ READ_AND_BYTESWAP(BE,64) #undef READ_AND_BYTESWAP #define BYTESWAP_AND_WRITE(endian, bits) \ - DECLSPEC int SDLCALL SDL_Write##endian##bits(SDL12_RWops *rwops12, Uint##bits val) { \ + DECLSPEC12 int SDLCALL SDL_Write##endian##bits(SDL12_RWops *rwops12, Uint##bits val) { \ val = SDL_Swap##endian##bits(val); \ return rwops12->write(rwops12, &val, sizeof (val), 1); \ } @@ -8056,7 +8068,7 @@ RWops12to20(SDL12_RWops *rwops12) return rwops20; } -DECLSPEC SDL12_Surface * SDLCALL +DECLSPEC12 SDL12_Surface * SDLCALL SDL_LoadBMP_RW(SDL12_RWops *rwops12, int freerwops12) { SDL_RWops *rwops20 = RWops12to20(rwops12); @@ -8071,7 +8083,7 @@ SDL_LoadBMP_RW(SDL12_RWops *rwops12, int freerwops12) return surface12; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_SaveBMP_RW(SDL12_Surface *surface12, SDL12_RWops *rwops12, int freerwops12) { SDL_RWops *rwops20 = RWops12to20(rwops12); @@ -8082,7 +8094,7 @@ SDL_SaveBMP_RW(SDL12_Surface *surface12, SDL12_RWops *rwops12, int freerwops12) return retval; } -DECLSPEC SDL_AudioSpec * SDLCALL +DECLSPEC12 SDL_AudioSpec * SDLCALL SDL_LoadWAV_RW(SDL12_RWops *rwops12, int freerwops12, SDL_AudioSpec *spec, Uint8 **buf, Uint32 *len) { @@ -8248,7 +8260,7 @@ QuitCDSubsystem(void) CDRomInit = SDL_FALSE; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDNumDrives(void) { if (!CDSubsystemIsInitialized()) { @@ -8284,13 +8296,13 @@ ValidCDDriveIndex(const int drive) return SDL_TRUE; } -DECLSPEC const char * SDLCALL +DECLSPEC12 const char * SDLCALL SDL_CDName(int drive) { return ValidCDDriveIndex(drive) ? CDRomPath : NULL; } -DECLSPEC SDL12_CD * SDLCALL +DECLSPEC12 SDL12_CD * SDLCALL SDL_CDOpen(int drive) { SDL12_CD *retval; @@ -8451,7 +8463,7 @@ ValidCDDevice(SDL12_CD *cdrom) } -DECLSPEC SDL12_CDstatus SDLCALL +DECLSPEC12 SDL12_CDstatus SDLCALL SDL_CDStatus(SDL12_CD *cdrom) { SDL12_CDstatus retval; @@ -8546,7 +8558,7 @@ StartCDAudioPlaying(SDL12_CD *cdrom, const int start_track, const int start_fram } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDPlayTracks(SDL12_CD *cdrom, int start_track, int start_frame, int ntracks, int nframes) { if ((cdrom = ValidCDDevice(cdrom)) == NULL) { @@ -8571,7 +8583,7 @@ SDL_CDPlayTracks(SDL12_CD *cdrom, int start_track, int start_frame, int ntracks, return StartCDAudioPlaying(cdrom, start_track, start_frame, ntracks, nframes); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDPlay(SDL12_CD *cdrom, int start, int length) { const Uint32 ui32start = (Uint32) start; @@ -8630,7 +8642,7 @@ SDL_CDPlay(SDL12_CD *cdrom, int start, int length) return StartCDAudioPlaying(cdrom, start_track, start_frame, ntracks, nframes); } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDPause(SDL12_CD *cdrom) { if ((cdrom = ValidCDDevice(cdrom)) == NULL) { @@ -8650,7 +8662,7 @@ SDL_CDPause(SDL12_CD *cdrom) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDResume(SDL12_CD *cdrom) { if ((cdrom = ValidCDDevice(cdrom)) == NULL) { @@ -8671,7 +8683,7 @@ SDL_CDResume(SDL12_CD *cdrom) } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDStop(SDL12_CD *cdrom) { SDL_RWops *oldrw = NULL; @@ -8696,7 +8708,7 @@ SDL_CDStop(SDL12_CD *cdrom) return 0; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_CDEject(SDL12_CD *cdrom) { if ((cdrom = ValidCDDevice(cdrom)) == NULL) { @@ -8713,7 +8725,7 @@ SDL_CDEject(SDL12_CD *cdrom) return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_CDClose(SDL12_CD *cdrom) { if ((cdrom = ValidCDDevice(cdrom)) == NULL) { @@ -8996,7 +9008,7 @@ CloseSDL2AudioDevice(void) } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained) { SDL_bool already_opened; @@ -9099,13 +9111,13 @@ SDL_OpenAudio(SDL_AudioSpec *want, SDL_AudioSpec *obtained) return 0; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_PauseAudio(int pause_on) { SDL20_AtomicSet(&audio_callback_paused, pause_on ? SDL_TRUE : SDL_FALSE); } -DECLSPEC SDL_AudioStatus SDLCALL +DECLSPEC12 SDL_AudioStatus SDLCALL SDL_GetAudioStatus(void) { SDL_AudioStatus retval = SDL_AUDIO_STOPPED; @@ -9117,7 +9129,7 @@ SDL_GetAudioStatus(void) return retval; } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_CloseAudio(void) { SDL20_LockAudio(); @@ -9168,7 +9180,7 @@ AudioCVT20to12(const SDL_AudioCVT *cvt20, SDL12_AudioCVT *cvt12) return cvt12; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_BuildAudioCVT(SDL12_AudioCVT *cvt12, Uint16 src_format, Uint8 src_channels, int src_rate, Uint16 dst_format, Uint8 dst_channels, int dst_rate) { SDL_AudioCVT cvt20; @@ -9177,7 +9189,7 @@ SDL_BuildAudioCVT(SDL12_AudioCVT *cvt12, Uint16 src_format, Uint8 src_channels, return retval; } -DECLSPEC int SDLCALL +DECLSPEC12 int SDLCALL SDL_ConvertAudio(SDL12_AudioCVT *cvt12) { /* neither SDL 1.2 nor 2.0 makes sure cvt12 isn't NULL here. :/ */ @@ -9193,13 +9205,13 @@ SDL_ConvertAudio(SDL12_AudioCVT *cvt12) these functions, to let them make a GL context current on a background thread, so we supply them as well to be binary compatible for those games. */ -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_DisableContext(void) { SDL20_GL_MakeCurrent(NULL, NULL); } -DECLSPEC void SDLCALL +DECLSPEC12 void SDLCALL SDL_GL_EnableContext_Thread(void) { const SDL_bool enable = (VideoGLContext20 && VideoWindow20) ? SDL_TRUE : SDL_FALSE; @@ -9216,7 +9228,7 @@ SDL_GL_EnableContext_Thread(void) to Wayland, and if there's some wildly-misbuilt win32 software, they can call it too. :) */ #if !(defined(_WIN32) || defined(__OS2__)) /* #if defined(__unix__) || defined(__APPLE__) ?? */ -DECLSPEC Uint16 SDLCALL +DECLSPEC12 Uint16 SDLCALL X11_KeyToUnicode(SDL12Key key, SDL12Mod mod) { if (((int) key) >= 127) { ```
icculus commented 2 years ago

CD audio / game audio device re-opening: The format conversion between the 22050/mono/8bit and 44100/stereo/16bit big-endian when mixing CD audio and SDL audio is broken if the (real) device is closed/re-opened to pick a better common format. Forcing sdl12-compat to always open the real device at 44100/stereo/16bit works around this, but there's clearly a real bug here.

While I'm just dumping stuff on @sulix here (sorry!), is this still broken? These should both be going through separate SDL_AudioStreams to convert as necessary, but maybe it wasn't at the time? I'll try to roll a simple reproduction case outside of Civ:CTP if this is still hosed for you.

icculus commented 2 years ago

For reopening the audio device, this patch makes loopwave.c play for 5 seconds, then open a CD and start playing it. It...sounds okay to me...?

Here's sample.wav converted to 22050/mono/8-bit:

sample-wav-22050-mono-8bit.zip

So I ran it like this (put any MP3 in the cdrom directory, named "track01.mp3")

SDL12COMPAT_FAKE_CDROM_PATH=/home/icculus/projects/sdl12-compat/cmake-build/cdrom SDL12COMPAT_DEBUG_LOGGING=1 LD_LIBRARY_PATH=. ./loopwave ~/Desktop/sample.wav
diff --git a/test/loopwave.c b/test/loopwave.c
index 414b9ed..ec6cfcf 100644
--- a/test/loopwave.c
+++ b/test/loopwave.c
@@ -64,9 +64,12 @@ int main(int argc, char *argv[])
 {
        char name[32];
        const char *file;
+    int delays = 0;
+    SDL_CD *cdrom = NULL;
+    CDstatus status;

        /* Load the SDL library */
-       if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) {
+       if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_CDROM) < 0 ) {
                fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
                return(1);
        }
@@ -100,12 +103,38 @@ int main(int argc, char *argv[])

        /* Let the audio run */
        printf("Using audio driver: %s\n", SDL_AudioDriverName(name, 32));
-       while ( ! done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) )
+       while ( ! done && (SDL_GetAudioStatus() == SDL_AUDIO_PLAYING) ) {
                SDL_Delay(1000);

+        if (delays < 5) {
+            delays++;
+        } else if (delays == 5) {
+            if (SDL_CDNumDrives() == 0) {
+                printf("No CD drives detected, not trying to play a disc.\n");
+            } else if ((cdrom = SDL_CDOpen(0)) == NULL) {
+                printf("Failed to open CD drive 0: %s\n", SDL_GetError());
+            } else if ((status = SDL_CDStatus(cdrom)) == CD_ERROR) {
+                printf("Failed to get CD status: %s\n", SDL_GetError());
+            } else if (status == CD_TRAYEMPTY) {
+                printf("CD tray is empty, can't play a disc.\n");
+            } else if (SDL_CDPlayTracks(cdrom, 0, 0, 0, 0) == -1) {
+                printf("Failed to play tracks from CD drive 0: %s\n", SDL_GetError());
+            } else {
+                printf("Playing tracks from CD drive 0!\n");
+            }
+            delays++;  /* make this not attempt CD work again. */
+        }
+    }
        /* Clean up on signal */
        SDL_CloseAudio();
        SDL_FreeWAV(wave.sound);
+
+    if (cdrom) {
+        SDL_CDStop(cdrom);
+        SDL_CDClose(cdrom);
+        cdrom = NULL;
+    }
+
        SDL_Quit();
        return(0);
 }
icculus commented 2 years ago

I'm thinking, though, whether this works now or not, it might be better to always open the audio device at CD-ROM quality if the app asks for less, so there's never a scenario where you have to close the audio device and reopen it to accommodate things. You just have to make sure the SDL_AudioStream manages conversion, which we already have to do anyhow.

This would eliminate the gap in audio you hear in that patched loopwave.c, where it has to dump the audio device after 5 seconds to handle CD playback, and also a FIXME that I handwaved away about what to do if the reopening fails and something that was working fine is now screwed.

sulix commented 2 years ago

I started testing this earlier today before getting hit by a few unrelated disasters.

tl;dr, it's still broken.

Alas, I'm going to be on planes and overseas for the next couple of weeks, so won't have too much time to look into this in more detail until I get back. I'll try to give this one more look tomorrow before I leave and upload any relevant stacktraces if I can, though…

icculus commented 2 years ago

CivCTP seems to render from multiple threads (even within the same frame), not just from a single background thread. For example, the mouse cursor seems to be rendered in a different thread from the game, leading to a black screen with mouse trails with the current implementation. SDL_RENDER_DRIVER=software SDL_FRAMEBUFFER_ACCELERATION=false fixes some of those issues, but make the screen terribly flickery, s though rendering directly to the frontbuffer.

So the latest tries to reset the GL context whenever we touch VideoRenderer20, which solves a lot of these problems, but I'm wondering if we should also wrap any access to VideoRenderer20 in a mutex, which might help if these threads are all competing for it.

In theory, dirty rectangles shouldn't move from the software surface to the renderer until it's time to present it on the screen, which should work with this scheme, but how sdl12-compat decides it's time to present is a whole different problem (doubly so if PumpEvents is going wild in (multiple?) other threads), and that might be the part we have to solve somehow, or perhaps wrap in the same mutex we wrap VideoRenderer20 in.

I think I'd tried hacking it to always open in CD quality, and that fixed it, but I'll have to look and see if I still have that hacky experimental branch lying around somewhere.

Just pushed changes to do this, so maybe it's fixed now? :)

Alas, I'm going to be on planes and overseas for the next couple of weeks

No worries, I'm going to bump this out of the milestone for now, and we can revisit it later.

sulix commented 2 years ago

Yup, the CD audio / sound stuff works now.

sulix commented 2 years ago

Stack realignment: CivCTP's stacks are 4-byte aligned. Most distros are built with 16-byte aligned stacks to use SSE. Use attribute((force_align_arg_pointer)) to realign stacks in sdl12-compat to bridge this gap.

I don't mind adding this, but I can't coerce GCC to output different code, either with this attribute or -mstackrealign on the gcc command line, at least with a little test program.

This is the current patch. If it works for you, regardless of my test program, let's put it in revision control, or something like it.

This patch does work for me: without it, CivCTP will crash on startup with a self-compiled SDL2 build, with it, it's fine (or, at least, only as broken as the distro-provided SDL2 build).

I can also see the fixups in the generated code (apart from these instructions at the start, the implementations are the same):

  00004421 <SDL_SetModState>:
      4421:       8d 4c 24 04             lea    0x4(%esp),%ecx
      4425:       83 e4 f0                and    $0xfffffff0,%esp
      4428:       ff 71 fc                push   -0x4(%ecx)
icculus commented 2 years ago

Pushed a thing to serialize access to the renderer, which maybe doesn't fix the remaining issue, but it's a start if we have two threads trying to present to the screen at the same time. I probably have to try to get a build of Call to Power running here next.

icculus commented 2 years ago

Okay, I got a running build of Civ:CTP running last night.

So this is a bit of a mess, I couldn't get the game to start because it couldn't open profile.txt, and when I strace'd it, it was trying to open it in a directory called ctdata/englisish instead of ctp_data/english, so there's either a buffer the game initialized incorrectly or a C runtime string function that behaves differently in modern times, or something like that, and likely other places this happens elsewhere.

A symlink fixed this one, but it's pretty clear we're only going to run so far with this one from the 23-year-old binaries. It was built in a time where Valgrind/AddressSanitizer/static analysis didn't exist, and C++ tools were just less good in general, so I wouldn't be surprised if we end up at the mercy of the OS's memory manager here even with perfect SDL-1.2 compatibility.

icculus commented 2 years ago

Ok, so this patch gets Call to Power rendering.

I can see this potentially causing regressions, so we're probably going to need to hide at least this last bullet point behind the quirks mechanism, if not all of them...but this patch does get the game to render correctly with sdl12-compat.

Also, the game is setting a 1920x1080 (my laptop's desktop resolution) video mode, and then centering the game in it. Forcing SDL_GetVideoInfo() to report something smaller didn't help, but I didn't investigate further, and the game could benefit from scaling, even if we need a quirk to force this, too. Something to consider.

It also reliably crashes (in the game, not in sdl12-compat) within three moves with a dereferenced NULL pointer, but that's probably beyond my control.

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 6ec6fb9..29825fc 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -1023,6 +1023,9 @@ static EventQueueType EventQueuePool[SDL12_MAXEVENTS];
 static EventQueueType *EventQueueHead = NULL;
 static EventQueueType *EventQueueTail = NULL;
 static EventQueueType *EventQueueAvailable = NULL;
+static unsigned long SetVideoModeThread = 0;
+static SDL_bool VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE;
+

 /* This is a KEYDOWN event which is being held for a follow-up TEXTINPUT */
 static SDL12_Event PendingKeydownEvent;
@@ -5295,6 +5298,9 @@ EndVidModeCreate(void)
     QueuedDisplayOverlays.next = NULL;
     QueuedDisplayOverlaysTail = &QueuedDisplayOverlays;

+    VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE;
+    SetVideoModeThread = 0;
+
     return NULL;
 }

@@ -6048,6 +6054,11 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12)
         }
     }

+    SetVideoModeThread = SDL20_ThreadID();
+    VideoSurfacePresentTicks = 0;
+    VideoSurfaceLastPresentTicks = 0;
+    VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE;
+
     SDL20_RaiseWindow(VideoWindow20);

     /* SDL 1.2 always grabbed input if the video mode was fullscreen. */
@@ -6055,9 +6066,6 @@ SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags12)
         HandleInputGrab(SDL12_GRAB_ON);
     }

-    VideoSurfacePresentTicks = 0;
-    VideoSurfaceLastPresentTicks = 0;
-
     if ((flags12 & SDL12_OPENGL) == 0) {
         /* see notes above these functions about GL context resetting. Force a lock/unlock here to set that up. */
         LockVideoRenderer();
@@ -6377,6 +6385,7 @@ PresentScreen(void)
     }

     SDL20_RenderPresent(renderer);
+    VideoSurfaceUpdatedInBackgroundThread = SDL_FALSE;
     VideoSurfaceLastPresentTicks = SDL20_GetTicks();
     VideoSurfacePresentTicks = 0;

@@ -6539,6 +6548,8 @@ SDL_GL_Unlock(void)
 DECLSPEC12 void SDLCALL
 SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12)
 {
+    const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread);
+
     /* strangely, SDL 1.2 doesn't check if surface12 is NULL before touching it */
     /* (UpdateRect, singular, does...) */

@@ -6558,6 +6569,7 @@ SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12)
      *  but in practice most apps never got a double-buffered surface and
      *  don't handle it correctly, so we have to work around it. */
     if (surface12 == VideoSurface12) {
+        const SDL_bool upload_later = !ThisIsSetVideoModeThread;
         SDL_Palette *logicalPal = surface12->surface20->format->palette;
         const int pixsize = surface12->format->BytesPerPixel;
         const int srcpitch = surface12->pitch;
@@ -6569,7 +6581,10 @@ SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12)

         for (i = 0; i < numrects; i++) {
             UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen);
-            if (!rect20.w || !rect20.h) {
+
+            if (upload_later) {
+                continue;
+            } else if (!rect20.w || !rect20.h) {
                 continue;
             } else if (SDL20_LockTexture(VideoTexture20, &rect20, &pixels, &pitch) < 0) {
                 continue;  /* oh well */
@@ -6608,7 +6623,10 @@ SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12)
             VideoConvertSurface20->h = VideoSurface12->h;
         }

-        if (whole_screen) {
+        if (upload_later) {
+            VideoSurfaceUpdatedInBackgroundThread = SDL_TRUE;
+            VideoSurfacePresentTicks = whole_screen ? 1 : VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame();  /* flip it later (or as soon as the main thread can). */
+        } else if (whole_screen) {
             PresentScreen();  /* flip it now. */
         } else {
             VideoSurfacePresentTicks = VideoSurfaceLastPresentTicks + GetDesiredMillisecondsPerFrame();  /* flip it later. */
@@ -6661,14 +6679,24 @@ HandleKeyRepeat(void)
 DECLSPEC12 void SDLCALL
 SDL_PumpEvents(void)
 {
+    const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread);
     SDL_Event e;

+    if (!ThisIsSetVideoModeThread) {
+        return;
+    }
+
     /* If the app is doing dirty rectangles, we set a flag and present the
      *  screen surface when they pump for new events if we're close to 60Hz,
      *  which we consider a sign that they are done rendering for the current
      *  frame and it would make sense to send it to the screen. */
+
     if (VideoSurfacePresentTicks && SDL_TICKS_PASSED(SDL20_GetTicks(), VideoSurfacePresentTicks)) {
-        PresentScreen();
+        if (VideoSurfaceUpdatedInBackgroundThread) {
+            SDL_Flip(VideoSurface12);  /* this will update the texture and present. */
+        } else {
+            PresentScreen();
+        }
     }

     if (EventQueueMutex) {
@@ -7618,6 +7646,18 @@ SDL_SetTimer(Uint32 interval, SDL12_TimerCallback callback)
     return 0;
 }

+DECLSPEC12 void SDLCALL
+SDL_Delay(Uint32 ticks)
+{
+    /* In case there's a loading screen from a background thread and the main thread is waiting... */
+    const SDL_bool ThisIsSetVideoModeThread = (SDL20_ThreadID() == SetVideoModeThread);
+    if (ThisIsSetVideoModeThread && VideoSurfaceUpdatedInBackgroundThread) {
+        SDL_Flip(VideoSurface12);  /* this will update the texture and present. */
+    }
+    SDL20_Delay(ticks);
+}
+
+
 DECLSPEC12 int SDLCALL
 SDL_putenv(const char *_var)
 {
diff --git a/src/SDL20_syms.h b/src/SDL20_syms.h
index df76bfa..e5469b8 100644
--- a/src/SDL20_syms.h
+++ b/src/SDL20_syms.h
@@ -232,7 +232,7 @@ SDL20_SYM_PASSTHROUGH(SDL_bool,HasAltiVec,(void),(),return)
 SDL20_SYM(SDL_TimerID,AddTimer,(Uint32 a, SDL_TimerCallback b, void *c),(a,b,c),return)
 SDL20_SYM(SDL_bool,RemoveTimer,(SDL_TimerID a),(a),return)
 SDL20_SYM_PASSTHROUGH(Uint32,GetTicks,(void),(),return)
-SDL20_SYM_PASSTHROUGH(void,Delay,(Uint32 a),(a),)
+SDL20_SYM(void,Delay,(Uint32 a),(a),)

 SDL20_SYM(SDL_bool,IsGameController,(int a),(a),return)
 SDL20_SYM(const char *,GameControllerNameForIndex,(int a),(a),return)
sulix commented 2 years ago

Okay, I got a running build of Civ:CTP running last night.

So this is a bit of a mess, I couldn't get the game to start because it couldn't open profile.txt, and when I strace'd it, it was trying to open it in a directory called ctdata/englisish instead of ctp_data/english, so there's either a buffer the game initialized incorrectly or a C runtime string function that behaves differently in modern times, or something like that, and likely other places this happens elsewhere.

A symlink fixed this one, but it's pretty clear we're only going to run so far with this one from the 23-year-old binaries. It was built in a time where Valgrind/AddressSanitizer/static analysis didn't exist, and C++ tools were just less good in general, so I wouldn't be surprised if we end up at the mercy of the OS's memory manager here even with perfect SDL-1.2 compatibility.

I'm still away (back next week), but IIRC, this was due to the CivCTP relying on the C string functions working with overlapping strings, hence the hacks here: https://gist.github.com/sulix/09f059c468fcb58eefbca8ef46006ccb#file-civctp_wrapper-c-L50

sulix commented 2 years ago

Didn't have time to test it in detail, but running with this patch (and my libcivctp_wrapper.so) was playable for quite a while under X11, but deadlocked pretty quickly under wayland.

It also had some flickering background for the wonder videos, and deadlocked afterwards.

Still, this is working much better than I expected!

icculus commented 2 years ago

Also, the game is setting a 1920x1080 (my laptop's desktop resolution) video mode, and then centering the game in it. Forcing SDL_GetVideoInfo() to report something smaller didn't help, but I didn't investigate further, and the game could benefit from scaling, even if we need a quirk to force this, too. Something to consider.

It gets this value from SDL_ListModes(), presumably (although it doesn't call that with SDL_FULLSCREEN, so it returns -1, so beats me), but setting this in the userprofile.txt lets you dictate a fullscreen resolution.

TryWindowsResolution=No
ScreenResWidth=1024
ScreenResHeight=768
fullscreen=Yes

640x480 seems to be the "native" resolution of the game, at least for menus and the intro video.

...but before I realized any of this, I added 40dff443364acf998c6cc0d9d45f3a152aba9b05 to let sdl12-compat clamp max reported resolution, which is still useful if we find a game that wants to find the largest resolution, and just wasn't prepared for a world that has retina displays.

icculus commented 2 years ago

this was due to the CivCTP relying on the C string functions working with overlapping strings, hence the hacks here:

Ah, I had this library, and patched the __builtin things, but forgot to patch the string functions! It runs much better now.

icculus commented 2 years ago

I don't know enough about this game to say that it always decides to render its mpeg videos unscaled and centered, but a quirk that says "scale all YUV video to the full resolution" might be useful for this game.

But also, this feels like meddling, a little bit.

icculus commented 2 years ago

Okay, I put that multithreaded wrangling patch behind a quirk and pushed it, which I think resolves the last pending thing with Civ:CTP.

If there are no objections, I'll close this bug.

sulix commented 1 year ago

FYI: I finally got around to writing up a guide on how to get this working, including how to patch the game's recursive mutex implementation (which seems to rely on LinuxThreads).

See: https://davidgow.net/hacks/civctp.html

With all of the patches listed there, the game seems to work perfectly on my system with the latest sdl12-compat.

icculus commented 1 year ago

Should we put that link in COMPATIBILITY.md?

sulix commented 1 year ago

Should we put that link in COMPATIBILITY.md?

Most of the issues are more general "this is ancient" than sdl12-compat specific, but nevertheless I've sent out PR #291 as some shameless self-promotion for my website. :-)