diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 9027616..05002d3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -210,3 +210,80 @@ jobs: with: name: UnleashedRecomp-Flatpak path: ./${{ env.FLATPAK_ID }}.flatpak + build-macos: + name: Build macOS + runs-on: macos-15 + strategy: + matrix: + arch: [ "arm64" ] + preset: ["macos-debug", "macos-release", "macos-relwithdebinfo"] + env: + CMAKE_PRESET: ${{ matrix.preset }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Checkout Private Repository + uses: actions/checkout@v4 + with: + repository: ${{ secrets.ASSET_REPO }} + token: ${{ secrets.ASSET_REPO_TOKEN }} + path: ./private + + - name: Setup latest Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ccache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.preset }} + + - name: Cache vcpkg + uses: actions/cache@v4 + with: + path: | + ./thirdparty/vcpkg/downloads + ./thirdparty/vcpkg/packages + key: vcpkg-${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('vcpkg.json') }} + restore-keys: | + vcpkg-${{ runner.os }}-${{ matrix.arch }}- + + - name: Install Dependencies (macOS) + run: | + brew install ninja + + - name: Cache ccache Directory + uses: actions/cache@v4 + with: + path: /tmp/ccache + key: ccache-${{ runner.os }}-${{ matrix.arch }}-${{ matrix.preset }} + + - name: Prepare Project + run: | + cp ./private/* ./UnleashedRecompLib/private + + - name: Configure Project + env: + CCACHE_DIR: /tmp/ccache + run: cmake . --preset ${{ env.CMAKE_PRESET }} -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} -DSDL2MIXER_VORBIS=VORBISFILE -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache + + - name: Build Project + env: + CCACHE_DIR: /tmp/ccache + run: cmake --build ./out/build/${{ env.CMAKE_PRESET }} --target UnleashedRecomp + + - name: Pack Release + run: | + codesign --deep -fs - "./out/build/${{ env.CMAKE_PRESET }}/UnleashedRecomp/Unleashed Recompiled.app" + tar -czf UnleashedRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }}.tar.gz -C ./out/build/${{ env.CMAKE_PRESET }}/UnleashedRecomp "Unleashed Recompiled.app" + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: UnleashedRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }} + path: UnleashedRecomp-macOS-${{ matrix.arch }}-${{ env.CMAKE_PRESET }}.tar.gz diff --git a/.gitmodules b/.gitmodules index 367a42b..fa8706a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,3 +61,9 @@ [submodule "UnleashedRecomp/api"] path = UnleashedRecomp/api url = https://github.com/hedge-dev/SWA.git +[submodule "thirdparty/MoltenVK/MoltenVK"] + path = thirdparty/MoltenVK/MoltenVK + url = https://github.com/KhronosGroup/MoltenVK.git +[submodule "thirdparty/MoltenVK/SPIRV-Cross"] + path = thirdparty/MoltenVK/SPIRV-Cross + url = https://github.com/KhronosGroup/SPIRV-Cross.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9032d76..b41ae44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,6 @@ if(NOT DEFINED ENV{VCPKG_ROOT}) message(FATAL_ERROR "VCPKG_ROOT is not defined!") endif() -include($ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) set(UNLEASHED_RECOMP_THIRDPARTY_ROOT ${CMAKE_SOURCE_DIR}/thirdparty) set(UNLEASHED_RECOMP_TOOLS_ROOT ${CMAKE_SOURCE_DIR}/tools) set(CMAKE_CXX_STANDARD 20) @@ -18,16 +17,28 @@ endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# Target Sandy Bridge for all projects -add_compile_options( - -march=sandybridge -) +project("UnleashedRecomp-ALL") + +if (CMAKE_OSX_ARCHITECTURES) + set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES}) +elseif(CMAKE_SYSTEM_PROCESSOR) + set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) +else() + set(UNLEASHED_RECOMP_ARCHITECTURE ${CMAKE_HOST_SYSTEM_PROCESSOR}) +endif() +string(TOLOWER "${UNLEASHED_RECOMP_ARCHITECTURE}" UNLEASHED_RECOMP_ARCHITECTURE) +message(STATUS "Detected architecture: ${UNLEASHED_RECOMP_ARCHITECTURE}") + +if (UNLEASHED_RECOMP_ARCHITECTURE STREQUAL "x86_64" OR UNLEASHED_RECOMP_ARCHITECTURE STREQUAL "amd64") + # Target Sandy Bridge for all projects + add_compile_options( + -march=sandybridge + ) +endif() add_subdirectory(${UNLEASHED_RECOMP_THIRDPARTY_ROOT}) add_subdirectory(${UNLEASHED_RECOMP_TOOLS_ROOT}) -project("UnleashedRecomp-ALL") - # Include sub-projects. add_subdirectory("UnleashedRecompLib") add_subdirectory("UnleashedRecomp") diff --git a/CMakePresets.json b/CMakePresets.json index 72710ec..b64ca36 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -113,6 +113,58 @@ "CMAKE_BUILD_TYPE": "Release", "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true } + }, + { + "name": "macos-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + }, + "CMAKE_OSX_DEPLOYMENT_TARGET": "13.0" + }, + "environment": { + "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "vendor": { + "microsoft.com/VisualStudioRemoteSettings/CMake/2.0": { + "remoteSourceRootDir": "$env{HOME}/.vs/$ms{projectDirName}" + } + } + }, + { + "name": "macos-debug", + "displayName": "macOS-Debug", + "inherits": "macos-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos-relwithdebinfo", + "displayName": "macOS-RelWithDebInfo", + "inherits": "macos-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "macos-release", + "displayName": "macOS-Release", + "inherits": "macos-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true + } } ] } diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 337b963..48908bf 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -97,6 +97,14 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") "os/linux/user_linux.cpp" "os/linux/version_linux.cpp" ) +elseif (APPLE) + set(UNLEASHED_RECOMP_OS_CXX_SOURCES + "os/macos/logger_macos.cpp" + "os/macos/media_macos.cpp" + "os/macos/process_macos.cpp" + "os/macos/user_macos.cpp" + "os/macos/version_macos.cpp" + ) endif() set(UNLEASHED_RECOMP_CPU_CXX_SOURCES @@ -119,7 +127,7 @@ endif() set(UNLEASHED_RECOMP_APU_CXX_SOURCES "apu/audio.cpp" - "apu/embedded_player.cpp" + "apu/embedded_player.cpp" "apu/driver/sdl2_driver.cpp" ) @@ -149,16 +157,16 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES set(UNLEASHED_RECOMP_UI_CXX_SOURCES "ui/achievement_menu.cpp" - "ui/achievement_overlay.cpp" + "ui/achievement_overlay.cpp" "ui/black_bar.cpp" "ui/button_guide.cpp" "ui/fader.cpp" - "ui/game_window.cpp" + "ui/game_window.cpp" "ui/imgui_utils.cpp" "ui/installer_wizard.cpp" "ui/message_window.cpp" "ui/options_menu.cpp" - "ui/options_menu_thumbnails.cpp" + "ui/options_menu_thumbnails.cpp" "ui/tv_static.cpp" ) @@ -180,7 +188,7 @@ set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES set(UNLEASHED_RECOMP_USER_CXX_SOURCES "user/achievement_data.cpp" "user/achievement_manager.cpp" - "user/config.cpp" + "user/config.cpp" "user/registry.cpp" "user/paths.cpp" "user/persistent_data.cpp" @@ -231,7 +239,7 @@ set(UNLEASHED_RECOMP_CXX_SOURCES "app.cpp" "exports.cpp" "main.cpp" - "misc_impl.cpp" + "misc_impl.cpp" "preload_executable.cpp" "sdl_listener.cpp" "stdafx.cpp" @@ -250,11 +258,11 @@ set(UNLEASHED_RECOMP_CXX_SOURCES ${UNLEASHED_RECOMP_USER_CXX_SOURCES} ${UNLEASHED_RECOMP_MOD_CXX_SOURCES} ${UNLEASHED_RECOMP_THIRDPARTY_SOURCES} -) +) -include("version.cmake") - -set(VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt") +include("version.cmake") + +set(VERSION_TXT "${PROJECT_SOURCE_DIR}/res/version.txt") # Only show Git info and build type if not Release. set(SHOW_GIT_INFO_AND_BUILD_TYPE 0) @@ -270,53 +278,102 @@ GenerateVersionSources( BUILD_TYPE ${CMAKE_BUILD_TYPE} SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE} SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE} -) +) if (WIN32) - # Create binary version number for Win32 integer attributes. - CreateVersionString( - VERSION_TXT ${VERSION_TXT} - OUTPUT_CSV 1 - OUTPUT_VAR WIN32_VERSION_BINARY - ) - - # Create string version number for Win32 detailed attributes. - CreateVersionString( - VERSION_TXT ${VERSION_TXT} - BUILD_TYPE ${CMAKE_BUILD_TYPE} - SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE} - SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE} - OUTPUT_VAR WIN32_VERSION_STRING - ) - + # Create binary version number for Win32 integer attributes. + CreateVersionString( + VERSION_TXT ${VERSION_TXT} + OUTPUT_CSV 1 + OUTPUT_VAR WIN32_VERSION_BINARY + ) + + # Create string version number for Win32 detailed attributes. + CreateVersionString( + VERSION_TXT ${VERSION_TXT} + BUILD_TYPE ${CMAKE_BUILD_TYPE} + SHOW_GIT_INFO ${SHOW_GIT_INFO_AND_BUILD_TYPE} + SHOW_BUILD_TYPE ${SHOW_GIT_INFO_AND_BUILD_TYPE} + OUTPUT_VAR WIN32_VERSION_STRING + ) + # Set Win32 icon path. - set(WIN32_ICON_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources/images/game_icon.ico") + set(WIN32_ICON_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources/images/game_icon.ico") configure_file("res/win32/res.rc.template" "${CMAKE_BINARY_DIR}/res.rc" @ONLY) - add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc") - - # Hide console for release configurations. - if (${CMAKE_BUILD_TYPE} MATCHES "Release") - target_link_options(UnleashedRecomp PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") + add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES} "${CMAKE_BINARY_DIR}/res.rc") + + # Hide console for release configurations. + if (${CMAKE_BUILD_TYPE} MATCHES "Release") + target_link_options(UnleashedRecomp PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") endif() +elseif (APPLE) + # Create version number for app bundle. + CreateVersionString( + VERSION_TXT ${VERSION_TXT} + OUTPUT_VAR MACOS_BUNDLE_VERSION + ) + + add_executable(UnleashedRecomp MACOSX_BUNDLE + ${UNLEASHED_RECOMP_CXX_SOURCES} + res/macos/game_icon.icns + ) + set_source_files_properties(res/macos/game_icon.icns PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) + set_target_properties(UnleashedRecomp PROPERTIES + OUTPUT_NAME "Unleashed Recompiled" + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/res/macos/MacOSXBundleInfo.plist.in + MACOSX_BUNDLE_GUI_IDENTIFIER hedge-dev.UnleashedRecomp + MACOSX_BUNDLE_BUNDLE_NAME "Unleashed Recompiled" + MACOSX_BUNDLE_BUNDLE_VERSION ${MACOS_BUNDLE_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${MACOS_BUNDLE_VERSION} + MACOSX_BUNDLE_ICON_FILE "game_icon.icns" + ) + + # Linking with MoltenVK directly would prevent using the system Vulkan loader to load with debug layers. + # Instead, copy the MoltenVK dylib to the app bundle along with an ICD file for the loader to find it. + # In the event the loader is not installed, the MoltenVK dylib can still be picked up directly in the app bundle. + set(MVK_BUNDLED_PATH "Resources/vulkan/icd.d") + set(MVK_DST "${CMAKE_CURRENT_BINARY_DIR}/Unleashed Recompiled.app/Contents/${MVK_BUNDLED_PATH}") + set_property(TARGET UnleashedRecomp APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLED_PATH}") + + set(MVK_ICD_SRC "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/MoltenVK/MoltenVK/MoltenVK/icd/MoltenVK_icd.json") + set(MVK_ICD_DST "${MVK_DST}/MoltenVK_icd.json") + set(MVK_DYLIB_SRC "${CMAKE_BINARY_DIR}/thirdparty/MoltenVK/libMoltenVK.dylib") + set(MVK_DYLIB_DST "${MVK_DST}/libMoltenVK.dylib") + + add_custom_command( + OUTPUT ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST}) + add_custom_command( + OUTPUT ${MVK_ICD_DST} + DEPENDS ${MVK_ICD_SRC} ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E copy ${MVK_ICD_SRC} ${MVK_ICD_DST}) + add_custom_command( + OUTPUT ${MVK_DYLIB_DST} + DEPENDS ${MVK_DYLIB_SRC} ${MVK_DST} + COMMAND ${CMAKE_COMMAND} -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST}) + add_custom_target(CopyMoltenVK DEPENDS ${MVK_ICD_DST} ${MVK_DYLIB_DST}) + add_dependencies(CopyMoltenVK MoltenVK) + add_dependencies(UnleashedRecomp CopyMoltenVK) else() add_executable(UnleashedRecomp ${UNLEASHED_RECOMP_CXX_SOURCES}) endif() if (UNLEASHED_RECOMP_FLATPAK) - target_compile_definitions(UnleashedRecomp PRIVATE - "UNLEASHED_RECOMP_FLATPAK" - "GAME_INSTALL_DIRECTORY=\"/var/data\"" + target_compile_definitions(UnleashedRecomp PRIVATE + "UNLEASHED_RECOMP_FLATPAK" + "GAME_INSTALL_DIRECTORY=\"/var/data\"" ) endif() if (UNLEASHED_RECOMP_D3D12) find_package(directx-headers CONFIG REQUIRED) find_package(directx12-agility CONFIG REQUIRED) - target_compile_definitions(UnleashedRecomp PRIVATE - UNLEASHED_RECOMP_D3D12 - D3D12MA_USING_DIRECTX_HEADERS - D3D12MA_OPTIONS16_SUPPORTED + target_compile_definitions(UnleashedRecomp PRIVATE + UNLEASHED_RECOMP_D3D12 + D3D12MA_USING_DIRECTX_HEADERS + D3D12MA_OPTIONS16_SUPPORTED ) endif() @@ -324,19 +381,17 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_compile_definitions(UnleashedRecomp PRIVATE SDL_VULKAN_ENABLED) endif() -find_package(directx-dxc REQUIRED) find_package(CURL REQUIRED) if (UNLEASHED_RECOMP_D3D12) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) add_custom_command(TARGET UnleashedRecomp POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_BINARY_DIR}/D3D12 - COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_BINARY_DIR}/D3D12 - COMMAND_EXPAND_LISTS + COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 + COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND_EXPAND_LISTS ) - - find_file(DIRECTX_DXIL_LIBRARY "dxil.dll") - file(COPY ${DIRECTX_DXIL_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(UnleashedRecomp PRIVATE Microsoft::DirectX-Headers @@ -348,19 +403,17 @@ if (UNLEASHED_RECOMP_D3D12) ) endif() -file(CHMOD ${DIRECTX_DXC_TOOL} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) - if (WIN32) target_link_libraries(UnleashedRecomp PRIVATE - comctl32 + comctl32 dwmapi - ntdll + ntdll Shcore Synchronization winmm windowsapp ) -endif() +endif() target_link_libraries(UnleashedRecomp PRIVATE fmt::fmt @@ -411,16 +464,16 @@ function(compile_shader FILE_PATH TARGET_NAME) endfunction() function(compile_vertex_shader FILE_PATH) - compile_shader(${FILE_PATH} vs_6_0 -fvk-invert-y) + compile_shader(${FILE_PATH} vs_6_0 -fvk-invert-y -DUNLEASHED_RECOMP) endfunction() function(compile_pixel_shader FILE_PATH) - compile_shader(${FILE_PATH} ps_6_0) + compile_shader(${FILE_PATH} ps_6_0 -DUNLEASHED_RECOMP) endfunction() -compile_pixel_shader(blend_color_alpha_ps) -compile_vertex_shader(copy_vs) -compile_pixel_shader(copy_color_ps) +compile_pixel_shader(blend_color_alpha_ps) +compile_vertex_shader(copy_vs) +compile_pixel_shader(copy_color_ps) compile_pixel_shader(copy_depth_ps) compile_pixel_shader(csd_filter_ps) compile_vertex_shader(csd_no_tex_vs) @@ -434,7 +487,7 @@ compile_pixel_shader(gamma_correction_ps) compile_pixel_shader(imgui_ps) compile_vertex_shader(imgui_vs) compile_pixel_shader(movie_ps) -compile_vertex_shader(movie_vs) +compile_vertex_shader(movie_vs) compile_pixel_shader(resolve_msaa_color_2x) compile_pixel_shader(resolve_msaa_color_4x) compile_pixel_shader(resolve_msaa_color_8x) @@ -444,7 +497,7 @@ compile_pixel_shader(resolve_msaa_depth_8x) set(RESOURCES_SOURCE_PATH "${PROJECT_SOURCE_DIR}/../UnleashedRecompResources") set(RESOURCES_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/res") - + ## Miscellaneous ## BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/bc_diff/button_bc_diff.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/bc_diff/button_bc_diff.bin" ARRAY_NAME "g_button_bc_diff" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/font/im_font_atlas.bin" DEST_FILE "${RESOURCES_OUTPUT_PATH}/font/im_font_atlas.bin" ARRAY_NAME "g_im_font_atlas" COMPRESSION_TYPE "zstd") @@ -456,7 +509,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select.dds" ARRAY_NAME "g_select" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/light.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/light.dds" ARRAY_NAME "g_light" COMPRESSION_TYPE "zstd") - + ## Installer ## BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd") @@ -468,23 +521,23 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/in BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_007.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_007.dds" ARRAY_NAME "g_install_007" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_008.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_008.dds" ARRAY_NAME "g_install_008" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/miles_electric_icon.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/miles_electric_icon.dds" ARRAY_NAME "g_miles_electric_icon" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/pulse_install.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/pulse_install.dds" ARRAY_NAME "g_pulse_install" COMPRESSION_TYPE "zstd") - +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/pulse_install.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/pulse_install.dds" ARRAY_NAME "g_pulse_install" COMPRESSION_TYPE "zstd") + ## Options Menu ## BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/achievement_notifications.dds" ARRAY_NAME "g_achievement_notifications" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input_ps.dds" ARRAY_NAME "g_allow_background_input_ps" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/allow_background_input_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/allow_background_input_xb.dds" ARRAY_NAME "g_allow_background_input_xb" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" ARRAY_NAME "g_antialiasing_none" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" ARRAY_NAME "g_antialiasing_2x" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" ARRAY_NAME "g_antialiasing_4x" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_none.dds" ARRAY_NAME "g_antialiasing_none" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_2x.dds" ARRAY_NAME "g_antialiasing_2x" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_4x.dds" ARRAY_NAME "g_antialiasing_4x" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/antialiasing_8x.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/antialiasing_8x.dds" ARRAY_NAME "g_antialiasing_8x" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/aspect_ratio.dds" ARRAY_NAME "g_aspect_ratio" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/battle_theme.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/battle_theme.dds" ARRAY_NAME "g_battle_theme" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/brightness.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/brightness.dds" ARRAY_NAME "g_brightness" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_stereo.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_stereo.dds" ARRAY_NAME "g_channel_stereo" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_surround.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_surround.dds" ARRAY_NAME "g_channel_surround" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/brightness.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/brightness.dds" ARRAY_NAME "g_brightness" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_stereo.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_stereo.dds" ARRAY_NAME "g_channel_stereo" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/channel_surround.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/channel_surround.dds" ARRAY_NAME "g_channel_surround" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_ps.dds" ARRAY_NAME "g_control_tutorial_ps" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" ARRAY_NAME "g_control_tutorial_xb" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/control_tutorial_xb.dds" ARRAY_NAME "g_control_tutorial_xb" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/controller_icons.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/controller_icons.dds" ARRAY_NAME "g_controller_icons" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/default.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/default.dds" ARRAY_NAME "g_default" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/effects_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/effects_volume.dds" ARRAY_NAME "g_effects_volume" COMPRESSION_TYPE "zstd") @@ -500,7 +553,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/motion_blur_off.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_off.dds" ARRAY_NAME "g_motion_blur_off" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/motion_blur_original.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_original.dds" ARRAY_NAME "g_motion_blur_original" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/motion_blur_enhanced.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/motion_blur_enhanced.dds" ARRAY_NAME "g_motion_blur_enhanced" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" ARRAY_NAME "g_movie_scale_fit" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fit.dds" ARRAY_NAME "g_movie_scale_fit" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/movie_scale_fill.dds" ARRAY_NAME "g_movie_scale_fill" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_attenuation.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_attenuation.dds" ARRAY_NAME "g_music_attenuation" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/music_volume.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/music_volume.dds" ARRAY_NAME "g_music_volume" COMPRESSION_TYPE "zstd") @@ -509,34 +562,34 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/op BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x2048.dds" ARRAY_NAME "g_shadow_resolution_x2048" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x4096.dds" ARRAY_NAME "g_shadow_resolution_x4096" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/shadow_resolution_x8192.dds" ARRAY_NAME "g_shadow_resolution_x8192" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" ARRAY_NAME "g_time_of_day_transition_playstation" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_ps.dds" ARRAY_NAME "g_time_of_day_transition_playstation" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/time_transition_xb.dds" ARRAY_NAME "g_time_of_day_transition_xbox" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_false.dds" ARRAY_NAME "g_transparency_antialiasing_false" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/transparency_antialiasing_true.dds" ARRAY_NAME "g_transparency_antialiasing_true" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" ARRAY_NAME "g_ui_alignment_centre" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_centre.dds" ARRAY_NAME "g_ui_alignment_centre" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/ui_alignment_edge.dds" ARRAY_NAME "g_ui_alignment_edge" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vertical_camera.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vertical_camera.dds" ARRAY_NAME "g_vertical_camera" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/voice_language.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/voice_language.dds" ARRAY_NAME "g_voice_language" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration_ps.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration_ps.dds" ARRAY_NAME "g_vibration_ps" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration_xb.dds" ARRAY_NAME "g_vibration_xb" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_on.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_on.dds" ARRAY_NAME "g_vsync_on" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vibration_xb.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vibration_xb.dds" ARRAY_NAME "g_vibration_xb" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_on.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_on.dds" ARRAY_NAME "g_vsync_on" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/vsync_off.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/vsync_off.dds" ARRAY_NAME "g_vsync_off" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/window_size.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/window_size.dds" ARRAY_NAME "g_window_size" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/thumbnails/xbox_color_correction.dds" ARRAY_NAME "g_xbox_color_correction" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/miles_electric.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/miles_electric.dds" ARRAY_NAME "g_miles_electric" COMPRESSION_TYPE "zstd") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/options_static.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/options_static.dds" ARRAY_NAME "g_options_static" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/miles_electric.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/miles_electric.dds" ARRAY_NAME "g_miles_electric" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/options_static.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/options_static.dds" ARRAY_NAME "g_options_static" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/options_menu/options_static_flash.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/options_menu/options_static_flash.dds" ARRAY_NAME "g_options_static_flash" COMPRESSION_TYPE "zstd") - + ## Game Icon ## BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon.bmp" ARRAY_NAME "g_game_icon") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night") - +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/game_icon_night.bmp" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/game_icon_night.bmp" ARRAY_NAME "g_game_icon_night") + ## Audio ## -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/music/installer.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/music/installer.ogg" ARRAY_NAME "g_installer_music") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/music/installer.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/music/installer.ogg" ARRAY_NAME "g_installer_music") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_cursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_cursor.ogg" ARRAY_NAME "g_sys_worldmap_cursor") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_worldmap_finaldecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_worldmap_finaldecide.ogg" ARRAY_NAME "g_sys_worldmap_finaldecide") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecansel.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecansel.ogg" ARRAY_NAME "g_sys_actstg_pausecansel") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausecursor.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausecursor.ogg" ARRAY_NAME "g_sys_actstg_pausecursor") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausedecide.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausedecide.ogg" ARRAY_NAME "g_sys_actstg_pausedecide") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinclose.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinclose.ogg" ARRAY_NAME "g_sys_actstg_pausewinclose") -BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinopen.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinopen.ogg" ARRAY_NAME "g_sys_actstg_pausewinopen") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/sounds/sys_actstg_pausewinopen.ogg" DEST_FILE "${RESOURCES_OUTPUT_PATH}/sounds/sys_actstg_pausewinopen.ogg" ARRAY_NAME "g_sys_actstg_pausewinopen") diff --git a/UnleashedRecomp/cpu/guest_thread.cpp b/UnleashedRecomp/cpu/guest_thread.cpp index 4249c58..3dbe915 100644 --- a/UnleashedRecomp/cpu/guest_thread.cpp +++ b/UnleashedRecomp/cpu/guest_thread.cpp @@ -40,29 +40,101 @@ GuestThreadContext::~GuestThreadContext() g_userHeap.Free(thread); } +#ifdef USE_PTHREAD +static size_t GetStackSize() +{ + // Cache as this should not change. + static size_t stackSize = 0; + if (stackSize == 0) + { + // 8 MiB is a typical default. + constexpr auto defaultSize = 8 * 1024 * 1024; + struct rlimit lim; + const auto ret = getrlimit(RLIMIT_STACK, &lim); + if (ret == 0 && lim.rlim_cur < defaultSize) + { + // Use what the system allows. + stackSize = lim.rlim_cur; + } + else + { + stackSize = defaultSize; + } + } + return stackSize; +} + +static void* GuestThreadFunc(void* arg) +{ + GuestThreadHandle* hThread = (GuestThreadHandle*)arg; +#else static void GuestThreadFunc(GuestThreadHandle* hThread) { +#endif hThread->suspended.wait(true); GuestThread::Start(hThread->params); +#ifdef USE_PTHREAD + return nullptr; +#endif } GuestThreadHandle::GuestThreadHandle(const GuestThreadParams& params) - : params(params), suspended((params.flags & 0x1) != 0), thread(GuestThreadFunc, this) + : params(params), suspended((params.flags & 0x1) != 0) +#ifdef USE_PTHREAD +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, GetStackSize()); + const auto ret = pthread_create(&thread, &attr, GuestThreadFunc, this); + if (ret != 0) { + fprintf(stderr, "pthread_create failed with error code 0x%X.\n", ret); + return; + } +} +#else + , thread(GuestThreadFunc, this) { } +#endif GuestThreadHandle::~GuestThreadHandle() { +#ifdef USE_PTHREAD + pthread_join(thread, nullptr); +#else if (thread.joinable()) thread.join(); +#endif +} + +template +static uint32_t CalcThreadId(const ThreadType& id) +{ + if constexpr (sizeof(id) == 4) + return *reinterpret_cast(&id); + else + return XXH32(&id, sizeof(id), 0); +} + +uint32_t GuestThreadHandle::GetThreadId() const +{ +#ifdef USE_PTHREAD + return CalcThreadId(thread); +#else + return CalcThreadId(thread.get_id()); +#endif } uint32_t GuestThreadHandle::Wait(uint32_t timeout) { assert(timeout == INFINITE); +#ifdef USE_PTHREAD + pthread_join(thread, nullptr); +#else if (thread.joinable()) thread.join(); +#endif return STATUS_WAIT_0; } @@ -80,27 +152,25 @@ uint32_t GuestThread::Start(const GuestThreadParams& params) return ctx.ppcContext.r3.u32; } -static uint32_t GetThreadId(const std::thread::id& id) -{ - if constexpr (sizeof(id) == 4) - return *reinterpret_cast(&id); - else - return XXH32(&id, sizeof(id), 0); -} - GuestThreadHandle* GuestThread::Start(const GuestThreadParams& params, uint32_t* threadId) { auto hThread = CreateKernelObject(params); if (threadId != nullptr) - *threadId = GetThreadId(hThread->thread.get_id()); + { + *threadId = hThread->GetThreadId(); + } return hThread; } uint32_t GuestThread::GetCurrentThreadId() { - return GetThreadId(std::this_thread::get_id()); +#ifdef USE_PTHREAD + return CalcThreadId(pthread_self()); +#else + return CalcThreadId(std::this_thread::get_id()); +#endif } void GuestThread::SetLastError(uint32_t error) diff --git a/UnleashedRecomp/cpu/guest_thread.h b/UnleashedRecomp/cpu/guest_thread.h index 15d16bf..e12e91a 100644 --- a/UnleashedRecomp/cpu/guest_thread.h +++ b/UnleashedRecomp/cpu/guest_thread.h @@ -2,6 +2,15 @@ #include +// Use pthreads directly on macOS to be able to increase default stack size. +#ifdef __APPLE__ +#define USE_PTHREAD 1 +#endif + +#ifdef USE_PTHREAD +#include +#endif + #define CURRENT_THREAD_HANDLE uint32_t(-2) struct GuestThreadContext @@ -24,11 +33,17 @@ struct GuestThreadHandle : KernelObject { GuestThreadParams params; std::atomic suspended; +#ifdef USE_PTHREAD + pthread_t thread; +#else std::thread thread; +#endif GuestThreadHandle(const GuestThreadParams& params); ~GuestThreadHandle() override; + uint32_t GetThreadId() const; + uint32_t Wait(uint32_t timeout) override; }; diff --git a/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp b/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp index 073ea68..04102c0 100644 --- a/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp +++ b/UnleashedRecomp/gpu/rhi/plume_d3d12.cpp @@ -3437,6 +3437,7 @@ namespace plume { adapter = adapterOption; d3d = deviceOption; shaderModel = dataShaderModel.HighestShaderModel; + capabilities.geometryShader = true; capabilities.raytracing = rtSupportOption; capabilities.raytracingStateUpdate = rtStateUpdateSupportOption; capabilities.sampleLocations = samplePositionsOption; diff --git a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h index 568160a..315274b 100644 --- a/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h +++ b/UnleashedRecomp/gpu/rhi/plume_render_interface_types.h @@ -26,7 +26,7 @@ #undef ControlMask #undef Success #elif defined(__APPLE__) -typedef struct _NSWindow NSWindow; +#include #endif #ifdef SDL_VULKAN_ENABLED @@ -52,7 +52,9 @@ namespace plume { }; #elif defined(__APPLE__) struct RenderWindow { - NSWindow* window; + SDL_Window* window; + void* view; + bool operator==(const struct RenderWindow& rhs) const { return window == rhs.window; } @@ -1784,6 +1786,9 @@ namespace plume { }; struct RenderDeviceCapabilities { + // Geometry shaders. + bool geometryShader = false; + // Raytracing. bool raytracing = false; bool raytracingStateUpdate = false; diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp index 94f91fa..8613481 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.cpp @@ -51,13 +51,18 @@ namespace plume { VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, # elif defined(__linux__) VK_KHR_XLIB_SURFACE_EXTENSION_NAME, +# elif defined(__APPLE__) + VK_EXT_METAL_SURFACE_EXTENSION_NAME, # endif }; static const std::unordered_set OptionalInstanceExtensions = { - // No optional instance extensions yet. +# if defined(__APPLE__) + // Tells the system Vulkan loader to enumerate portability drivers, if supported. + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, +# endif }; - + static const std::unordered_set RequiredDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, @@ -79,6 +84,8 @@ namespace plume { VK_KHR_PRESENT_ID_EXTENSION_NAME, VK_KHR_PRESENT_WAIT_EXTENSION_NAME, VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, + // Vulkan spec requires this to be enabled if supported by the driver. + VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, }; // Common functions. @@ -567,14 +574,16 @@ namespace plume { } } - static VkPipelineStageFlags toStageFlags(RenderBarrierStages stages, bool rtSupported) { + static VkPipelineStageFlags toStageFlags(RenderBarrierStages stages, bool geometrySupported, bool rtSupported) { VkPipelineStageFlags flags = 0; if (stages & RenderBarrierStage::GRAPHICS) { flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + if (geometrySupported) { + flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + } flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; flags |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; @@ -2051,6 +2060,19 @@ namespace plume { fprintf(stderr, "vkCreateXlibSurfaceKHR failed with error code 0x%X.\n", res); return; } +# elif defined(__APPLE__) + assert(renderWindow.window != 0); + assert(renderWindow.view != 0); + VkMetalSurfaceCreateInfoEXT surfaceCreateInfo = {}; + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; + surfaceCreateInfo.pLayer = renderWindow.view; + + VulkanInterface *renderInterface = commandQueue->device->renderInterface; + res = vkCreateMetalSurfaceEXT(renderInterface->instance, &surfaceCreateInfo, nullptr, &surface); + if (res != VK_SUCCESS) { + fprintf(stderr, "vkCreateMetalSurfaceEXT failed with error code 0x%X.\n", res); + return; + } # endif VkBool32 presentSupported = false; @@ -2191,7 +2213,14 @@ namespace plume { } // Handle the error silently. +#if defined(__APPLE__) + // Under MoltenVK, VK_SUBOPTIMAL_KHR does not result in a valid state for rendering. We intentionally + // only check for this error during present to avoid having to synchronize manually against the semaphore + // signalled by vkAcquireNextImageKHR. + if (res != VK_SUCCESS) { +#else if ((res != VK_SUCCESS) && (res != VK_SUBOPTIMAL_KHR)) { +#endif return false; } @@ -2360,6 +2389,8 @@ namespace plume { // The attributes width and height members do not include the border. dstWidth = attributes.width; dstHeight = attributes.height; +# elif defined(__APPLE__) + SDL_GetWindowSizeInPixels(renderWindow.window, (int *)(&dstWidth), (int *)(&dstHeight)); # endif } @@ -2683,9 +2714,10 @@ namespace plume { endActiveRenderPass(); + const bool geometryEnabled = device->capabilities.geometryShader; const bool rtEnabled = device->capabilities.raytracing; VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT | toStageFlags(stages, rtEnabled); + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT | toStageFlags(stages, geometryEnabled, rtEnabled); thread_local std::vector bufferMemoryBarriers; thread_local std::vector imageMemoryBarriers; bufferMemoryBarriers.clear(); @@ -2704,7 +2736,7 @@ namespace plume { bufferMemoryBarrier.offset = 0; bufferMemoryBarrier.size = interfaceBuffer->desc.size; bufferMemoryBarriers.emplace_back(bufferMemoryBarrier); - srcStageMask |= toStageFlags(interfaceBuffer->barrierStages, rtEnabled); + srcStageMask |= toStageFlags(interfaceBuffer->barrierStages, geometryEnabled, rtEnabled); interfaceBuffer->barrierStages = stages; } @@ -2724,7 +2756,7 @@ namespace plume { imageMemoryBarrier.subresourceRange.layerCount = interfaceTexture->desc.arraySize; imageMemoryBarrier.subresourceRange.aspectMask = (interfaceTexture->desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarriers.emplace_back(imageMemoryBarrier); - srcStageMask |= toStageFlags(interfaceTexture->barrierStages, rtEnabled); + srcStageMask |= toStageFlags(interfaceTexture->barrierStages, geometryEnabled, rtEnabled); interfaceTexture->textureLayout = textureBarrier.layout; interfaceTexture->barrierStages = stages; } @@ -2890,6 +2922,9 @@ namespace plume { offsetVector.clear(); for (uint32_t i = 0; i < viewCount; i++) { const VulkanBuffer *interfaceBuffer = static_cast(views[i].buffer.ref); + if (interfaceBuffer == nullptr && !device->nullDescriptorSupported) { + interfaceBuffer = static_cast(device->nullBuffer.get()); + } bufferVector.emplace_back((interfaceBuffer != nullptr) ? interfaceBuffer->vk : VK_NULL_HANDLE); offsetVector.emplace_back(views[i].buffer.offset); } @@ -3696,6 +3731,11 @@ namespace plume { bufferDeviceAddressFeatures.pNext = featuresChain; featuresChain = &bufferDeviceAddressFeatures; + VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures = {}; + portabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR; + portabilityFeatures.pNext = featuresChain; + featuresChain = &portabilityFeatures; + VkPhysicalDeviceFeatures2 deviceFeatures = {}; deviceFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; deviceFeatures.pNext = featuresChain; @@ -3766,6 +3806,12 @@ namespace plume { createDeviceChain = &bufferDeviceAddressFeatures; } + const bool portabilitySubset = supportedOptionalExtensions.find(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) != supportedOptionalExtensions.end(); + if (portabilitySubset) { + portabilityFeatures.pNext = createDeviceChain; + createDeviceChain = &portabilityFeatures; + } + // Retrieve the information for the queue families. uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); @@ -3778,6 +3824,7 @@ namespace plume { uint32_t familyIndex = 0; uint32_t familySetBits = sizeof(uint32_t) * 8; uint32_t familyQueueCount = 0; + bool familyUsed = false; for (uint32_t i = 0; i < queueFamilyCount; i++) { const VkQueueFamilyProperties &props = queueFamilyProperties[i]; @@ -3787,11 +3834,14 @@ namespace plume { } // Prefer picking the queues with the least amount of bits set that match the mask we're looking for. + // If the queue families have matching capabilities but one is already used, prefer the unused one. uint32_t setBits = numberOfSetBits(props.queueFlags); - if ((setBits < familySetBits) || ((setBits == familySetBits) && (props.queueCount > familyQueueCount))) { + bool used = queueFamilyUsed[i]; + if ((setBits < familySetBits) || ((setBits == familySetBits) && ((props.queueCount > familyQueueCount) || (familyUsed && !used)))) { familyIndex = i; familySetBits = setBits; familyQueueCount = props.queueCount; + familyUsed = used; } } @@ -3912,6 +3962,7 @@ namespace plume { description.dedicatedVideoMemory = memoryHeapSize; // Fill capabilities. + capabilities.geometryShader = deviceFeatures.features.geometryShader; capabilities.raytracing = rtSupported; capabilities.raytracingStateUpdate = false; capabilities.sampleLocations = sampleLocationsSupported; @@ -3920,13 +3971,25 @@ namespace plume { capabilities.presentWait = presentWait; capabilities.displayTiming = supportedOptionalExtensions.find(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME) != supportedOptionalExtensions.end(); capabilities.preferHDR = memoryHeapSize > (512 * 1024 * 1024); +#if defined(__APPLE__) + // MoltenVK supports triangle fans but does so via compute shaders to translate to lists, since it has to + // support all cases including indirect draw. This results in renderpass restarts that can harm performance, + // so force disable native triangle fan support and rely on the game to emulate fans if needed. + capabilities.triangleFan = false; +#else capabilities.triangleFan = true; +#endif capabilities.dynamicDepthBias = true; capabilities.uma = (description.type == RenderDeviceType::INTEGRATED) && hasHostVisibleDeviceLocalMemory; capabilities.gpuUploadHeap = capabilities.uma; // Fill Vulkan-only capabilities. loadStoreOpNoneSupported = supportedOptionalExtensions.find(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME) != supportedOptionalExtensions.end(); + nullDescriptorSupported = nullDescriptor; + + if (!nullDescriptorSupported) { + nullBuffer = createBuffer(RenderBufferDesc::DefaultBuffer(16, RenderBufferFlag::VERTEX)); + } } VulkanDevice::~VulkanDevice() { @@ -4253,6 +4316,10 @@ namespace plume { createInfo.ppEnabledLayerNames = nullptr; createInfo.enabledLayerCount = 0; +# ifdef __APPLE__ + createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +# endif + // Check for extensions. uint32_t extensionCount; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); diff --git a/UnleashedRecomp/gpu/rhi/plume_vulkan.h b/UnleashedRecomp/gpu/rhi/plume_vulkan.h index e25e186..bbdd62c 100644 --- a/UnleashedRecomp/gpu/rhi/plume_vulkan.h +++ b/UnleashedRecomp/gpu/rhi/plume_vulkan.h @@ -20,8 +20,13 @@ #define VK_USE_PLATFORM_ANDROID_KHR #elif defined(__linux__) #define VK_USE_PLATFORM_XLIB_KHR +#elif defined(__APPLE__) +#define VK_USE_PLATFORM_METAL_EXT #endif +// For VK_KHR_portability_subset +#define VK_ENABLE_BETA_EXTENSIONS + #include #ifdef __clang__ @@ -403,7 +408,9 @@ namespace plume { RenderDeviceDescription description; VkPhysicalDeviceRayTracingPipelinePropertiesKHR rtPipelineProperties = {}; VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationProperties = {}; + std::unique_ptr nullBuffer = nullptr; bool loadStoreOpNoneSupported = false; + bool nullDescriptorSupported = false; VulkanDevice(VulkanInterface *renderInterface, const std::string &preferredDeviceName); ~VulkanDevice() override; diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 2333dfb..64f6513 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -37,6 +37,7 @@ #include #endif +#define UNLEASHED_RECOMP #include "../../tools/XenosRecomp/XenosRecomp/shader_common.h" #ifdef UNLEASHED_RECOMP_D3D12 diff --git a/UnleashedRecomp/install/update_checker.cpp b/UnleashedRecomp/install/update_checker.cpp index 706a0e0..2a6f5b1 100644 --- a/UnleashedRecomp/install/update_checker.cpp +++ b/UnleashedRecomp/install/update_checker.cpp @@ -166,6 +166,9 @@ void UpdateChecker::visitWebsite() #elif defined(__linux__) std::string command = "xdg-open " + std::string(VISIT_URL) + " &"; std::system(command.c_str()); +#elif defined(__APPLE__) + std::string command = "open " + std::string(VISIT_URL) + " &"; + std::system(command.c_str()); #else static_assert(false, "Visit website not implemented for this platform."); #endif diff --git a/UnleashedRecomp/kernel/imports.cpp b/UnleashedRecomp/kernel/imports.cpp index 2cc2aae..58b33fa 100644 --- a/UnleashedRecomp/kernel/imports.cpp +++ b/UnleashedRecomp/kernel/imports.cpp @@ -660,7 +660,7 @@ void KeQueryBasePriorityThread() uint32_t NtSuspendThread(GuestThreadHandle* hThread, uint32_t* suspendCount) { - assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->thread.get_id() == std::this_thread::get_id()); + assert(hThread != GetKernelObject(CURRENT_THREAD_HANDLE) && hThread->GetThreadId() == GuestThread::GetCurrentThreadId()); hThread->suspended = true; hThread->suspended.wait(true); diff --git a/UnleashedRecomp/kernel/xam.cpp b/UnleashedRecomp/kernel/xam.cpp index 3d7ca77..357b791 100644 --- a/UnleashedRecomp/kernel/xam.cpp +++ b/UnleashedRecomp/kernel/xam.cpp @@ -306,26 +306,26 @@ uint32_t XamContentCreateEx(uint32_t dwUserIndex, const char* szRootName, const if (!exists) { - std::string root = ""; + std::filesystem::path rootPath; if (pContentData->dwContentType == XCONTENTTYPE_SAVEDATA) { - std::u8string savePathU8 = GetSavePath(true).u8string(); - root = (const char *)(savePathU8.c_str()); + rootPath = GetSavePath(true); } else if (pContentData->dwContentType == XCONTENTTYPE_DLC) { - root = GAME_INSTALL_DIRECTORY "/dlc"; + rootPath = GetGamePath() / "dlc"; } else { - root = GAME_INSTALL_DIRECTORY; + rootPath = GetGamePath(); } + const std::string root = (const char*)rootPath.u8string().c_str(); XamRegisterContent(*pContentData, root); std::error_code ec; - std::filesystem::create_directory(std::u8string_view((const char8_t*)(root.c_str())), ec); + std::filesystem::create_directory(rootPath, ec); XamRootCreate(szRootName, root); } diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index e8123b3..bb24176 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -1,5 +1,7 @@ #include +#ifdef __x86_64__ #include +#endif #include #include #include @@ -69,8 +71,10 @@ void KiSystemStartup() const auto gameContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Game"); const auto updateContent = XamMakeContent(XCONTENTTYPE_RESERVED, "Update"); - XamRegisterContent(gameContent, GAME_INSTALL_DIRECTORY "/game"); - XamRegisterContent(updateContent, GAME_INSTALL_DIRECTORY "/update"); + const std::string gamePath = (const char*)(GetGamePath() / "game").u8string().c_str(); + const std::string updatePath = (const char*)(GetGamePath() / "update").u8string().c_str(); + XamRegisterContent(gameContent, gamePath); + XamRegisterContent(updateContent, updatePath); const auto saveFilePath = GetSaveFilePath(true); bool saveFileExists = std::filesystem::exists(saveFilePath); @@ -102,7 +106,7 @@ void KiSystemStartup() XamContentCreateEx(0, "D", &gameContent, OPEN_EXISTING, nullptr, nullptr, 0, 0, nullptr); std::error_code ec; - for (auto& file : std::filesystem::directory_iterator(GAME_INSTALL_DIRECTORY "/dlc", ec)) + for (auto& file : std::filesystem::directory_iterator(GetGamePath() / "dlc", ec)) { if (file.is_directory()) { @@ -165,10 +169,10 @@ uint32_t LdrLoadModule(const std::filesystem::path &path) return entry; } +#ifdef __x86_64__ __attribute__((constructor(101), target("no-avx,no-avx2"), noinline)) void init() { -#ifdef __x86_64__ uint32_t eax, ebx, ecx, edx; // Execute CPUID for processor info and feature bits. @@ -185,8 +189,8 @@ void init() std::_Exit(1); } -#endif } +#endif int main(int argc, char *argv[]) { @@ -232,7 +236,7 @@ int main(int argc, char *argv[]) { // Set the current working directory to the executable's path. std::error_code ec; - std::filesystem::current_path(os::process::GetExecutablePath().parent_path(), ec); + std::filesystem::current_path(os::process::GetExecutableRoot(), ec); } Config::Load(); @@ -321,7 +325,7 @@ int main(int argc, char *argv[]) HostStartup(); std::filesystem::path modulePath; - bool isGameInstalled = Installer::checkGameInstall(GAME_INSTALL_DIRECTORY, modulePath); + bool isGameInstalled = Installer::checkGameInstall(GetGamePath(), modulePath); bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; if (runInstallerWizard) { @@ -331,7 +335,7 @@ int main(int argc, char *argv[]) std::_Exit(1); } - if (!InstallerWizard::Run(GAME_INSTALL_DIRECTORY, isGameInstalled && forceDLCInstaller)) + if (!InstallerWizard::Run(GetGamePath(), isGameInstalled && forceDLCInstaller)) { std::_Exit(0); } diff --git a/UnleashedRecomp/mod/mod_loader.cpp b/UnleashedRecomp/mod/mod_loader.cpp index 6fc3aec..804cc2f 100644 --- a/UnleashedRecomp/mod/mod_loader.cpp +++ b/UnleashedRecomp/mod/mod_loader.cpp @@ -100,7 +100,7 @@ void ModLoader::Init() { configIni = {}; - if (!configIni.read(GAME_INSTALL_DIRECTORY "/cpkredir.ini")) + if (!configIni.read(GetGamePath() / "cpkredir.ini")) return; } diff --git a/UnleashedRecomp/os/linux/process_linux.cpp b/UnleashedRecomp/os/linux/process_linux.cpp index bb93b1e..0af7ab6 100644 --- a/UnleashedRecomp/os/linux/process_linux.cpp +++ b/UnleashedRecomp/os/linux/process_linux.cpp @@ -15,6 +15,11 @@ std::filesystem::path os::process::GetExecutablePath() } } +std::filesystem::path os::process::GetExecutableRoot() +{ + return GetExecutablePath().remove_filename(); +} + std::filesystem::path os::process::GetWorkingDirectory() { char cwd[PATH_MAX] = {}; diff --git a/UnleashedRecomp/os/macos/logger_macos.cpp b/UnleashedRecomp/os/macos/logger_macos.cpp new file mode 100644 index 0000000..df5708d --- /dev/null +++ b/UnleashedRecomp/os/macos/logger_macos.cpp @@ -0,0 +1,17 @@ +#include + +void os::logger::Init() +{ +} + +void os::logger::Log(const std::string_view str, ELogType type, const char* func) +{ + if (func) + { + fmt::println("[{}] {}", func, str); + } + else + { + fmt::println("{}", str); + } +} diff --git a/UnleashedRecomp/os/macos/media_macos.cpp b/UnleashedRecomp/os/macos/media_macos.cpp new file mode 100644 index 0000000..81d1e83 --- /dev/null +++ b/UnleashedRecomp/os/macos/media_macos.cpp @@ -0,0 +1,7 @@ +#include + +bool os::media::IsExternalMediaPlaying() +{ + // This functionality is not supported in macOS. + return false; +} diff --git a/UnleashedRecomp/os/macos/process_macos.cpp b/UnleashedRecomp/os/macos/process_macos.cpp new file mode 100644 index 0000000..e60ab0b --- /dev/null +++ b/UnleashedRecomp/os/macos/process_macos.cpp @@ -0,0 +1,97 @@ +#include + +#include +#include +#include +#include +#include +#include + +std::filesystem::path os::process::GetExecutablePath() +{ + uint32_t exePathSize = PATH_MAX; + char exePath[PATH_MAX] = {}; + if (_NSGetExecutablePath(exePath, &exePathSize) == 0) + { + return std::filesystem::path(std::u8string_view((const char8_t*)(exePath))); + } + else + { + return std::filesystem::path(); + } +} + +std::filesystem::path os::process::GetExecutableRoot() +{ + std::filesystem::path resultPath = GetExecutablePath().remove_filename(); + if (CFBundleRef bundleRef = CFBundleGetMainBundle()) + { + if (CFURLRef bundleUrlRef = CFBundleCopyBundleURL(bundleRef)) + { + char appBundlePath[MAXPATHLEN]; + if (CFURLGetFileSystemRepresentation(bundleUrlRef, true, (uint8_t*)(appBundlePath), sizeof(appBundlePath))) + { + resultPath = std::filesystem::path(appBundlePath).parent_path(); + } + CFRelease(bundleUrlRef); + } + } + return resultPath; +} + +std::filesystem::path os::process::GetWorkingDirectory() +{ + char cwd[PATH_MAX] = {}; + char *res = getcwd(cwd, sizeof(cwd)); + if (res != nullptr) + { + return std::filesystem::path(std::u8string_view((const char8_t*)(cwd))); + } + else + { + return std::filesystem::path(); + } +} + +bool os::process::SetWorkingDirectory(const std::filesystem::path& path) +{ + return chdir(path.c_str()) == 0; +} + +bool os::process::StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work) +{ + pid_t pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) + { + setsid(); + + std::u8string workU8 = work.u8string(); + chdir((const char*)(workU8.c_str())); + + std::u8string pathU8 = path.u8string(); + std::vector argStrs; + argStrs.push_back((char*)(pathU8.c_str())); + for (const std::string& arg : args) + argStrs.push_back((char *)(arg.c_str())); + + argStrs.push_back(nullptr); + execvp((const char*)(pathU8.c_str()), argStrs.data()); + raise(SIGKILL); + } + + return true; +} + +void os::process::CheckConsole() +{ + // Always visible on macOS. + g_consoleVisible = true; +} + +void os::process::ShowConsole() +{ + // Unnecessary on macOS. +} diff --git a/UnleashedRecomp/os/macos/registry_macos.inl b/UnleashedRecomp/os/macos/registry_macos.inl new file mode 100644 index 0000000..d371777 --- /dev/null +++ b/UnleashedRecomp/os/macos/registry_macos.inl @@ -0,0 +1,21 @@ +#include + +// TODO: Implement +inline bool os::registry::Init() +{ + return false; +} + +// TODO: read from file? +template +bool os::registry::ReadValue(const std::string_view& name, T& data) +{ + return false; +} + +// TODO: write to file? +template +bool os::registry::WriteValue(const std::string_view& name, const T& data) +{ + return false; +} diff --git a/UnleashedRecomp/os/macos/user_macos.cpp b/UnleashedRecomp/os/macos/user_macos.cpp new file mode 100644 index 0000000..a9679df --- /dev/null +++ b/UnleashedRecomp/os/macos/user_macos.cpp @@ -0,0 +1,6 @@ +#include + +bool os::user::IsDarkTheme() +{ + return false; +} diff --git a/UnleashedRecomp/os/macos/version_macos.cpp b/UnleashedRecomp/os/macos/version_macos.cpp new file mode 100644 index 0000000..381b5c4 --- /dev/null +++ b/UnleashedRecomp/os/macos/version_macos.cpp @@ -0,0 +1,7 @@ +#include + +os::version::OSVersion os::version::GetOSVersion() +{ + assert(false && "Unimplemented."); + return os::version::OSVersion(); +} diff --git a/UnleashedRecomp/os/process.h b/UnleashedRecomp/os/process.h index d85fc27..1fa8317 100644 --- a/UnleashedRecomp/os/process.h +++ b/UnleashedRecomp/os/process.h @@ -5,6 +5,7 @@ namespace os::process inline bool g_consoleVisible; std::filesystem::path GetExecutablePath(); + std::filesystem::path GetExecutableRoot(); std::filesystem::path GetWorkingDirectory(); bool SetWorkingDirectory(const std::filesystem::path& path); bool StartProcess(const std::filesystem::path& path, const std::vector& args, std::filesystem::path work = {}); diff --git a/UnleashedRecomp/os/registry.h b/UnleashedRecomp/os/registry.h index 760512e..d95b36a 100644 --- a/UnleashedRecomp/os/registry.h +++ b/UnleashedRecomp/os/registry.h @@ -15,4 +15,6 @@ namespace os::registry #include #elif defined(__linux__) #include +#elif defined(__APPLE__) +#include #endif diff --git a/UnleashedRecomp/os/win32/process_win32.cpp b/UnleashedRecomp/os/win32/process_win32.cpp index d6ce9a9..c6f8e4b 100644 --- a/UnleashedRecomp/os/win32/process_win32.cpp +++ b/UnleashedRecomp/os/win32/process_win32.cpp @@ -10,6 +10,11 @@ std::filesystem::path os::process::GetExecutablePath() return std::filesystem::path(exePath); } +std::filesystem::path os::process::GetExecutableRoot() +{ + return GetExecutablePath().remove_filename(); +} + std::filesystem::path os::process::GetWorkingDirectory() { WCHAR workPath[MAX_PATH]; diff --git a/UnleashedRecomp/res/macos/MacOSXBundleInfo.plist.in b/UnleashedRecomp/res/macos/MacOSXBundleInfo.plist.in new file mode 100644 index 0000000..fe0ff5b --- /dev/null +++ b/UnleashedRecomp/res/macos/MacOSXBundleInfo.plist.in @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + LSMinimumSystemVersion + 13.0 + LSApplicationCategoryType + public.app-category.games + GCSupportsGameMode + + NSHighResolutionCapable + + + \ No newline at end of file diff --git a/UnleashedRecomp/res/macos/game_icon.icns b/UnleashedRecomp/res/macos/game_icon.icns new file mode 100644 index 0000000..807bb3b Binary files /dev/null and b/UnleashedRecomp/res/macos/game_icon.icns differ diff --git a/UnleashedRecomp/ui/button_guide.cpp b/UnleashedRecomp/ui/button_guide.cpp index d926d14..6db93d5 100644 --- a/UnleashedRecomp/ui/button_guide.cpp +++ b/UnleashedRecomp/ui/button_guide.cpp @@ -20,7 +20,7 @@ std::unique_ptr g_upKBMIcons; float g_sideMargins = DEFAULT_SIDE_MARGINS; -std::vector