Make re3 relocatable
#967
madebr/re3_conan_libsndfile
into master
@ -1,82 +1,152 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
set(EXECUTABLE re3)
|
||||
set(PROJECT RE3)
|
||||
set(STEAMID 12100)
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
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")
|
||||
|
||||
if(WIN32)
|
||||
set(${PROJECT}_AUDIOS "OAL" "MSS")
|
||||
set(RE3_AUDIOS "OAL" "MSS")
|
||||
else()
|
||||
set(${PROJECT}_AUDIOS "OAL")
|
||||
set(RE3_AUDIOS "OAL")
|
||||
endif()
|
||||
|
||||
set(${PROJECT}_AUDIO "OAL" CACHE STRING "Audio")
|
||||
set(RE3_AUDIO "OAL" CACHE STRING "Audio")
|
||||
|
||||
include(CMakeDependentOption)
|
||||
|
||||
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)
|
||||
|
||||
option(${PROJECT}_WITH_OPUS "Build ${EXECUTABLE} with opus support" OFF)
|
||||
option(${PROJECT}_WITH_LIBSNDFILE "Build ${EXECUTABLE} with libsndfile (instead of internal decoder)" 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})
|
||||
message(STATUS "${PROJECT}_AUDIO = ${${PROJECT}_AUDIO} (choices=${${PROJECT}_AUDIOS})")
|
||||
set("${PROJECT}_AUDIO_${${PROJECT}_AUDIO}" ON)
|
||||
if(NOT ${PROJECT}_AUDIO IN_LIST ${PROJECT}_AUDIOS)
|
||||
message(FATAL_ERROR "Illegal ${PROJECT}_AUDIO=${${PROJECT}_AUDIO}")
|
||||
set_property(CACHE RE3_AUDIO PROPERTY STRINGS ${RE3_AUDIOS})
|
||||
message(STATUS "RE3_AUDIO = ${RE3_AUDIO} (choices=${RE3_AUDIOS})")
|
||||
set("RE3_AUDIO_${RE3_AUDIO}" ON)
|
||||
if(NOT RE3_AUDIO IN_LIST RE3_AUDIOS)
|
||||
message(FATAL_ERROR "Illegal RE3_AUDIO=${RE3_AUDIO}")
|
||||
endif()
|
||||
|
||||
option(${PROJECT}_VENDORED_LIBRW "Use vendored librw" ON)
|
||||
if(${PROJECT}_VENDORED_LIBRW)
|
||||
option(RE3_VENDORED_LIBRW "Use vendored librw" ON)
|
||||
if(RE3_VENDORED_LIBRW)
|
||||
add_subdirectory(vendor/librw)
|
||||
else()
|
||||
find_package(librw REQUIRED)
|
||||
endif()
|
||||
add_subdirectory(src)
|
||||
set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT ${EXECUTABLE})
|
||||
|
||||
if(${PROJECT}_INSTALL)
|
||||
install(DIRECTORY gamefiles/ DESTINATION ".")
|
||||
if(LIBRW_PLATFORM_NULL)
|
||||
set(platform "-null")
|
||||
elseif(LIBRW_PLATFORM_PS2)
|
||||
set(platform "-ps2")
|
||||
elseif(LIBRW_PLATFORM_GL3)
|
||||
if(LIBRW_GL3_GFXLIB STREQUAL "GLFW")
|
||||
set(platform "-gl3-glfw")
|
||||
else()
|
||||
set(platform "-gl3-sdl2")
|
||||
endif()
|
||||
elseif(LIBRW_PLATFORM_D3D9)
|
||||
set(platform "-d3d9")
|
||||
endif()
|
||||
if(${PROJECT}_AUDIO_OAL)
|
||||
set(audio "-oal")
|
||||
elseif(${PROJECT}_AUDIO_MSS)
|
||||
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()
|
||||
if(RE3_INSTALL)
|
||||
install(DIRECTORY gamefiles/ DESTINATION "." COMPONENT comp2_gamedata)
|
||||
configure_file(cmake/CPackProjectConfig.cmake.in CPackProjectConfig.cmake @ONLY)
|
||||
#file(GENERATE OUTPUT CPackProjectConfig.cmake INPUT "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CPackProjectConfig.cmake.in" TARGET ${EXECUTABLE})
|
||||
set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/dist")
|
||||
|
||||
if(RE3_INSTALL_SRC)
|
||||
install(
|
||||
DIRECTORY cmake gamefiles resources src utils
|
||||
DESTINATION .
|
||||
COMPONENT compZ_sources
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
install(
|
||||
FILES conanfile.py CMakeLists.txt premake5.lua README.md CODING_STYLE.md
|
||||
DESTINATION .
|
||||
COMPONENT compZ_sources
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
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_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)
|
||||
# set(CPACK_PACKAGE_DESCRIPTION_FILE "${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")
|
||||
# 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)
|
||||
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 |