diff --git a/.github/workflows/mac-universal.yml b/.github/workflows/mac-universal.yml new file mode 100644 index 000000000..874e2ccdf --- /dev/null +++ b/.github/workflows/mac-universal.yml @@ -0,0 +1,73 @@ +name: Mac universal build +on: + push: + branches: + - universal + release: + types: [published] +jobs: + build: + runs-on: macos-14 + env: + CONFIGURATION: ${{ matrix.configuration }} + BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }} + PROD_MACOS_CERTIFICATE: '${{ secrets.PROD_MACOS_CERTIFICATE }}' + PROD_MACOS_CERTIFICATE_PWD: '${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}' + PROD_MACOS_CERTIFICATE_NAME: '${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}' + PROD_MACOS_CI_KEYCHAIN_PWD: '${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}' + PROD_MACOS_NOTARIZATION_APPLE_ID: '${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}' + PROD_MACOS_NOTARIZATION_TEAM_ID: '${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}' + PROD_MACOS_NOTARIZATION_PWD: '${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}' + BUILD_OS: macos + strategy: + fail-fast: false + matrix: + configuration: + - Release + - Debug + steps: + - uses: actions/checkout@v4 + + # Download macos-intel + - uses: robinraju/release-downloader@v1 + id: download + with: + latest: true + fileName: 'cboe-macos-intel-${{ matrix.configuration }}.tar' + extract: true + out-file-path: 'cboe-macos-intel-${{ matrix.configuration }}' + + # Download macos-silicon + - uses: robinraju/release-downloader@v1 + with: + latest: true + fileName: 'cboe-macos-silicon-${{ matrix.configuration }}.tar' + extract: true + out-file-path: 'cboe-macos-silicon-${{ matrix.configuration }}' + + - run: .github/workflows/scripts/mac/make-universal.sh + + # Skipping this for now because of issue nqnstudios#13 + - name: Codesign and notarize + run: 'SIGN="no" NOTARIZE="no" .github/workflows/scripts/mac/sign-apps.sh' + + - name: 'Tar files' + run: 'tar -cvf cboe-macos-universal-${{matrix.configuration}}.tar "build/Blades of Exile"' + + # Upload everything as artifact + - uses: actions/upload-artifact@v4 + with: + name: mac-universal-dependencies-${{matrix.configuration}} + path: cboe-macos-universal-${{matrix.configuration}}.tar + + # upload a release + - name: Github release + uses: softprops/action-gh-release@v2 + with: + files: cboe-macos-universal-${{ matrix.configuration }}.tar + tag_name: ${{ steps.download.outputs.tag_name }} + + - name: 'Itch.io release' + run: './.github/workflows/scripts/butler_push.sh' + shell: bash + if: ${{ matrix.configuration == 'Release' }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..fdfa824aa --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,131 @@ +on: + push: + branches: + - 'itch-edition' + tags: + - "v*.*.*" + pull_request: + branches: + - itch-edition +jobs: + release: + env: + ARCH: ${{ matrix.os.flag }} + MACOSX_DEPLOYMENT_TARGET: 10.15 + BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }} + PROD_MACOS_CERTIFICATE: '${{ secrets.PROD_MACOS_CERTIFICATE }}' + PROD_MACOS_CERTIFICATE_PWD: '${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}' + PROD_MACOS_CERTIFICATE_NAME: '${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}' + PROD_MACOS_CI_KEYCHAIN_PWD: '${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}' + PROD_MACOS_NOTARIZATION_APPLE_ID: '${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}' + PROD_MACOS_NOTARIZATION_TEAM_ID: '${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}' + PROD_MACOS_NOTARIZATION_PWD: '${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}' + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + DEBUG_FLAG: ${{ matrix.configuration == 'Debug' && 'true' || 'false' }} + BUILD_OS: ${{ matrix.os.name }} + strategy: + fail-fast: false + matrix: + os: + - name: macos + suffix: '-intel' + flag: x86_64 + deps: macos-universal + version: 13 + scons-script: './.github/workflows/scripts/mac/scons-build.sh' + - name: macos + flag: arm64 + suffix: '-silicon' + deps: macos-universal + version: 14 + scons-script: './.github/workflows/scripts/mac/scons-build.sh' +# - name: ubuntu +# suffix: '' +# version: 22.04 +# scons-script: scons + - name: windows + suffix: '' + version: 2019 + scons-script: './.github/workflows/scripts/win/scons-build.bat' + configuration: + - Release + - Debug + runs-on: '${{ matrix.os.name }}-${{ matrix.os.version }}' + steps: + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v7 + with: + script: "core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');" + - name: checkout + uses: actions/checkout@v4 + with: + submodules: true + - name: Download dependency build + uses: robinraju/release-downloader@v1 + with: + repository: NQNStudios/cboe-dependencies + latest: true + fileName: 'dependencies-${{ matrix.os.deps || matrix.os.name }}-${{ matrix.configuration }}.tar' + extract: true + out-file-path: 'deps' + - name: Windows build dependencies + run: 'vcpkg install libxml2 && pip install scons' + if: ${{ matrix.os.name == 'windows' }} + - name: Mac build dependencies + run: brew install scons + if: ${{ matrix.os.name == 'macos' }} + - name: Linux build dependencies + run: sudo apt-get update && sudo apt-get install scons libxml2-utils libgl-dev libopenal-dev + if: ${{ matrix.os.name == 'ubuntu' }} + - name: Install TGUI + run: 'sudo ./.github/workflows/scripts/linux/install-tgui.sh' + if: ${{ matrix.os.name == 'ubuntu' }} + - name: Build + run: '${{ matrix.os.scons-script }} test=false debug=$DEBUG_FLAG' + shell: bash + + - name: Download fix-rpaths.py script + run: git clone https://gist.github.com/NQNStudios/7145bcf6621891f5176c8caa165d6b93 + if: ${{ matrix.os.name == 'macos' }} + - name: Fix rpaths game + run: 'python 7145bcf6621891f5176c8caa165d6b93/fix-rpaths.py "build/Blades of Exile/Blades of Exile.app"' + if: ${{ matrix.os.name == 'macos' }} + - name: Fix rpaths scenario editor + run: 'python 7145bcf6621891f5176c8caa165d6b93/fix-rpaths.py "build/Blades of Exile/BoE Scenario Editor.app"' + if: ${{ matrix.os.name == 'macos' }} + - name: Fix rpaths character editor + run: 'python 7145bcf6621891f5176c8caa165d6b93/fix-rpaths.py "build/Blades of Exile/BoE Character Editor.app"' + if: ${{ matrix.os.name == 'macos' }} + + - run: cp .itch.toml "build/Blades of Exile/" + shell: bash + + - name: 'Tar unsigned files' + run: 'tar -cvf cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}-unsigned.tar "Blades of Exile"' + working-directory: '${{ github.workspace }}/build' + - name: upload pre-signing artifact + uses: actions/upload-artifact@v4 + with: + name: cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}-unsigned + path: '${{ github.workspace }}/build/cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}-unsigned.tar' + # Skipping this for now because of issue nqnstudios#13 + - name: Codesign and notarize + run: 'SIGN="no" NOTARIZE="no" ./.github/workflows/scripts/mac/sign-apps.sh' + if: ${{ matrix.os.name == 'macos' }} + - name: 'Tar files' + run: 'tar -cvf cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}.tar "Blades of Exile"' + working-directory: '${{ github.workspace }}/build' + - name: 'Upload Artifact' + uses: actions/upload-artifact@v4 + with: + name: cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }} + path: '${{ github.workspace }}/build/cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}.tar' + - name: Github release + uses: softprops/action-gh-release@v2 + with: + files: '${{ github.workspace }}/build/cboe-${{ matrix.os.name }}${{ matrix.os.suffix }}-${{ matrix.configuration }}.tar' + if: ${{ startsWith(github.ref, 'refs/tags/') }} + - name: 'Itch.io release' + run: './.github/workflows/scripts/butler_push.sh' + shell: bash + if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.configuration == 'Release' && matrix.os.name != 'macos' }} diff --git a/.github/workflows/scripts/butler_push.sh b/.github/workflows/scripts/butler_push.sh new file mode 100755 index 000000000..94cc5c151 --- /dev/null +++ b/.github/workflows/scripts/butler_push.sh @@ -0,0 +1,28 @@ +#! /bin/bash + +butler_channel="" +butler_exe="" +release_dir="" +if [ "$BUILD_OS" = "ubuntu" ]; then + butler_channel=linux-amd64 + butler_exe=butler + release_dir="linux" +elif [ "$BUILD_OS" = "windows" ]; then + butler_channel=windows-amd64 + butler_exe=butler.exe + release_dir="windows" +elif [ "$BUILD_OS" = "macos" ]; then + butler_channel=darwin-amd64 + butler_exe=butler + release_dir="macos" +fi + +# -L follows redirects +# -O specifies output name +curl -L -o butler.zip https://broth.itch.ovh/butler/${butler_channel}/LATEST/archive/default +unzip butler.zip +# GNU unzip tends to not set the executable bit even though it's set in the .zip +chmod +x ${butler_exe} +# just a sanity check run (and also helpful in case you're sharing CI logs) +./${butler_exe} -V +./${butler_exe} push "build/Blades of Exile/" nqn/blades-of-exile:${butler_channel} diff --git a/.github/workflows/scripts/linux/install-tgui.sh b/.github/workflows/scripts/linux/install-tgui.sh index 876bcdccb..9b32464df 100755 --- a/.github/workflows/scripts/linux/install-tgui.sh +++ b/.github/workflows/scripts/linux/install-tgui.sh @@ -3,7 +3,7 @@ git clone --depth 1 -b 0.9 https://github.com/texus/TGUI.git cd TGUI export CLICOLOR_FORCE=1 -cmake -D TGUI_CXX_STANDARD=14 . +SFML_DIR=../deps/lib/cmake/SFML cmake -D TGUI_CXX_STANDARD=14 -D SFML_DIR=../deps/lib/cmake/SFML . make cmake --install . cd .. # Probably not needed but... diff --git a/.github/workflows/scripts/mac/make-universal.sh b/.github/workflows/scripts/mac/make-universal.sh new file mode 100755 index 000000000..0675d9680 --- /dev/null +++ b/.github/workflows/scripts/mac/make-universal.sh @@ -0,0 +1,27 @@ +#! /bin/bash + +if [ -z "$CONFIGURATION" ]; then + CONFIGURATION=Release +fi + +INTEL=cboe-macos-intel-$CONFIGURATION +SILICON=cboe-macos-silicon-$CONFIGURATION + +combine() { + mkdir -p "build/Blades of Exile/$1.app/Contents/MacOS" + lipo -create "$SILICON/Blades of Exile/$1.app/Contents/MacOS/$1" "$INTEL/Blades of Exile/$1.app/Contents/MacOS/$1" -output "build/Blades of Exile/$1.app/Contents/MacOS/$1" + cp -r "$SILICON/Blades of Exile/$1.app/Contents/Frameworks" "build/Blades of Exile/$1.app/Contents/" + cp -r "$SILICON/Blades of Exile/$1.app/Contents/Resources" "build/Blades of Exile/$1.app/Contents/" + cp "$SILICON/Blades of Exile/$1.app/Contents/Info.plist" "build/Blades of Exile/$1.app/Contents/" + cp "$SILICON/Blades of Exile/$1.app/Contents/PkgInfo" "build/Blades of Exile/$1.app/Contents/" +} + +combine "Blades of Exile" +combine "BoE Scenario Editor" +combine "BoE Character Editor" + +cp -r "$SILICON/Blades of Exile/Blades of Exile Base" "build/Blades of Exile/" +cp -r "$SILICON/Blades of Exile/Blades of Exile Scenarios" "build/Blades of Exile/" +cp -r "$SILICON/Blades of Exile/data" "build/Blades of Exile/" +cp -r "$SILICON/Blades of Exile/docs" "build/Blades of Exile/" +cp "$SILICON/Blades of Exile/.itch.toml" "build/Blades of Exile/" \ No newline at end of file diff --git a/.github/workflows/scripts/mac/scons-build.sh b/.github/workflows/scripts/mac/scons-build.sh index 4cdf6d328..a130f4780 100755 --- a/.github/workflows/scripts/mac/scons-build.sh +++ b/.github/workflows/scripts/mac/scons-build.sh @@ -4,4 +4,4 @@ export CC="$(brew --prefix llvm)/bin/clang" export CXX="$(brew --prefix llvm)/bin/clang++" export SDKROOT="$(xcrun --show-sdk-path)" -scons CXXFLAGS="-I/usr/local/opt/zlib/include" LINKFLAGS="-L/usr/local/opt/zlib/lib" $@ +scons CXXFLAGS="-I/usr/local/opt/zlib/include -arch $ARCH" LINKFLAGS="-L/usr/local/opt/zlib/lib" $@ diff --git a/.github/workflows/scripts/mac/sign-apps.sh b/.github/workflows/scripts/mac/sign-apps.sh new file mode 100755 index 000000000..40ae904be --- /dev/null +++ b/.github/workflows/scripts/mac/sign-apps.sh @@ -0,0 +1,79 @@ +#! /bin/bash + +# CODE-SIGNING STEP +# Original Source: https://federicoterzi.com/blog/automatic-code-signing-and-notarization-for-macos-apps-using-github-actions/ +# Modified by NQNStudios + +# Turn our base64-encoded certificate back to a regular .p12 file + +echo $PROD_MACOS_CERTIFICATE | base64 --decode > certificate.p12 + +# We need to create a new keychain, otherwise using the certificate will prompt +# with a UI dialog asking for the certificate password, which we can't +# use in a headless CI environment + +security create-keychain -p "$PROD_MACOS_CI_KEYCHAIN_PWD" build.keychain +security default-keychain -s build.keychain +security unlock-keychain -p "$PROD_MACOS_CI_KEYCHAIN_PWD" build.keychain +security import certificate.p12 -k build.keychain -P "$PROD_MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign +security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$PROD_MACOS_CI_KEYCHAIN_PWD" build.keychain + +sign() { + if [ "$SIGN" = "no" ]; + then + return + fi + APP_PATH="build/Blades of Exile/$1.app" + + # We finally codesign our app bundle, specifying the Hardened runtime option + + /usr/bin/codesign --force -s "$PROD_MACOS_CERTIFICATE_NAME" --options runtime "$APP_PATH"/Contents/Frameworks/*.dylib -v + (cd "$APP_PATH" && frameworks=Contents/Frameworks/*.framework/Versions/A/* && \ + for framework in $frameworks; do + if [ -f "$framework" ]; then + /usr/bin/codesign --force -s "$PROD_MACOS_CERTIFICATE_NAME" --options runtime "$framework" -v + fi + done) + /usr/bin/codesign --force -s "$PROD_MACOS_CERTIFICATE_NAME" --options runtime "$APP_PATH"/Contents/Frameworks/*.framework/Versions/A/Resources/Info.plist -v + /usr/bin/codesign --force -s "$PROD_MACOS_CERTIFICATE_NAME" --options runtime "$APP_PATH/Contents/Info.plist" -v + + /usr/bin/codesign --force -s "$PROD_MACOS_CERTIFICATE_NAME" --options runtime "$APP_PATH" -v + + # NOTARIZATION STEP + if [ "$NOTARIZE" = "no" ]; + then + return + fi + + # (same source) + + # Store the notarization credentials so that we can prevent a UI password dialog + # from blocking the CI + + echo "Create keychain profile" + xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD" + + # We can't notarize an app bundle directly, but we need to compress it as an archive. + # Therefore, we create a zip file containing our app bundle, so that we can send it to the + # notarization service + + echo "Creating temp notarization archive" + ditto -c -k --keepParent "$APP_PATH" "notarization.zip" + + # Here we send the notarization request to the Apple's Notarization service, waiting for the result. + # This typically takes a few seconds inside a CI environment, but it might take more depending on the App + # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if + # you're curious + + echo "Notarize app" + xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait + + # Finally, we need to "attach the staple" to our executable, which will allow our app to be + # validated by macOS even when an internet connection is not available. + echo "Attach staple" + xcrun stapler staple "$APP_PATH" || exit 1 +} + +sign "Blades of Exile" +sign "BoE Scenario Editor" +sign "BoE Character Editor" \ No newline at end of file diff --git a/.itch.toml b/.itch.toml new file mode 100644 index 000000000..b3c65d051 --- /dev/null +++ b/.itch.toml @@ -0,0 +1,21 @@ +[[actions]] +name = "Play" +path = "Blades of Exile.exe" +platform = "windows" + +[[actions]] +name = "Play, Logging Everything To Help The Devs Fix Bugs" +path = "Blades of Exile.exe" +platform = "windows" +args = ["--record", "log"] + +[[actions]] +name = "Play" +path = "Blades of Exile.app" +platform = "osx" + +[[actions]] +name = "Play, Logging Everything To Help The Devs Fix Bugs" +path = "Blades of Exile.app" +platform = "osx" +args = ["--record", "log"] \ No newline at end of file diff --git a/SConstruct b/SConstruct index 979e10f6a..ff8ea7cf7 100644 --- a/SConstruct +++ b/SConstruct @@ -221,13 +221,15 @@ elif platform == "win32": vcpkg_other_libs = list(filter(path.exists, vcpkg_other_libs)) vcpkg_other_bins = [path.join(d.get_abspath(), 'bin') for d in vcpkg_other_packages] vcpkg_other_bins = list(filter(path.exists, vcpkg_other_bins)) - - include_paths=[path.join(vcpkg_installed, 'include')] + vcpkg_other_includes + project_includes = [] + for (root, dirs, files) in os.walk('src'): + project_includes.append(path.join(os.getcwd(), root)) + include_paths=project_includes env.Append( - LINKFLAGS=['/SUBSYSTEM:WINDOWS','/ENTRY:mainCRTStartup',f'/MACHINE:X{arch_short}'], + LINKFLAGS=['/SUBSYSTEM:WINDOWS','/ENTRY:mainCRTStartup',f'/MACHINE:X{arch_short}', '/VERBOSE', '/NODEFAULTLIB:libboost_filesystem-vc142-mt-x64-1_85.lib'], CXXFLAGS=['/EHsc','/MD','/FIglobal.hpp'], CPPPATH=include_paths, - LIBPATH=[path.join(vcpkg_installed, 'lib')] + vcpkg_other_libs + vcpkg_other_bins, + LIBPATH=[], LIBS=Split(""" kernel32 user32 @@ -248,7 +250,10 @@ elif platform == "win32": def build_app_package(env, source, build_dir, info): env.Install(build_dir, source) elif platform == "posix": - env.Append(CXXFLAGS=["-std=c++14","-include","global.hpp"]) + env.Append( + CXXFLAGS=["-std=c++14","-include","global.hpp"], + #LINKFLAGS=["-rpath-link", "deps/lib/", "-rpath", "./"] + ) def build_app_package(env, source, build_dir, info): env.Install(build_dir, source) @@ -390,6 +395,7 @@ if not env.GetOption('clean'): if platform == 'posix': def check_tgui(conf, second_attempt=False): if conf.CheckLib('libtgui', language='C++'): + bundled_libs.append('tgui') return conf else: if second_attempt: @@ -471,7 +477,31 @@ Export("data_dir") SConscript(["rsrc/SConscript", "doc/SConscript"]) # Bundle required frameworks and libraries - +def handle_bundled_libs(extension, prefix=''): + target_dirs = ["#build/Blades of Exile", "#build/test"] + for lib in bundled_libs: + for lpath in env['LIBPATH']: + def check_path(src_file): + _dir = os.path.dirname(src_file) + print(f'checking {_dir} for {prefix}{lib}') + try: + print(os.listdir(_dir)) + except: + pass + if path.exists(src_file) and src_file != "/usr/lib/x86_64-linux-gnu/libz.so": + for targ in target_dirs: + for so in os.listdir(_dir): + if os.path.basename(src_file) in so: + print(f'found {path.join(_dir, so)}') + env.Install(targ, path.join(_dir, so)) + return True + return False + if check_path(path.join(lpath, prefix + lib + extension)): + break + elif check_path(path.join(lpath.replace('lib', 'bin'), prefix + lib + extension)): + break + elif check_path(path.join(lpath, 'x86_64-linux-gnu', prefix + lib + extension)): + break if platform == "darwin": app_targets = [] if 'game' in targets: @@ -510,20 +540,7 @@ elif platform == "win32": brotlidec brotlicommon """) - target_dirs = ["#build/Blades of Exile", "#build/test"] - for lib in bundled_libs: - for lpath in env['LIBPATH']: - src_file = path.join(lpath, lib + ".dll") - if path.exists(src_file): - for targ in target_dirs: - env.Install(targ, src_file) - break - elif 'lib' in lpath: - src_file = path.join(lpath.replace('lib', 'bin'), lib + ".dll") - if path.exists(src_file): - for targ in target_dirs: - env.Install(targ, src_file) - break + handle_bundled_libs(".dll") # Extra: Microsoft redistributable libraries installer if 'msvc' in env["TOOLS"]: if path.exists("deps/VCRedistInstall.exe"): @@ -531,11 +548,41 @@ elif platform == "win32": else: print("WARNING: Cannot find installer for the MSVC redistributable libraries for your version of Visual Studio.") print("Please download it from Microsoft's website and place it at:") - print(" deps/VCRedistInstall.exe") + print(" deps/VCRedistInstall.exe") # Create it so its lack doesn't cause makensis to break # (Because the installer is an optional component.) os.makedirs("build/Blades of Exile", exist_ok=True) open("build/Blades of Exile/VCRedistInstall.exe", 'w').close() +elif platform == "posix": + targets = [ + "Blades of Exile", + "BoE Character Editor", + "BoE Scenario Editor", + ] + def patchelf(): + to_patch = targets + [so for so in os.listdir("build/Blades of Exile") if '.so' in so] + for targ in to_patch: + subprocess.call(['patchelf', '--set-rpath', '.', targ], cwd='build/Blades of Exile') + atexit.register(patchelf) + bundled_libs += Split(""" + GL + X11 + stdc++ + Xrandr + Xcursor + udev + openal + vorbisenc + vorbisfile + vorbis + ogg + FLAC + freetype + GLdispatch + GLX + xcb + """) + handle_bundled_libs(".so", "lib") if env["package"]: if platform == "darwin":