diff --git a/CMakeLists.txt b/CMakeLists.txt index b74165cc7..511cb0e47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,8 +76,15 @@ add_subdirectory(lua) add_subdirectory(Multiplayer) add_subdirectory(wine) +# A non-MSVC toolchain rebuilds the Bink import library and points +# binkw32_lib at it (see clang-cl.cmake); MSVC binds the shipped +# library fine, so fall back to that. +if(NOT binkw32_lib) + set(binkw32_lib "${CMAKE_SOURCE_DIR}/binkw32.lib") +endif() + set(Ja2_Libraries -"${CMAKE_SOURCE_DIR}/binkw32.lib" +"${binkw32_lib}" "${CMAKE_SOURCE_DIR}/libexpatMT.lib" "${CMAKE_SOURCE_DIR}/lua51.lib" "${CMAKE_SOURCE_DIR}/lua51.vc9.lib" diff --git a/binkw32.def b/binkw32.def new file mode 100644 index 000000000..1ac234d5f --- /dev/null +++ b/binkw32.def @@ -0,0 +1,104 @@ +; Import definition for binkw32.dll, used to rebuild the Bink import library +; when building with clang-cl.cmake +; +; Two things forced the shape of this file: +; 1. lld-link cannot bind the ordinal-based long-format import library the +; game ships (binkw32.lib). It leaves every Bink call unresolved, and at +; run time the calls jump into arbitrary code and crash at startup. +; 2. binkw32.dll exports decorated stdcall names that keep their leading +; underscore (_BinkOpen@8). llvm-dlltool strips that underscore from the +; imported name whatever the --no-leading-underscore flag says, so a +; by-name import would look up "BinkOpen@8", not find it in the DLL, and +; Wine would substitute a builtin stub that aborts ("unimplemented +; function binkw32.dll.BinkSetSoundSystem@8"). +; +; Importing by ordinal (NONAME) sidesteps both: there is no name to mismatch, +; and the ordinals are those of the retail binkw32.dll the game ships. MSVC's +; link.exe binds the shipped library fine and keeps using it. +LIBRARY binkw32.dll +EXPORTS +_BinkBufferBlit@12 @1 NONAME +_BinkBufferCheckWinPos@12 @2 NONAME +_BinkBufferClear@8 @3 NONAME +_BinkBufferClose@4 @4 NONAME +_BinkBufferGetDescription@4 @5 NONAME +_BinkBufferGetError@0 @6 NONAME +_BinkBufferLock@4 @7 NONAME +_BinkBufferOpen@16 @8 NONAME +_BinkBufferSetDirectDraw@8 @9 NONAME +_BinkBufferSetHWND@8 @10 NONAME +_BinkBufferSetOffset@12 @11 NONAME +_BinkBufferSetResolution@12 @12 NONAME +_BinkBufferSetScale@12 @13 NONAME +_BinkBufferUnlock@4 @14 NONAME +_BinkCheckCursor@20 @15 NONAME +_BinkClose@4 @16 NONAME +_BinkCloseTrack@4 @17 NONAME +_BinkCopyToBuffer@28 @18 NONAME +_BinkCopyToBufferRect@44 @19 NONAME +_BinkDDSurfaceType@4 @20 NONAME +_BinkDX8SurfaceType@4 @21 NONAME +_BinkDoFrame@4 @22 NONAME +_BinkGetError@0 @23 NONAME +_BinkGetKeyFrame@12 @24 NONAME +_BinkGetRealtime@12 @25 NONAME +_BinkGetRects@8 @26 NONAME +_BinkGetSummary@8 @27 NONAME +_BinkGetTrackData@8 @28 NONAME +_BinkGetTrackID@8 @29 NONAME +_BinkGetTrackMaxSize@8 @30 NONAME +_BinkGetTrackType@8 @31 NONAME +_BinkGoto@12 @32 NONAME +_BinkIsSoftwareCursor@8 @33 NONAME +_BinkLogoAddress@0 @34 NONAME +_BinkNextFrame@4 @35 NONAME +_BinkOpen@8 @36 NONAME +_BinkOpenDirectSound@4 @37 NONAME +_BinkOpenMiles@4 @38 NONAME +_BinkOpenTrack@8 @39 NONAME +_BinkOpenWaveOut@4 @40 NONAME +_BinkPause@8 @41 NONAME +_BinkRestoreCursor@4 @42 NONAME +_BinkService@4 @43 NONAME +_BinkSetError@4 @44 NONAME +_BinkSetFrameRate@8 @45 NONAME +_BinkSetIO@4 @46 NONAME +_BinkSetIOSize@4 @47 NONAME +_BinkSetMixBinVolumes@20 @48 NONAME +_BinkSetMixBins@16 @49 NONAME +_BinkSetPan@12 @50 NONAME +_BinkSetSimulate@4 @51 NONAME +_BinkSetSoundOnOff@8 @52 NONAME +_BinkSetSoundSystem@8 @53 NONAME +_BinkSetSoundTrack@8 @54 NONAME +_BinkSetVideoOnOff@8 @55 NONAME +_BinkSetVolume@12 @56 NONAME +_BinkWait@4 @57 NONAME +_RADSetMemory@8 @58 NONAME +_RADTimerRead@0 @59 NONAME +_YUV_blit_16a1bpp@52 @60 NONAME +_YUV_blit_16a1bpp_mask@60 @61 NONAME +_YUV_blit_16a4bpp@52 @62 NONAME +_YUV_blit_16a4bpp_mask@60 @63 NONAME +_YUV_blit_16bpp@48 @64 NONAME +_YUV_blit_16bpp_mask@56 @65 NONAME +_YUV_blit_24bpp@48 @66 NONAME +_YUV_blit_24bpp_mask@56 @67 NONAME +_YUV_blit_24rbpp@48 @68 NONAME +_YUV_blit_24rbpp_mask@56 @69 NONAME +_YUV_blit_32abpp@52 @70 NONAME +_YUV_blit_32abpp_mask@60 @71 NONAME +_YUV_blit_32bpp@48 @72 NONAME +_YUV_blit_32bpp_mask@56 @73 NONAME +_YUV_blit_32rabpp@52 @74 NONAME +_YUV_blit_32rabpp_mask@60 @75 NONAME +_YUV_blit_32rbpp@48 @76 NONAME +_YUV_blit_32rbpp_mask@56 @77 NONAME +_YUV_blit_UYVY@48 @78 NONAME +_YUV_blit_UYVY_mask@56 @79 NONAME +_YUV_blit_YUY2@48 @80 NONAME +_YUV_blit_YUY2_mask@56 @81 NONAME +_YUV_blit_YV12@52 @82 NONAME +_YUV_init@4 @83 NONAME +_radfree@4 @84 NONAME +_radmalloc@4 @85 NONAME diff --git a/cmake/clang-toolchain.cmake b/cmake/clang-toolchain.cmake deleted file mode 100644 index e70a13752..000000000 --- a/cmake/clang-toolchain.cmake +++ /dev/null @@ -1,39 +0,0 @@ -set(CMAKE_SYSTEM_NAME Windows) - -set(CMAKE_C_COMPILER clang) -set(CMAKE_CXX_COMPILER clang++) -set(CMAKE_RC_COMPILER llvm-rc) - -set(triple i386-pc-win32-msvc) -set(CMAKE_C_COMPILER_TARGET ${triple}) -set(CMAKE_CXX_COMPILER_TARGET ${triple}) -set(CMAKE_RC_COMPILER_TARGET ${triple}) - -set(msvc_dir "$ENV{MSVC_DIRECTORY}") -if(msvc_dir STREQUAL "") - message(FATAL_ERROR "Please set a valid MSVC_DIRECTORY environment variable") -endif() - -# Don't remember which version of Visual Studio Build Tools this is. -# When updating this, make sure to pin the version so people can get it. -set(msvc_include - "${msvc_dir}/14.34.31933/include" - "${msvc_dir}/14.34.31933/ATLMFC/include" - "${msvc_dir}/include/10.0.22000.0/ucrt" - "${msvc_dir}/include/10.0.22000.0/um" - "${msvc_dir}/include/10.0.22000.0/shared" - "${msvc_dir}/include/10.0.22000.0/winrt" - "${msvc_dir}/include/10.0.22000.0/cppwinrt" -) -set(msvc_libraries - "${msvc_dir}/14.34.31933/ATLMFC/lib/x86" - "${msvc_dir}/14.34.31933/lib/x86" - "${msvc_dir}/lib/10.0.22000.0/ucrt/x86" - "${msvc_dir}/lib/10.0.22000.0/um/x86" -) - -foreach(LANG C CXX) - set(CMAKE_${LANG}_STANDARD_INCLUDE_DIRECTORIES ${msvc_include}) -endforeach() - -link_directories(${msvc_libraries}) diff --git a/cmake/presets/CMakePresets.json b/cmake/presets/CMakePresets.json index 381951d31..86202416c 100644 --- a/cmake/presets/CMakePresets.json +++ b/cmake/presets/CMakePresets.json @@ -4,12 +4,11 @@ { "name": "__userBase", "hidden": true, + "description": "Applications takes any of JA2, JA2MAPEDITOR, JA2UB, JA2UBMAPEDITOR, and builds all four when empty. Set CMAKE_RUNTIME_OUTPUT_DIRECTORY to a gamedir, e.g. C:/Games/JA2, to put the executables where they can be debugged.", "generator": "Ninja", "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - // Valid choices: JA2;JA2MAPEDITOR;JA2UB;JA2UBMAPEDITOR. If empty (""), will configure all. "Applications": "JA2", - // For debugging: enter your desired gamedir. e.g. C:/Games/JA2 "CMAKE_RUNTIME_OUTPUT_DIRECTORY": "." }, "architecture": { diff --git a/cmake/toolchains/clang-cl.cmake b/cmake/toolchains/clang-cl.cmake new file mode 100644 index 000000000..f04d1c7f0 --- /dev/null +++ b/cmake/toolchains/clang-cl.cmake @@ -0,0 +1,91 @@ +# Usage: +# MSVC_SDK=/path/to/msvc cmake --toolchain cmake/toolchains/clang-cl.cmake ... +# +# MSVC_SDK is where the cross headers and libraries live: the directory holding +# vc/ and kits/, which for msvc-wine is the root it was installed to. It comes +# from the environment. + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR x86) + +set(MSVC_SDK "$ENV{MSVC_SDK}") +if(NOT IS_DIRECTORY "${MSVC_SDK}/vc/tools/msvc" + OR NOT IS_DIRECTORY "${MSVC_SDK}/kits/10/include") + message(FATAL_ERROR + "MSVC_SDK (${MSVC_SDK}) does not hold vc/tools/msvc and kits/10/include. " + "Point it at the MSVC installation, e.g. MSVC_SDK=/home/you/msvc") +endif() + +# Both trees are versioned directories; take the newest of each rather than +# pinning a version that goes stale the next time the toolchain is updated. +function(_newest_subdirectory result dir) + file(GLOB versions "${dir}/*") + list(SORT versions) + list(POP_BACK versions newest) + set(${result} "${newest}" PARENT_SCOPE) +endfunction() + +_newest_subdirectory(_msvc "${MSVC_SDK}/vc/tools/msvc") +_newest_subdirectory(_sdk "${MSVC_SDK}/kits/10/include") +get_filename_component(_sdk_version "${_sdk}" NAME) +set(_sdk_lib "${MSVC_SDK}/kits/10/lib/${_sdk_version}") + +# Find every tool here rather than trusting PATH, so the choice of compiler, +# linker and rc is the toolchain file's alone. +foreach(tool clang-cl lld-link llvm-rc llvm-lib llvm-mt llvm-dlltool) + string(TOUPPER "${tool}" _variable) + string(REPLACE "-" "_" _variable "${_variable}") + # An absolute path: CMake resolves a bare name against the source directory. + find_program(${_variable} NAMES ${tool} REQUIRED) +endforeach() + +set(CMAKE_C_COMPILER "${CLANG_CL}") +set(CMAKE_CXX_COMPILER "${CLANG_CL}") +set(CMAKE_LINKER "${LLD_LINK}") +set(CMAKE_RC_COMPILER "${LLVM_RC}") +set(CMAKE_AR "${LLVM_LIB}") +set(CMAKE_MT "${LLVM_MT}") + +# clang-cl does not read INCLUDE and LIB the way cl does, so every directory +# has to be named. -imsvc rather than -I marks them as system headers, which +# keeps warnings about the SDK out of the build log. +set(_includes + "${_msvc}/atlmfc/include" + "${_msvc}/include" + "${_sdk}/shared" + "${_sdk}/ucrt" + "${_sdk}/um" + "${_sdk}/winrt" + "${_sdk}/km" +) + +set(_flags "--target=i386-pc-windows-msvc") +foreach(directory ${_includes}) + string(APPEND _flags " -imsvc ${directory}") +endforeach() +set(CMAKE_C_FLAGS_INIT "${_flags}") +set(CMAKE_CXX_FLAGS_INIT "${_flags}") + +# The resource compiler is preprocessed by clang-cl, which needs the same +# headers again: ja2.rc includes windows.h. It takes -I, not -imsvc. +set(_rc_flags "") +foreach(directory ${_includes}) + string(APPEND _rc_flags " -I ${directory}") +endforeach() +set(CMAKE_RC_FLAGS_INIT "${_rc_flags}") + +set(CMAKE_EXE_LINKER_FLAGS_INIT + "/libpath:${_msvc}/lib/x86 /libpath:${_sdk_lib}/ucrt/x86 /libpath:${_sdk_lib}/um/x86") + +# lld-link cannot bind the Bink import library the game ships, so rebuild a +# bindable one from binkw32.def (which explains why) and hand it to the top-level +# CMakeLists through binkw32_lib. MSVC keeps using the shipped library. +execute_process( + COMMAND "${LLVM_DLLTOOL}" -m i386 --no-leading-underscore + -d "${CMAKE_CURRENT_LIST_DIR}/../../binkw32.def" + -l "${CMAKE_BINARY_DIR}/binkw32.lib" + RESULT_VARIABLE _binkw32DlltoolResult) +if(NOT _binkw32DlltoolResult EQUAL 0) + message(FATAL_ERROR "llvm-dlltool failed to build the binkw32 import library") +endif() +set(binkw32_lib "${CMAKE_BINARY_DIR}/binkw32.lib") diff --git a/cmake/mingw-toolchain.cmake b/cmake/toolchains/mingw.cmake similarity index 100% rename from cmake/mingw-toolchain.cmake rename to cmake/toolchains/mingw.cmake