Add first-class macOS build and launch support (#42)

* fix: fix GLFW video mode fallback on startup

* fix: fix GLFW mouse activation on startup

* feat: locate macOS game directory when launched from Finder

* feat: ask the user to choose a game directory if gamefiles could not be found

* feat: create an app bundle for macOS builds

* feat: resolve gamefiles inside or beside the app bundle

* fix: distinguish ARM64 Premake platforms

* fix: use libmpg123 pkg-config metadata

* feat: add Xcode support to Premake and CMake

* chore: ignore .DS_Store files

* fix: center mouse cursor when launching and quitting the game

* fix: address a TODO comment

* feat: bundle reVC gamefiles as runtime overrides

* feat: use macOS user directories for saves and settings

* refactor: store macOS game path in reVC.ini

* fix: make Xcode recognize the archive as a macOS app archive

* fix: enable Hardened Runtime for the Xcode app target

* fix: support CMake’s Xcode generator on macOS

* build: remove stale libsndfile linkage

* build: make the app bundle self-contained

* fix: prevent buffer overflow with long game directory paths

* chore: add application category to Info.plist

* fix: omit missing Homebrew OpenAL search paths when using MacPorts

* refactor: move game path declarations to FileMgr header

* build: include the LICENSE file in the app bundle
This commit is contained in:
Elnur Hajiyev
2026-09-05 19:00:00 +04:00
committed by GitHub
parent ed6c611ef6
commit a03a68de95
22 changed files with 2338 additions and 135 deletions
+3
View File
@@ -3,6 +3,9 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# macOS
.DS_Store
# User-specific files
*.rsuser
*.suo
+137
View File
@@ -3,6 +3,29 @@ cmake_minimum_required(VERSION 3.14)
set(EXECUTABLE reVC)
set(PROJECT REVC)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS architectures")
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12" CACHE STRING
"Minimum macOS version" FORCE)
endif()
if(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0")
set("CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET[arch=arm64]" "11.0")
endif()
elseif(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
(NOT CMAKE_OSX_ARCHITECTURES AND
CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64"))
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12" CACHE STRING
"Minimum macOS version" FORCE)
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING
"Minimum macOS version" FORCE)
endif()
endif()
endif()
project(${EXECUTABLE} C CXX)
set(${PROJECT}_AUTHOR "${PROJECT} Team")
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
@@ -11,6 +34,117 @@ include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1 "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
message(STATUS "Building ${CMAKE_PROJECT_NAME} GIT SHA1: ${GIT_SHA1}")
if(APPLE)
macro(reVC_define_macos_dependency key alias formula library filename)
list(APPEND ${PROJECT}_MACOS_DEPENDENCIES "${key}")
set(${PROJECT}_MACOS_${key}_ALIAS "${alias}")
set(${PROJECT}_MACOS_${key}_FORMULA "${formula}")
set(${PROJECT}_MACOS_${key}_LIBRARY "${library}")
set(${PROJECT}_MACOS_${key}_FILENAME "${filename}")
endmacro()
reVC_define_macos_dependency(OPENAL OpenAL::OpenAL
openal-soft openal libopenal.1.dylib)
reVC_define_macos_dependency(MPG123 MPG123::libmpg123
mpg123 mpg123 libmpg123.0.dylib)
reVC_define_macos_dependency(GLFW glfw
glfw glfw libglfw.3.dylib)
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(${PROJECT}_HOMEBREW_ARM64_PREFIX "/opt/homebrew" CACHE PATH "arm64 Homebrew prefix")
set(${PROJECT}_HOMEBREW_X86_64_PREFIX "/usr/local" CACHE PATH "x86_64 Homebrew prefix")
function(reVC_macos_dependency target alias dylibs formula library filename)
if(EXISTS "/opt/local/lib/${filename}")
add_library(${target} INTERFACE)
add_library(${alias} ALIAS ${target})
target_include_directories(${target} INTERFACE "/opt/local/include")
target_link_libraries(${target} INTERFACE "/opt/local/lib/${filename}")
set(${dylibs} "/opt/local/lib/${filename}" PARENT_SCOPE)
return()
endif()
set(include_prefix)
set(link_options)
set(dependency_dylibs)
foreach(architecture arm64 x86_64)
if(architecture IN_LIST CMAKE_OSX_ARCHITECTURES)
string(TOUPPER "${architecture}" architecture_upper)
set(prefix "${${PROJECT}_HOMEBREW_${architecture_upper}_PREFIX}")
set(dylib "${prefix}/opt/${formula}/lib/${filename}")
if(NOT EXISTS "${dylib}")
message(FATAL_ERROR
"${architecture} ${formula} was not found under ${prefix}"
)
endif()
if(NOT include_prefix)
set(include_prefix "${prefix}")
endif()
list(APPEND dependency_dylibs "${dylib}")
list(APPEND link_options
"SHELL:-Xarch_${architecture} -L${prefix}/opt/${formula}/lib"
)
endif()
endforeach()
add_library(${target} INTERFACE)
add_library(${alias} ALIAS ${target})
target_include_directories(${target} INTERFACE
"${include_prefix}/opt/${formula}/include"
)
target_link_options(${target} INTERFACE ${link_options} "-l${library}")
set(${dylibs} "${dependency_dylibs}" PARENT_SCOPE)
endfunction()
if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES OR
"x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
foreach(dependency IN LISTS ${PROJECT}_MACOS_DEPENDENCIES)
string(TOLOWER "${dependency}" dependency_lower)
reVC_macos_dependency(${PROJECT}_macos_${dependency_lower}
${${PROJECT}_MACOS_${dependency}_ALIAS}
${PROJECT}_MACOS_${dependency}_DYLIBS
${${PROJECT}_MACOS_${dependency}_FORMULA}
${${PROJECT}_MACOS_${dependency}_LIBRARY}
${${PROJECT}_MACOS_${dependency}_FILENAME})
endforeach()
endif()
else()
function(reVC_macos_dependency_dylib output formula filename)
set(prefix "$ENV{HOMEBREW_PREFIX}")
set(candidates "/opt/local/lib/${filename}")
if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR
(NOT CMAKE_OSX_ARCHITECTURES AND
CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64"))
list(APPEND candidates
"/usr/local/opt/${formula}/lib/${filename}"
)
else()
if(prefix)
list(APPEND candidates "${prefix}/opt/${formula}/lib/${filename}")
endif()
list(APPEND candidates
"/opt/homebrew/opt/${formula}/lib/${filename}"
"/usr/local/opt/${formula}/lib/${filename}"
)
endif()
list(REMOVE_DUPLICATES candidates)
foreach(candidate IN LISTS candidates)
if(EXISTS "${candidate}")
set(${output} "${candidate}" PARENT_SCOPE)
return()
endif()
endforeach()
endfunction()
foreach(dependency IN LISTS ${PROJECT}_MACOS_DEPENDENCIES)
reVC_macos_dependency_dylib(
${PROJECT}_MACOS_${dependency}_DYLIBS
${${PROJECT}_MACOS_${dependency}_FORMULA}
${${PROJECT}_MACOS_${dependency}_FILENAME})
endforeach()
endif()
endif()
if(NINTENDO_SWITCH)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/nx")
@@ -42,6 +176,9 @@ endif()
option(${PROJECT}_VENDORED_LIBRW "Use vendored librw" ON)
if(${PROJECT}_VENDORED_LIBRW)
if(NOT DEFINED LIBRW_PLATFORM AND NOT PS2)
set(LIBRW_PLATFORM "GL3" CACHE STRING "librw platform")
endif()
add_subdirectory(vendor/librw)
else()
find_package(librw REQUIRED)
+2 -2
View File
@@ -9,7 +9,7 @@
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_search_module(PKG_MPG123 mpg123)
pkg_search_module(PKG_MPG123 libmpg123)
endif()
find_path(mpg123_INCLUDE_DIR mpg123.h
@@ -19,7 +19,7 @@ find_path(mpg123_INCLUDE_DIR mpg123.h
)
find_library(mpg123_LIBRARIES NAMES mpg123 mpg123-0 libmpg123-0
HINTS ${PKG_MPG123_LIBRARIES}
HINTS ${PKG_MPG123_LIBRARY_DIRS}
PATHS "${mpg123_DIR}"
PATH_SUFFIXES lib
)
+244 -36
View File
@@ -68,6 +68,126 @@ function getarch(a)
return a
end
local function dependencyincludedirs(paths)
if _ACTION == "xcode4" then
externalincludedirs(paths)
else
includedirs(paths)
end
end
local macosxHomebrewPrefix = os.getenv("HOMEBREW_PREFIX")
if not macosxHomebrewPrefix or macosxHomebrewPrefix == "" then
macosxHomebrewPrefix = os.host() == "macosx" and os.hostarch() == "ARM64" and "/opt/homebrew" or "/usr/local"
end
local function resolveMacosxDependencyDylib(formula, filename, homebrewPrefix)
local candidates = {
path.join("/opt/local/lib", filename),
path.join(homebrewPrefix, "opt", formula, "lib", filename),
}
for _, candidate in ipairs(candidates) do
if os.isfile(candidate) then
return candidate
end
end
return candidates[2]
end
local macosxDependencies = {
{ formula = "openal-soft", filename = "libopenal.1.dylib" },
{ formula = "mpg123", filename = "libmpg123.0.dylib" },
{ formula = "glfw", filename = "libglfw.3.dylib" },
}
local function resolveMacosxDependencyDylibs(homebrewPrefix)
local dylibs = {}
for _, dependency in ipairs(macosxDependencies) do
table.insert(dylibs, {
filename = dependency.filename,
path = resolveMacosxDependencyDylib(dependency.formula, dependency.filename, homebrewPrefix),
})
end
return dylibs
end
local macosxDependencyDylibs = resolveMacosxDependencyDylibs(macosxHomebrewPrefix)
local macosxArm64HomebrewPrefix = os.hostarch() == "ARM64" and macosxHomebrewPrefix or "/opt/homebrew"
local macosxAmd64HomebrewPrefix = os.hostarch() == "ARM64" and "/usr/local" or macosxHomebrewPrefix
local macosxArm64DependencyDylibs = resolveMacosxDependencyDylibs(macosxArm64HomebrewPrefix)
local macosxAmd64DependencyDylibs = resolveMacosxDependencyDylibs(macosxAmd64HomebrewPrefix)
local function existingDirectories(directories)
local existing = {}
for _, directory in ipairs(directories) do
if os.isdir(directory) then
table.insert(existing, directory)
end
end
return existing
end
local function existingXcodeSearchPaths(directories)
local existing = existingDirectories(directories)
table.insert(existing, 1, "$(inherited)")
return existing
end
local macosxXcodeArchitectures = _OPTIONS["arch"]
if not macosxXcodeArchitectures or macosxXcodeArchitectures == "universal" then
macosxXcodeArchitectures = "$(ARCHS_STANDARD)"
end
local macosxXcodeDeploymentTarget = macosxXcodeArchitectures == "x86_64" and "10.12" or "11.0"
local macosxXcodeBuildSettings = {
["ARCHS"] = { macosxXcodeArchitectures },
["MACOSX_DEPLOYMENT_TARGET"] = { macosxXcodeDeploymentTarget },
["ONLY_ACTIVE_ARCH"] = { "YES" },
}
if macosxXcodeArchitectures == "$(ARCHS_STANDARD)" then
macosxXcodeBuildSettings["MACOSX_DEPLOYMENT_TARGET[arch=x86_64]"] = { "10.12" }
end
local function macosxDependencyPaths(dylibs)
local paths = {}
for _, dylib in ipairs(dylibs) do
table.insert(paths, dylib.path)
end
return paths
end
local function macosxDependencyFilenames(dylibs)
local filenames = {}
for _, dylib in ipairs(dylibs) do
table.insert(filenames, dylib.filename)
end
return filenames
end
local function macosxInstallNameCommands(dylibs, executable)
local commands = {}
for _, dylib in ipairs(dylibs) do
table.insert(commands, 'install_name_tool -change "' .. dylib.path .. '" "@rpath/' .. dylib.filename .. '" "' .. executable .. '"')
end
return commands
end
local function macosxGmakeBundleCommands(dylibs)
local commands = {}
for _, dylib in ipairs(dylibs) do
table.insert(commands, '{COPYFILE} "' .. dylib.path .. '" "%{cfg.targetdir}/reVC.app/Contents/Frameworks/' .. dylib.filename .. '"')
end
for _, command in ipairs(macosxInstallNameCommands(dylibs, "%{cfg.targetdir}/reVC.app/Contents/MacOS/reVC")) do
table.insert(commands, command)
end
for _, dylib in ipairs(dylibs) do
table.insert(commands, 'codesign --force --sign - "%{cfg.targetdir}/reVC.app/Contents/Frameworks/' .. dylib.filename .. '"')
end
table.insert(commands, 'codesign --force --sign - "%{cfg.targetdir}/reVC.app"')
return commands
end
workspace "reVC"
language "C++"
configurations { "Debug", "Release" }
@@ -111,10 +231,15 @@ workspace "reVC"
}
filter { "system:macosx" }
cppdialect "gnu++14"
if _ACTION == "xcode4" then
platforms { "macosx-librw_gl3_glfw-oal" }
else
platforms {
"macosx-arm64-librw_gl3_glfw-oal",
"macosx-amd64-librw_gl3_glfw-oal",
}
end
filter "configurations:Debug"
defines { "DEBUG" }
@@ -144,20 +269,22 @@ workspace "reVC"
filter { "platforms:*amd64*" }
architecture "amd64"
filter { "platforms:*arm*" }
filter { "platforms:*arm-*" }
architecture "ARM"
filter { "platforms:macosx-arm64-*", "files:**.cpp"}
buildoptions { "-target", "arm64-apple-macos11", "-std=gnu++14" }
filter { "platforms:*arm64*" }
architecture "ARM64"
filter { "platforms:macosx-arm64-*", "files:**.c"}
filter { "platforms:macosx-arm64-*", "action:not xcode4" }
buildoptions { "-target", "arm64-apple-macos11" }
linkoptions { "-target", "arm64-apple-macos11" }
filter { "platforms:macosx-amd64-*", "files:**.cpp"}
buildoptions { "-target", "x86_64-apple-macos10.12", "-std=gnu++14" }
filter { "platforms:macosx-amd64-*", "files:**.c"}
filter { "platforms:macosx-amd64-*", "action:not xcode4" }
buildoptions { "-target", "x86_64-apple-macos10.12" }
linkoptions { "-target", "x86_64-apple-macos10.12" }
filter { "system:macosx", "action:xcode4" }
xcodebuildsettings(macosxXcodeBuildSettings)
filter { "platforms:*librw_d3d9*" }
defines { "RW_D3D9" }
@@ -198,7 +325,15 @@ if(_OPTIONS["with-librw"]) then
project "librw"
kind "StaticLib"
targetname "rw"
if _ACTION == "xcode4" then
targetdir "${BUILD_DIR}/%{cfg.buildcfg}"
xcodebuildsettings {
["SKIP_INSTALL"] = "YES",
}
else
targetdir(path.join(Librw, "lib/%{cfg.platform}/%{cfg.buildcfg}"))
end
files { path.join(Librw, "src/*.*") }
files { path.join(Librw, "src/*/*.*") }
files { path.join(Librw, "src/gl/*/*.*") }
@@ -220,16 +355,19 @@ project "librw"
-- Support MacPorts and Homebrew
filter "platforms:macosx-arm64-*"
includedirs { "/opt/local/include" }
includedirs {"/opt/homebrew/include" }
libdirs { "/opt/local/lib" }
libdirs { "/opt/homebrew/lib" }
dependencyincludedirs { "/opt/local/include", "/opt/homebrew/include" }
libdirs { "/opt/local/lib", "/opt/homebrew/lib" }
filter "platforms:macosx-amd64-*"
includedirs { "/opt/local/include" }
includedirs {"/usr/local/include" }
libdirs { "/opt/local/lib" }
libdirs { "/usr/local/lib" }
dependencyincludedirs { "/opt/local/include", "/usr/local/include" }
libdirs { "/opt/local/lib", "/usr/local/lib" }
filter { "system:macosx", "action:xcode4" }
dependencyincludedirs { "/opt/local/include", "/opt/homebrew/include", "/usr/local/include" }
xcodebuildsettings {
["LIBRARY_SEARCH_PATHS[arch=arm64]"] = { "$(inherited)", "/opt/local/lib", "/opt/homebrew/lib" },
["LIBRARY_SEARCH_PATHS[arch=x86_64]"] = { "$(inherited)", "/opt/local/lib", "/usr/local/lib" },
}
filter "platforms:*gl3_glfw*"
staticruntime "off"
@@ -246,7 +384,62 @@ end
project "reVC"
kind "WindowedApp"
targetname "reVC"
if _ACTION == "xcode4" then
targetdir "${BUILD_DIR}/%{cfg.buildcfg}"
else
targetdir "bin/%{cfg.platform}/%{cfg.buildcfg}"
end
filter { "system:macosx" }
files { "res/images/reVC.icns", "res/macos/Info.plist", "res/macos/ThirdPartyNotices.txt" }
filter { "system:macosx", "action:xcode4" }
xcodebuildresources { "res/macos/ThirdPartyNotices.txt" }
xcodebuildsettings {
["PRODUCT_BUNDLE_IDENTIFIER"] = "io.github.mrxenginner.reVC",
["INSTALL_PATH"] = "$(LOCAL_APPS_DIR)",
["CODE_SIGN_STYLE"] = "Automatic",
["LD_RUNPATH_SEARCH_PATHS"] = "$(inherited) @executable_path/../Frameworks",
}
links(macosxDependencyPaths(macosxDependencyDylibs))
links { "pthread" }
embedAndSign(macosxDependencyFilenames(macosxDependencyDylibs))
postbuildcommands {
'{MKDIR} "%{cfg.targetdir}/reVC.app/Contents/Resources"',
'{COPYFILE} "%{prj.location}/../LICENSE.md" "%{cfg.targetdir}/reVC.app/Contents/Resources/LICENSE"',
'{RMDIR} "%{cfg.targetdir}/reVC.app/Contents/Resources/gamefiles"',
'{COPYDIR} "%{prj.location}/../gamefiles" "%{cfg.targetdir}/reVC.app/Contents/Resources"',
}
postbuildcommands(macosxInstallNameCommands(macosxDependencyDylibs, "${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"))
filter { "system:macosx", "action:xcode4", "configurations:Release" }
xcodebuildsettings {
["CODE_SIGN_IDENTITY"] = "Apple Development",
["ENABLE_HARDENED_RUNTIME"] = "YES",
}
filter { "system:macosx", "action:gmake*" }
linkoptions { "-Wl,-rpath,@executable_path/../Frameworks" }
postbuildcommands {
'{MKDIR} "%{cfg.targetdir}/reVC.app/Contents/MacOS"',
'{MKDIR} "%{cfg.targetdir}/reVC.app/Contents/Frameworks"',
'{MKDIR} "%{cfg.targetdir}/reVC.app/Contents/Resources"',
'{COPYFILE} "%{cfg.buildtarget.abspath}" "%{cfg.targetdir}/reVC.app/Contents/MacOS/reVC"',
'{COPYFILE} "%{prj.location}/../res/images/reVC.icns" "%{cfg.targetdir}/reVC.app/Contents/Resources/reVC.icns"',
'{COPYFILE} "%{prj.location}/../res/macos/ThirdPartyNotices.txt" "%{cfg.targetdir}/reVC.app/Contents/Resources/ThirdPartyNotices.txt"',
'{COPYFILE} "%{prj.location}/../LICENSE.md" "%{cfg.targetdir}/reVC.app/Contents/Resources/LICENSE"',
'{COPYFILE} "%{prj.location}/../res/macos/Info.plist" "%{cfg.targetdir}/reVC.app/Contents/Info.plist"',
'{RMDIR} "%{cfg.targetdir}/reVC.app/Contents/Resources/gamefiles"',
'{COPYDIR} "%{prj.location}/../gamefiles" "%{cfg.targetdir}/reVC.app/Contents/Resources"',
}
filter { "action:gmake*", "platforms:macosx-arm64-*" }
postbuildcommands(macosxGmakeBundleCommands(macosxArm64DependencyDylibs))
filter { "action:gmake*", "platforms:macosx-amd64-*" }
postbuildcommands(macosxGmakeBundleCommands(macosxAmd64DependencyDylibs))
filter {}
if(_OPTIONS["with-librw"]) then
dependson "librw"
@@ -305,6 +498,11 @@ project "reVC"
includedirs { "src/weapons" }
includedirs { "src/extras" }
filter "action:xcode4"
externalincludedirs { "src/audio/eax", "src/fakerw", Librw }
filter {}
if(not _OPTIONS["no-git-hash"]) then
defines { "USE_OUR_VERSIONING" }
end
@@ -396,21 +594,21 @@ project "reVC"
libdirs { "vendor/openal-soft/libs/Win64" }
filter "platforms:linux*oal"
links { "openal", "mpg123", "sndfile", "pthread" }
links { "openal", "mpg123", "pthread" }
filter "platforms:bsd*oal"
links { "openal", "mpg123", "sndfile", "pthread" }
links { "openal", "mpg123", "pthread" }
filter "platforms:macosx*oal"
links { "openal", "mpg123", "sndfile", "pthread" }
filter { "platforms:macosx*oal", "action:not xcode4" }
links { "openal", "mpg123", "pthread" }
filter "platforms:macosx-arm64-*oal"
includedirs { "/opt/homebrew/opt/openal-soft/include" }
libdirs { "/opt/homebrew/opt/openal-soft/lib" }
dependencyincludedirs(existingDirectories { "/opt/homebrew/opt/openal-soft/include" })
libdirs(existingDirectories { "/opt/homebrew/opt/openal-soft/lib" })
filter "platforms:macosx-amd64-*oal"
includedirs { "/usr/local/opt/openal-soft/include" }
libdirs { "/usr/local/opt/openal-soft/lib" }
dependencyincludedirs(existingDirectories { "/usr/local/opt/openal-soft/include" })
libdirs(existingDirectories { "/usr/local/opt/openal-soft/lib" })
if _OPTIONS["with-opus"] then
filter {}
@@ -431,7 +629,7 @@ project "reVC"
files { addSrcFiles("src/fakerw") }
includedirs { "src/fakerw" }
includedirs { Librw }
if(_OPTIONS["with-librw"]) then
if(_OPTIONS["with-librw"] and _ACTION ~= "xcode4") then
libdirs { "vendor/librw/lib/%{cfg.platform}/%{cfg.buildcfg}" }
end
links { "rw" }
@@ -460,18 +658,28 @@ project "reVC"
includedirs { "/usr/local/include" }
libdirs { "/usr/local/lib" }
filter "platforms:macosx-arm64-*gl3_glfw*"
filter { "platforms:macosx*gl3_glfw*", "action:not xcode4" }
links { "glfw" }
linkoptions { "-framework OpenGL" }
includedirs { "/opt/local/include" }
includedirs {"/opt/homebrew/include" }
libdirs { "/opt/local/lib" }
libdirs { "/opt/homebrew/lib" }
filter "platforms:macosx-arm64-*gl3_glfw*"
dependencyincludedirs { "/opt/local/include", "/opt/homebrew/include" }
libdirs { "/opt/local/lib", "/opt/homebrew/lib" }
filter "platforms:macosx-amd64-*gl3_glfw*"
links { "glfw" }
linkoptions { "-framework OpenGL" }
includedirs { "/opt/local/include" }
includedirs {"/usr/local/include" }
libdirs { "/opt/local/lib" }
libdirs { "/usr/local/lib" }
dependencyincludedirs { "/opt/local/include", "/usr/local/include" }
libdirs { "/opt/local/lib", "/usr/local/lib" }
filter { "system:macosx", "action:xcode4" }
links { "OpenGL.framework" }
dependencyincludedirs(existingDirectories {
"/opt/local/include",
"/opt/homebrew/include",
"/usr/local/include",
"/opt/homebrew/opt/openal-soft/include",
"/usr/local/opt/openal-soft/include",
})
xcodebuildsettings {
["LIBRARY_SEARCH_PATHS[arch=arm64]"] = existingXcodeSearchPaths { "/opt/local/lib", "/opt/homebrew/lib", "/opt/homebrew/opt/openal-soft/lib" },
["LIBRARY_SEARCH_PATHS[arch=x86_64]"] = existingXcodeSearchPaths { "/opt/local/lib", "/usr/local/lib", "/usr/local/opt/openal-soft/lib" },
}
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>reVC</string>
<key>CFBundleIconFile</key>
<string>reVC.icns</string>
<key>CFBundleIdentifier</key>
<string>io.github.mrxenginner.reVC</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>reVC</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>arm64</key>
<string>11.0</string>
<key>x86_64</key>
<string>10.12</string>
</dict>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
+124
View File
@@ -2,6 +2,7 @@ find_package(Threads REQUIRED)
set(THREADS_PREFER_PTHREAD_FLAG ON)
file(GLOB_RECURSE ${PROJECT}_SOURCES "*.cpp" "*.h" "*.rc")
list(REMOVE_ITEM ${PROJECT}_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/extras/GitSHA1.cpp")
function(header_directories RETURN_LIST)
file(GLOB_RECURSE ALL_SRCS *.h *.cpp *.c)
@@ -84,7 +85,18 @@ else()
endif()
if(${PROJECT}_AUDIO STREQUAL "OAL")
if(NOT TARGET OpenAL::OpenAL)
if(APPLE)
find_package(OpenAL CONFIG QUIET
PATHS
"/opt/homebrew/opt/openal-soft"
"/usr/local/opt/openal-soft"
)
endif()
if(NOT TARGET OpenAL::OpenAL)
find_package(OpenAL REQUIRED)
endif()
endif()
if(TARGET OpenAL::OpenAL)
target_link_libraries(${EXECUTABLE} PRIVATE OpenAL::OpenAL)
else()
@@ -106,7 +118,9 @@ elseif(${PROJECT}_AUDIO STREQUAL "MSS")
target_link_libraries(${EXECUTABLE} PRIVATE MilesSDK::MilesSDK)
endif()
if(NOT TARGET MPG123::libmpg123)
find_package(mpg123 REQUIRED)
endif()
target_link_libraries(${EXECUTABLE} PRIVATE
MPG123::libmpg123
)
@@ -220,10 +234,120 @@ set_target_properties(${EXECUTABLE}
CXX_STANDARD_REQUIRED ON
)
if(APPLE AND NOT ANDROID)
set(${PROJECT}_MACOS_ICON "${PROJECT_SOURCE_DIR}/res/images/reVC.icns")
set(${PROJECT}_MACOS_NOTICES "${PROJECT_SOURCE_DIR}/res/macos/ThirdPartyNotices.txt")
target_sources(${EXECUTABLE} PRIVATE
"${${PROJECT}_MACOS_ICON}"
"${${PROJECT}_MACOS_NOTICES}"
)
set_source_files_properties(
"${${PROJECT}_MACOS_ICON}"
"${${PROJECT}_MACOS_NOTICES}"
PROPERTIES
MACOSX_PACKAGE_LOCATION "Resources"
)
set_target_properties(${EXECUTABLE} PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/res/macos/Info.plist"
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "@executable_path/../Frameworks"
INSTALL_RPATH_USE_LINK_PATH FALSE
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "io.github.mrxenginner.reVC"
XCODE_ATTRIBUTE_CODE_SIGN_STYLE "$<$<CONFIG:Release>:Automatic>"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "$<$<CONFIG:Release>:Apple Development>"
"XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[sdk=macosx*]" "$<IF:$<CONFIG:Debug>,NO,YES>"
"XCODE_ATTRIBUTE_INSTALL_PATH[sdk=macosx*]" "$(LOCAL_APPS_DIR)"
XCODE_ATTRIBUTE_SKIP_INSTALL "NO"
)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/LICENSE.md"
"$<TARGET_BUNDLE_CONTENT_DIR:${EXECUTABLE}>/Resources/LICENSE"
COMMAND ${CMAKE_COMMAND} -E remove_directory
"$<TARGET_BUNDLE_CONTENT_DIR:${EXECUTABLE}>/Resources/gamefiles"
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/gamefiles"
"$<TARGET_BUNDLE_CONTENT_DIR:${EXECUTABLE}>/Resources/gamefiles"
COMMENT "Copying gamefiles into the app bundle"
)
set(macos_dependency_dylibs_available TRUE)
foreach(dependency IN LISTS ${PROJECT}_MACOS_DEPENDENCIES)
if(NOT DEFINED ${PROJECT}_MACOS_${dependency}_DYLIBS)
set(macos_dependency_dylibs_available FALSE)
break()
endif()
endforeach()
if(macos_dependency_dylibs_available)
set(shell_dollar "$")
set(codesign_command
"identity=${shell_dollar}EXPANDED_CODE_SIGN_IDENTITY; test -n \"${shell_dollar}identity\" || identity=-; /usr/bin/codesign --force --sign \"${shell_dollar}identity\" \"${shell_dollar}1\""
)
set(app_codesign_command
"identity=${shell_dollar}EXPANDED_CODE_SIGN_IDENTITY; test -n \"${shell_dollar}identity\" || identity=-; if test \"${shell_dollar}2\" = YES; then /usr/bin/codesign --force --sign \"${shell_dollar}identity\" --options runtime \"${shell_dollar}1\"; else /usr/bin/codesign --force --sign \"${shell_dollar}identity\" \"${shell_dollar}1\"; fi"
)
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(app_hardened "$<IF:$<CONFIG:Debug>,NO,YES>")
else()
set(app_hardened NO)
endif()
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"$<TARGET_BUNDLE_CONTENT_DIR:${EXECUTABLE}>/Frameworks"
)
function(reVC_bundle_macos_dylib target filename)
set(destination
"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Frameworks/${filename}"
)
list(LENGTH ARGN source_count)
if(source_count GREATER 1)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND /usr/bin/lipo -create ${ARGN} -output "${destination}"
)
else()
list(GET ARGN 0 source)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${source}" "${destination}"
)
endif()
foreach(source IN LISTS ARGN)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND install_name_tool -change
"${source}" "@rpath/${filename}" "$<TARGET_FILE:${target}>"
)
endforeach()
add_custom_command(TARGET ${target} POST_BUILD
COMMAND /bin/sh -c
"${codesign_command}"
codesign "${destination}"
VERBATIM
)
endfunction()
foreach(dependency IN LISTS ${PROJECT}_MACOS_DEPENDENCIES)
reVC_bundle_macos_dylib(${EXECUTABLE}
${${PROJECT}_MACOS_${dependency}_FILENAME}
${${PROJECT}_MACOS_${dependency}_DYLIBS})
endforeach()
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND /bin/sh -c
"${app_codesign_command}"
codesign "$<TARGET_BUNDLE_DIR:${EXECUTABLE}>"
"${app_hardened}"
COMMENT "Bundling macOS dependencies"
VERBATIM
)
endif()
endif()
if(${PROJECT}_INSTALL)
install(
TARGETS ${EXECUTABLE}
EXPORT ${EXECUTABLE}-targets
BUNDLE DESTINATION "."
RUNTIME DESTINATION "."
)
if(MSVC)
+2 -1
View File
@@ -3,6 +3,7 @@
#ifdef AUDIO_OAL
#include <AL/al.h>
#include "crossplatform.h"
#define NUM_STREAMBUFFERS 8
@@ -116,7 +117,7 @@ public:
#endif
class CStream
{
char m_aFilename[128];
char m_aFilename[MAX_PATH];
ALuint *m_pAlSources;
ALuint (&m_alBuffers)[NUM_STREAMBUFFERS];
+210 -4
View File
@@ -3,6 +3,10 @@
#ifdef _WIN32
#include <direct.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <sys/stat.h>
#endif
#include "common.h"
#include "crossplatform.h"
@@ -61,12 +65,162 @@ void mychdir(char const *path)
#define mychdir chdir
#endif
#ifdef __APPLE__
static char bundledGameFilesDir[FILEMGR_PATH_SIZE] = {'\0'};
static bool
GameFileExists(const char *root, const char *name)
{
char path[FILEMGR_PATH_SIZE];
int length = snprintf(path, sizeof(path), "%s%s%s", root,
root[0] != '\0' && root[strlen(root) - 1] == '/' ? "" : "/", name);
if(length < 0 || length >= (int)sizeof(path))
return false;
FILE *file = fcaseopen(path, "rb");
if(file == nil)
return false;
fclose(file);
return true;
}
static bool
SetGameRoot(const char *root, char *path, size_t pathSize)
{
char realRoot[FILEMGR_PATH_SIZE];
if(realpath(root, realRoot) == nil ||
!GameFileExists(realRoot, "data/gta_vc.dat") ||
!GameFileExists(realRoot, "models/gta3.img"))
return false;
int length = snprintf(path, pathSize, "%s/", realRoot);
return length >= 0 && length < (int)pathSize;
}
static bool
GetExecutableDir(char *path, size_t pathSize)
{
char executable[FILEMGR_PATH_SIZE];
uint32 size = sizeof(executable);
if(_NSGetExecutablePath(executable, &size) != 0)
return false;
char *slash = strrchr(executable, '/');
if(slash == nil)
return false;
*slash = '\0';
char realPath[FILEMGR_PATH_SIZE];
const char *dir = realpath(executable, realPath) == nil ? executable : realPath;
int length = snprintf(path, pathSize, "%s", dir);
return length >= 0 && length < (int)pathSize;
}
static bool
FindBundledGameFiles(char *path, size_t pathSize)
{
char executableDir[FILEMGR_PATH_SIZE];
if(!GetExecutableDir(executableDir, sizeof(executableDir)))
return false;
char bundleDir[FILEMGR_PATH_SIZE];
int length = snprintf(bundleDir, sizeof(bundleDir),
"%s/../Resources/gamefiles", executableDir);
if(length < 0 || length >= (int)sizeof(bundleDir))
return false;
char realBundleDir[FILEMGR_PATH_SIZE];
struct stat status;
if(realpath(bundleDir, realBundleDir) == nil ||
stat(realBundleDir, &status) != 0 || !S_ISDIR(status.st_mode))
return false;
length = snprintf(path, pathSize, "%s", realBundleDir);
return length >= 0 && length < (int)pathSize;
}
static bool
ReadGameRoot(char *path, size_t pathSize)
{
char root[FILEMGR_PATH_SIZE];
if(!ReadGamePathFromINI(root, sizeof(root)))
return false;
return SetGameRoot(root, path, pathSize);
}
static void
WriteGameRoot(const char *root)
{
WriteGamePathToINI(root);
}
static bool
ChooseGameRoot(char *path, size_t pathSize)
{
bool retry = false;
for(;;){
const char *prompt = retry ?
"That folder is not a GTA Vice City installation. Choose the folder containing the data and models folders." :
"Choose the GTA Vice City installation folder.";
char command[512];
snprintf(command, sizeof(command),
"/usr/bin/osascript -e 'POSIX path of (choose folder with prompt \"%s\")' 2>/dev/null", prompt);
FILE *pipe = popen(command, "r");
if(pipe == nil)
return false;
char root[FILEMGR_PATH_SIZE];
bool found = fgets(root, sizeof(root), pipe) != nil;
int status = pclose(pipe);
if(!found || status != 0)
return false;
root[strcspn(root, "\r\n")] = '\0';
if(SetGameRoot(root, path, pathSize)){
WriteGameRoot(path);
return true;
}
retry = true;
}
}
static bool
FindGameRoot(char *path, size_t pathSize)
{
char executableDir[FILEMGR_PATH_SIZE];
if(GetExecutableDir(executableDir, sizeof(executableDir))){
if(SetGameRoot(executableDir, path, pathSize))
return true;
char defaultRoot[FILEMGR_PATH_SIZE];
int length = snprintf(defaultRoot, sizeof(defaultRoot), "%s/../../..", executableDir);
if(length >= 0 && length < (int)sizeof(defaultRoot) &&
SetGameRoot(defaultRoot, path, pathSize))
return true;
}
char cwd[FILEMGR_PATH_SIZE];
if(_getcwd(cwd, sizeof(cwd)) != nil && SetGameRoot(cwd, path, pathSize))
return true;
return ReadGameRoot(path, pathSize) || ChooseGameRoot(path, pathSize);
}
#endif
/* Force file to open as binary but remember if it was text mode */
static int
myfopen(const char *filename, const char *mode)
{
int fd;
char realmode[10], *p;
const char *openPath = filename;
#ifdef __APPLE__
char bundledPath[FILEMGR_PATH_SIZE];
if(mode[0] == 'r' && strchr(mode, '+') == nil &&
CFileMgr::ResolveBundledGameFile(filename, bundledPath, sizeof(bundledPath)))
openPath = bundledPath;
#endif
for(fd = 1; fd < NUMFILES; fd++)
if(myfiles[fd].file == nil)
@@ -83,7 +237,7 @@ found:
*p++ = 'b';
*p = '\0';
myfiles[fd].file = fcaseopen(filename, realmode);
myfiles[fd].file = fcaseopen(openPath, realmode);
if(myfiles[fd].file == nil)
return 0;
return fd;
@@ -206,8 +360,8 @@ myfeof(int fd)
}
char CFileMgr::ms_rootDirName[128] = {'\0'};
char CFileMgr::ms_dirName[128];
char CFileMgr::ms_rootDirName[FILEMGR_PATH_SIZE] = {'\0'};
char CFileMgr::ms_dirName[FILEMGR_PATH_SIZE];
void
CFileMgr::Initialise(void)
@@ -218,12 +372,64 @@ CFileMgr::Initialise(void)
strcat(ms_rootDirName, "/");
debug("Android: Root Dir: %s\n", ms_rootDirName);
}
#elif defined(__APPLE__)
FindBundledGameFiles(bundledGameFilesDir, sizeof(bundledGameFilesDir));
if(ms_rootDirName[0] == '\0' && !FindGameRoot(ms_rootDirName, sizeof(ms_rootDirName)))
_exit(0);
strcpy(ms_dirName, ms_rootDirName);
mychdir(ms_rootDirName);
#else
_getcwd(ms_rootDirName, 128);
_getcwd(ms_rootDirName, sizeof(ms_rootDirName));
strcat(ms_rootDirName, "\\");
#endif
}
bool
CFileMgr::ResolveBundledGameFile(const char *file, char *path, size_t pathSize)
{
#ifdef __APPLE__
const char *rootDir = CFileMgr::GetRootDirName();
if(bundledGameFilesDir[0] == '\0' || rootDir[0] == '\0' ||
file[0] == '\0' || file[0] == '/' || file[0] == '\\')
return false;
char cwd[FILEMGR_PATH_SIZE];
if(_getcwd(cwd, sizeof(cwd)) == nil)
return false;
size_t rootLength = strlen(rootDir);
while(rootLength > 0 && rootDir[rootLength - 1] == '/')
rootLength--;
if(strncmp(cwd, rootDir, rootLength) != 0 ||
(cwd[rootLength] != '\0' && cwd[rootLength] != '/'))
return false;
char candidate[FILEMGR_PATH_SIZE];
int length = snprintf(candidate, sizeof(candidate), "%s%s/%s",
bundledGameFilesDir, cwd + rootLength, file);
if(length < 0 || length >= (int)sizeof(candidate))
return false;
char *realPath = casepath(candidate);
const char *resolvedPath = realPath == nil ? candidate : realPath;
FILE *bundleFile = fopen(resolvedPath, "rb");
if(bundleFile == nil){
free(realPath);
return false;
}
fclose(bundleFile);
length = snprintf(path, pathSize, "%s", resolvedPath);
free(realPath);
return length >= 0 && length < (int)pathSize;
#else
(void)file;
(void)path;
(void)pathSize;
return false;
#endif
}
void
CFileMgr::ChangeDir(const char *dir)
{
+10 -2
View File
@@ -1,10 +1,12 @@
#ifndef __GTA_FILEMGR_H__
#define __GTA_FILEMGR_H__
#define FILEMGR_PATH_SIZE 1024
class CFileMgr
{
static char ms_rootDirName[128];
static char ms_dirName[128];
static char ms_rootDirName[FILEMGR_PATH_SIZE];
static char ms_dirName[FILEMGR_PATH_SIZE];
public:
static void Initialise(void);
static void ChangeDir(const char *dir);
@@ -20,7 +22,13 @@ public:
static bool ReadLine(int fd, char *buf, int len);
static int CloseFile(int fd);
static int GetErrorReadWrite(int fd);
static bool ResolveBundledGameFile(const char *file, char *path, size_t pathSize);
static char *GetRootDirName() { return ms_rootDirName; }
};
#ifdef __APPLE__
bool ReadGamePathFromINI(char *path, size_t pathSize);
void WriteGamePathToINI(const char *path);
#endif
#endif // __GTA_FILEMGR_H__
+2 -14
View File
@@ -627,19 +627,8 @@ void
CMenuManager::CentreMousePointer()
{
if (SCREEN_WIDTH * 0.5f != 0.0f && 0.0f != SCREEN_HEIGHT * 0.5f) {
#if defined RW_D3D9 || defined RWLIBS
tagPOINT Point;
Point.x = SCREEN_WIDTH / 2;
Point.y = SCREEN_HEIGHT / 2;
ClientToScreen(PSGLOBAL(window), &Point);
SetCursorPos(Point.x, Point.y);
#elif defined RW_GL3 && !defined(LIBRW_SDL2)
glfwSetCursorPos(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
#elif defined(RW_GL3) && defined(LIBRW_SDL2)
SDL_WarpMouseInWindow(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
#endif
PSGLOBAL(lastMousePos.x) = SCREEN_WIDTH / 2;
PSGLOBAL(lastMousePos.y) = SCREEN_HEIGHT / 2;
RwV2d pos = { SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 };
RsMouseSetPos(&pos);
}
}
@@ -4866,7 +4855,6 @@ CMenuManager::ProcessUserInput(uint8 goDown, uint8 goUp, uint8 optionSelected, u
DMAudio.Service();
CentreMousePointer();
m_bShowMouse = true;
m_nCurrOption = 5; // TODO(Miami): Because selected option is resetted after res. change. We'll need to revisit that.
m_nOptionHighlightTransitionBlend = 0;
SaveSettings();
}
+1
View File
@@ -191,6 +191,7 @@ void ReplaceAtomicPipeCallback();
bool
CGame::InitialiseRenderWare(void)
{
CFileMgr::SetDir("");
ValidateVersion();
#ifdef USE_TEXTURE_POOL
_TexturePoolsInitialise();
+1 -1
View File
@@ -332,7 +332,7 @@ DoFade(void)
bool
RwGrabScreen(RwCamera *camera, RwChar *filename)
{
char temp[255];
char temp[FILEMGR_PATH_SIZE + 255];
RwImage *pImage = RsGrabScreen(camera);
bool result = true;
+38
View File
@@ -197,9 +197,47 @@ CustomFrontendOptionsPopulate(void)
#define MINI_CASE_SENSITIVE
#include "ini.h"
#ifdef __APPLE__
static const char *
GetINIFilePath()
{
static char path[PATH_MAX];
int length = snprintf(path, sizeof(path), "%s/reVC.ini", GetMacOSUserFilesFolder());
return length >= 0 && length < (int)sizeof(path) ? path : "reVC.ini";
}
mINI::INIFile ini(GetINIFilePath());
#else
mINI::INIFile ini("reVC.ini");
#endif
mINI::INIStructure cfg;
#ifdef __APPLE__
bool
ReadGamePathFromINI(char *path, size_t pathSize)
{
mINI::INIStructure settings;
if(!ini.read(settings))
return false;
mINI::INIMap<std::string> general = settings.get("General");
if(!general.has("GamePath"))
return false;
int length = snprintf(path, pathSize, "%s", general.get("GamePath").c_str());
return length >= 0 && length < (int)pathSize;
}
void
WriteGamePathToINI(const char *path)
{
mINI::INIStructure settings;
ini.read(settings);
settings["General"]["GamePath"] = path;
ini.write(settings);
}
#endif
bool ReadIniIfExists(const char *cat, const char *key, uint32 *out)
{
mINI::INIMap<std::string> section = cfg.get(cat);
+6 -4
View File
@@ -1,15 +1,13 @@
#define _CRT_SECURE_NO_WARNINGS
#define WITH_D3D // not WITHD3D, so it's librw define
#include <rwcore.h>
#include <rpworld.h>
#include "common.h"
#include <rpmatfx.h>
#include <rphanim.h>
#include <rpskin.h>
#include <assert.h>
#include <string.h>
#ifndef _WIN32
#include "crossplatform.h"
#endif
#include "FileMgr.h"
using namespace rw;
@@ -347,6 +345,7 @@ RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const v
StreamMemory *mem;
RwMemory *memargs;
const char *mode;
char bundledPath[FILEMGR_PATH_SIZE];
switch(accessType){
case rwSTREAMREAD: mode = "rb"; break;
@@ -358,6 +357,9 @@ RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const v
// oh god this is horrible. librw streams really need fixing
switch(type){
case rwSTREAMFILENAME:{
if(accessType == rwSTREAMREAD &&
CFileMgr::ResolveBundledGameFile((const char*)pData, bundledPath, sizeof(bundledPath)))
pData = bundledPath;
StreamFile fakefile;
file = rwNewT(StreamFile, 1, 0);
memcpy(file, &fakefile, sizeof(StreamFile));
+3 -1
View File
@@ -19,9 +19,11 @@ C_PcSave PcSaveHelper;
void
C_PcSave::SetSaveDirectory(const char *path)
{
#if defined ANDROID
#if defined ANDROID || defined __APPLE__
sprintf(DefaultPCSaveFileName, "%s/%s", path, "GTAVCsf");
#if defined ANDROID
debug("SetSaveDirectory: %s", DefaultPCSaveFileName);
#endif
#else
sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTAVCsf");
#endif
+56
View File
@@ -2,6 +2,62 @@
#include "crossplatform.h"
#include "FileMgr.h"
#ifdef __APPLE__
#include <errno.h>
#include <pwd.h>
#include <sys/stat.h>
#include <unistd.h>
static bool
CreateDirectoryTree(const char *path)
{
char current[PATH_MAX];
int length = snprintf(current, sizeof(current), "%s", path);
if(length < 0 || length >= (int)sizeof(current))
return false;
for(char *separator = current + 1; *separator != '\0'; separator++){
if(*separator != '/')
continue;
*separator = '\0';
if(mkdir(current, 0755) != 0 && errno != EEXIST){
*separator = '/';
return false;
}
*separator = '/';
}
return mkdir(current, 0755) == 0 || errno == EEXIST;
}
const char *
GetMacOSUserFilesFolder()
{
static char path[PATH_MAX];
const char *home = getenv("HOME");
if(home == nil || home[0] == '\0'){
struct passwd *user = getpwuid(getuid());
home = user == nil ? nil : user->pw_dir;
}
if(home == nil || home[0] == '\0')
return "userfiles";
#ifdef USE_MY_DOCUMENTS
const char *relativePath = "Library/Application Support/Grand Theft Auto - Vice City/p_drive/Documents/GTA Vice City User Files";
#else
const char *relativePath = "Library/Application Support/reVC";
#endif
int length = snprintf(path, sizeof(path), "%s/%s", home, relativePath);
if(length < 0 || length >= (int)sizeof(path))
return "userfiles";
if(!CreateDirectoryTree(path))
debug("Couldn't create macOS user files directory: %s\n", path);
return path;
}
#endif
// Codes compatible with Windows and Linux
#ifndef _WIN32
+9
View File
@@ -1,3 +1,6 @@
#ifndef __GTA_CROSSPLATFORM_H__
#define __GTA_CROSSPLATFORM_H__
#ifndef GTA_PS2
#include <time.h>
@@ -110,6 +113,10 @@ extern RwUInt32 gGameState;
RwBool IsForegroundApp();
#ifdef __APPLE__
const char *GetMacOSUserFilesFolder();
#endif
#ifndef MAX_PATH
#if !defined _WIN32 || defined __MINGW32__
#define MAX_PATH PATH_MAX
@@ -199,3 +206,5 @@ void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int);
#endif
#endif
#endif // __GTA_CROSSPLATFORM_H__
+75 -20
View File
@@ -78,6 +78,7 @@ static RwBool RwInitialised = FALSE;
static RwSubSystemInfo GsubSysInfo[MAX_SUBSYSTEMS];
static RwInt32 GnumSubSystems = 0;
static RwInt32 GcurSel = 0, GcurSelVM = 0;
static RwInt32 desktopWidth = 0, desktopHeight = 0;
static RwBool useDefault;
@@ -167,6 +168,8 @@ const char *_psGetUserFilesFolder()
strcpy(szUserFiles, "data");
return szUserFiles;
#elif defined __APPLE__
return GetMacOSUserFilesFolder();
#else
static char szUserFiles[256];
strcpy(szUserFiles, "userfiles");
@@ -276,7 +279,19 @@ psTimer(void)
void
psMouseSetPos(RwV2d *pos)
{
glfwSetCursorPos(PSGLOBAL(window), pos->x, pos->y);
int winw, winh;
glfwGetWindowSize(PSGLOBAL(window), &winw, &winh);
glfwSetCursorPos(PSGLOBAL(window),
pos->x * (double)winw / RsGlobal.maximumWidth,
pos->y * (double)winh / RsGlobal.maximumHeight);
FrontEndMenuManager.m_nMouseTempPosX = pos->x;
FrontEndMenuManager.m_nMouseTempPosY = pos->y;
FrontEndMenuManager.m_nMousePosX = pos->x;
FrontEndMenuManager.m_nMousePosY = pos->y;
FrontEndMenuManager.m_nMouseOldPosX = pos->x;
FrontEndMenuManager.m_nMouseOldPosY = pos->y;
if (glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED))
PSGLOBAL(cursorIsInWindow) = true;
PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x;
@@ -746,6 +761,11 @@ psSelectDevice()
RwInt32 subSysNum;
RwInt32 AutoRenderer = 0;
const GLFWvidmode *desktopMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
if (desktopMode) {
desktopWidth = desktopMode->width;
desktopHeight = desktopMode->height;
}
RwBool modeFound = FALSE;
@@ -834,16 +854,16 @@ psSelectDevice()
if(FrontEndMenuManager.m_nPrefsWidth == 0 ||
FrontEndMenuManager.m_nPrefsHeight == 0 ||
FrontEndMenuManager.m_nPrefsDepth == 0){
// Defaults if nothing specified
const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
FrontEndMenuManager.m_nPrefsWidth = mode->width;
FrontEndMenuManager.m_nPrefsHeight = mode->height;
FrontEndMenuManager.m_nPrefsDepth = 32;
FrontEndMenuManager.m_nPrefsWindowed = 0;
}
// Find the videomode that best fits what we got from the settings file
bestWndMode = -1;
RwInt32 bestFsMode = -1;
RwInt32 firstFsMode = -1;
RwInt32 bestWidth = -1;
RwInt32 bestHeight = -1;
RwInt32 bestDepth = -1;
@@ -853,6 +873,8 @@ psSelectDevice()
if (!(vm.flags & rwVIDEOMODEEXCLUSIVE)){
bestWndMode = GcurSelVM;
} else {
if(firstFsMode < 0)
firstFsMode = GcurSelVM;
// try the largest one that isn't larger than what we wanted
if(vm.width >= bestWidth && vm.width <= FrontEndMenuManager.m_nPrefsWidth &&
vm.height >= bestHeight && vm.height <= FrontEndMenuManager.m_nPrefsHeight &&
@@ -865,32 +887,46 @@ psSelectDevice()
}
}
if(bestFsMode < 0){
RwInt32 selectedFsMode = bestFsMode >= 0 ? bestFsMode : firstFsMode;
GcurSelVM = selectedFsMode;
if(GcurSelVM < 0 && FrontEndMenuManager.m_nPrefsWindowed)
GcurSelVM = bestWndMode;
if(GcurSelVM < 0){
printf("WARNING: Cannot find desired video mode, selecting device cancelled\n");
return FALSE;
}
GcurSelVM = bestFsMode;
FrontEndMenuManager.m_nDisplayVideoMode = GcurSelVM;
if(selectedFsMode >= 0){
FrontEndMenuManager.m_nDisplayVideoMode = selectedFsMode;
FrontEndMenuManager.m_nPrefsVideoMode = FrontEndMenuManager.m_nDisplayVideoMode;
}
FrontEndMenuManager.m_nSelectedScreenMode = FrontEndMenuManager.m_nPrefsWindowed;
}
#endif
#ifdef IMPROVED_VIDEOMODE
if (FrontEndMenuManager.m_nPrefsWindowed && bestWndMode < 0){
printf("WARNING: Cannot find windowed video mode, selecting device cancelled\n");
return FALSE;
}
#endif
RwEngineGetVideoModeInfo(&vm, GcurSelVM);
#ifdef IMPROVED_VIDEOMODE
if (FrontEndMenuManager.m_nPrefsWindowed)
GcurSelVM = bestWndMode;
// Now GcurSelVM is 0 but vm has sizes(and fullscreen flag) of the video mode we want, that's why we changed the rwVIDEOMODEEXCLUSIVE conditions below
if ((!FrontEndMenuManager.m_nPrefsWindowed || (vm.flags & rwVIDEOMODEEXCLUSIVE)) &&
vm.width > 0 && vm.height > 0 && vm.depth > 0) {
FrontEndMenuManager.m_nPrefsWidth = vm.width;
FrontEndMenuManager.m_nPrefsHeight = vm.height;
FrontEndMenuManager.m_nPrefsDepth = vm.depth;
}
if (FrontEndMenuManager.m_nPrefsWindowed)
GcurSelVM = bestWndMode;
#endif
#ifndef PS2_MENU
if (!useDefault)
FrontEndMenuManager.m_nCurrOption = 0;
#endif
@@ -975,8 +1011,11 @@ void _InputInitialiseJoys()
PSGLOBAL(joy2id) = -1;
// Load our gamepad mappings.
#define SDL_GAMEPAD_DB_PATH "gamecontrollerdb.txt"
FILE *f = fopen(SDL_GAMEPAD_DB_PATH, "rb");
char bundledPath[FILEMGR_PATH_SIZE];
const char *gamepadDbPath = CFileMgr::ResolveBundledGameFile(
"gamecontrollerdb.txt", bundledPath, sizeof(bundledPath)) ?
bundledPath : "gamecontrollerdb.txt";
FILE *f = fcaseopen(gamepadDbPath, "rb");
if (f) {
fseek(f, 0, SEEK_END);
size_t fsize = ftell(f);
@@ -987,16 +1026,14 @@ void _InputInitialiseJoys()
db[fsize] = '\0';
if (glfwUpdateGamepadMappings(db) == GLFW_FALSE)
Error("glfwUpdateGamepadMappings didn't succeed, check " SDL_GAMEPAD_DB_PATH ".\n");
Error("glfwUpdateGamepadMappings didn't succeed, check %s.\n", gamepadDbPath);
} else
Error("fread on " SDL_GAMEPAD_DB_PATH " wasn't successful.\n");
Error("fread on %s wasn't successful.\n", gamepadDbPath);
free(db);
fclose(f);
} else
printf("You don't seem to have copied " SDL_GAMEPAD_DB_PATH " file from reVC/gamefiles to GTA: Vice City directory. Some gamepads may not be recognized.\n");
#undef SDL_GAMEPAD_DB_PATH
printf("You don't seem to have copied %s file from reVC/gamefiles to GTA: Vice City directory. Some gamepads may not be recognized.\n", gamepadDbPath);
// But always overwrite it with the one in SDL_GAMECONTROLLERCONFIG.
char const* EnvControlConfig = getenv("SDL_GAMECONTROLLERCONFIG");
@@ -1031,12 +1068,22 @@ long _InputInitialiseMouse(bool exclusive)
// Disabled = keep cursor centered and hide
lastCursorMode = exclusive ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_HIDDEN;
glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, lastCursorMode);
if (exclusive && glfwGetWindowAttrib(PSGLOBAL(window), GLFW_FOCUSED))
PSGLOBAL(cursorIsInWindow) = true;
return 0;
}
void _InputShutdownMouse()
{
// Not needed
lastCursorMode = GLFW_CURSOR_NORMAL;
glfwSetInputMode(PSGLOBAL(window), GLFW_CURSOR, lastCursorMode);
}
static void
centreMouseForDesktop()
{
if (desktopWidth > 0 && desktopHeight > 0)
glfwSetCursorPos(PSGLOBAL(window), desktopWidth * 0.5, desktopHeight * 0.5);
}
// Not "needs exclusive" on GLFW, but more like "needs to change mode"
@@ -1088,6 +1135,8 @@ RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode)
{
RwInitialised = FALSE;
_InputShutdownMouse();
centreMouseForDesktop();
RsEventHandler(rsRWTERMINATE, nil);
GcurSel = subSystem;
@@ -1845,8 +1894,8 @@ cursorCB(GLFWwindow* window, double xpos, double ypos) {
int winw, winh;
glfwGetWindowSize(PSGLOBAL(window), &winw, &winh);
FrontEndMenuManager.m_nMouseTempPosX = xpos * (RsGlobal.maximumWidth / winw);
FrontEndMenuManager.m_nMouseTempPosY = ypos * (RsGlobal.maximumHeight / winh);
FrontEndMenuManager.m_nMouseTempPosX = xpos * ((double)RsGlobal.maximumWidth / winw);
FrontEndMenuManager.m_nMouseTempPosY = ypos * ((double)RsGlobal.maximumHeight / winh);
}
void
@@ -1857,6 +1906,10 @@ cursorEnterCB(GLFWwindow* window, int entered) {
void
windowFocusCB(GLFWwindow* window, int focused) {
WindowFocused = !!focused;
if (!focused)
PSGLOBAL(cursorIsInWindow) = false;
else if (lastCursorMode == GLFW_CURSOR_DISABLED || glfwGetWindowAttrib(window, GLFW_HOVERED))
PSGLOBAL(cursorIsInWindow) = true;
}
void
@@ -2445,6 +2498,8 @@ main(int argc, char *argv[])
/*
* Tidy up the 3D (RenderWare) components of the application...
*/
_InputShutdownMouse();
centreMouseForDesktop();
RsEventHandler(rsRWTERMINATE, nil);
/*
+9 -1
View File
@@ -106,10 +106,14 @@ void _psCreateFolder(const char *path)
*/
const char *_psGetUserFilesFolder()
{
#ifdef __APPLE__
return GetMacOSUserFilesFolder();
#else
static char szUserFiles[256];
strcpy(szUserFiles, "userfiles");
_psCreateFolder(szUserFiles);
return szUserFiles;
#endif
}
/*
@@ -617,6 +621,7 @@ psSelectDevice()
#endif
#ifndef PS2_MENU
if (!useDefault)
FrontEndMenuManager.m_nCurrOption = 0;
#endif
@@ -703,7 +708,10 @@ void _InputInitialiseJoys()
char SDL_GAMEPAD_DB_PATH[MAX_PATH];
snprintf(SDL_GAMEPAD_DB_PATH, sizeof(SDL_GAMEPAD_DB_PATH), "%s%s", pathRoot, "gamecontrollerdb.txt");
#else
const char* SDL_GAMEPAD_DB_PATH = "gamecontrollerdb.txt";
char bundledPath[FILEMGR_PATH_SIZE];
const char* SDL_GAMEPAD_DB_PATH = CFileMgr::ResolveBundledGameFile(
"gamecontrollerdb.txt", bundledPath, sizeof(bundledPath)) ?
bundledPath : "gamecontrollerdb.txt";
#endif
if (SDL_GameControllerAddMappingsFromFile(SDL_GAMEPAD_DB_PATH) <= 0) {
debug ("You don't seem to have copied %s file from reVC/gamefiles "
+1
View File
@@ -1566,6 +1566,7 @@ psSelectDevice()
#endif
#ifndef PS2_MENU
if (!useDefault)
FrontEndMenuManager.m_nCurrOption = 0;
#endif