Make re3 relocatable
#967
madebr/re3_conan_libsndfile
into master
@ -1,82 +1,152 @@
|
|||||||
cmake_minimum_required(VERSION 3.8)
|
|
||||||
|
|
||||||
set(EXECUTABLE re3)
|
set(EXECUTABLE re3)
|
||||||
set(PROJECT RE3)
|
set(STEAMID 12100)
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.8)
|
||||||
project(${EXECUTABLE} C CXX)
|
project(${EXECUTABLE} C CXX)
|
||||||
|
set(PROJECT_AUTHOR "GTAModding")
|
||||||
|
set(PROJECT_URL "https://github.com/gtamodding/re3")
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(${PROJECT}_AUDIOS "OAL" "MSS")
|
set(RE3_AUDIOS "OAL" "MSS")
|
||||||
else()
|
else()
|
||||||
set(${PROJECT}_AUDIOS "OAL")
|
set(RE3_AUDIOS "OAL")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(${PROJECT}_AUDIO "OAL" CACHE STRING "Audio")
|
set(RE3_AUDIO "OAL" CACHE STRING "Audio")
|
||||||
|
|
||||||
option(${PROJECT}_WITH_OPUS "Build ${EXECUTABLE} with opus support" OFF)
|
include(CMakeDependentOption)
|
||||||
option(${PROJECT}_WITH_LIBSNDFILE "Build ${EXECUTABLE} with libsndfile (instead of internal decoder)" OFF)
|
|
||||||
|
option(RE3_INSTALL "Install ${EXECUTABLE}" OFF)
|
||||||
|
option(RE3_WITH_OPUS "Build ${EXECUTABLE} with opus support" OFF)
|
||||||
|
option(RE3_WITH_LIBSNDFILE "Build ${EXECUTABLE} with libsndfile (instead of internal decoder)" OFF)
|
||||||
|
|
||||||
|
cmake_dependent_option(RE3_INSTALL_NSIS "Create NSIS installer" ON "WIN32;RE3_INSTALL" OFF)
|
||||||
|
cmake_dependent_option(RE3_INSTALL_SRC "Create sources archive" OFF "RE3_INSTALL" OFF)
|
||||||
|
|
||||||
|
if(RE3_INSTALL_NSIS)
|
||||||
|
find_program(MAKENSIS_BIN makensis)
|
||||||
|
if(NOT MAKENSIS_BIN)
|
||||||
|
message(FATAL_ERROR "Cannot find `makensis` for creating NSIS installer.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
set_property(CACHE ${PROJECT}_AUDIO PROPERTY STRINGS ${${PROJECT}_AUDIOS})
|
set_property(CACHE RE3_AUDIO PROPERTY STRINGS ${RE3_AUDIOS})
|
||||||
message(STATUS "${PROJECT}_AUDIO = ${${PROJECT}_AUDIO} (choices=${${PROJECT}_AUDIOS})")
|
message(STATUS "RE3_AUDIO = ${RE3_AUDIO} (choices=${RE3_AUDIOS})")
|
||||||
set("${PROJECT}_AUDIO_${${PROJECT}_AUDIO}" ON)
|
set("RE3_AUDIO_${RE3_AUDIO}" ON)
|
||||||
if(NOT ${PROJECT}_AUDIO IN_LIST ${PROJECT}_AUDIOS)
|
if(NOT RE3_AUDIO IN_LIST RE3_AUDIOS)
|
||||||
message(FATAL_ERROR "Illegal ${PROJECT}_AUDIO=${${PROJECT}_AUDIO}")
|
message(FATAL_ERROR "Illegal RE3_AUDIO=${RE3_AUDIO}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(${PROJECT}_VENDORED_LIBRW "Use vendored librw" ON)
|
option(RE3_VENDORED_LIBRW "Use vendored librw" ON)
|
||||||
if(${PROJECT}_VENDORED_LIBRW)
|
if(RE3_VENDORED_LIBRW)
|
||||||
add_subdirectory(vendor/librw)
|
add_subdirectory(vendor/librw)
|
||||||
else()
|
else()
|
||||||
find_package(librw REQUIRED)
|
find_package(librw REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${EXECUTABLE})
|
||||||
|
|
||||||
if(${PROJECT}_INSTALL)
|
if(RE3_INSTALL)
|
||||||
install(DIRECTORY gamefiles/ DESTINATION ".")
|
install(DIRECTORY gamefiles/ DESTINATION "." COMPONENT comp2_gamedata)
|
||||||
if(LIBRW_PLATFORM_NULL)
|
configure_file(cmake/CPackProjectConfig.cmake.in CPackProjectConfig.cmake @ONLY)
|
||||||
set(platform "-null")
|
#file(GENERATE OUTPUT CPackProjectConfig.cmake INPUT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CPackProjectConfig.cmake.in" TARGET ${EXECUTABLE})
|
||||||
elseif(LIBRW_PLATFORM_PS2)
|
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/dist")
|
||||||
set(platform "-ps2")
|
|
||||||
elseif(LIBRW_PLATFORM_GL3)
|
if(RE3_INSTALL_SRC)
|
||||||
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
|
install(
|
||||||
set(platform "-gl3-glfw")
|
DIRECTORY cmake gamefiles resources src utils
|
||||||
else()
|
DESTINATION .
|
||||||
set(platform "-gl3-sdl2")
|
COMPONENT compZ_sources
|
||||||
endif()
|
EXCLUDE_FROM_ALL
|
||||||
elseif(LIBRW_PLATFORM_D3D9)
|
)
|
||||||
set(platform "-d3d9")
|
install(
|
||||||
endif()
|
FILES conanfile.py CMakeLists.txt premake5.lua README.md CODING_STYLE.md
|
||||||
if(${PROJECT}_AUDIO_OAL)
|
DESTINATION .
|
||||||
set(audio "-oal")
|
COMPONENT compZ_sources
|
||||||
elseif(${PROJECT}_AUDIO_MSS)
|
EXCLUDE_FROM_ALL
|
||||||
set(audio "-mss")
|
)
|
||||||
endif()
|
|
||||||
if(${PROJECT}_WITH_OPUS)
|
|
||||||
set(audio "${audio}-opus")
|
|
||||||
endif()
|
|
||||||
if(NOT LIBRW_PLATFORM_PS2)
|
|
||||||
if(WIN32)
|
|
||||||
set(os "-win")
|
|
||||||
elseif(APPLE)
|
|
||||||
set(os "-apple")
|
|
||||||
elseif(UNIX)
|
|
||||||
set(os "-linux")
|
|
||||||
else()
|
|
||||||
set(compiler "-UNK")
|
|
||||||
message(WARNING "Unknown os. Created cpack package will be wrong. (override using cpack -P)")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}${platform}${audio}${os}${compiler}")
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
|
||||||
|
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}")
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GTA III reversed")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "GTA III reversed")
|
||||||
set(CPACK_PACKAGE_VENDOR "GTAModding")
|
set(CPACK_PACKAGE_VENDOR "${PROJECT_AUTHOR}")
|
||||||
|
set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/CPackProjectConfig.cmake")
|
||||||
# FIXME: missing license (https://github.com/GTAmodding/re3/issues/794)
|
# FIXME: missing license (https://github.com/GTAmodding/re3/issues/794)
|
||||||
# set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/LICENSE")
|
# set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||||
# set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
# set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}")
|
|
||||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}")
|
|
||||||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}")
|
|
||||||
set(CPACK_GENERATOR "TXZ")
|
set(CPACK_GENERATOR "TXZ")
|
||||||
|
# FIXME: this is displayed in the GUI installer!!! Needs a disclaimer about trademarks etc.
|
||||||
|
# set(CPACK_RESOURCE_FILE_LICENSE)
|
||||||
|
set(CPACK_PACKAGE_ICON "${PROJECT_SOURCE_DIR}\\\\resources\\\\logo.bmp")
|
||||||
|
set(CPACK_PACKAGE_CHECKSUM "SHA256")
|
||||||
|
|
||||||
|
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "compZ_sources")
|
||||||
|
|
||||||
|
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(NEED_RUNTIME OFF)
|
||||||
|
if(MSVC)
|
||||||
|
if(NOT "-MT" MATCHES CMAKE_C_FLAGS)
|
||||||
|
# Install MSVC runtime when using dynamic runtime (-MD, -MDd)
|
||||||
|
if("-MDd" MATCHES CMAKE_C_FLAGS)
|
||||||
|
set(CMAKE_INSTALL_DEBUG_LIBRARIES_ONLY ON)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ".")
|
||||||
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_COMPONENT comp3_runtime)
|
||||||
|
include(InstallRequiredSystemLibraries)
|
||||||
|
set(NEED_RUNTIME ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(RE3_INSTALL_NSIS)
|
||||||
|
list(APPEND CPACK_GENERATOR "NSIS")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CPACK_NSIS_COMPONENT_INSTALL ON)
|
||||||
|
set(CPACK_NSIS_DEFINES "!RequestExecutionLevel user")
|
||||||
|
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||||
|
set(CPACK_NSIS_DISPLAY_NAME "${PROJECT_NAME} (${PROJECT_AUTHOR})")
|
||||||
|
set(CPACK_NSIS_HELP_LINK "${PROJECT_URL}")
|
||||||
|
set(CPACK_NSIS_URL_INFO_ABOUT "${PROJECT_URL}")
|
||||||
|
set(CPACK_NSIS_MUI_ICON "${PROJECT_SOURCE_DIR}\\\\resources\\\\logo.ico")
|
||||||
|
set(CPACK_NSIS_MUI_UNIICON "${PROJECT_SOURCE_DIR}\\\\resources\\\\logo.ico")
|
||||||
|
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
|
set(CPACK_NSIS_MUI_FINISHPAGE_RUN "${EXECUTABLE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(CPackComponent)
|
||||||
|
|
||||||
|
cpack_add_component(comp1_core
|
||||||
|
DISPLAY_NAME "Game executable(s)"
|
||||||
|
DESCRIPTION "Install the game executable(s)."
|
||||||
|
REQUIRED
|
||||||
|
)
|
||||||
|
cpack_add_component(comp2_gamedata
|
||||||
|
DISPLAY_NAME "Game data fixes"
|
||||||
|
DESCRIPTION "Install game data fixes."
|
||||||
|
REQUIRED
|
||||||
|
DEPENDS "comp1_core"
|
||||||
|
)
|
||||||
|
cpack_add_component(comp3_runtime
|
||||||
|
DISPLAY_NAME "System runtime"
|
||||||
|
DESCRIPTION "Install C runtime. Not required if a suitable MSVC runtime is already available."
|
||||||
|
DEPENDS "comp1_core"
|
||||||
|
)
|
||||||
|
cpack_add_component(comp4_debug
|
||||||
|
DISPLAY_NAME "Debug symbols"
|
||||||
|
DESCRIPTION "Enables debugging your problems. Hopefully not needed."
|
||||||
|
DEPENDS "comp1_core"
|
||||||
|
DISABLED
|
||||||
|
)
|
||||||
|
cpack_add_component(compZ_sources
|
||||||
|
DISPLAY_NAME "sources"
|
||||||
|
DESCRIPTION "The sources of ${PROJECT_NAME}."
|
||||||
|
DISABLED
|
||||||
|
HIDDEN
|
||||||
|
)
|
||||||
|
|
||||||
include(CPack)
|
include(CPack)
|
||||||
endif()
|
endif()
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
set(PROJECT_NAME "@PROJECT_NAME@")
|
||||||
|
set(PROJECT_AUTHOR "@PROJECT_AUTHOR@")
|
||||||
|
|
||||||
|
set(LIBRW_PLATFORM "@LIBRW_PLATFORM@")
|
||||||
|
set(LIBRW_GL3_GFXLIB "@LIBRW_GL3_GFXLIB@")
|
||||||
|
set(RE3_AUDIO "@RE3_AUDIO@")
|
||||||
|
set(RE3_WITH_OPUS "@RE3_WITH_OPUS@")
|
||||||
|
set(WIN32 @WIN32@)
|
||||||
|
set(APPLE @APPLE@)
|
||||||
|
set(UNIZ @UNIX@)
|
||||||
|
set(CMAKE_SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@)
|
||||||
|
|
||||||
|
if(LIBRW_PLATFORM STREQUAL "NULL")
|
||||||
|
set(platform "-null")
|
||||||
|
elseif(LIBRW_PLATFORM STREQUAL "PS2")
|
||||||
|
set(platform "-ps2")
|
||||||
|
elseif(LIBRW_PLATFORM STREQUAL "GL3")
|
||||||
|
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
|
||||||
|
set(platform "-gl3-glfw")
|
||||||
|
else()
|
||||||
|
set(platform "-gl3-sdl2")
|
||||||
|
endif()
|
||||||
|
elseif(LIBRW_PLATFORM STREQUAL "D3D9")
|
||||||
|
set(platform "-d3d9")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(RE3_AUDIO STREQUAL "OAL")
|
||||||
|
set(audio "-oal")
|
||||||
|
elseif(RE3_AUDIO STREQUAL "MSS")
|
||||||
|
set(audio "-mss")
|
||||||
|
endif()
|
||||||
|
if(RE3_WITH_OPUS)
|
||||||
|
set(audio "${audio}-opus")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(os "-win")
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(os "${os}64")
|
||||||
|
else()
|
||||||
|
set(os "${os}32")
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
set(os "-apple")
|
||||||
|
elseif(UNIX)
|
||||||
|
set(os "-linux")
|
||||||
|
else()
|
||||||
|
set(os "-UNK")
|
||||||
|
message(WARNING "Unknown os. Created cpack package will be wrong. (override using cpack -P)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(name_osarch "${PROJECT_NAME}${platform}${audio}${os}${compiler}")
|
||||||
|
|
||||||
|
set(CPACK_PACKAGE_FILE_NAME "${name_osarch}")
|
||||||
|
|
||||||
|
|
||||||
|
if(CPACK_GENERATOR MATCHES "^(7Z|TBZ|TGZ|TXZ|TZ|TZST|ZIP)$")
|
||||||
|
set(CPACK_COMPONENTS_GROUPING "ONE_PER_GROUP")
|
||||||
|
|
||||||
|
set(CPACK_COMPONENT_COMP1_CORE_GROUP "BinaryGroup")
|
||||||
|
set(CPACK_COMPONENT_COMP2_GAMEDATA_GROUP "BinaryGroup")
|
||||||
|
set(CPACK_COMPONENT_COMP3_RUNTIME_GROUP "BinaryGroup")
|
||||||
|
set(CPACK_COMPONENT_COMP4_DEBUG_GROUP "BinaryGroup")
|
||||||
|
set(CPACK_COMPONENT_COMPZ_SOURCES_GROUP "SourceGroup")
|
||||||
|
|
||||||
|
set(CPACK_ARCHIVE_SOURCEGROUP_FILE_NAME "${PROJECT_NAME}-src")
|
||||||
|
set(CPACK_ARCHIVE_BINARYGROUP_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}")
|
||||||
|
elseif(CPACK_GENERATOR MATCHES "^NSIS(64)?$")
|
||||||
|
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "compZ_sources")
|
||||||
|
|
||||||
|
set(CPACK_PACKAGE_FILE_NAME "${name_osarch}-setup")
|
||||||
|
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME} (${PROJECT_AUTHOR})")
|
||||||
|
|
||||||
|
set(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\${PROJECT_NAME}.lnk\" \"$INSTDIR\\${PROJECT_NAME}.exe\"")
|
||||||
|
set(CPACK_NSIS_DELETE_ICONS_EXTRA "Delete \"$SMPROGRAMS\\$MUI_TEMP\${PROJECT_NAME}.lnk\"")
|
||||||
|
endif()
|
@ -0,0 +1,17 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
original = Image.open("logo.png")
|
||||||
|
icon_sizes = [(256, 256), (128, 128), (64, 64), (48, 48), (32, 32), (16, 16)]
|
||||||
|
original.save("logo.ico", sizes=icon_sizes)
|
||||||
|
|
||||||
|
# Crop logo + make sure that background is white
|
||||||
|
width, height = original.size
|
||||||
|
crop_area = (0, height // 6, width, height - height // 6)
|
||||||
|
cropped_image = original.crop(crop_area)
|
||||||
|
cropped_size = (crop_area[2] - crop_area[0], crop_area[3] - crop_area[1])
|
||||||
|
new_image = Image.new("RGBA", cropped_size, "WHITE")
|
||||||
|
new_image.paste(cropped_image, (0, 0), cropped_image)
|
||||||
|
new_image.convert("RGB").save("logo.bmp")
|
After Width: | Height: | Size: 2.0 MiB |
After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
@ -0,0 +1,492 @@
|
|||||||
|
#ifdef RELOCATABLE
|
||||||
|
#include "common.h"
|
||||||
|
#include "FileMgr.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <io.h>
|
||||||
|
#else
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <direct.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WITHWINDOWS
|
||||||
|
#include "crossplatform.h"
|
||||||
|
#include "relocatable.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef R_OK
|
||||||
|
#define R_OK 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define PATHJOIN '\\'
|
||||||
|
#define PATHJOIN_S "\\"
|
||||||
|
#define PATHSEP ';'
|
||||||
|
#else
|
||||||
|
#define PATHJOIN '/'
|
||||||
|
#define PATHJOIN_S "/"
|
||||||
|
#define PATHSEP ':'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool
|
||||||
|
FileExists(const char *path)
|
||||||
|
{
|
||||||
|
#if _WIN32
|
||||||
|
bool exists = PathFileExistsA(path);
|
||||||
|
struct stat status;
|
||||||
|
stat(path, &status);
|
||||||
|
return (status.st_mode & _S_IFREG) != 0;
|
||||||
|
#else
|
||||||
|
return access(path, R_OK) == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
containsGta3Data(const std::string &dirPath)
|
||||||
|
{
|
||||||
|
std::string path(dirPath);
|
||||||
|
path.append("models" PATHJOIN_S "gta3.img");
|
||||||
|
return FileExists(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string
|
||||||
|
winReadRegistry(HKEY hParentKey, const char *keyName, const char *valueName)
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
if (RegOpenKeyExA(hParentKey, keyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||||
|
std::string res;
|
||||||
|
DWORD valueSize = 1024;
|
||||||
|
res.resize(valueSize);
|
||||||
|
DWORD keyType;
|
||||||
|
if (RegQueryValueEx(hKey, valueName, NULL, &keyType, (LPBYTE)res.data(), &valueSize) == ERROR_SUCCESS) {
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
if(keyType == REG_SZ) {
|
||||||
|
res.resize(strlen(res.data()));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
psExePath()
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
buf.resize(MAX_PATH);
|
||||||
|
if (GetModuleFileName(NULL, (LPSTR)buf.c_str(), MAX_PATH) == 0) {
|
||||||
|
debug("GetModuleFileNameA failed: cannot get path of executable.\n");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
buf.resize(strlen(buf.c_str()));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
std::string
|
||||||
|
psExePath()
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
uint32_t bufsize = 0;
|
||||||
|
_NSGetExecutablePath(NULL, &bufsize);
|
||||||
|
buf.resize(bufsize + 1);
|
||||||
|
if(_NSGetExecutablePath((char*)buf.c_str(), &bufsize) != 0) {
|
||||||
|
debug("_NSGetExecutablePath failed: cannot get path of executable.\n");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
buf.resize(bufsize);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
std::string
|
||||||
|
psExePath()
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
buf.resize(MAX_PATH);
|
||||||
|
ssize_t nb = readlink("/proc/self/exe", (char*)buf.c_str(), MAX_PATH);
|
||||||
|
if (nb == -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
buf.resize(strlen(buf.c_str()));
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string
|
||||||
|
psExeDir()
|
||||||
|
{
|
||||||
|
std::string exePath = psExePath();
|
||||||
|
if (!exePath.size()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::string::size_type lastSepPos = exePath.rfind(PATHJOIN);
|
||||||
|
if (lastSepPos >= 0) {
|
||||||
|
exePath.resize(lastSepPos);
|
||||||
|
return exePath;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
return dirname((char*)exePath.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string
|
||||||
|
winGetGtaDataFolder()
|
||||||
|
{
|
||||||
|
if (containsGta3Data("C:\\Program Files\\Rockstar Games\\GTAIII\\")) {
|
||||||
|
return "C:\\Program Files\\Rockstar Games\\GTAIII\\";
|
||||||
|
}
|
||||||
|
std::string location;
|
||||||
|
location.resize(MAX_PATH);
|
||||||
|
BOOL success = SHGetSpecialFolderPathA(NULL, (LPSTR)location.c_str(), CSIDL_PROGRAM_FILESX86, FALSE);
|
||||||
|
if (success) {
|
||||||
|
location.resize(strlen(location.c_str()));
|
||||||
|
location.append("\\Rockstar Games\\GTAIII\\");
|
||||||
|
if(containsGta3Data(location)) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(STEAMID)
|
||||||
|
location = winReadRegistry(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STR(STEAMID), "InstallLocation");
|
||||||
|
if (location.size()) {
|
||||||
|
location.push_back('\\');
|
||||||
|
if (containsGta3Data(location)) {
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
normalizePathCorrectPathJoiner(std::string &s)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define PATHJOIN_BAD '/'
|
||||||
|
#else
|
||||||
|
#define PATHJOIN_BAD '\\'
|
||||||
|
#endif
|
||||||
|
std::string::size_type pos(0);
|
||||||
|
while((pos = s.find(PATHJOIN_BAD, pos)) != std::string::npos) {
|
||||||
|
s[pos] = PATHJOIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ensureTrailingPathJoiner(std::string &s)
|
||||||
|
{
|
||||||
|
if(s.size() && *s.rbegin() != PATHJOIN) {
|
||||||
|
s.push_back(PATHJOIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
std::vector<std::string> searchPaths;
|
||||||
|
|
||||||
|
void
|
||||||
|
reloc_initialize()
|
||||||
|
{
|
||||||
|
// FIXME: is called twice
|
||||||
|
searchPaths.clear();
|
||||||
|
std::string path;
|
||||||
|
// 1. add search paths from command line
|
||||||
|
// FIXME
|
||||||
|
// 2. `GTA_III_RE_DIR` environment variable
|
||||||
|
const char *pEnv = ::getenv("GTA3DIR");
|
||||||
|
if (pEnv) {
|
||||||
|
std::string env = pEnv;
|
||||||
|
std::string::size_type start(0);
|
||||||
|
std::string::size_type end;
|
||||||
|
while ((end = env.find(PATHSEP, start)) != std::string::npos) {
|
||||||
|
searchPaths.push_back(env.substr(start, end - start));
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
searchPaths.push_back(env.substr(start));
|
||||||
|
}
|
||||||
|
// 3. add search paths from INI
|
||||||
|
// FIXME
|
||||||
|
// 4. add current working directory to search path
|
||||||
|
path.resize(MAX_PATH);
|
||||||
|
char *cwd = getcwd((char *)path.c_str(), path.size());
|
||||||
|
if (cwd) {
|
||||||
|
path.resize(strlen(path.c_str()));
|
||||||
|
searchPaths.push_back(path);
|
||||||
|
}
|
||||||
|
// 5. add exe path to search path
|
||||||
|
path = psExeDir();
|
||||||
|
if(path.size()) {
|
||||||
|
searchPaths.push_back(path);
|
||||||
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
// 6. add default installation paths
|
||||||
|
path = winGetGtaDataFolder();
|
||||||
|
if(path.size()) {
|
||||||
|
searchPaths.push_back(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// 6. built-in path
|
||||||
|
#ifdef GTA_III_RE_DIR
|
||||||
|
searchPaths.push_back(GTA_III_RE_DIR);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Normalize paths
|
||||||
|
for(std::string &path : searchPaths) {
|
||||||
|
normalizePathCorrectPathJoiner(path);
|
||||||
|
ensureTrailingPathJoiner(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("Full search path is:\n");
|
||||||
|
for (const std::string &s: searchPaths) {
|
||||||
|
debug("- %s\n", s.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
searchPathsFindData(const char *path)
|
||||||
|
{
|
||||||
|
const char *cwd = CFileMgr::GetWorkingDir();
|
||||||
|
std::string suffix;
|
||||||
|
if (cwd && *cwd != '\0') {
|
||||||
|
suffix.append(cwd);
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
while(suffix[pos] == PATHJOIN) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
suffix = suffix.substr(pos, suffix.size() - pos);
|
||||||
|
if (suffix[suffix.size() - 1] != PATHJOIN) {
|
||||||
|
suffix.push_back(PATHJOIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suffix.append(path);
|
||||||
|
normalizePathCorrectPathJoiner(suffix);
|
||||||
|
for (const auto &searchpath : searchPaths) {
|
||||||
|
std::string curPath = searchpath + suffix;
|
||||||
|
if (FileExists(curPath.c_str())) {
|
||||||
|
return curPath;
|
||||||
|
}
|
||||||
|
FILE *f = fopen(curPath.c_str(), "rb");
|
||||||
|
if(f) {
|
||||||
|
fclose(f);
|
||||||
|
debug("FileExists failed, but fopen succeeded. huh?\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE("Could not find \"%s\" in search paths (cwd=\"%s\").\n", path, cwd);
|
||||||
|
std::string res;
|
||||||
|
if (cwd && *cwd != '\0') {
|
||||||
|
res.append(cwd);
|
||||||
|
res.push_back(PATHJOIN);
|
||||||
|
}
|
||||||
|
res.append(path);
|
||||||
|
normalizePathCorrectPathJoiner(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "FileMgr.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
FILE *
|
||||||
|
reloc_fopen(const char *path, const char *mode)
|
||||||
|
{
|
||||||
|
std::string fullPath = searchPathsFindData(path);
|
||||||
|
TRACE("reloc_fopen(%s, %s) -> fopen(%s, %s)", path, mode, fullPath.c_str(), mode);
|
||||||
|
return fopen(fullPath.c_str(), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
reloc_realpath(const char *filename)
|
||||||
|
{
|
||||||
|
static std::string fullPath;
|
||||||
|
fullPath = searchPathsFindData(filename);
|
||||||
|
TRACE("reloc_realpath(%s) -> %s", filename, fullPath.c_str());
|
||||||
|
return fullPath.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct reloc_HANDLE {
|
||||||
|
HANDLE hFind;
|
||||||
|
std::string fullPathName;
|
||||||
|
int searchPathIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
HANDLE
|
||||||
|
reloc_FindFirstFile(const char *pathName, WIN32_FIND_DATA *findFileData)
|
||||||
|
{
|
||||||
|
std::string suffix = CFileMgr::GetWorkingDir();
|
||||||
|
if (suffix.size()) {
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
while(suffix[pos] == PATHJOIN) { ++pos; }
|
||||||
|
suffix = suffix.substr(pos, suffix.size() - pos);
|
||||||
|
if (suffix[suffix.size() - 1] != PATHJOIN) {
|
||||||
|
suffix.push_back(PATHJOIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suffix.append(pathName);
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)searchPaths.size(); ++i) {
|
||||||
|
HANDLE hFind = FindFirstFile((searchPaths[i] + suffix).c_str(), findFileData);
|
||||||
|
if (hFind != INVALID_HANDLE_VALUE) {
|
||||||
|
return (HANDLE) new reloc_HANDLE {hFind, suffix, i};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
reloc_FindNextFile(HANDLE hFind, WIN32_FIND_DATA *findFileData)
|
||||||
|
{
|
||||||
|
reloc_HANDLE *relocHandle = (reloc_HANDLE *) hFind;
|
||||||
|
if (FindNextFile(relocHandle->hFind, findFileData)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
FindClose(relocHandle->hFind);
|
||||||
|
relocHandle->hFind = NULL;
|
||||||
|
|
||||||
|
for (int i = relocHandle->searchPathIndex + 1; i < (int)searchPaths.size(); ++i) {
|
||||||
|
HANDLE newHFind = FindFirstFile((searchPaths[i] + relocHandle->fullPathName).c_str(), findFileData);
|
||||||
|
if (newHFind != INVALID_HANDLE_VALUE) {
|
||||||
|
relocHandle->hFind = newHFind;
|
||||||
|
relocHandle->searchPathIndex = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
reloc_FindClose(HANDLE hFind)
|
||||||
|
{
|
||||||
|
reloc_HANDLE *relocHandle = (reloc_HANDLE *)hFind;
|
||||||
|
bool res = true;
|
||||||
|
if (relocHandle->hFind != NULL) {
|
||||||
|
res = FindClose(relocHandle->hFind);
|
||||||
|
}
|
||||||
|
delete relocHandle;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static bool
|
||||||
|
casepath_subpath(const std::string &basePath, const char *path, std::string &result) {
|
||||||
|
const char *curLevelStart;
|
||||||
|
size_t curLevelSize;
|
||||||
|
bool currentIsFile;
|
||||||
|
|
||||||
|
if (path == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail early when the base path does not exist.
|
||||||
|
DIR *d = opendir(basePath.c_str());
|
||||||
|
if (d == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split path in parts, ignoring empty levels (e.g. '/a//b' --> {'a', 'b'})
|
||||||
|
const char *nextCurLevelStart = path;
|
||||||
|
while (true) {
|
||||||
|
curLevelStart = nextCurLevelStart;
|
||||||
|
const char *delimPos = strpbrk(curLevelStart, "/\\");
|
||||||
|
if (delimPos == NULL) {
|
||||||
|
curLevelSize = strlen(curLevelStart);
|
||||||
|
currentIsFile = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
curLevelSize = delimPos - curLevelStart;
|
||||||
|
nextCurLevelStart = delimPos + 1;
|
||||||
|
currentIsFile = false;
|
||||||
|
|
||||||
|
if (curLevelSize > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char *curLevel = (char*) alloca(curLevelSize + 1);
|
||||||
|
strncpy(curLevel, curLevelStart, curLevelSize);
|
||||||
|
curLevel[curLevelSize] = '\0';
|
||||||
|
|
||||||
|
struct dirent *e;
|
||||||
|
while ((e = readdir(d)) != NULL) {
|
||||||
|
if (strcasecmp(curLevel, e->d_name) == 0) {
|
||||||
|
if (currentIsFile) {
|
||||||
|
result = basePath + e->d_name;
|
||||||
|
closedir(d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool subres = casepath_subpath(basePath + e->d_name + PATHJOIN_S, nextCurLevelStart, result);
|
||||||
|
if (subres) {
|
||||||
|
return subres;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(d);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char*
|
||||||
|
reloc_casepath(const char *path, bool checkPathFirst)
|
||||||
|
{
|
||||||
|
std::string realPath;
|
||||||
|
realPath.reserve(PATH_MAX);
|
||||||
|
if (checkPathFirst) {
|
||||||
|
for (const std::string &searchPath : searchPaths) {
|
||||||
|
realPath = searchPath + CFileMgr::GetWorkingDir() + path;
|
||||||
|
if (access(realPath.c_str(), F_OK) != -1) {
|
||||||
|
// File path is correct
|
||||||
|
debug("reloc_casepath(%s, %d) -> %s\n", path, checkPathFirst, realPath.c_str());
|
||||||
|
return strdup(realPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string suffixPath = std::string{CFileMgr::GetWorkingDir()} + path;
|
||||||
|
for (const std::string &searchPath : searchPaths) {
|
||||||
|
bool res = casepath_subpath(searchPath, suffixPath.c_str(), realPath);
|
||||||
|
if (res) {
|
||||||
|
debug("reloc_casepath(%s, %d) -> %s\n", path, checkPathFirst, realPath.c_str());
|
||||||
|
return strdup(realPath.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug("reloc_casepath(%s, %d) failed (wd=%s)\n", path, checkPathFirst, CFileMgr::GetWorkingDir());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *
|
||||||
|
reloc_fcaseopen(char const *filename, char const *mode)
|
||||||
|
{
|
||||||
|
FILE *result;
|
||||||
|
char *real = reloc_casepath(filename);
|
||||||
|
if (real == NULL) {
|
||||||
|
result = fopen(filename, mode);
|
||||||
|
} else {
|
||||||
|
result = fopen(real, mode);
|
||||||
|
free(real);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined _WIN32
|
||||||
|
#ifdef WITHWINDOWS
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#include "crossplatform.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RELOCATABLE
|
||||||
|
|
||||||
|
void reloc_initialize(void);
|
||||||
|
|
||||||
|
FILE *reloc_fopen(const char *path, const char *mode);
|
||||||
|
const char *reloc_realpath(const char *filename);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define reloc_casepath(PATH, CHECKFIRST) reloc_realpath(PATH)
|
||||||
|
#define reloc_fcaseopen reloc_fopen
|
||||||
|
#else
|
||||||
|
char *reloc_casepath(char const *path, bool checkPathFirst = true);
|
||||||
|
FILE *reloc_fcaseopen(char const *filename, char const *mode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined WITHWINDOWS
|
||||||
|
HANDLE reloc_FindFirstFile(const char *pathName, WIN32_FIND_DATA *findFileData);
|
||||||
|
bool reloc_FindNextFile(HANDLE hFind, WIN32_FIND_DATA *findFileData);
|
||||||
|
bool reloc_FindClose(HANDLE hFind);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define reloc_initialize()
|
||||||
|
|
||||||
|
#define reloc_fopen(path, mode) fopen(path, mode)
|
||||||
|
#define reloc_realpath(filename) (filename)
|
||||||
|
|
||||||
|
#define reloc_casepath casepath
|
||||||
|
#define reloc_fcaseopen fcaseopen
|
||||||
|
|
||||||
|
#define reloc_FindFirstFile FindFirstFile
|
||||||
|
#define reloc_FindNextFile FindNextFile
|
||||||
|
#define reloc_FindClose FindClose
|
||||||
|
|
||||||
|
#endif
|
Before Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 21 KiB |