mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #5458 from OpenRCT2/refactor/nosdl/phase-1b
Remove SDL2 dependencies: Phase 1
This commit is contained in:
commit
ab8e8952e0
|
@ -1,7 +1,7 @@
|
|||
language: c
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_OS_NAME == "linux" ]]; then bash scripts/linux/install.sh; export OPENRCT_MAKE_OPTS="-j2 -k" ; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "linux" ]]; then bash scripts/linux/install.sh; export OPENRCT_MAKE_OPTS="-j2 -k all openrct2-cli" ; fi
|
||||
- if [[ $TRAVIS_OS_NAME == "osx" ]]; then sudo gem install xcpretty-travis-formatter; fi
|
||||
|
||||
sudo: required
|
||||
|
@ -16,13 +16,13 @@ matrix:
|
|||
services:
|
||||
- docker
|
||||
env:
|
||||
- OPENRCT2_CMAKE_OPTS="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=OpenRCT2" TARGET=ubuntu_amd64
|
||||
- OPENRCT2_CMAKE_OPTS="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=OpenRCT2 -DPORTABLE=ON" TARGET=ubuntu_amd64
|
||||
- secure: "S3u2VCE2Vy8KNXoeh+DhnzjCmgTX0r95uEZrXDU+IKANOOCKn7Dg4OFDZE3LY/i1y2/EUDpnR5yLC38Ks795EUP/sv/OoMl4tjQ20yERjqWh+gcIRrgx7SdVabuAh3t4aBdaLD4Pfnj5avxeCt6rL7yGnj0wdbrbJSBZPsgSnuQ="
|
||||
after_success:
|
||||
- sudo chown -R $USER build
|
||||
- cd build
|
||||
# make install is done inside docker
|
||||
- mv OpenRCT2/bin/openrct2 OpenRCT2/ && mv OpenRCT2/share/openrct2 OpenRCT2/data && mv OpenRCT2/share/doc/openrct2 OpenRCT2/doc
|
||||
- mv OpenRCT2/bin/openrct2 OpenRCT2/ && mv OpenRCT2/bin/libopenrct2.so OpenRCT2/ && mv OpenRCT2/share/openrct2 OpenRCT2/data && mv OpenRCT2/share/doc/openrct2 OpenRCT2/doc
|
||||
- rm -rf OpenRCT2/bin OpenRCT2/share # remove empty dirs
|
||||
- tar cvzf openrct2-linux.tar.gz OpenRCT2/
|
||||
- if [[ "z${TRAVIS_TAG}" != "z" ]] ; then
|
||||
|
|
|
@ -7,10 +7,15 @@
|
|||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/openrct2",
|
||||
"args": [],
|
||||
"stopAtEntry": true,
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}/bin",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"setupCommands": [
|
||||
{
|
||||
"text": "-enable-pretty-printing"
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"MIMode": "gdb"
|
||||
},
|
||||
|
@ -27,6 +32,11 @@
|
|||
"request": "attach",
|
||||
"program": "${workspaceRoot}/bin/openrct2",
|
||||
"processId": "${command.pickProcess}",
|
||||
"setupCommands": [
|
||||
{
|
||||
"text": "-enable-pretty-printing"
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"MIMode": "gdb"
|
||||
},
|
||||
|
|
497
CMakeLists.txt
497
CMakeLists.txt
|
@ -1,484 +1,103 @@
|
|||
# CMAKE project for openrct2
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
#
|
||||
# Execute these commands in this directory:
|
||||
#
|
||||
# 1. mkdir build/; cd build/
|
||||
#
|
||||
# 2. Choose compiler:
|
||||
# Build with native toolchain:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
#
|
||||
# Build with mingw:
|
||||
# cmake -DCMAKE_TOOLCHAIN_FILE=../CMakeLists_mingw.txt -DCMAKE_BUILD_TYPE=Debug ..
|
||||
#
|
||||
# 3. make
|
||||
#
|
||||
|
||||
|
||||
# project title
|
||||
set (PROJECT openrct2)
|
||||
# OpenRCT2 resource directory
|
||||
set (ORCT2_RESOURCE_DIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/)
|
||||
|
||||
project(${PROJECT})
|
||||
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif()
|
||||
add_definitions(-DORCT2_RESOURCE_DIR="${ORCT2_RESOURCE_DIR}")
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
set(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
set(CMAKE_MACOSX_RPATH 1)
|
||||
|
||||
# Define current git branch.
|
||||
set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v0.0.5/title-sequence-v0.0.5.zip")
|
||||
set(TITLE_SEQUENCE_SHA1 "79ffb2585d12abcbfce205d7696e3472a504b005")
|
||||
|
||||
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
|
||||
option(WITH_TESTS "Build tests")
|
||||
option(PORTABLE "Create a portable build (-rpath=$ORIGIN)" OFF)
|
||||
|
||||
# Define current git branch
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${ROOT_DIR}
|
||||
OUTPUT_VARIABLE OPENRCT2_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
add_definitions(-DOPENRCT2_BRANCH="${OPENRCT2_BRANCH}")
|
||||
|
||||
# Define commit hash.
|
||||
# Define commit hash
|
||||
execute_process(
|
||||
COMMAND git rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${ROOT_DIR}
|
||||
OUTPUT_VARIABLE OPENRCT2_COMMIT_SHA1
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
add_definitions(-DOPENRCT2_COMMIT_SHA1="${OPENRCT2_COMMIT_SHA1}")
|
||||
|
||||
# Define short commit hash.
|
||||
# Define short commit hash
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${ROOT_DIR}
|
||||
OUTPUT_VARIABLE OPENRCT2_COMMIT_SHA1_SHORT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Tagged builds are not meant to display commit info
|
||||
if (NOT OPENRCT2_COMMIT_SHA1_SHORT STREQUAL "HEAD")
|
||||
add_definitions(-DOPENRCT2_COMMIT_SHA1_SHORT="${OPENRCT2_COMMIT_SHA1_SHORT}")
|
||||
endif()
|
||||
|
||||
# Convenience functions to set compiler flags only if available
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
# Include sub-projects
|
||||
include("${ROOT_DIR}/src/openrct2/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||
include("${ROOT_DIR}/src/openrct2-cli/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||
include("${ROOT_DIR}/src/openrct2-ui/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||
|
||||
function(ADD_CHECK_C_COMPILER_FLAG
|
||||
_CFLAGS
|
||||
_CACHE_VAR
|
||||
_FLAG
|
||||
)
|
||||
# g2
|
||||
add_custom_command(
|
||||
OUTPUT g2.dat
|
||||
COMMAND ./openrct2 sprite build ${CMAKE_BINARY_DIR}/g2.dat ${ROOT_DIR}/resources/g2/sprites.json
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(g2 DEPENDS ${PROJECT} g2.dat)
|
||||
|
||||
CHECK_C_COMPILER_FLAG("${_FLAG}" "${_CACHE_VAR}")
|
||||
if(${_CACHE_VAR})
|
||||
# message(STATUS "Using CFLAG: ${_FLAG}")
|
||||
set(${_CFLAGS} "${${_CFLAGS}} ${_FLAG}" PARENT_SCOPE)
|
||||
else()
|
||||
message(STATUS "Unsupported CFLAG: ${_FLAG}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(ADD_CHECK_CXX_COMPILER_FLAG
|
||||
_CXXFLAGS
|
||||
_CACHE_VAR
|
||||
_FLAG
|
||||
)
|
||||
|
||||
CHECK_CXX_COMPILER_FLAG("${_FLAG}" "${_CACHE_VAR}")
|
||||
if(${_CACHE_VAR})
|
||||
# message(STATUS "Using CXXFLAG: ${_FLAG}")
|
||||
set(${_CXXFLAGS} "${${_CXXFLAGS}} ${_FLAG}" PARENT_SCOPE)
|
||||
else()
|
||||
message(STATUS "Unsupported CXXFLAG: ${_FLAG}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# pkg-config
|
||||
INCLUDE(FindPkgConfig)
|
||||
|
||||
# Needed for linking with non-broken OpenSSL on Apple platforms
|
||||
if (APPLE)
|
||||
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl/lib/pkgconfig")
|
||||
endif (APPLE)
|
||||
|
||||
# Options
|
||||
|
||||
option(DISABLE_HTTP_TWITCH "Disable HTTP and Twitch support.")
|
||||
option(DISABLE_NETWORK "Disable multiplayer functionality. Mainly for testing.")
|
||||
option(STATIC "Create a static build.")
|
||||
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags")
|
||||
option(DISABLE_OPENGL "Disable OpenGL support.")
|
||||
option(DISABLE_RCT2 "Build a standalone version, without using code and data segments from vanilla. On by default." ON)
|
||||
option(USE_MMAP "Use mmap to try loading rct2's data segment into memory.")
|
||||
option(WITH_TESTS "Build tests")
|
||||
option(DISABLE_TTF "Disable support for TTF provided by SDL2_ttf.")
|
||||
option(ENABLE_LIGHTFX "Enable lighting effects." ON)
|
||||
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fstrict-aliasing -Werror -Wundef -Wmissing-declarations -Winit-self -Wall -Wno-unknown-pragmas -Wno-unused-function -Wno-missing-braces ")
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -Wno-comment -Wshadow -Wmissing-declarations -Wnonnull")
|
||||
|
||||
# macOS builds fail on the use of tmpnam otherwise (#4959)
|
||||
if(APPLE)
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -Wno-error=deprecated-declarations -Wno-error=objc-method-access")
|
||||
endif()
|
||||
|
||||
# On mingw all code is already PIC, this will avoid compiler error on redefining this option
|
||||
if(NOT MINGW)
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fPIC")
|
||||
endif()
|
||||
|
||||
if (NOT DISABLE_RCT2)
|
||||
set (FORCE32 ON)
|
||||
message("DISABLE_RCT2 implies FORCE32")
|
||||
endif()
|
||||
|
||||
if (DISABLE_HTTP_TWITCH)
|
||||
add_definitions(-DDISABLE_HTTP -DDISABLE_TWITCH)
|
||||
endif (DISABLE_HTTP_TWITCH)
|
||||
|
||||
if (DISABLE_TTF)
|
||||
add_definitions(-DNO_TTF)
|
||||
endif (DISABLE_TTF)
|
||||
|
||||
if (ENABLE_LIGHTFX)
|
||||
add_definitions(-D__ENABLE_LIGHTFX__)
|
||||
endif (ENABLE_LIGHTFX)
|
||||
|
||||
# Launchpad turns on -Wdate-time for compilers that support it, this shouldn't break our build
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_WRITE_STRINGS -Wno-error=date-time)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_WRITE_STRINGS -Wno-error=date-time)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_NULL_DEREFERENCE -Wnull-dereference)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_FINAL_TYPES -Wsuggest-final-types)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_FINAL_METHODS -Wsuggest-final-methods)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_OVERRIDE -Wsuggest-override)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_DUPLICATED_COND -Wduplicated-cond)
|
||||
if (MINGW)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_OVERRIDE -Wno-error=redundant-decls)
|
||||
endif ()
|
||||
# Items below are not supported by ICC
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_REDUNDANT_DECLS -Wredundant-decls)
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_IGNORED_QUALIFIERS -Wignored-qualifiers)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_REDUNDANT_DECLS -Wredundant-decls)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_IGNORED_QUALIFIERS -Wignored-qualifiers)
|
||||
|
||||
# -Wstrict-overflow is only active when -fstrict-overflow is enabled, but -fstrict-overflow
|
||||
# is enabled on -O2, -O3, -Os. This should help catch bugs locally before they reach Travis
|
||||
# As of 2a435bf -Wstrict-overflow=1 passes, but higher values do not.
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fstrict-overflow")
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_STRICT_OVERFLOW -Wstrict-overflow=1)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_STRICT_OVERFLOW -Wstrict-overflow=1)
|
||||
|
||||
if (FORCE32)
|
||||
set(TARGET_M "-m32")
|
||||
endif()
|
||||
|
||||
if (FORCE32)
|
||||
set(OBJ_FORMAT "elf32-i386")
|
||||
set(LINKER_SCRIPT "ld_script_i386.xc")
|
||||
endif ()
|
||||
|
||||
if (DISABLE_OPENGL)
|
||||
add_definitions(-DDISABLE_OPENGL)
|
||||
else (DISABLE_OPENGL)
|
||||
# Makes OpenGL function get queried in run-time rather than linked-in
|
||||
add_definitions(-DOPENGL_NO_LINK)
|
||||
endif (DISABLE_OPENGL)
|
||||
|
||||
if (USE_MMAP)
|
||||
add_definitions(-DUSE_MMAP)
|
||||
endif (USE_MMAP)
|
||||
|
||||
if (DISABLE_NETWORK)
|
||||
add_definitions(-DDISABLE_NETWORK)
|
||||
else (DISABLE_NETWORK)
|
||||
if (WIN32)
|
||||
SET(NETWORKLIBS ${NETWORKLIBS} ws2_32)
|
||||
endif (WIN32)
|
||||
# If you are on macOS, CMake might try using system-provided OpenSSL.
|
||||
# This is too old and will not work.
|
||||
PKG_CHECK_MODULES(SSL REQUIRED openssl>=1.0.0)
|
||||
endif (DISABLE_NETWORK)
|
||||
|
||||
if (DISABLE_RCT2)
|
||||
add_definitions(-DNO_RCT2)
|
||||
endif (DISABLE_RCT2)
|
||||
|
||||
# Start of library checks
|
||||
|
||||
PKG_CHECK_MODULES(PNG libpng>=1.6)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG libpng16)
|
||||
endif (NOT PNG_FOUND)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG libpng>=1.2)
|
||||
endif (NOT PNG_FOUND)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG REQUIRED libpng12)
|
||||
endif (NOT PNG_FOUND)
|
||||
|
||||
PKG_CHECK_MODULES(ZLIB REQUIRED zlib)
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.5)
|
||||
|
||||
# Handle creating the rct2 text and data files on macOS and Linux
|
||||
# See details in src/openrct2/rct2/interop.c:rct2_interop_setup_segment for how the values
|
||||
# were derived.
|
||||
if ((NOT DISABLE_RCT2) AND UNIX)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_text
|
||||
COMMAND dd if="${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe" of="${CMAKE_BINARY_DIR}/openrct2_text" bs=4096 skip=1 count=1187
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_data
|
||||
COMMAND dd if="${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1188 count=318
|
||||
COMMAND dd if=/dev/zero of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 seek=318 count=2630 conv=notrunc
|
||||
COMMAND dd if="${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1506 seek=2948 count=1 conv=notrunc
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/openrct2.exe
|
||||
)
|
||||
add_custom_target(segfiles DEPENDS openrct2_text openrct2_data)
|
||||
if (NOT USE_MMAP)
|
||||
if (APPLE)
|
||||
set(RCT2_SEGMENT_LINKER_FLAGS "-sectcreate rct2_text __text ${CMAKE_BINARY_DIR}/openrct2_text -sectcreate rct2_data __data ${CMAKE_BINARY_DIR}/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -read_only_relocs suppress")
|
||||
else (APPLE)
|
||||
# For Linux we have to use objcopy to wrap regular binaries into a linkable
|
||||
# format. We use specific section names which are then referenced in a
|
||||
# bespoke linker script so they can be placed at predefined VMAs.
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_text_section.o
|
||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code
|
||||
DEPENDS segfiles
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_data_section.o
|
||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data
|
||||
DEPENDS segfiles
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o)
|
||||
SET_SOURCE_FILES_PROPERTIES(
|
||||
openrct2_text_section.o openrct2_data_section.o
|
||||
PROPERTIES
|
||||
EXTERNAL_OBJECT true
|
||||
GENERATED true
|
||||
)
|
||||
# can't use GLOB here, as the files don't exist yet at cmake-time
|
||||
set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o")
|
||||
set(RCT2_SEGMENT_LINKER_FLAGS "-Wl,-T,\"${CMAKE_CURRENT_SOURCE_DIR}/distribution/linux/${LINKER_SCRIPT}\"")
|
||||
endif (APPLE)
|
||||
endif (NOT USE_MMAP)
|
||||
elseif (USE_MMAP)
|
||||
# No dd here, can't extract data segment
|
||||
message(WARNING "Sorry, your platform is not supported, you have to extract data segment manually")
|
||||
endif ((NOT DISABLE_RCT2) AND UNIX)
|
||||
set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in range 0–3.")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
|
||||
# include lib
|
||||
include_directories("lib/")
|
||||
# add source files
|
||||
file(GLOB_RECURSE ORCT2_SOURCES "src/openrct2/*.c" "src/openrct2/*.cpp" "src/openrct2/*.h" "src/openrct2/*.hpp")
|
||||
if (APPLE)
|
||||
file(GLOB_RECURSE ORCT2_MM_SOURCES "src/openrct2/*.m")
|
||||
set_source_files_properties(${ORCT2_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c -fmodules")
|
||||
endif (APPLE)
|
||||
|
||||
|
||||
if (APPLE AND NOT USE_MMAP)
|
||||
set(PIE_FLAG "-fno-pie")
|
||||
else ()
|
||||
set(PIE_FLAG "-fpie")
|
||||
endif ()
|
||||
|
||||
# set necessary flags to compile code as is
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_M} -std=gnu11 ${COMMON_COMPILE_OPTIONS} -Wimplicit")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TARGET_M} -std=gnu++14 ${COMMON_COMPILE_OPTIONS} -Wnon-virtual-dtor")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TARGET_M}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS} ${PIE_FLAG}")
|
||||
|
||||
if (MINGW)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++")
|
||||
endif ()
|
||||
|
||||
option(WITH_BREAKPAD "Enable breakpad")
|
||||
if (WITH_BREAKPAD)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_BREAKPAD")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BREAKPAD")
|
||||
set(BREAKPAD_DIR "/home/janisozaur/workspace/breakpad/src")
|
||||
set(BREAKPAD_INCLUDE_DIR "${BREAKPAD_DIR}/src")
|
||||
set(BREAKPAD_LIBRARY_DIR "${BREAKPAD_DIR}/src/client/linux")
|
||||
set(BREAKPAD_LIBS breakpad_client pthread)
|
||||
endif (WITH_BREAKPAD)
|
||||
|
||||
PKG_CHECK_MODULES(LIBZIP REQUIRED libzip>=1.0)
|
||||
# find and include SDL2
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
|
||||
if (NOT DISABLE_TTF)
|
||||
PKG_CHECK_MODULES(SDL2_TTF REQUIRED SDL2_ttf)
|
||||
endif (NOT DISABLE_TTF)
|
||||
|
||||
if (STATIC)
|
||||
if (NOT DISABLE_TTF)
|
||||
# FreeType is required by SDL2_ttf, but not wired up properly in package
|
||||
PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2)
|
||||
endif (NOT DISABLE_TTF)
|
||||
SET(SDL2LIBS ${SDL2_STATIC_LIBRARIES} ${SDL2_TTF_STATIC_LIBRARIES} ${FREETYPE_STATIC_LIBRARIES})
|
||||
else (STATIC)
|
||||
SET(SDL2LIBS ${SDL2_LIBRARIES} ${SDL2_TTF_LIBRARIES})
|
||||
endif (STATIC)
|
||||
|
||||
if (STATIC)
|
||||
set(STATIC_START "-static")
|
||||
SET(REQUIREDLIBS ${PNG_STATIC_LIBRARIES} ${JANSSON_STATIC_LIBRARIES} ${ZLIB_STATIC_LIBRARIES} ${SSL_STATIC_LIBRARIES} ${LIBZIP_STATIC_LIBRARIES})
|
||||
else (STATIC)
|
||||
SET(REQUIREDLIBS ${PNG_LIBRARIES} ${JANSSON_LIBRARIES} ${ZLIB_LIBRARIES} ${SSL_LIBRARIES} ${LIBZIP_LIBRARIES})
|
||||
endif (STATIC)
|
||||
|
||||
if (NOT DISABLE_OPENGL)
|
||||
if (WIN32)
|
||||
# Curl depends on openssl and ws2 in mingw builds, but is not wired up in pkg-config
|
||||
set(GLLIBS opengl32)
|
||||
# mingw complains about "%zu" not being a valid format specifier for printf, unless we
|
||||
# tell it that it is
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__USE_MINGW_ANSI_STDIO=1")
|
||||
elseif (APPLE)
|
||||
# GL doesn't work nicely with macOS, while find_package doesn't work with multiarch on Ubuntu.
|
||||
find_package(OpenGL REQUIRED)
|
||||
set(GLLIBS ${OPENGL_LIBRARY})
|
||||
else (WIN32)
|
||||
PKG_CHECK_MODULES(GL REQUIRED gl)
|
||||
set(GLLIBS ${GL_LIBRARIES})
|
||||
endif (WIN32)
|
||||
endif (NOT DISABLE_OPENGL)
|
||||
|
||||
if (NOT DISABLE_HTTP_TWITCH)
|
||||
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
|
||||
if (WIN32)
|
||||
# Curl depends on openssl and ws2 in mingw builds, but is not wired up in pkg-config
|
||||
set(WSLIBS ws2_32)
|
||||
endif (WIN32)
|
||||
if (STATIC)
|
||||
SET(HTTPLIBS ${LIBCURL_STATIC_LIBRARIES} ${WSLIBS})
|
||||
else (STATIC)
|
||||
SET(HTTPLIBS ${LIBCURL_LIBRARIES} ${WSLIBS})
|
||||
endif (STATIC)
|
||||
endif (NOT DISABLE_HTTP_TWITCH)
|
||||
|
||||
PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp)
|
||||
|
||||
if (UNIX AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
# Include libdl for dlopen
|
||||
set(DLLIB dl)
|
||||
endif ()
|
||||
|
||||
# libzip library has issues with some of the enabled warning, include it as system library
|
||||
INCLUDE_DIRECTORIES(SYSTEM ${LIBZIP_INCLUDE_DIRS})
|
||||
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIRS} ${LIBCURL_INCLUDE_DIRS} ${JANSSON_INCLUDE_DIRS} ${SPEEX_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIR} ${SSL_INCLUDE_DIRS})
|
||||
|
||||
LINK_DIRECTORIES(${SDL2_LIBRARY_DIRS} ${JANSSON_LIBRARY_DIRS} ${LIBCURL_LIBRARY_DIRS} ${PNG_LIBRARY_DIRS} ${ZLIB_LIBRARY_DIRS} ${BREAKPAD_LIBRARY_DIR} ${SSL_LIBRARY_DIRS} ${LIBZIP_LIBRARY_DIRS})
|
||||
|
||||
if (NOT DISABLE_RCT2)
|
||||
# Disable optimizations for addresses.c for all compilers, to allow optimized
|
||||
# builds without need for -fno-omit-frame-pointer
|
||||
set_source_files_properties(src/openrct2/addresses.c PROPERTIES COMPILE_FLAGS -O0)
|
||||
endif (NOT DISABLE_RCT2)
|
||||
|
||||
if (WIN32)
|
||||
# build as library for now, replace with add_executable
|
||||
if (USE_MMAP OR DISABLE_RCT2)
|
||||
add_executable(${PROJECT} ${ORCT2_SOURCES} ${SPEEX_SOURCES})
|
||||
else ()
|
||||
add_library(${PROJECT} SHARED ${ORCT2_SOURCES} ${SPEEX_SOURCES})
|
||||
# Include tests
|
||||
if (WITH_TESTS)
|
||||
enable_testing()
|
||||
if (UNIX AND (NOT USE_MMAP) AND (NOT DISABLE_RCT2) AND (FORCE32))
|
||||
include("${ROOT_DIR}/test/testpaint/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||
endif ()
|
||||
else (WIN32)
|
||||
add_executable(${PROJECT} ${ORCT2_SOURCES} ${ORCT2_MM_SOURCES} ${RCT2_SECTIONS})
|
||||
if (NOT DISABLE_RCT2)
|
||||
add_dependencies(${PROJECT} segfiles)
|
||||
if (NOT APPLE AND NOT USE_MMAP)
|
||||
add_dependencies(${PROJECT} linkable_sections)
|
||||
endif ()
|
||||
set_target_properties(${PROJECT} PROPERTIES LINK_FLAGS ${RCT2_SEGMENT_LINKER_FLAGS})
|
||||
endif (NOT DISABLE_RCT2)
|
||||
add_custom_command(
|
||||
OUTPUT g2.dat
|
||||
COMMAND ./openrct2 sprite build ${CMAKE_BINARY_DIR}/g2.dat ${CMAKE_CURRENT_SOURCE_DIR}/resources/g2/sprites.json
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(g2 DEPENDS ${PROJECT} g2.dat)
|
||||
endif (WIN32)
|
||||
|
||||
if (UNIX AND NOT APPLE AND NOT DISABLE_TTF)
|
||||
# FontConfig for TrueType fonts.
|
||||
PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig)
|
||||
INCLUDE_DIRECTORIES(${FONTCONFIG_INCLUDE_DIRS})
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${FONTCONFIG_LIBRARIES})
|
||||
endif (UNIX AND NOT APPLE AND NOT DISABLE_TTF)
|
||||
|
||||
|
||||
# install into ${CMAKE_INSTALL_PREFIX}/bin/
|
||||
#install (TARGETS ${PROJECT} DESTINATION bin)
|
||||
|
||||
# libopenrct2.dll -> openrct2.dll
|
||||
set_target_properties(${PROJECT} PROPERTIES PREFIX "")
|
||||
set_target_properties(${PROJECT} PROPERTIES COMPILE_FLAGS "-Wundef")
|
||||
|
||||
# Link shared libs first
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} pthread ${GLLIBS})
|
||||
# if creating a static binary, precede libraries with -static, then name all the libs
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${STATIC_START} ${SDL2LIBS} ${HTTPLIBS} ${NETWORKLIBS} ${SPEEX_LIBRARIES} ${DLLIB} ${REQUIREDLIBS} ${BREAKPAD_LIBS})
|
||||
|
||||
if (APPLE OR STATIC OR ${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c)
|
||||
TARGET_LINK_LIBRARIES(${PROJECT} ${ICONV_LIBRARIES})
|
||||
include("${ROOT_DIR}/test/tests/CMakeLists.txt" NO_POLICY_SCOPE)
|
||||
endif ()
|
||||
|
||||
# Install
|
||||
# Don't recurse, grab all *.txt and *.md files
|
||||
file(GLOB DOC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/distribution/*.txt")
|
||||
list(APPEND DOC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/contributors.md" "${CMAKE_CURRENT_SOURCE_DIR}/licence.txt")
|
||||
file(GLOB DOC_FILES "${ROOT_DIR}/distribution/*.txt")
|
||||
list(APPEND DOC_FILES "${ROOT_DIR}/contributors.md"
|
||||
"${ROOT_DIR}/licence.txt")
|
||||
|
||||
# CMake does not allow specifying a dependency chain which includes built-in
|
||||
# targets, like `install`, so we have to trick it and execute dependency ourselves.
|
||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_CURRENT_BINARY_DIR}\" --target g2)")
|
||||
install(CODE "file(DOWNLOAD https://github.com/OpenRCT2/title-sequences/releases/download/v0.0.5/title-sequence-v0.0.5.zip \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/title/title-sequences.zip EXPECTED_HASH SHA1=79ffb2585d12abcbfce205d7696e3472a504b005 SHOW_PROGRESS)")
|
||||
install(CODE "file(DOWNLOAD ${TITLE_SEQUENCE_URL} \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/title/title-sequences.zip EXPECTED_HASH SHA1=${TITLE_SEQUENCE_SHA1} SHOW_PROGRESS)")
|
||||
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E chdir \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/title/ \"${CMAKE_COMMAND}\" -E tar xvf title-sequences.zip)")
|
||||
install(CODE "file(REMOVE \$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/title/title-sequences.zip)")
|
||||
install(TARGETS ${PROJECT} RUNTIME DESTINATION bin)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/g2.dat" DESTINATION share/${PROJECT})
|
||||
install(DIRECTORY data/ DESTINATION share/${PROJECT})
|
||||
install(FILES ${DOC_FILES} DESTINATION share/doc/${PROJECT})
|
||||
|
||||
install(FILES resources/logo/icon_x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME openrct2.png)
|
||||
install(FILES resources/logo/icon_x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME openrct2.png)
|
||||
install(FILES resources/logo/icon_x64.png DESTINATION share/icons/hicolor/64x64/apps RENAME openrct2.png)
|
||||
install(FILES resources/logo/icon_x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME openrct2.png)
|
||||
install(FILES resources/logo/icon_x256.png DESTINATION share/icons/hicolor/256x256/apps RENAME openrct2.png)
|
||||
install(FILES resources/logo/icon_flag.svg DESTINATION share/icons/hicolor/scalable/apps RENAME openrct2.svg)
|
||||
install(FILES distribution/linux/openrct2.desktop DESTINATION share/applications)
|
||||
|
||||
if (WITH_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/test/tests/)
|
||||
if (WIN32)
|
||||
install(TARGETS "libopenrct2" RUNTIME DESTINATION "bin")
|
||||
else ()
|
||||
if (PORTABLE)
|
||||
install(TARGETS "libopenrct2" LIBRARY DESTINATION "bin")
|
||||
else ()
|
||||
install(TARGETS "libopenrct2" LIBRARY DESTINATION "lib")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (UNIX AND (NOT USE_MMAP) AND (NOT DISABLE_RCT2) AND (FORCE32))
|
||||
set(OPENRCT2_SRCPATH "src/openrct2")
|
||||
file(GLOB_RECURSE ORCT2_RIDE_SOURCES "${OPENRCT2_SRCPATH}/ride/*/*.c")
|
||||
file(GLOB_RECURSE ORCT2_RIDE_DEP_SOURCES "${OPENRCT2_SRCPATH}/ride/ride_data.c" "${OPENRCT2_SRCPATH}/ride/track_data.c" "${OPENRCT2_SRCPATH}/ride/track_data_old.c" "${OPENRCT2_SRCPATH}/ride/track_paint.c" "${OPENRCT2_SRCPATH}/rct2/addresses.c" "${OPENRCT2_SRCPATH}/diagnostic.c" "${OPENRCT2_SRCPATH}/rct2/hook.c" "${OPENRCT2_SRCPATH}/paint/map_element/map_element.c" "${OPENRCT2_SRCPATH}/paint/paint_helpers.c")
|
||||
file(GLOB_RECURSE ORCT2_TESTPAINT_SOURCES "test/testpaint/*.c" "test/testpaint/*.cpp" "test/testpaint/*.h")
|
||||
|
||||
add_executable(testpaint EXCLUDE_FROM_ALL ${ORCT2_RIDE_SOURCES} ${ORCT2_RIDE_DEP_SOURCES} ${ORCT2_TESTPAINT_SOURCES} ${RCT2_SECTIONS})
|
||||
target_include_directories(testpaint PRIVATE "src/")
|
||||
set_target_properties(testpaint PROPERTIES COMPILE_FLAGS "-DNO_VEHICLES -D__TESTPAINT__ -Wno-unused")
|
||||
set_target_properties(testpaint PROPERTIES LINK_FLAGS ${RCT2_SEGMENT_LINKER_FLAGS})
|
||||
add_dependencies(testpaint segfiles)
|
||||
endif ()
|
||||
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR 0)
|
||||
set(CPACK_PACKAGE_VERSION_MINOR 0)
|
||||
set(CPACK_PACKAGE_VERSION_PATCH 8)
|
||||
INCLUDE(CPack)
|
||||
install(TARGETS "openrct2" RUNTIME DESTINATION "bin")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/g2.dat" DESTINATION "share/openrct2")
|
||||
install(DIRECTORY "data/" DESTINATION "share/openrct2")
|
||||
install(FILES ${DOC_FILES} DESTINATION "share/doc/openrct2")
|
||||
install(FILES "resources/logo/icon_x16.png" DESTINATION "share/icons/hicolor/16x16/apps RENAME openrct2.png")
|
||||
install(FILES "resources/logo/icon_x32.png" DESTINATION "share/icons/hicolor/32x32/apps RENAME openrct2.png")
|
||||
install(FILES "resources/logo/icon_x64.png" DESTINATION "share/icons/hicolor/64x64/apps RENAME openrct2.png")
|
||||
install(FILES "resources/logo/icon_x128.png" DESTINATION "share/icons/hicolor/128x128/apps RENAME openrct2.png")
|
||||
install(FILES "resources/logo/icon_x256.png" DESTINATION "share/icons/hicolor/256x256/apps RENAME openrct2.png")
|
||||
install(FILES "resources/logo/icon_flag.svg" DESTINATION "share/icons/hicolor/scalable/apps RENAME openrct2.svg")
|
||||
install(FILES "distribution/linux/openrct2.desktop" DESTINATION "share/applications")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,11 @@
|
|||
0.0.8 (in development)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: OpenRCT2 now starts up on the display it was last shown on.
|
||||
- Improved: Mouse can now be dragged to select scenery when saving track designs
|
||||
- Fix: [#3178, #5456] Paths with non-ASCII characters not handled properly on macOS.
|
||||
- Fix: [#3681] Steel Twister rollercoaster always shows all track designs
|
||||
- Fix: Track components added by OpenRCT2 are now usable in older scenarios.
|
||||
- Technical: [#5458] Begin offering headless build with reduced compile- and run-time dependencies
|
||||
|
||||
0.0.7 (2017-05-03)
|
||||
------------------------------------------------------------------------
|
||||
|
|
100
openrct2.sln
100
openrct2.sln
|
@ -1,16 +1,18 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26228.9
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openrct2-dll", "src\openrct2-dll\openrct2-dll.vcxproj", "{7B8DB129-79EF-417E-B372-8A18E009D261}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951} = {8DD8AB7D-2EA6-44E3-8265-BAF08E832951}
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {D24D94F6-2A74-480C-B512-629C306CE92F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openrct2-win", "src\openrct2-win\openrct2-win.vcxproj", "{7A9A57D5-7006-4208-A290-5491BA3C8808}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261} = {7B8DB129-79EF-417E-B372-8A18E009D261}
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951} = {8DD8AB7D-2EA6-44E3-8265-BAF08E832951}
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {D24D94F6-2A74-480C-B512-629C306CE92F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
|
@ -27,6 +29,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2202A816-377
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{480B577D-4E4A-4757-9A42-28A9AD33E6B0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libopenrct2ui", "src\openrct2-ui\libopenrct2ui.vcxproj", "{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {D24D94F6-2A74-480C-B512-629C306CE92F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openrct2-cli", "src\openrct2-cli\openrct2-cli.vcxproj", "{B6808F71-30B4-4499-8FF6-0B1C86391842}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {D24D94F6-2A74-480C-B512-629C306CE92F}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
|
@ -35,35 +47,6 @@ Global
|
|||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|x64.Build.0 = Debug|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.Build.0 = Release|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|x64.ActiveCfg = Release|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|x64.Build.0 = Release|x64
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Release|x64.ActiveCfg = Release|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|x64.Build.0 = Debug|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|Win32.Build.0 = Release|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|x64.ActiveCfg = Release|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|x64.Build.0 = Release|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|x64.Build.0 = Debug|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|Win32.Build.0 = Release|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|x64.ActiveCfg = Release|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|x64.Build.0 = Release|x64
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Debug|x64.ActiveCfg = Debug|x64
|
||||
|
@ -72,15 +55,62 @@ Global
|
|||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Release|Win32.Build.0 = Release|Win32
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Release|x64.ActiveCfg = Release|x64
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261}.Release|x64.Build.0 = Release|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Debug|x64.Build.0 = Debug|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|Win32.Build.0 = Release|Win32
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|x64.ActiveCfg = Release|x64
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808}.Release|x64.Build.0 = Release|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Debug|x64.Build.0 = Debug|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|Win32.Build.0 = Release|Win32
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|x64.ActiveCfg = Release|x64
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F}.Release|x64.Build.0 = Release|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Debug|x64.Build.0 = Debug|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|Win32.Build.0 = Release|Win32
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|x64.ActiveCfg = Release|x64
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155}.Release|x64.Build.0 = Release|x64
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327}.Release|x64.ActiveCfg = Release|Win32
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Debug|x64.Build.0 = Debug|x64
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Release|Win32.Build.0 = Release|Win32
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Release|x64.ActiveCfg = Release|x64
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}.Release|x64.Build.0 = Release|x64
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Debug|x64.Build.0 = Debug|x64
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Release|Win32.Build.0 = Release|Win32
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Release|x64.ActiveCfg = Release|x64
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327} = {480B577D-4E4A-4757-9A42-28A9AD33E6B0}
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155} = {480B577D-4E4A-4757-9A42-28A9AD33E6B0}
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{7B8DB129-79EF-417E-B372-8A18E009D261} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{7A9A57D5-7006-4208-A290-5491BA3C8808} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{D24D94F6-2A74-480C-B512-629C306CE92F} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{62B020FA-E4FB-4C6E-B32A-DC999470F155} = {480B577D-4E4A-4757-9A42-28A9AD33E6B0}
|
||||
{57E60BA1-FB76-4316-909E-C1449C142327} = {480B577D-4E4A-4757-9A42-28A9AD33E6B0}
|
||||
{8DD8AB7D-2EA6-44E3-8265-BAF08E832951} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
{B6808F71-30B4-4499-8FF6-0B1C86391842} = {2202A816-377D-4FA0-A7AF-7D4105F8A4FB}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# CMAKE project for openrct2-cli (CLI-only build of OpenRCT2)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif ()
|
||||
|
||||
if (PORTABLE)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
endif ()
|
||||
|
||||
# CMake dependencies
|
||||
include(FindPkgConfig)
|
||||
|
||||
# Third party libraries (which we want to eventually remove from openrct2-cli)
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
|
||||
|
||||
# Sources
|
||||
file(GLOB_RECURSE OPENRCT2_CLI_SOURCES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.hpp")
|
||||
|
||||
# Outputs
|
||||
set (PROJECT openrct2-cli)
|
||||
project(${PROJECT})
|
||||
add_executable(${PROJECT} EXCLUDE_FROM_ALL ${OPENRCT2_CLI_SOURCES})
|
||||
|
||||
target_link_libraries(${PROJECT} "libopenrct2")
|
||||
|
||||
# Includes
|
||||
target_include_directories(${PROJECT} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/.."
|
||||
${SDL2_INCLUDE_DIRS})
|
||||
|
||||
# Compiler flags
|
||||
if (FORCE32)
|
||||
set(TARGET_M "-m32")
|
||||
endif ()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 ${TARGET_M}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 ${TARGET_M}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TARGET_M}")
|
|
@ -0,0 +1,30 @@
|
|||
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/Context.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
/**
|
||||
* Main entry point for non-Windows sytems. Windows instead uses its own DLL proxy.
|
||||
*/
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
IContext * context = CreateContext();
|
||||
int exitCode = context->RunOpenRCT2(argc, argv);
|
||||
delete context;
|
||||
return exitCode;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SolutionDir Condition="'$(SolutionDir)'==''">..\..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{B6808F71-30B4-4499-8FF6-0B1C86391842}</ProjectGuid>
|
||||
<RootNamespace>openrct2-cli</RootNamespace>
|
||||
<ProjectName>openrct2-cli</ProjectName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\openrct2.common.props" />
|
||||
<PropertyGroup>
|
||||
<TargetName>openrct2-cli</TargetName>
|
||||
<LibraryPath>$(SolutionDir)bin;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<ObjectFileName>$(IntDir)\%(RelativeDir)</ObjectFileName>
|
||||
<AdditionalOptions>$(OPENRCT2_CL_ADDITIONALOPTIONS) %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>libopenrct2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ProgramDatabaseFile>$(OutDir)openrct2-cli.pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<TargetMachine Condition="'$(Platform)'=='Win32'">MachineX86</TargetMachine>
|
||||
<TargetMachine Condition="'$(Platform)'=='x64'">MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\resources\OpenRCT2.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Cli.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -20,7 +20,16 @@
|
|||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <openrct2/audio/AudioContext.h>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include <openrct2-ui/audio/AudioContext.h>
|
||||
#include <openrct2-ui/UiContext.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
#define DLLEXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
|
@ -28,6 +37,25 @@ static char * * GetCommandLineArgs(int argc, wchar_t * * argvW);
|
|||
static void FreeCommandLineArgs(int argc, char * * argv);
|
||||
static char * ConvertUTF16toUTF8(const wchar_t * src);
|
||||
|
||||
static int NormalisedMain(int argc, char * * argv)
|
||||
{
|
||||
core_init();
|
||||
int runGame = cmdline_run((const char * *)argv, argc);
|
||||
if (runGame == 1)
|
||||
{
|
||||
IAudioContext * audioContext = CreateAudioContext();
|
||||
IUiContext * uiContext = CreateUiContext();
|
||||
IContext * context = CreateContext(audioContext, uiContext);
|
||||
|
||||
context->RunOpenRCT2(argc, argv);
|
||||
|
||||
delete context;
|
||||
delete uiContext;
|
||||
delete audioContext;
|
||||
}
|
||||
return gExitCode;
|
||||
}
|
||||
|
||||
DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t * * argvW)
|
||||
{
|
||||
char * * argv = GetCommandLineArgs(argc, argvW);
|
||||
|
@ -37,7 +65,8 @@ DLLEXPORT int LaunchOpenRCT2(int argc, wchar_t * * argvW)
|
|||
return -1;
|
||||
}
|
||||
|
||||
int exitCode = RunOpenRCT2(argc, argv);
|
||||
int exitCode = NormalisedMain(argc, argv);
|
||||
|
||||
FreeCommandLineArgs(argc, argv);
|
||||
return exitCode;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<AdditionalOptions>$(OPENRCT2_CL_ADDITIONALOPTIONS) %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>libopenrct2.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>libopenrct2.lib;libopenrct2ui.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ProgramDatabaseFile>$(OutDir)openrct2-dll.pdb</ProgramDatabaseFile>
|
||||
</Link>
|
||||
<Lib>
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# CMAKE project for openrct2-cli (UI build of OpenRCT2)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif ()
|
||||
|
||||
# Options
|
||||
option(DISABLE_OPENGL "Disable OpenGL support.")
|
||||
|
||||
if (PORTABLE)
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
endif ()
|
||||
|
||||
# CMake dependencies
|
||||
include(FindPkgConfig)
|
||||
|
||||
# Third party libraries
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
|
||||
PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp)
|
||||
if (NOT DISABLE_OPENGL)
|
||||
# GL doesn't work nicely with macOS, while find_package doesn't work with multiarch on Ubuntu.
|
||||
if (APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
elseif (NOT WIN32)
|
||||
PKG_CHECK_MODULES(GL REQUIRED gl)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Sources
|
||||
file(GLOB_RECURSE OPENRCT2_UI_SOURCES
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.hpp")
|
||||
|
||||
if (APPLE)
|
||||
file(GLOB_RECURSE OPENRCT2_UI_M_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.m")
|
||||
set_source_files_properties(${OPENRCT2_UI_M_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c -fmodules")
|
||||
|
||||
file(GLOB_RECURSE OPENRCT2_UI_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.mm")
|
||||
set_source_files_properties(${OPENRCT2_UI_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c++ -fmodules")
|
||||
endif ()
|
||||
|
||||
# Outputs
|
||||
set (PROJECT openrct2)
|
||||
project(${PROJECT})
|
||||
add_executable(${PROJECT} ${OPENRCT2_UI_SOURCES} ${OPENRCT2_UI_M_SOURCES} ${OPENRCT2_UI_MM_SOURCES})
|
||||
|
||||
target_link_libraries(${PROJECT} "libopenrct2"
|
||||
${SDL2_LIBRARIES}
|
||||
${SDL2_TTF_LIBRARIES}
|
||||
${SPEEX_LIBRARIES})
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(${PROJECT} "-framework Cocoa")
|
||||
endif ()
|
||||
|
||||
if (NOT DISABLE_OPENGL)
|
||||
if (WIN32)
|
||||
target_link_libraries(${PROJECT} opengl32)
|
||||
elseif (APPLE)
|
||||
target_link_libraries(${PROJECT} ${OPENGL_LIBRARY})
|
||||
else ()
|
||||
target_link_libraries(${PROJECT} ${GL_LIBRARIES})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Includes
|
||||
target_include_directories(${PROJECT} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/.."
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
${SPEEX_INCLUDE_DIRS})
|
||||
|
||||
# Compiler flags
|
||||
if (FORCE32)
|
||||
set(TARGET_M "-m32")
|
||||
endif ()
|
||||
if (WIN32)
|
||||
# mingw complains about "%zu" not being a valid format specifier for printf, unless we
|
||||
# tell it that it is
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__USE_MINGW_ANSI_STDIO=1")
|
||||
endif ()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 ${TARGET_M}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 ${TARGET_M}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TARGET_M}")
|
||||
|
||||
# Defines
|
||||
if (DISABLE_OPENGL)
|
||||
add_definitions(-DDISABLE_OPENGL)
|
||||
else ()
|
||||
# Makes OpenGL function get queried in run-time rather than linked-in
|
||||
add_definitions(-DOPENGL_NO_LINK)
|
||||
endif ()
|
|
@ -14,9 +14,10 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "Cursors.h"
|
||||
#include <openrct2/interface/Cursors.h>
|
||||
#include "CursorRepository.h"
|
||||
|
||||
namespace Cursors
|
||||
namespace OpenRCT2 { namespace Ui
|
||||
{
|
||||
static const CursorData BlankCursorData =
|
||||
{
|
||||
|
@ -654,7 +655,7 @@ namespace Cursors
|
|||
&HandClosedDownCursorData, // CURSOR_HAND_CLOSED
|
||||
};
|
||||
|
||||
const CursorData * GetCursorData(CURSOR_ID cursorId)
|
||||
const CursorData * CursorRepository::GetCursorData(CURSOR_ID cursorId)
|
||||
{
|
||||
const CursorData * result = nullptr;
|
||||
if (cursorId >= 0 && cursorId < CURSOR_COUNT)
|
||||
|
@ -663,4 +664,4 @@ namespace Cursors
|
|||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,80 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/core/Guard.hpp>
|
||||
#include <openrct2/interface/Cursors.h>
|
||||
#include "CursorRepository.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
CursorRepository::~CursorRepository()
|
||||
{
|
||||
for (size_t i = 0; i < CURSOR_COUNT; i++)
|
||||
{
|
||||
SDL_FreeCursor(_loadedCursors[i]);
|
||||
_loadedCursors[i] = nullptr;
|
||||
}
|
||||
_currentCursor = CURSOR_UNDEFINED;
|
||||
}
|
||||
|
||||
void CursorRepository::LoadCursors()
|
||||
{
|
||||
// Using system cursors
|
||||
_loadedCursors[CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
_loadedCursors[CURSOR_HAND_POINT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
// Using custom cursors
|
||||
for (size_t i = 0; i < CURSOR_COUNT; i++)
|
||||
{
|
||||
const CursorData * cursorData = GetCursorData((CURSOR_ID)i);
|
||||
if (cursorData != nullptr)
|
||||
{
|
||||
_loadedCursors[i] = Create(cursorData);
|
||||
}
|
||||
}
|
||||
|
||||
_currentCursor = CURSOR_UNDEFINED;
|
||||
SetCurrentCursor(CURSOR_ARROW);
|
||||
}
|
||||
|
||||
CURSOR_ID CursorRepository::GetCurrentCursor()
|
||||
{
|
||||
return _currentCursor;
|
||||
}
|
||||
|
||||
void CursorRepository::SetCurrentCursor(CURSOR_ID cursorId)
|
||||
{
|
||||
if (_currentCursor != cursorId)
|
||||
{
|
||||
SDL_Cursor * cursor = _loadedCursors[cursorId];
|
||||
SDL_SetCursor(cursor);
|
||||
_currentCursor = cursorId;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Cursor * CursorRepository::Create(const CursorData * cursorInfo)
|
||||
{
|
||||
SDL_Cursor * cursor = SDL_CreateCursor(
|
||||
cursorInfo->Data,
|
||||
cursorInfo->Mask,
|
||||
CURSOR_WIDTH,
|
||||
CURSOR_HEIGHT,
|
||||
cursorInfo->HotSpot.X,
|
||||
cursorInfo->HotSpot.Y);
|
||||
return cursor;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/interface/Cursors.h>
|
||||
|
||||
struct SDL_Cursor;
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
namespace Ui
|
||||
{
|
||||
class CursorRepository
|
||||
{
|
||||
private:
|
||||
constexpr static sint32 CURSOR_WIDTH = 32;
|
||||
constexpr static sint32 CURSOR_HEIGHT = 32;
|
||||
|
||||
SDL_Cursor * _loadedCursors[CURSOR_COUNT] = { nullptr };
|
||||
CURSOR_ID _currentCursor = CURSOR_UNDEFINED;
|
||||
|
||||
public:
|
||||
~CursorRepository();
|
||||
void LoadCursors();
|
||||
CURSOR_ID GetCurrentCursor();
|
||||
void SetCurrentCursor(CURSOR_ID cursorId);
|
||||
|
||||
private:
|
||||
SDL_Cursor * Create(const CursorData * cursorInfo);
|
||||
static const CursorData * GetCursorData(CURSOR_ID cursorId);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* An exception which wraps an SDL error.
|
||||
*/
|
||||
class SDLException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit SDLException(const std::string& message)
|
||||
: runtime_error(message.c_str())
|
||||
{
|
||||
SDL_GetError();
|
||||
}
|
||||
|
||||
explicit SDLException(const char * message)
|
||||
: runtime_error(message)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an SDL exception with a message containing the given call information
|
||||
* and the message given by SDL_GetError.
|
||||
*/
|
||||
static void Throw(const char * call)
|
||||
{
|
||||
std::string message = std::string(call) + ": " + std::string(SDL_GetError());
|
||||
throw SDLException(message);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,301 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include "TextComposition.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <openrct2/interface/console.h>
|
||||
#include <openrct2/interface/window.h>
|
||||
#include <openrct2/localisation/localisation.h>
|
||||
}
|
||||
|
||||
#ifdef __MACOSX__
|
||||
// macOS uses COMMAND rather than CTRL for many keyboard shortcuts
|
||||
#define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI
|
||||
#else
|
||||
#define KEYBOARD_PRIMARY_MODIFIER KMOD_CTRL
|
||||
#endif
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
bool TextComposition::IsActive()
|
||||
{
|
||||
return SDL_IsTextInputActive() && _session.Buffer != nullptr;
|
||||
}
|
||||
|
||||
TextInputSession * TextComposition::Start(utf8 * buffer, size_t bufferSize)
|
||||
{
|
||||
Guard::ArgumentNotNull(buffer);
|
||||
|
||||
// TODO This doesn't work, and position could be improved to where text entry is
|
||||
SDL_Rect rect = { 10, 10, 100, 100 };
|
||||
SDL_SetTextInputRect(&rect);
|
||||
SDL_StartTextInput();
|
||||
|
||||
_session.Buffer = buffer;
|
||||
_session.BufferSize = bufferSize - 1;
|
||||
_session.Size = strlen(buffer);
|
||||
_session.SelectionStart = _session.Size;
|
||||
_session.SelectionSize = 0;
|
||||
_session.ImeBuffer = _imeBuffer;
|
||||
RecalculateLength();
|
||||
return &_session;
|
||||
}
|
||||
|
||||
void TextComposition::Stop()
|
||||
{
|
||||
SDL_StopTextInput();
|
||||
_session.Buffer = nullptr;
|
||||
_session.ImeBuffer = nullptr;
|
||||
_imeActive = false;
|
||||
}
|
||||
|
||||
void TextComposition::HandleMessage(const SDL_Event * e)
|
||||
{
|
||||
switch (e->type) {
|
||||
case SDL_TEXTEDITING:
|
||||
// When inputting Korean characters, `edit.length` is always zero
|
||||
String::Set(_imeBuffer, sizeof(_imeBuffer), e->edit.text);
|
||||
_imeStart = e->edit.start;
|
||||
_imeLength = e->edit.length;
|
||||
_imeActive = ((e->edit.length != 0 || String::SizeOf(e->edit.text) != 0) && _imeBuffer[0] != '\0');
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
// will receive an `SDL_TEXTINPUT` event when a composition is committed
|
||||
_imeActive = false;
|
||||
if (_session.Buffer != nullptr)
|
||||
{
|
||||
// HACK ` will close console, so don't input any text
|
||||
if (e->text.text[0] == '`' && gConsoleOpen) {
|
||||
break;
|
||||
}
|
||||
|
||||
utf8 * newText = String::Duplicate(e->text.text);
|
||||
utf8_remove_formatting(newText, false);
|
||||
Insert(newText);
|
||||
Memory::Free(newText);
|
||||
|
||||
console_refresh_caret();
|
||||
window_update_textbox();
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
if (_imeActive)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
uint16 modifier = e->key.keysym.mod;
|
||||
SDL_Keycode key = e->key.keysym.sym;
|
||||
if (key == SDLK_KP_ENTER)
|
||||
{
|
||||
// Map Keypad enter to regular enter.
|
||||
key = SDLK_RETURN;
|
||||
}
|
||||
|
||||
GetContext()->GetUiContext()->SetKeysPressed(key, e->key.keysym.scancode);
|
||||
|
||||
// Text input
|
||||
if (_session.Buffer == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear the input on <CTRL>Backspace (Windows/Linux) or <MOD>Backspace (macOS)
|
||||
if (key == SDLK_BACKSPACE && (modifier & KEYBOARD_PRIMARY_MODIFIER))
|
||||
{
|
||||
Clear();
|
||||
console_refresh_caret();
|
||||
window_update_textbox();
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case SDLK_BACKSPACE:
|
||||
// If backspace and we have input text with a cursor position none zero
|
||||
if (_session.SelectionStart > 0)
|
||||
{
|
||||
size_t endOffset = _session.SelectionStart;
|
||||
CursorLeft();
|
||||
_session.SelectionSize = endOffset - _session.SelectionStart;
|
||||
Delete();
|
||||
|
||||
console_refresh_caret();
|
||||
window_update_textbox();
|
||||
}
|
||||
break;
|
||||
case SDLK_HOME:
|
||||
CursorHome();
|
||||
console_refresh_caret();
|
||||
break;
|
||||
case SDLK_END:
|
||||
CursorEnd();
|
||||
console_refresh_caret();
|
||||
break;
|
||||
case SDLK_DELETE:
|
||||
{
|
||||
size_t startOffset = _session.SelectionStart;
|
||||
CursorRight();
|
||||
_session.SelectionSize = _session.SelectionStart - startOffset;
|
||||
_session.SelectionStart = startOffset;
|
||||
Delete();
|
||||
console_refresh_caret();
|
||||
window_update_textbox();
|
||||
break;
|
||||
}
|
||||
case SDLK_RETURN:
|
||||
window_cancel_textbox();
|
||||
break;
|
||||
case SDLK_LEFT:
|
||||
CursorLeft();
|
||||
console_refresh_caret();
|
||||
break;
|
||||
case SDLK_RIGHT:
|
||||
CursorRight();
|
||||
console_refresh_caret();
|
||||
break;
|
||||
case SDLK_v:
|
||||
if ((modifier & KEYBOARD_PRIMARY_MODIFIER) && SDL_HasClipboardText())
|
||||
{
|
||||
utf8 * text = SDL_GetClipboardText();
|
||||
utf8_remove_formatting(text, false);
|
||||
Insert(text);
|
||||
SDL_free(text);
|
||||
window_update_textbox();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextComposition::CursorHome()
|
||||
{
|
||||
_session.SelectionStart = 0;
|
||||
}
|
||||
|
||||
void TextComposition::CursorEnd()
|
||||
{
|
||||
_session.SelectionStart = _session.SelectionSize;
|
||||
}
|
||||
|
||||
void TextComposition::CursorLeft()
|
||||
{
|
||||
size_t selectionOffset = _session.SelectionStart;
|
||||
if (selectionOffset > 0)
|
||||
{
|
||||
const utf8 * ch = _session.Buffer + selectionOffset;
|
||||
do
|
||||
{
|
||||
ch--;
|
||||
selectionOffset--;
|
||||
}
|
||||
while (!utf8_is_codepoint_start(ch) && selectionOffset > 0);
|
||||
|
||||
_session.SelectionStart = selectionOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void TextComposition::CursorRight()
|
||||
{
|
||||
size_t selectionOffset = _session.SelectionStart;
|
||||
size_t selectionMaxOffset = _session.Size;
|
||||
if (selectionOffset < selectionMaxOffset)
|
||||
{
|
||||
const utf8 * ch = _session.Buffer + _session.SelectionStart;
|
||||
do
|
||||
{
|
||||
ch++;
|
||||
selectionOffset++;
|
||||
}
|
||||
while (!utf8_is_codepoint_start(ch) && selectionOffset < selectionMaxOffset);
|
||||
|
||||
_session.SelectionSize = Math::Max<size_t>(0, _session.SelectionSize - (selectionOffset - _session.SelectionStart));
|
||||
_session.SelectionStart = selectionOffset;
|
||||
}
|
||||
}
|
||||
|
||||
void TextComposition::Insert(const utf8 * text)
|
||||
{
|
||||
const utf8 * ch = text;
|
||||
uint32 codepoint;
|
||||
while ((codepoint = utf8_get_next(ch, &ch)) != 0)
|
||||
{
|
||||
InsertCodepoint(codepoint);
|
||||
}
|
||||
}
|
||||
|
||||
void TextComposition::InsertCodepoint(codepoint_t codepoint)
|
||||
{
|
||||
size_t codepointLength = utf8_get_codepoint_length(codepoint);
|
||||
size_t remainingSize = _session.BufferSize - _session.Size;
|
||||
if (codepointLength <= remainingSize)
|
||||
{
|
||||
utf8 * buffer = _session.Buffer;
|
||||
utf8 * insertPtr = buffer + _session.SelectionStart;
|
||||
if (_session.SelectionStart < _session.Size)
|
||||
{
|
||||
// Shift bytes (including null terminator) right to make room for new codepoint
|
||||
utf8 * targetShiftPtr = insertPtr + codepointLength;
|
||||
size_t shiftSize = _session.Size - _session.SelectionStart + 1;
|
||||
memmove(targetShiftPtr, insertPtr, shiftSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Character is appended onto the end, so set byte after it to null terminator
|
||||
buffer[_session.Size + codepointLength] = 0;
|
||||
}
|
||||
|
||||
utf8_write_codepoint(insertPtr, codepoint);
|
||||
_session.SelectionStart += codepointLength;
|
||||
_session.Size += codepointLength;
|
||||
_session.Length++;
|
||||
}
|
||||
}
|
||||
|
||||
void TextComposition::Clear()
|
||||
{
|
||||
utf8 * buffer = _session.Buffer;
|
||||
buffer[0] = 0;
|
||||
_session.Size = 0;
|
||||
_session.Length = 0;
|
||||
_session.SelectionStart = 0;
|
||||
_session.SelectionSize = 0;
|
||||
}
|
||||
|
||||
void TextComposition::Delete()
|
||||
{
|
||||
utf8 * buffer = _session.Buffer;
|
||||
utf8 * targetShiftPtr = buffer + _session.SelectionStart;
|
||||
utf8 * sourceShiftPtr = targetShiftPtr + _session.SelectionSize;
|
||||
size_t shiftSize = _session.Size - (_session.SelectionStart - _session.SelectionSize + 1);
|
||||
memmove(targetShiftPtr, sourceShiftPtr, shiftSize);
|
||||
_session.SelectionSize = 0;
|
||||
RecalculateLength();
|
||||
}
|
||||
|
||||
void TextComposition::RecalculateLength()
|
||||
{
|
||||
_session.Size = String::SizeOf(_session.Buffer);
|
||||
_session.Length = String::LengthOf(_session.Buffer);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
union SDL_Event;
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
namespace Ui
|
||||
{
|
||||
/**
|
||||
* Represents a
|
||||
*/
|
||||
class TextComposition
|
||||
{
|
||||
private:
|
||||
TextInputSession _session = { 0 };
|
||||
|
||||
bool _imeActive = false;
|
||||
sint32 _imeStart = 0;
|
||||
sint32 _imeLength = 0;
|
||||
utf8 _imeBuffer[32] = { 0 };
|
||||
|
||||
public:
|
||||
bool IsActive();
|
||||
TextInputSession * Start(utf8 * buffer, size_t bufferSize);
|
||||
void Stop();
|
||||
void HandleMessage(const SDL_Event * e);
|
||||
|
||||
private:
|
||||
void CursorHome();
|
||||
void CursorEnd();
|
||||
void CursorLeft();
|
||||
void CursorRight();
|
||||
void Insert(const utf8 * text);
|
||||
void InsertCodepoint(codepoint_t codepoint);
|
||||
void Clear();
|
||||
void Delete();
|
||||
void RecalculateLength();
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
#include <openrct2/audio/AudioContext.h>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include "audio/AudioContext.h"
|
||||
#include "UiContext.h"
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
/**
|
||||
* Main entry point for non-Windows sytems. Windows instead uses its own DLL proxy.
|
||||
*/
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
core_init();
|
||||
int runGame = cmdline_run((const char * *)argv, argc);
|
||||
if (runGame == 1)
|
||||
{
|
||||
// Run OpenRCT2 with a UI context
|
||||
IAudioContext * audioContext = CreateAudioContext();
|
||||
IUiContext * uiContext = CreateUiContext();
|
||||
IContext * context = CreateContext(audioContext, uiContext);
|
||||
|
||||
context->RunOpenRCT2(argc, argv);
|
||||
|
||||
delete context;
|
||||
delete uiContext;
|
||||
delete audioContext;
|
||||
}
|
||||
return gExitCode;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,348 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <sstream>
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include "UiContext.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace OpenRCT2 { namespace Ui
|
||||
{
|
||||
enum class DIALOG_TYPE
|
||||
{
|
||||
NONE,
|
||||
KDIALOG,
|
||||
ZENITY,
|
||||
};
|
||||
|
||||
class LinuxContext final : public IPlatformUiContext
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
LinuxContext()
|
||||
{
|
||||
}
|
||||
|
||||
void SetWindowIcon(SDL_Window * window) override
|
||||
{
|
||||
}
|
||||
|
||||
bool IsSteamOverlayAttached() override
|
||||
{
|
||||
// See http://syprog.blogspot.ru/2011/12/listing-loaded-shared-objects-in-linux.html
|
||||
struct lmap
|
||||
{
|
||||
void * base_address;
|
||||
char * path;
|
||||
void * unused;
|
||||
lmap * next;
|
||||
lmap * prev;
|
||||
};
|
||||
|
||||
struct dummy
|
||||
{
|
||||
void * pointers[3];
|
||||
dummy * ptr;
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
void * processHandle = dlopen(nullptr, RTLD_NOW);
|
||||
if (processHandle != nullptr)
|
||||
{
|
||||
dummy * p = ((dummy *)processHandle)->ptr;
|
||||
lmap * pl = (lmap *)p->ptr;
|
||||
while (pl != nullptr)
|
||||
{
|
||||
if (strstr(pl->path, "gameoverlayrenderer.so") != nullptr)
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
pl = pl->next;
|
||||
}
|
||||
dlclose(processHandle);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void ShowMessageBox(SDL_Window * window, const std::string &message) override
|
||||
{
|
||||
log_verbose(message.c_str());
|
||||
|
||||
std::string executablePath;
|
||||
DIALOG_TYPE dtype = GetDialogApp(&executablePath);
|
||||
|
||||
switch (dtype) {
|
||||
case DIALOG_TYPE::KDIALOG:
|
||||
{
|
||||
std::string cmd = String::Format("%s --title \"OpenRCT2\" --msgbox \"%s\"", executablePath.c_str(), message.c_str());
|
||||
Execute(cmd);
|
||||
break;
|
||||
}
|
||||
case DIALOG_TYPE::ZENITY:
|
||||
{
|
||||
std::string cmd = String::Format("%s --title=\"OpenRCT2\" --info --text=\"%s\"", executablePath.c_str(), message.c_str());
|
||||
Execute(cmd);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "OpenRCT2", message.c_str(), window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) override
|
||||
{
|
||||
std::string result;
|
||||
std::string executablePath;
|
||||
DIALOG_TYPE dtype = GetDialogApp(&executablePath);
|
||||
switch (dtype) {
|
||||
case DIALOG_TYPE::KDIALOG:
|
||||
{
|
||||
std::string action =
|
||||
(desc.Type == FILE_DIALOG_TYPE::OPEN) ? "--getopenfilename" :
|
||||
"--getsavefilename";
|
||||
std::string filter = GetKDialogFilterString(desc.Filters);
|
||||
std::string cmd = String::StdFormat("%s --title '%s' %s '%s' '%s'",
|
||||
executablePath.c_str(),
|
||||
desc.Title.c_str(),
|
||||
action.c_str(),
|
||||
desc.InitialDirectory.c_str(),
|
||||
filter.c_str());
|
||||
std::string output;
|
||||
if (Execute(cmd, &output) == 0)
|
||||
{
|
||||
result = output;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIALOG_TYPE::ZENITY:
|
||||
{
|
||||
std::string action = "--file-selection";
|
||||
std::string flags;
|
||||
if (desc.Type == FILE_DIALOG_TYPE::SAVE)
|
||||
{
|
||||
flags = "--confirm-overwrite --save";
|
||||
}
|
||||
std::string filters = GetZenityFilterString(desc.Filters);
|
||||
std::string cmd = String::StdFormat("%s %s --filename='%s/' %s --title='%s' / %s",
|
||||
executablePath.c_str(),
|
||||
action.c_str(),
|
||||
desc.InitialDirectory.c_str(),
|
||||
flags.c_str(),
|
||||
desc.Title.c_str(),
|
||||
filters.c_str());
|
||||
std::string output;
|
||||
if (Execute(cmd, &output) == 0)
|
||||
{
|
||||
result = output;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_error("KDialog or Zenity not installed.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.empty())
|
||||
{
|
||||
if (desc.Type == FILE_DIALOG_TYPE::OPEN && access(result.c_str(), F_OK) == -1)
|
||||
{
|
||||
std::string msg = String::StdFormat("\"%s\" not found: %s, please choose another file\n", result.c_str(), strerror(errno));
|
||||
ShowMessageBox(window, msg);
|
||||
return ShowFileDialog(window, desc);
|
||||
}
|
||||
else if (desc.Type == FILE_DIALOG_TYPE::SAVE && access(result.c_str(), F_OK) != -1 && dtype == DIALOG_TYPE::KDIALOG)
|
||||
{
|
||||
std::string cmd = String::StdFormat("%s --yesno \"Overwrite %s?\"", executablePath.c_str(), result.c_str());
|
||||
if (Execute(cmd) != 0)
|
||||
{
|
||||
result = std::string();
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) override
|
||||
{
|
||||
std::string result;
|
||||
std::string executablePath;
|
||||
DIALOG_TYPE dtype = GetDialogApp(&executablePath);
|
||||
switch (dtype) {
|
||||
case DIALOG_TYPE::KDIALOG:
|
||||
{
|
||||
std::string output;
|
||||
std::string cmd = String::Format("%s --title '%s' --getexistingdirectory /", executablePath.c_str(), title.c_str());
|
||||
if (Execute(cmd, &output) == 0)
|
||||
{
|
||||
result = output;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIALOG_TYPE::ZENITY:
|
||||
{
|
||||
std::string output;
|
||||
std::string cmd = String::Format("%s --title='%s' --file-selection --directory /", executablePath.c_str(), title.c_str());
|
||||
if (Execute(cmd, &output) == 0)
|
||||
{
|
||||
result = output;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_error("KDialog or Zenity not installed.");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static DIALOG_TYPE GetDialogApp(std::string * executablePath)
|
||||
{
|
||||
// Prefer zenity as it offers more required features, e.g., overwrite
|
||||
// confirmation and selecting only existing files
|
||||
if (Execute("which zenity", executablePath) == 0)
|
||||
{
|
||||
return DIALOG_TYPE::ZENITY;
|
||||
}
|
||||
if (Execute("which kdialog", executablePath) == 0)
|
||||
{
|
||||
return DIALOG_TYPE::KDIALOG;
|
||||
}
|
||||
return DIALOG_TYPE::NONE;
|
||||
}
|
||||
|
||||
static sint32 Execute(const std::string &command, std::string * output = nullptr)
|
||||
{
|
||||
log_verbose("executing \"%s\"...\n", command.c_str());
|
||||
FILE * fpipe = popen(command.c_str(), "r");
|
||||
if (fpipe == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (output != nullptr)
|
||||
{
|
||||
// Read output into buffer
|
||||
std::vector<char> outputBuffer;
|
||||
char buffer[1024];
|
||||
size_t readBytes;
|
||||
while ((readBytes = fread(buffer, 1, sizeof(buffer), fpipe)) > 0)
|
||||
{
|
||||
outputBuffer.insert(outputBuffer.begin(), buffer, buffer + readBytes);
|
||||
}
|
||||
|
||||
// Trim line breaks
|
||||
size_t outputLength = outputBuffer.size();
|
||||
for (size_t i = outputLength - 1; i != SIZE_MAX; i--)
|
||||
{
|
||||
if (outputBuffer[i] == '\n')
|
||||
{
|
||||
outputLength = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to string
|
||||
*output = std::string(outputBuffer.data(), outputLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
fflush(fpipe);
|
||||
}
|
||||
|
||||
// Return exit code
|
||||
return pclose(fpipe);
|
||||
}
|
||||
|
||||
static std::string GetKDialogFilterString(const std::vector<FileDialogDesc::Filter> filters)
|
||||
{
|
||||
std::stringstream filtersb;
|
||||
bool first = true;
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
// KDialog wants filters space-delimited and we don't expect ';' anywhere else
|
||||
std::string pattern = filter.Pattern;
|
||||
for (size_t i = 0; i < pattern.size(); i++)
|
||||
{
|
||||
if (pattern[i] == ';')
|
||||
{
|
||||
pattern[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
filtersb << String::StdFormat("%s | %s", pattern.c_str(), filter.Name.c_str());
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
filtersb << String::StdFormat("\\n%s | %s", pattern.c_str(), filter.Name.c_str());
|
||||
}
|
||||
}
|
||||
return filtersb.str();
|
||||
}
|
||||
|
||||
static std::string GetZenityFilterString(const std::vector<FileDialogDesc::Filter> filters)
|
||||
{
|
||||
// Zenity seems to be case sensitive, while KDialog isn't
|
||||
std::stringstream filtersb;
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
filtersb << " --file-filter='" << filter.Name << " | ";
|
||||
for (char c : filter.Pattern)
|
||||
{
|
||||
if (c == ';')
|
||||
{
|
||||
filtersb << ' ';
|
||||
}
|
||||
else if (isalpha(c))
|
||||
{
|
||||
filtersb << '['
|
||||
<< (char)tolower(c)
|
||||
<< (char)toupper(c)
|
||||
<< ']';
|
||||
}
|
||||
else
|
||||
{
|
||||
filtersb << c;
|
||||
}
|
||||
}
|
||||
filtersb << "'";
|
||||
}
|
||||
return filtersb.str();
|
||||
}
|
||||
};
|
||||
|
||||
IPlatformUiContext * CreatePlatformUiContext()
|
||||
{
|
||||
return new LinuxContext();
|
||||
}
|
||||
} }
|
||||
|
||||
#endif // __linux__
|
|
@ -0,0 +1,224 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// 0x0600 == vista
|
||||
#define WINVER 0x0600
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#endif // __MINGW32__
|
||||
|
||||
#include <sstream>
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Path.hpp>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include "UiContext.h"
|
||||
|
||||
#undef interface
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
// Native resource IDs
|
||||
#include "../../resources/resource.h"
|
||||
|
||||
static std::wstring SHGetPathFromIDListLongPath(LPCITEMIDLIST pidl)
|
||||
{
|
||||
std::wstring pszPath(MAX_PATH, 0);
|
||||
while (!SHGetPathFromIDListEx(pidl, &pszPath[0], (DWORD)pszPath.size(), 0))
|
||||
{
|
||||
if (pszPath.size() >= SHRT_MAX)
|
||||
{
|
||||
// Clearly not succeeding at all, bail
|
||||
return std::wstring();
|
||||
}
|
||||
pszPath.resize(pszPath.size() * 2);
|
||||
}
|
||||
return pszPath;
|
||||
}
|
||||
|
||||
namespace OpenRCT2 { namespace Ui
|
||||
{
|
||||
class Win32Context : public IPlatformUiContext
|
||||
{
|
||||
private:
|
||||
HMODULE _win32module;
|
||||
|
||||
public:
|
||||
Win32Context()
|
||||
{
|
||||
_win32module = GetModuleHandleA(nullptr);
|
||||
}
|
||||
|
||||
void SetWindowIcon(SDL_Window * window) override
|
||||
{
|
||||
if (_win32module != nullptr)
|
||||
{
|
||||
HICON icon = LoadIconA(_win32module, MAKEINTRESOURCEA(IDI_ICON));
|
||||
if (icon != nullptr)
|
||||
{
|
||||
HWND hwnd = GetHWND(window);
|
||||
if (hwnd != nullptr)
|
||||
{
|
||||
SendMessageA(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSteamOverlayAttached() override
|
||||
{
|
||||
return (GetModuleHandleA("GameOverlayRenderer.dll") != nullptr);
|
||||
}
|
||||
|
||||
void ShowMessageBox(SDL_Window * window, const std::string &message) override
|
||||
{
|
||||
HWND hwnd = GetHWND(window);
|
||||
std::wstring messageW = String::ToUtf16(message);
|
||||
MessageBoxW(hwnd, messageW.c_str(), L"OpenRCT2", MB_OK);
|
||||
}
|
||||
|
||||
std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) override
|
||||
{
|
||||
std::wstring wcFilename = String::ToUtf16(desc.DefaultFilename);
|
||||
wcFilename.resize(Math::Max<size_t>(wcFilename.size(), MAX_PATH));
|
||||
|
||||
std::wstring wcTitle = String::ToUtf16(desc.Title);
|
||||
std::wstring wcInitialDirectory = String::ToUtf16(desc.InitialDirectory);
|
||||
std::wstring wcFilters = GetFilterString(desc.Filters);
|
||||
|
||||
// Set open file name options
|
||||
OPENFILENAMEW openFileName = { 0 };
|
||||
openFileName.lStructSize = sizeof(OPENFILENAMEW);
|
||||
openFileName.hwndOwner = GetHWND(window);
|
||||
openFileName.lpstrTitle = wcTitle.c_str();
|
||||
openFileName.lpstrInitialDir = wcInitialDirectory.c_str();
|
||||
openFileName.lpstrFilter = wcFilters.c_str();
|
||||
openFileName.lpstrFile = &wcFilename[0];
|
||||
openFileName.nMaxFile = (DWORD)wcFilename.size();
|
||||
|
||||
// Open dialog
|
||||
BOOL dialogResult = FALSE;
|
||||
DWORD commonFlags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
|
||||
if (desc.Type == FILE_DIALOG_TYPE::OPEN)
|
||||
{
|
||||
openFileName.Flags = commonFlags | OFN_NONETWORKBUTTON | OFN_FILEMUSTEXIST;
|
||||
dialogResult = GetOpenFileNameW(&openFileName);
|
||||
}
|
||||
else if (desc.Type == FILE_DIALOG_TYPE::SAVE)
|
||||
{
|
||||
openFileName.Flags = commonFlags | OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
|
||||
dialogResult = GetSaveFileNameW(&openFileName);
|
||||
}
|
||||
|
||||
std::string resultFilename;
|
||||
if (dialogResult)
|
||||
{
|
||||
resultFilename = String::ToUtf8(openFileName.lpstrFile);
|
||||
|
||||
// If there is no extension, append the pattern
|
||||
std::string resultExtension = Path::GetExtension(resultFilename);
|
||||
if (resultExtension.empty())
|
||||
{
|
||||
sint32 filterIndex = openFileName.nFilterIndex - 1;
|
||||
|
||||
assert(filterIndex >= 0);
|
||||
assert(filterIndex < (sint32)desc.Filters.size());
|
||||
|
||||
std::string pattern = desc.Filters[filterIndex].Pattern;
|
||||
std::string patternExtension = Path::GetExtension(pattern);
|
||||
if (!patternExtension.empty())
|
||||
{
|
||||
resultFilename += patternExtension;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultFilename;
|
||||
}
|
||||
|
||||
std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) override
|
||||
{
|
||||
std::string result;
|
||||
|
||||
// Initialize COM and get a pointer to the shell memory allocator
|
||||
LPMALLOC lpMalloc;
|
||||
if (SUCCEEDED(CoInitializeEx(0, COINIT_APARTMENTTHREADED)) &&
|
||||
SUCCEEDED(SHGetMalloc(&lpMalloc)))
|
||||
{
|
||||
std::wstring titleW = String::ToUtf16(title);
|
||||
BROWSEINFOW bi = { 0 };
|
||||
bi.hwndOwner = GetHWND(window);
|
||||
bi.lpszTitle = titleW.c_str();
|
||||
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_NONEWFOLDERBUTTON;
|
||||
|
||||
LPITEMIDLIST pidl = SHBrowseForFolderW(&bi);
|
||||
if (pidl != nullptr)
|
||||
{
|
||||
result = String::ToUtf8(SHGetPathFromIDListLongPath(pidl));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("Error opening directory browse window");
|
||||
}
|
||||
CoUninitialize();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
HWND GetHWND(SDL_Window * window)
|
||||
{
|
||||
HWND result = nullptr;
|
||||
if (window != nullptr)
|
||||
{
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
if (SDL_GetWindowWMInfo(window, &wmInfo) != SDL_TRUE)
|
||||
{
|
||||
log_error("SDL_GetWindowWMInfo failed %s", SDL_GetError());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
result = wmInfo.info.win.window;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::wstring GetFilterString(const std::vector<FileDialogDesc::Filter> filters)
|
||||
{
|
||||
std::wstringstream filtersb;
|
||||
for (auto filter : filters)
|
||||
{
|
||||
filtersb << String::ToUtf16(filter.Name)
|
||||
<< '\0'
|
||||
<< String::ToUtf16(filter.Pattern)
|
||||
<< '\0';
|
||||
}
|
||||
return filtersb.str();
|
||||
}
|
||||
};
|
||||
|
||||
IPlatformUiContext * CreatePlatformUiContext()
|
||||
{
|
||||
return new Win32Context();
|
||||
}
|
||||
} }
|
||||
|
||||
#endif // _WIN32
|
|
@ -0,0 +1,688 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/audio/AudioMixer.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/drawing/IDrawingEngine.h>
|
||||
#include <openrct2/platform/Platform2.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include <openrct2/Version.h>
|
||||
#include "CursorRepository.h"
|
||||
#include "drawing/engines/DrawingEngines.h"
|
||||
#include "SDLException.h"
|
||||
#include "TextComposition.h"
|
||||
#include "UiContext.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <openrct2/input.h>
|
||||
#include <openrct2/interface/console.h>
|
||||
#include <openrct2/interface/window.h>
|
||||
}
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
#ifdef __MACOSX__
|
||||
// macOS uses COMMAND rather than CTRL for many keyboard shortcuts
|
||||
#define KEYBOARD_PRIMARY_MODIFIER KMOD_GUI
|
||||
#else
|
||||
#define KEYBOARD_PRIMARY_MODIFIER KMOD_CTRL
|
||||
#endif
|
||||
|
||||
class UiContext final : public IUiContext
|
||||
{
|
||||
private:
|
||||
constexpr static uint32 TOUCH_DOUBLE_TIMEOUT = 300;
|
||||
|
||||
IPlatformUiContext * const _platformUiContext;
|
||||
|
||||
CursorRepository _cursorRepository;
|
||||
|
||||
SDL_Window * _window = nullptr;
|
||||
sint32 _width = 0;
|
||||
sint32 _height = 0;
|
||||
uint32 _windowFlags = 0;
|
||||
uint32 _windowFlagsLastCheckTick = 0;
|
||||
|
||||
bool _resolutionsAllowAnyAspectRatio = false;
|
||||
std::vector<Resolution> _fsResolutions;
|
||||
|
||||
bool _steamOverlayActive = false;
|
||||
|
||||
// Input
|
||||
TextComposition _textComposition;
|
||||
CursorState _cursorState = { 0 };
|
||||
uint32 _lastKeyPressed = 0;
|
||||
const uint8 * _keysState = nullptr;
|
||||
uint8 _keysPressed[256] = { 0 };
|
||||
uint32 _lastGestureTimestamp = 0;
|
||||
float _gestureRadius = 0;
|
||||
|
||||
public:
|
||||
UiContext()
|
||||
: _platformUiContext(CreatePlatformUiContext())
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
SDLException::Throw("SDL_Init(SDL_INIT_VIDEO)");
|
||||
}
|
||||
_cursorRepository.LoadCursors();
|
||||
}
|
||||
|
||||
~UiContext() override
|
||||
{
|
||||
CloseWindow();
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
delete _platformUiContext;
|
||||
}
|
||||
|
||||
// Window
|
||||
SDL_Window * GetWindow() override
|
||||
{
|
||||
return _window;
|
||||
}
|
||||
|
||||
sint32 GetWidth() override
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
|
||||
sint32 GetHeight() override
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
void SetFullscreenMode(FULLSCREEN_MODE mode) override
|
||||
{
|
||||
static const sint32 SDLFSFlags[] = { 0, SDL_WINDOW_FULLSCREEN, SDL_WINDOW_FULLSCREEN_DESKTOP };
|
||||
uint32 windowFlags = SDLFSFlags[(sint32)mode];
|
||||
|
||||
// HACK Changing window size when in fullscreen usually has no effect
|
||||
if (mode == FULLSCREEN_MODE::FULLSCREEN)
|
||||
{
|
||||
SDL_SetWindowFullscreen(_window, 0);
|
||||
}
|
||||
|
||||
// Set window size
|
||||
if (mode == FULLSCREEN_MODE::FULLSCREEN)
|
||||
{
|
||||
UpdateFullscreenResolutions();
|
||||
Resolution resolution = GetClosestResolution(gConfigGeneral.fullscreen_width, gConfigGeneral.fullscreen_height);
|
||||
SDL_SetWindowSize(_window, resolution.Width, resolution.Height);
|
||||
}
|
||||
else if (mode == FULLSCREEN_MODE::WINDOWED)
|
||||
{
|
||||
SDL_SetWindowSize(_window, gConfigGeneral.window_width, gConfigGeneral.window_height);
|
||||
}
|
||||
|
||||
if (SDL_SetWindowFullscreen(_window, windowFlags))
|
||||
{
|
||||
log_fatal("SDL_SetWindowFullscreen %s", SDL_GetError());
|
||||
exit(1);
|
||||
|
||||
// TODO try another display mode rather than just exiting the game
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Resolution> GetFullscreenResolutions() override
|
||||
{
|
||||
UpdateFullscreenResolutions();
|
||||
return _fsResolutions;
|
||||
}
|
||||
|
||||
bool HasFocus() override
|
||||
{
|
||||
uint32 windowFlags = GetWindowFlags();
|
||||
return (windowFlags & SDL_WINDOW_INPUT_FOCUS) != 0;
|
||||
}
|
||||
|
||||
bool IsMinimised() override
|
||||
{
|
||||
uint32 windowFlags = GetWindowFlags();
|
||||
return (windowFlags & SDL_WINDOW_MINIMIZED) ||
|
||||
(windowFlags & SDL_WINDOW_HIDDEN);
|
||||
}
|
||||
|
||||
bool IsSteamOverlayActive() override
|
||||
{
|
||||
return _steamOverlayActive;
|
||||
}
|
||||
|
||||
// Input
|
||||
const CursorState * GetCursorState() override
|
||||
{
|
||||
return &_cursorState;
|
||||
}
|
||||
|
||||
const uint8 * GetKeysState() override
|
||||
{
|
||||
return _keysState;
|
||||
}
|
||||
|
||||
const uint8 * GetKeysPressed() override
|
||||
{
|
||||
return _keysPressed;
|
||||
}
|
||||
|
||||
CURSOR_ID GetCursor() override
|
||||
{
|
||||
return _cursorRepository.GetCurrentCursor();
|
||||
}
|
||||
|
||||
void SetCursor(CURSOR_ID cursor) override
|
||||
{
|
||||
_cursorRepository.SetCurrentCursor(cursor);
|
||||
}
|
||||
|
||||
void SetCursorVisible(bool value) override
|
||||
{
|
||||
SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE);
|
||||
}
|
||||
|
||||
void GetCursorPosition(sint32 * x, sint32 * y) override
|
||||
{
|
||||
SDL_GetMouseState(x, y);
|
||||
}
|
||||
|
||||
void SetCursorPosition(sint32 x, sint32 y) override
|
||||
{
|
||||
SDL_WarpMouseInWindow(nullptr, x, y);
|
||||
}
|
||||
|
||||
void SetCursorTrap(bool value) override
|
||||
{
|
||||
SDL_SetWindowGrab(_window, value ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
void SetKeysPressed(uint32 keysym, uint8 scancode) override
|
||||
{
|
||||
_lastKeyPressed = keysym;
|
||||
_keysPressed[scancode] = 1;
|
||||
}
|
||||
|
||||
// Drawing
|
||||
IDrawingEngine * CreateDrawingEngine(DRAWING_ENGINE_TYPE type) override
|
||||
{
|
||||
switch ((sint32)type) {
|
||||
case DRAWING_ENGINE_SOFTWARE:
|
||||
return CreateSoftwareDrawingEngine(this);
|
||||
case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY:
|
||||
return CreateHardwareDisplayDrawingEngine(this);
|
||||
#ifndef DISABLE_OPENGL
|
||||
case DRAWING_ENGINE_OPENGL:
|
||||
return CreateOpenGLDrawingEngine(this);
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Text input
|
||||
bool IsTextInputActive() override
|
||||
{
|
||||
return _textComposition.IsActive();
|
||||
}
|
||||
|
||||
TextInputSession * StartTextInput(utf8 * buffer, size_t bufferSize) override
|
||||
{
|
||||
return _textComposition.Start(buffer, bufferSize);
|
||||
}
|
||||
|
||||
void StopTextInput() override
|
||||
{
|
||||
_textComposition.Stop();
|
||||
}
|
||||
|
||||
void ProcessMessages() override
|
||||
{
|
||||
_lastKeyPressed = 0;
|
||||
_cursorState.left &= ~CURSOR_CHANGED;
|
||||
_cursorState.middle &= ~CURSOR_CHANGED;
|
||||
_cursorState.right &= ~CURSOR_CHANGED;
|
||||
_cursorState.old = 0;
|
||||
_cursorState.touch = false;
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e))
|
||||
{
|
||||
switch (e.type) {
|
||||
case SDL_QUIT:
|
||||
rct2_quit();
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
// HACK: Fix #2158, OpenRCT2 does not draw if it does not think that the window is
|
||||
// visible - due a bug in SDL 2.0.3 this hack is required if the
|
||||
// window is maximised, minimised and then restored again.
|
||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||
{
|
||||
if (SDL_GetWindowFlags(_window) & SDL_WINDOW_MAXIMIZED)
|
||||
{
|
||||
SDL_RestoreWindow(_window);
|
||||
SDL_MaximizeWindow(_window);
|
||||
}
|
||||
if ((SDL_GetWindowFlags(_window) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
|
||||
{
|
||||
SDL_RestoreWindow(_window);
|
||||
SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
|
||||
{
|
||||
OnResize(e.window.data1, e.window.data2);
|
||||
}
|
||||
|
||||
switch (e.window.event) {
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
case SDL_WINDOWEVENT_MAXIMIZED:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
{
|
||||
// Update default display index
|
||||
sint32 displayIndex = SDL_GetWindowDisplayIndex(_window);
|
||||
if (displayIndex != gConfigGeneral.default_display)
|
||||
{
|
||||
gConfigGeneral.default_display = displayIndex;
|
||||
config_save_default();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (gConfigSound.audio_focus && gConfigSound.sound_enabled)
|
||||
{
|
||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||
{
|
||||
Mixer_SetVolume(1);
|
||||
}
|
||||
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
||||
{
|
||||
Mixer_SetVolume(0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
_cursorState.x = (sint32)(e.motion.x / gConfigGeneral.window_scale);
|
||||
_cursorState.y = (sint32)(e.motion.y / gConfigGeneral.window_scale);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
if (gConsoleOpen)
|
||||
{
|
||||
console_scroll(e.wheel.y);
|
||||
break;
|
||||
}
|
||||
_cursorState.wheel += e.wheel.y * 128;
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale);
|
||||
sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale);
|
||||
switch (e.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y);
|
||||
_cursorState.left = CURSOR_PRESSED;
|
||||
_cursorState.old = 1;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
_cursorState.middle = CURSOR_PRESSED;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y);
|
||||
_cursorState.right = CURSOR_PRESSED;
|
||||
_cursorState.old = 2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
sint32 x = (sint32)(e.button.x / gConfigGeneral.window_scale);
|
||||
sint32 y = (sint32)(e.button.y / gConfigGeneral.window_scale);
|
||||
switch (e.button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y);
|
||||
_cursorState.left = CURSOR_RELEASED;
|
||||
_cursorState.old = 3;
|
||||
break;
|
||||
case SDL_BUTTON_MIDDLE:
|
||||
_cursorState.middle = CURSOR_RELEASED;
|
||||
break;
|
||||
case SDL_BUTTON_RIGHT:
|
||||
store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y);
|
||||
_cursorState.right = CURSOR_RELEASED;
|
||||
_cursorState.old = 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Apple sends touchscreen events for trackpads, so ignore these events on macOS
|
||||
#ifndef __MACOSX__
|
||||
case SDL_FINGERMOTION:
|
||||
_cursorState.x = (sint32)(e.tfinger.x * _width);
|
||||
_cursorState.y = (sint32)(e.tfinger.y * _height);
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
{
|
||||
sint32 x = (sint32)(e.tfinger.x * _width);
|
||||
sint32 y = (sint32)(e.tfinger.y * _height);
|
||||
|
||||
_cursorState.touchIsDouble = (!_cursorState.touchIsDouble &&
|
||||
e.tfinger.timestamp - _cursorState.touchDownTimestamp < TOUCH_DOUBLE_TIMEOUT);
|
||||
|
||||
if (_cursorState.touchIsDouble)
|
||||
{
|
||||
store_mouse_input(MOUSE_STATE_RIGHT_PRESS, x, y);
|
||||
_cursorState.right = CURSOR_PRESSED;
|
||||
_cursorState.old = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
store_mouse_input(MOUSE_STATE_LEFT_PRESS, x, y);
|
||||
_cursorState.left = CURSOR_PRESSED;
|
||||
_cursorState.old = 1;
|
||||
}
|
||||
_cursorState.touch = true;
|
||||
_cursorState.touchDownTimestamp = e.tfinger.timestamp;
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERUP:
|
||||
{
|
||||
sint32 x = (sint32)(e.tfinger.x * _width);
|
||||
sint32 y = (sint32)(e.tfinger.y * _height);
|
||||
|
||||
if (_cursorState.touchIsDouble)
|
||||
{
|
||||
store_mouse_input(MOUSE_STATE_RIGHT_RELEASE, x, y);
|
||||
_cursorState.left = CURSOR_RELEASED;
|
||||
_cursorState.old = 4;
|
||||
}
|
||||
else {
|
||||
store_mouse_input(MOUSE_STATE_LEFT_RELEASE, x, y);
|
||||
_cursorState.left = CURSOR_RELEASED;
|
||||
_cursorState.old = 3;
|
||||
}
|
||||
_cursorState.touch = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case SDL_KEYDOWN:
|
||||
_textComposition.HandleMessage(&e);
|
||||
break;
|
||||
case SDL_MULTIGESTURE:
|
||||
if (e.mgesture.numFingers == 2)
|
||||
{
|
||||
if (e.mgesture.timestamp > _lastGestureTimestamp + 1000)
|
||||
{
|
||||
_gestureRadius = 0;
|
||||
}
|
||||
_lastGestureTimestamp = e.mgesture.timestamp;
|
||||
_gestureRadius += e.mgesture.dDist;
|
||||
|
||||
// Zoom gesture
|
||||
constexpr sint32 tolerance = 128;
|
||||
sint32 gesturePixels = (sint32)(_gestureRadius * _width);
|
||||
if (abs(gesturePixels) > tolerance)
|
||||
{
|
||||
_gestureRadius = 0;
|
||||
main_window_zoom(gesturePixels > 0, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SDL_TEXTEDITING:
|
||||
_textComposition.HandleMessage(&e);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
_textComposition.HandleMessage(&e);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_cursorState.any = _cursorState.left | _cursorState.middle | _cursorState.right;
|
||||
|
||||
// Updates the state of the keys
|
||||
sint32 numKeys = 256;
|
||||
_keysState = SDL_GetKeyboardState(&numKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set various render target features.
|
||||
* Does not get triggered on resize, but rather manually on config changes.
|
||||
*/
|
||||
void TriggerResize() override
|
||||
{
|
||||
char scaleQualityBuffer[4];
|
||||
uint8 scaleQuality = gConfigGeneral.scale_quality;
|
||||
if (gConfigGeneral.use_nn_at_integer_scales &&
|
||||
gConfigGeneral.window_scale == std::floor(gConfigGeneral.window_scale))
|
||||
{
|
||||
scaleQuality = 0;
|
||||
}
|
||||
snprintf(scaleQualityBuffer, sizeof(scaleQualityBuffer), "%u", scaleQuality);
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, scaleQualityBuffer);
|
||||
|
||||
sint32 width, height;
|
||||
SDL_GetWindowSize(_window, &width, &height);
|
||||
OnResize(width, height);
|
||||
}
|
||||
|
||||
void CreateWindow() override
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, gConfigGeneral.minimize_fullscreen_focus_loss ? "1" : "0");
|
||||
|
||||
// TODO This should probably be called somewhere else. It has nothing to do with window creation and can be done as soon as
|
||||
// g1.dat is loaded.
|
||||
// sub_68371D();
|
||||
|
||||
// Set window position to default display
|
||||
sint32 defaultDisplay = Math::Clamp(0, gConfigGeneral.default_display, 0xFFFF);
|
||||
sint32 x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(defaultDisplay);
|
||||
sint32 y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(defaultDisplay);
|
||||
|
||||
// Get saved window size
|
||||
sint32 width = gConfigGeneral.window_width;
|
||||
sint32 height = gConfigGeneral.window_height;
|
||||
if (width <= 0) width = 640;
|
||||
if (height <= 0) height = 480;
|
||||
|
||||
// Create window in window first rather than fullscreen so we have the display the window is on first
|
||||
uint32 flags = SDL_WINDOW_RESIZABLE;
|
||||
if (gConfigGeneral.drawing_engine == DRAWING_ENGINE_OPENGL)
|
||||
{
|
||||
flags |= SDL_WINDOW_OPENGL;
|
||||
}
|
||||
|
||||
_window = SDL_CreateWindow(OPENRCT2_NAME, x, y, width, height, flags);
|
||||
if (_window == nullptr)
|
||||
{
|
||||
SDLException::Throw("SDL_CreateWindow(...)");
|
||||
}
|
||||
|
||||
SDL_SetWindowMinimumSize(_window, 720, 480);
|
||||
SetCursorTrap(gConfigGeneral.trap_cursor);
|
||||
_platformUiContext->SetWindowIcon(_window);
|
||||
|
||||
// Initialise the surface, palette and draw buffer
|
||||
OnResize(width, height);
|
||||
|
||||
UpdateFullscreenResolutions();
|
||||
SetFullscreenMode((FULLSCREEN_MODE)gConfigGeneral.fullscreen_mode);
|
||||
|
||||
// Check if steam overlay renderer is loaded into the process
|
||||
_steamOverlayActive = _platformUiContext->IsSteamOverlayAttached();
|
||||
TriggerResize();
|
||||
}
|
||||
|
||||
void CloseWindow() override
|
||||
{
|
||||
drawing_engine_dispose();
|
||||
SDL_DestroyWindow(_window);
|
||||
}
|
||||
|
||||
void ShowMessageBox(const std::string &message) override
|
||||
{
|
||||
_platformUiContext->ShowMessageBox(_window, message);
|
||||
}
|
||||
|
||||
std::string ShowFileDialog(const FileDialogDesc &desc) override
|
||||
{
|
||||
return _platformUiContext->ShowFileDialog(_window, desc);
|
||||
}
|
||||
|
||||
std::string ShowDirectoryDialog(const std::string &title) override
|
||||
{
|
||||
return _platformUiContext->ShowDirectoryDialog(_window, title);
|
||||
}
|
||||
|
||||
private:
|
||||
void OnResize(sint32 width, sint32 height)
|
||||
{
|
||||
// Scale the native window size to the game's canvas size
|
||||
_width = (sint32)(width / gConfigGeneral.window_scale);
|
||||
_height = (sint32)(height / gConfigGeneral.window_scale);
|
||||
|
||||
drawing_engine_resize();
|
||||
|
||||
uint32 flags = SDL_GetWindowFlags(_window);
|
||||
if ((flags & SDL_WINDOW_MINIMIZED) == 0)
|
||||
{
|
||||
window_resize_gui(_width, _height);
|
||||
window_relocate_windows(_width, _height);
|
||||
}
|
||||
|
||||
gfx_invalidate_screen();
|
||||
|
||||
// Check if the window has been resized in windowed mode and update the config file accordingly
|
||||
// This is called in rct2_update and is only called after resizing a window has finished
|
||||
sint32 nonWindowFlags = SDL_WINDOW_MAXIMIZED |
|
||||
SDL_WINDOW_MINIMIZED |
|
||||
SDL_WINDOW_FULLSCREEN |
|
||||
SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
if (!(flags & nonWindowFlags))
|
||||
{
|
||||
if (width != gConfigGeneral.window_width || height != gConfigGeneral.window_height)
|
||||
{
|
||||
gConfigGeneral.window_width = width;
|
||||
gConfigGeneral.window_height = height;
|
||||
config_save_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateFullscreenResolutions()
|
||||
{
|
||||
// Query number of display modes
|
||||
sint32 displayIndex = SDL_GetWindowDisplayIndex(_window);
|
||||
sint32 numDisplayModes = SDL_GetNumDisplayModes(displayIndex);
|
||||
|
||||
// Get desktop aspect ratio
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetDesktopDisplayMode(displayIndex, &mode);
|
||||
|
||||
// Get resolutions
|
||||
auto resolutions = std::vector<Resolution>(numDisplayModes);
|
||||
float desktopAspectRatio = (float)mode.w / mode.h;
|
||||
for (sint32 i = 0; i < numDisplayModes; i++)
|
||||
{
|
||||
SDL_GetDisplayMode(displayIndex, i, &mode);
|
||||
|
||||
float aspectRatio = (float)mode.w / mode.h;
|
||||
if (_resolutionsAllowAnyAspectRatio || std::fabs(desktopAspectRatio - aspectRatio) < 0.0001f)
|
||||
{
|
||||
resolutions.push_back({ mode.w, mode.h });
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by area
|
||||
std::sort(resolutions.begin(), resolutions.end(),
|
||||
[](const Resolution &a, const Resolution &b) -> bool
|
||||
{
|
||||
sint32 areaA = a.Width * a.Height;
|
||||
sint32 areaB = b.Width * b.Height;
|
||||
return areaA < areaB;
|
||||
});
|
||||
|
||||
// Remove duplicates
|
||||
auto last = std::unique(resolutions.begin(), resolutions.end(),
|
||||
[](const Resolution &a, const Resolution &b) -> bool
|
||||
{
|
||||
return (a.Width == b.Width && a.Height == b.Height);
|
||||
});
|
||||
resolutions.erase(last, resolutions.end());
|
||||
|
||||
// Update config fullscreen resolution if not set
|
||||
if (gConfigGeneral.fullscreen_width == -1 || gConfigGeneral.fullscreen_height == -1)
|
||||
{
|
||||
gConfigGeneral.fullscreen_width = resolutions.back().Width;
|
||||
gConfigGeneral.fullscreen_height = resolutions.back().Height;
|
||||
}
|
||||
}
|
||||
|
||||
Resolution GetClosestResolution(sint32 inWidth, sint32 inHeight)
|
||||
{
|
||||
Resolution result = { 640, 480 };
|
||||
sint32 closestAreaDiff = -1;
|
||||
sint32 destinationArea = inWidth * inHeight;
|
||||
for (const Resolution &resolution : _fsResolutions)
|
||||
{
|
||||
// Check if exact match
|
||||
if (resolution.Width == inWidth && resolution.Height == inHeight)
|
||||
{
|
||||
result = resolution;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if area is closer to best match
|
||||
sint32 areaDiff = std::abs((resolution.Width * resolution.Height) - destinationArea);
|
||||
if (closestAreaDiff == -1 || areaDiff < closestAreaDiff)
|
||||
{
|
||||
closestAreaDiff = areaDiff;
|
||||
result = resolution;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 GetWindowFlags()
|
||||
{
|
||||
// Don't check if window is minimised too frequently (every second is fine)
|
||||
uint32 tick = Platform::GetTicks();
|
||||
if (tick > _windowFlagsLastCheckTick + 1000)
|
||||
{
|
||||
_windowFlags = SDL_GetWindowFlags(_window);
|
||||
_windowFlagsLastCheckTick = tick;
|
||||
}
|
||||
return _windowFlags;
|
||||
}
|
||||
};
|
||||
|
||||
IUiContext * OpenRCT2::Ui::CreateUiContext()
|
||||
{
|
||||
return new UiContext();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <openrct2/common.h>
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
interface IContext;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
struct FileDialogDesc;
|
||||
interface IUiContext;
|
||||
|
||||
interface IPlatformUiContext
|
||||
{
|
||||
virtual ~IPlatformUiContext() = default;
|
||||
virtual void SetWindowIcon(SDL_Window * window) abstract;
|
||||
virtual bool IsSteamOverlayAttached() abstract;
|
||||
|
||||
virtual void ShowMessageBox(SDL_Window * window, const std::string &message) abstract;
|
||||
virtual std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) abstract;
|
||||
virtual std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) abstract;
|
||||
};
|
||||
|
||||
IUiContext * CreateUiContext();
|
||||
IPlatformUiContext * CreatePlatformUiContext();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
#include <dlfcn.h>
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include "UiContext.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
namespace OpenRCT2 { namespace Ui
|
||||
{
|
||||
class macOSContext final : public IPlatformUiContext
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
macOSContext()
|
||||
{
|
||||
@autoreleasepool {
|
||||
if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)]) {
|
||||
[NSWindow setAllowsAutomaticWindowTabbing:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetWindowIcon(SDL_Window * window) override
|
||||
{
|
||||
}
|
||||
|
||||
bool IsSteamOverlayAttached() override
|
||||
{
|
||||
STUB();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShowMessageBox(SDL_Window * window, const std::string &message) override
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
|
||||
[alert addButtonWithTitle:@"OK"];
|
||||
alert.messageText = [NSString stringWithUTF8String:message.c_str()];
|
||||
[alert runModal];
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShowFileDialog(SDL_Window * window, const FileDialogDesc &desc) override
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSMutableArray *extensions = [NSMutableArray new];
|
||||
for (const OpenRCT2::Ui::FileDialogDesc::Filter &filter: desc.Filters) {
|
||||
if (filter.Pattern != "") {
|
||||
NSString *fp = [NSString stringWithUTF8String:filter.Pattern.c_str()];
|
||||
fp = [fp stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
||||
[extensions addObjectsFromArray:[fp componentsSeparatedByString:@";"]];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *directory;
|
||||
NSSavePanel *panel;
|
||||
if (desc.Type == FILE_DIALOG_TYPE::SAVE)
|
||||
{
|
||||
NSString *filePath = [NSString stringWithUTF8String:desc.DefaultFilename.c_str()];
|
||||
directory = filePath.stringByDeletingLastPathComponent;
|
||||
NSString *basename = filePath.lastPathComponent;
|
||||
panel = [NSSavePanel savePanel];
|
||||
panel.nameFieldStringValue = [NSString stringWithFormat:@"%@.%@", basename, extensions.firstObject];
|
||||
}
|
||||
else if (desc.Type == FILE_DIALOG_TYPE::OPEN)
|
||||
{
|
||||
directory = [NSString stringWithUTF8String:desc.InitialDirectory.c_str()];
|
||||
NSOpenPanel *open = [NSOpenPanel openPanel];
|
||||
open.canChooseDirectories = false;
|
||||
open.canChooseFiles = true;
|
||||
open.allowsMultipleSelection = false;
|
||||
panel = open;
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
panel.title = [NSString stringWithUTF8String:desc.Title.c_str()];
|
||||
panel.allowedFileTypes = extensions;
|
||||
panel.directoryURL = [NSURL fileURLWithPath:directory];
|
||||
if ([panel runModal] == NSFileHandlingPanelCancelButton){
|
||||
return std::string();
|
||||
} else {
|
||||
return panel.URL.path.UTF8String;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ShowDirectoryDialog(SDL_Window * window, const std::string &title) override
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
panel.canChooseFiles = false;
|
||||
panel.canChooseDirectories = true;
|
||||
panel.allowsMultipleSelection = false;
|
||||
utf8 *url = NULL;
|
||||
if ([panel runModal] == NSFileHandlingPanelOKButton)
|
||||
{
|
||||
NSString *selectedPath = panel.URL.path;
|
||||
const char *path = selectedPath.UTF8String;
|
||||
url = _strdup(path);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static sint32 Execute(const std::string &command, std::string * output = nullptr)
|
||||
{
|
||||
log_verbose("executing \"%s\"...\n", command.c_str());
|
||||
FILE * fpipe = popen(command.c_str(), "r");
|
||||
if (fpipe == nullptr)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (output != nullptr)
|
||||
{
|
||||
// Read output into buffer
|
||||
std::vector<char> outputBuffer;
|
||||
char buffer[1024];
|
||||
size_t readBytes;
|
||||
while ((readBytes = fread(buffer, 1, sizeof(buffer), fpipe)) > 0)
|
||||
{
|
||||
outputBuffer.insert(outputBuffer.begin(), buffer, buffer + readBytes);
|
||||
}
|
||||
|
||||
// Trim line breaks
|
||||
size_t outputLength = outputBuffer.size();
|
||||
for (size_t i = outputLength - 1; i != SIZE_MAX; i--)
|
||||
{
|
||||
if (outputBuffer[i] == '\n')
|
||||
{
|
||||
outputLength = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to string
|
||||
*output = std::string(outputBuffer.data(), outputLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
fflush(fpipe);
|
||||
}
|
||||
|
||||
// Return exit code
|
||||
return pclose(fpipe);
|
||||
}
|
||||
};
|
||||
|
||||
IPlatformUiContext * CreatePlatformUiContext()
|
||||
{
|
||||
return new macOSContext();
|
||||
}
|
||||
} }
|
||||
|
||||
#endif // __APPLE__ && __MACH__
|
|
@ -0,0 +1,297 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <cmath>
|
||||
#include <SDL.h>
|
||||
#include <speex/speex_resampler.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/audio/AudioChannel.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
class AudioChannelImpl : public ISDLAudioChannel
|
||||
{
|
||||
private:
|
||||
ISDLAudioSource * _source = nullptr;
|
||||
SpeexResamplerState * _resampler = nullptr;
|
||||
|
||||
sint32 _group = MIXER_GROUP_SOUND;
|
||||
double _rate = 0;
|
||||
uint64 _offset = 0;
|
||||
sint32 _loop = 0;
|
||||
|
||||
sint32 _volume = 1;
|
||||
float _volume_l = 0.f;
|
||||
float _volume_r = 0.f;
|
||||
float _oldvolume_l = 0.f;
|
||||
float _oldvolume_r = 0.f;
|
||||
sint32 _oldvolume = 0;
|
||||
float _pan = 0;
|
||||
|
||||
bool _stopping = false;
|
||||
bool _done = true;
|
||||
bool _deleteondone = false;
|
||||
bool _deletesourceondone = false;
|
||||
|
||||
public:
|
||||
AudioChannelImpl()
|
||||
{
|
||||
SetRate(1);
|
||||
SetVolume(SDL_MIX_MAXVOLUME);
|
||||
SetPan(0.5f);
|
||||
}
|
||||
|
||||
~AudioChannelImpl() override
|
||||
{
|
||||
if (_resampler != nullptr)
|
||||
{
|
||||
speex_resampler_destroy(_resampler);
|
||||
_resampler = nullptr;
|
||||
}
|
||||
if (_deletesourceondone)
|
||||
{
|
||||
delete _source;
|
||||
}
|
||||
}
|
||||
|
||||
IAudioSource * GetSource() const override
|
||||
{
|
||||
return _source;
|
||||
}
|
||||
|
||||
SpeexResamplerState * GetResampler() const override
|
||||
{
|
||||
return _resampler;
|
||||
}
|
||||
|
||||
void SetResampler(SpeexResamplerState * value) override
|
||||
{
|
||||
_resampler = value;
|
||||
}
|
||||
|
||||
sint32 GetGroup() const override
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
void SetGroup(sint32 group) override
|
||||
{
|
||||
_group = group;
|
||||
}
|
||||
|
||||
double GetRate() const override
|
||||
{
|
||||
return _rate;
|
||||
}
|
||||
|
||||
void SetRate(double rate) override
|
||||
{
|
||||
_rate = Math::Max(0.001, rate);
|
||||
}
|
||||
|
||||
uint64 GetOffset() const override
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
|
||||
bool SetOffset(uint64 offset) override
|
||||
{
|
||||
if (_source != nullptr && offset < _source->GetLength())
|
||||
{
|
||||
AudioFormat format = _source->GetFormat();
|
||||
sint32 samplesize = format.channels * format.BytesPerSample();
|
||||
_offset = (offset / samplesize) * samplesize;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual sint32 GetLoop() const override
|
||||
{
|
||||
return _loop;
|
||||
}
|
||||
|
||||
virtual void SetLoop(sint32 value) override
|
||||
{
|
||||
_loop = value;
|
||||
}
|
||||
|
||||
sint32 GetVolume() const override
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
|
||||
float GetVolumeL() const override
|
||||
{
|
||||
return _volume_l;
|
||||
}
|
||||
|
||||
float GetVolumeR() const override
|
||||
{
|
||||
return _volume_r;
|
||||
}
|
||||
|
||||
float GetOldVolumeL() const override
|
||||
{
|
||||
return _oldvolume_l;
|
||||
}
|
||||
|
||||
float GetOldVolumeR() const override
|
||||
{
|
||||
return _oldvolume_r;
|
||||
}
|
||||
|
||||
sint32 GetOldVolume() const override
|
||||
{
|
||||
return _oldvolume;
|
||||
}
|
||||
|
||||
void SetVolume(sint32 volume) override
|
||||
{
|
||||
_volume = Math::Clamp(0, volume, SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
|
||||
float GetPan() const override
|
||||
{
|
||||
return _pan;
|
||||
}
|
||||
|
||||
void SetPan(float pan) override
|
||||
{
|
||||
_pan = Math::Clamp(0.0f, pan, 1.0f);
|
||||
double decibels = (std::abs(_pan - 0.5) * 2.0) * 100.0;
|
||||
double attenuation = pow(10, decibels / 20.0);
|
||||
if (_pan <= 0.5)
|
||||
{
|
||||
_volume_l = 1.0;
|
||||
_volume_r = (float)(1.0 / attenuation);
|
||||
}
|
||||
else
|
||||
{
|
||||
_volume_r = 1.0;
|
||||
_volume_l = (float)(1.0 / attenuation);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStopping() const override
|
||||
{
|
||||
return _stopping;
|
||||
}
|
||||
|
||||
void SetStopping(bool value) override
|
||||
{
|
||||
_stopping = value;
|
||||
}
|
||||
|
||||
bool IsDone() const override
|
||||
{
|
||||
return _done;
|
||||
}
|
||||
|
||||
void SetDone(bool value) override
|
||||
{
|
||||
_done = value;
|
||||
}
|
||||
|
||||
bool DeleteOnDone() const override
|
||||
{
|
||||
return _deleteondone;
|
||||
}
|
||||
|
||||
void SetDeleteOnDone(bool value) override
|
||||
{
|
||||
_deleteondone = value;
|
||||
}
|
||||
|
||||
void SetDeleteSourceOnDone(bool value) override
|
||||
{
|
||||
_deletesourceondone = value;
|
||||
}
|
||||
|
||||
bool IsPlaying() const override
|
||||
{
|
||||
return !_done;
|
||||
}
|
||||
|
||||
void Play(IAudioSource * source, sint32 loop) override
|
||||
{
|
||||
_source = static_cast<ISDLAudioSource *>(source);
|
||||
_loop = loop;
|
||||
_offset = 0;
|
||||
_done = false;
|
||||
}
|
||||
|
||||
void UpdateOldVolume() override
|
||||
{
|
||||
_oldvolume = _volume;
|
||||
_oldvolume_l = _volume_l;
|
||||
_oldvolume_r = _volume_r;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() const override
|
||||
{
|
||||
AudioFormat result = { 0 };
|
||||
if (_source != nullptr)
|
||||
{
|
||||
result = _source->GetFormat();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, size_t len) override
|
||||
{
|
||||
size_t bytesRead = 0;
|
||||
size_t bytesToRead = len;
|
||||
while (bytesToRead > 0 && !_done)
|
||||
{
|
||||
size_t readLen = _source->Read(dst, _offset, bytesToRead);
|
||||
if (readLen > 0)
|
||||
{
|
||||
dst = (void *)((uintptr_t)dst + readLen);
|
||||
bytesToRead -= readLen;
|
||||
bytesRead += readLen;
|
||||
_offset += readLen;
|
||||
}
|
||||
if (_offset >= _source->GetLength())
|
||||
{
|
||||
if (_loop == 0)
|
||||
{
|
||||
_done = true;
|
||||
}
|
||||
else if (_loop == MIXER_LOOP_INFINITE)
|
||||
{
|
||||
_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loop--;
|
||||
_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
};
|
||||
|
||||
ISDLAudioChannel * AudioChannel::Create()
|
||||
{
|
||||
return new (std::nothrow) AudioChannelImpl();
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,100 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/audio/AudioContext.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include "../SDLException.h"
|
||||
#include "AudioContext.h"
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
class AudioContext : public IAudioContext
|
||||
{
|
||||
private:
|
||||
IAudioMixer * _audioMixer;
|
||||
|
||||
public:
|
||||
AudioContext()
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
|
||||
}
|
||||
_audioMixer = AudioMixer::Create();
|
||||
}
|
||||
|
||||
~AudioContext() override
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
IAudioMixer * GetMixer() override
|
||||
{
|
||||
return _audioMixer;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetOutputDevices() override
|
||||
{
|
||||
std::vector<std::string> devices;
|
||||
sint32 numDevices = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
for (sint32 i = 0; i < numDevices; i++)
|
||||
{
|
||||
std::string deviceName = String::ToStd(SDL_GetAudioDeviceName(i, SDL_FALSE));
|
||||
devices.push_back(deviceName);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
void SetOutputDevice(const std::string &deviceName) override
|
||||
{
|
||||
const char * szDeviceName = nullptr;
|
||||
if (!deviceName.empty())
|
||||
{
|
||||
szDeviceName = deviceName.c_str();
|
||||
}
|
||||
_audioMixer->Init(szDeviceName);
|
||||
}
|
||||
|
||||
IAudioSource * CreateStreamFromWAV(const std::string &path) override
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(path);
|
||||
}
|
||||
|
||||
void StartTitleMusic() override { }
|
||||
|
||||
IAudioChannel * PlaySound(sint32 soundId, sint32 volume, sint32 pan) override { return nullptr; }
|
||||
IAudioChannel * PlaySoundAtLocation(sint32 soundId, sint16 x, sint16 y, sint16 z) override { return nullptr; }
|
||||
IAudioChannel * PlaySoundPanned(sint32 soundId, sint32 pan, sint16 x, sint16 y, sint16 z) override { return nullptr; }
|
||||
|
||||
void ToggleAllSounds() override { }
|
||||
void PauseSounds() override { }
|
||||
void UnpauseSounds() override { }
|
||||
|
||||
void StopAll() override { }
|
||||
void StopCrowdSound() override { }
|
||||
void StopRainSound() override { }
|
||||
void StopRideMusic() override { }
|
||||
void StopTitleMusic() override { }
|
||||
void StopVehicleSounds() override { }
|
||||
};
|
||||
|
||||
IAudioContext * CreateAudioContext()
|
||||
{
|
||||
return new AudioContext();
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/audio/AudioChannel.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
|
||||
struct SDL_RWops;
|
||||
struct SpeexResamplerState_;
|
||||
typedef struct SpeexResamplerState_ SpeexResamplerState;
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
struct AudioFormat;
|
||||
interface IAudioContext;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WaveFormat
|
||||
{
|
||||
uint16 encoding;
|
||||
uint16 channels;
|
||||
uint32 frequency;
|
||||
uint32 byterate;
|
||||
uint16 blockalign;
|
||||
uint16 bitspersample;
|
||||
};
|
||||
assert_struct_size(WaveFormat, 16);
|
||||
|
||||
struct WaveFormatEx
|
||||
{
|
||||
uint16 encoding;
|
||||
uint16 channels;
|
||||
uint32 frequency;
|
||||
uint32 byterate;
|
||||
uint16 blockalign;
|
||||
uint16 bitspersample;
|
||||
uint16 extrasize;
|
||||
};
|
||||
assert_struct_size(WaveFormatEx, 18);
|
||||
#pragma pack(pop)
|
||||
|
||||
interface ISDLAudioSource : public IAudioSource
|
||||
{
|
||||
virtual AudioFormat GetFormat() const abstract;
|
||||
};
|
||||
|
||||
interface ISDLAudioChannel : public IAudioChannel
|
||||
{
|
||||
virtual AudioFormat GetFormat() const abstract;
|
||||
virtual SpeexResamplerState * GetResampler() const abstract;
|
||||
virtual void SetResampler(SpeexResamplerState * value) abstract;
|
||||
};
|
||||
|
||||
namespace AudioSource
|
||||
{
|
||||
IAudioSource * CreateMemoryFromCSS1(const std::string &path, size_t index, const AudioFormat * targetFormat = nullptr);
|
||||
IAudioSource * CreateMemoryFromWAV(const std::string &path, const AudioFormat * targetFormat = nullptr);
|
||||
IAudioSource * CreateStreamFromWAV(const std::string &path);
|
||||
IAudioSource * CreateStreamFromWAV(SDL_RWops * rw);
|
||||
}
|
||||
|
||||
namespace AudioChannel
|
||||
{
|
||||
ISDLAudioChannel * Create();
|
||||
}
|
||||
|
||||
namespace AudioMixer
|
||||
{
|
||||
IAudioMixer * Create();
|
||||
}
|
||||
|
||||
IAudioContext * CreateAudioContext();
|
||||
} }
|
|
@ -16,38 +16,41 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
|
||||
/**
|
||||
* Represents the size, frequency and number of channels for
|
||||
* an audio stream or buffer.
|
||||
*/
|
||||
struct AudioFormat
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
sint32 freq;
|
||||
SDL_AudioFormat format;
|
||||
sint32 channels;
|
||||
|
||||
sint32 BytesPerSample() const
|
||||
/**
|
||||
* Represents the size, frequency and number of channels for
|
||||
* an audio stream or buffer.
|
||||
*/
|
||||
struct AudioFormat
|
||||
{
|
||||
return (SDL_AUDIO_BITSIZE(format)) / 8;
|
||||
sint32 freq;
|
||||
SDL_AudioFormat format;
|
||||
sint32 channels;
|
||||
|
||||
sint32 BytesPerSample() const
|
||||
{
|
||||
return (SDL_AUDIO_BITSIZE(format)) / 8;
|
||||
}
|
||||
|
||||
sint32 GetByteRate() const
|
||||
{
|
||||
return BytesPerSample() * channels;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator ==(const AudioFormat& lhs, const AudioFormat& rhs)
|
||||
{
|
||||
return lhs.freq == rhs.freq &&
|
||||
lhs.format == rhs.format &&
|
||||
lhs.channels == rhs.channels;
|
||||
}
|
||||
|
||||
sint32 GetByteRate() const
|
||||
inline bool operator !=(const AudioFormat& lhs, const AudioFormat& rhs)
|
||||
{
|
||||
return BytesPerSample() * channels;
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator ==(const AudioFormat& lhs, const AudioFormat& rhs)
|
||||
{
|
||||
return lhs.freq == rhs.freq &&
|
||||
lhs.format == rhs.format &&
|
||||
lhs.channels == rhs.channels;
|
||||
}
|
||||
|
||||
inline bool operator !=(const AudioFormat& lhs, const AudioFormat& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,542 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <speex/speex_resampler.h>
|
||||
#include <list>
|
||||
#include <openrct2/core/Guard.hpp>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/core/Util.hpp>
|
||||
#include <openrct2/audio/audio.h>
|
||||
#include <openrct2/audio/AudioChannel.h>
|
||||
#include <openrct2/audio/AudioMixer.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/localisation/localisation.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/platform/platform.h>
|
||||
#include <openrct2/rct2.h>
|
||||
}
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
struct Buffer
|
||||
{
|
||||
private:
|
||||
void * _data = nullptr;
|
||||
size_t _capacity = 0;
|
||||
|
||||
public:
|
||||
void * GetData() const { return _data; }
|
||||
void * GetData() { return _data; }
|
||||
size_t GetCapacity() const { return _capacity; }
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
void Free()
|
||||
{
|
||||
Memory::Free(_data);
|
||||
_data = nullptr;
|
||||
_capacity = 0;
|
||||
}
|
||||
|
||||
void EnsureCapacity(size_t capacity)
|
||||
{
|
||||
if (_capacity < capacity)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_data = Memory::Reallocate(_data, capacity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AudioMixerImpl final : public IAudioMixer
|
||||
{
|
||||
private:
|
||||
IAudioSource * _nullSource = nullptr;
|
||||
|
||||
SDL_AudioDeviceID _deviceId = 0;
|
||||
AudioFormat _format = { 0 };
|
||||
std::list<ISDLAudioChannel *> _channels;
|
||||
float _volume = 1.0f;
|
||||
float _adjustSoundVolume = 0.0f;
|
||||
float _adjustMusicVolume = 0.0f;
|
||||
uint8 _settingSoundVolume = 0xFF;
|
||||
uint8 _settingMusicVolume = 0xFF;
|
||||
|
||||
IAudioSource * _css1Sources[SOUND_MAXID] = { nullptr };
|
||||
IAudioSource * _musicSources[PATH_ID_END] = { nullptr };
|
||||
|
||||
Buffer _channelBuffer;
|
||||
Buffer _convertBuffer;
|
||||
Buffer _effectBuffer;
|
||||
|
||||
public:
|
||||
AudioMixerImpl()
|
||||
{
|
||||
_nullSource = AudioSource::CreateNull();
|
||||
}
|
||||
|
||||
~AudioMixerImpl()
|
||||
{
|
||||
Close();
|
||||
delete _nullSource;
|
||||
}
|
||||
|
||||
void Init(const char * device) override
|
||||
{
|
||||
Close();
|
||||
|
||||
SDL_AudioSpec want = { 0 };
|
||||
want.freq = 44100;
|
||||
want.format = AUDIO_S16SYS;
|
||||
want.channels = 2;
|
||||
want.samples = 1024;
|
||||
want.callback = [](void * arg, uint8 * dst, sint32 length) -> void
|
||||
{
|
||||
auto mixer = static_cast<AudioMixerImpl *>(arg);
|
||||
mixer->GetNextAudioChunk(dst, (size_t)length);
|
||||
};
|
||||
want.userdata = this;
|
||||
|
||||
SDL_AudioSpec have;
|
||||
_deviceId = SDL_OpenAudioDevice(device, 0, &want, &have, 0);
|
||||
_format.format = have.format;
|
||||
_format.channels = have.channels;
|
||||
_format.freq = have.freq;
|
||||
|
||||
LoadAllSounds();
|
||||
|
||||
SDL_PauseAudioDevice(_deviceId, 0);
|
||||
}
|
||||
|
||||
void Close() override
|
||||
{
|
||||
// Free channels
|
||||
Lock();
|
||||
for (IAudioChannel * channel : _channels)
|
||||
{
|
||||
delete channel;
|
||||
}
|
||||
_channels.clear();
|
||||
Unlock();
|
||||
|
||||
SDL_CloseAudioDevice(_deviceId);
|
||||
|
||||
// Free sources
|
||||
for (size_t i = 0; i < Util::CountOf(_css1Sources); i++)
|
||||
{
|
||||
if (_css1Sources[i] != _nullSource)
|
||||
{
|
||||
SafeDelete(_css1Sources[i]);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < Util::CountOf(_musicSources); i++)
|
||||
{
|
||||
if (_musicSources[i] != _nullSource)
|
||||
{
|
||||
SafeDelete(_musicSources[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Free buffers
|
||||
_channelBuffer.Free();
|
||||
_convertBuffer.Free();
|
||||
_effectBuffer.Free();
|
||||
}
|
||||
|
||||
void Lock() override
|
||||
{
|
||||
SDL_LockAudioDevice(_deviceId);
|
||||
}
|
||||
|
||||
void Unlock() override
|
||||
{
|
||||
SDL_UnlockAudioDevice(_deviceId);
|
||||
}
|
||||
|
||||
IAudioChannel * Play(IAudioSource * source, sint32 loop, bool deleteondone, bool deletesourceondone) override
|
||||
{
|
||||
Lock();
|
||||
ISDLAudioChannel * channel = AudioChannel::Create();
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->Play(source, loop);
|
||||
channel->SetDeleteOnDone(deleteondone);
|
||||
channel->SetDeleteSourceOnDone(deletesourceondone);
|
||||
_channels.push_back(channel);
|
||||
}
|
||||
Unlock();
|
||||
return channel;
|
||||
}
|
||||
|
||||
void Stop(IAudioChannel * channel) override
|
||||
{
|
||||
Lock();
|
||||
channel->SetStopping(true);
|
||||
Unlock();
|
||||
}
|
||||
|
||||
bool LoadMusic(size_t pathId) override
|
||||
{
|
||||
bool result = false;
|
||||
if (pathId < Util::CountOf(_musicSources))
|
||||
{
|
||||
IAudioSource * source = _musicSources[pathId];
|
||||
if (source == nullptr)
|
||||
{
|
||||
const utf8 * path = get_file_path((sint32)pathId);
|
||||
source = AudioSource::CreateMemoryFromWAV(path, &_format);
|
||||
if (source == nullptr)
|
||||
{
|
||||
source = _nullSource;
|
||||
}
|
||||
_musicSources[pathId] = source;
|
||||
}
|
||||
result = source != _nullSource;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetVolume(float volume) override
|
||||
{
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
IAudioSource * GetSoundSource(sint32 id) override
|
||||
{
|
||||
return _css1Sources[id];
|
||||
}
|
||||
|
||||
IAudioSource * GetMusicSource(sint32 id) override
|
||||
{
|
||||
return _musicSources[id];
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadAllSounds()
|
||||
{
|
||||
const utf8 * css1Path = get_file_path(PATH_ID_CSS1);
|
||||
for (size_t i = 0; i < Util::CountOf(_css1Sources); i++)
|
||||
{
|
||||
auto source = AudioSource::CreateMemoryFromCSS1(css1Path, i, &_format);
|
||||
if (source == nullptr)
|
||||
{
|
||||
source = _nullSource;
|
||||
}
|
||||
_css1Sources[i] = source;
|
||||
}
|
||||
}
|
||||
|
||||
void GetNextAudioChunk(uint8 * dst, size_t length)
|
||||
{
|
||||
UpdateAdjustedSound();
|
||||
|
||||
// Zero the output buffer
|
||||
Memory::Set(dst, 0, length);
|
||||
|
||||
// Mix channels onto output buffer
|
||||
auto it = _channels.begin();
|
||||
while (it != _channels.end())
|
||||
{
|
||||
auto channel = *it;
|
||||
sint32 group = channel->GetGroup();
|
||||
if (group != MIXER_GROUP_SOUND || gConfigSound.sound_enabled)
|
||||
{
|
||||
MixChannel(channel, dst, length);
|
||||
}
|
||||
if ((channel->IsDone() && channel->DeleteOnDone()) || channel->IsStopping())
|
||||
{
|
||||
delete channel;
|
||||
it = _channels.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAdjustedSound()
|
||||
{
|
||||
// Did the volume level get changed? Recalculate level in this case.
|
||||
if (_settingSoundVolume != gConfigSound.sound_volume)
|
||||
{
|
||||
_settingSoundVolume = gConfigSound.sound_volume;
|
||||
_adjustSoundVolume = powf(_settingSoundVolume / 100.f, 10.f / 6.f);
|
||||
}
|
||||
if (_settingMusicVolume != gConfigSound.ride_music_volume)
|
||||
{
|
||||
_settingMusicVolume = gConfigSound.ride_music_volume;
|
||||
_adjustMusicVolume = powf(_settingMusicVolume / 100.f, 10.f / 6.f);
|
||||
}
|
||||
}
|
||||
|
||||
void MixChannel(ISDLAudioChannel * channel, uint8 * data, size_t length)
|
||||
{
|
||||
sint32 byteRate = _format.GetByteRate();
|
||||
sint32 numSamples = (sint32)(length / byteRate);
|
||||
double rate = 1;
|
||||
if (_format.format == AUDIO_S16SYS)
|
||||
{
|
||||
rate = channel->GetRate();
|
||||
}
|
||||
|
||||
bool mustConvert = false;
|
||||
SDL_AudioCVT cvt;
|
||||
cvt.len_ratio = 1;
|
||||
AudioFormat streamformat = channel->GetFormat();
|
||||
if (streamformat != _format)
|
||||
{
|
||||
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, _format.format, _format.channels, _format.freq) == -1)
|
||||
{
|
||||
// Unable to convert channel data
|
||||
return;
|
||||
}
|
||||
mustConvert = true;
|
||||
}
|
||||
|
||||
// Read raw PCM from channel
|
||||
sint32 readSamples = (sint32)(numSamples * rate);
|
||||
size_t readLength = (size_t)(readSamples / cvt.len_ratio) * byteRate;
|
||||
_channelBuffer.EnsureCapacity(readLength);
|
||||
size_t bytesRead = channel->Read(_channelBuffer.GetData(), readLength);
|
||||
|
||||
// Convert data to required format if necessary
|
||||
void * buffer = nullptr;
|
||||
size_t bufferLen = 0;
|
||||
if (mustConvert)
|
||||
{
|
||||
if (Convert(&cvt, _channelBuffer.GetData(), bytesRead))
|
||||
{
|
||||
buffer = cvt.buf;
|
||||
bufferLen = cvt.len_cvt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = _channelBuffer.GetData();
|
||||
bufferLen = bytesRead;
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
if (rate != 1)
|
||||
{
|
||||
sint32 srcSamples = (sint32)(bufferLen / byteRate);
|
||||
sint32 dstSamples = numSamples;
|
||||
bufferLen = ApplyResample(channel, buffer, srcSamples, dstSamples);
|
||||
buffer = _effectBuffer.GetData();
|
||||
}
|
||||
|
||||
// Apply panning and volume
|
||||
ApplyPan(channel, buffer, bufferLen, byteRate);
|
||||
sint32 mixVolume = ApplyVolume(channel, buffer, bufferLen);
|
||||
|
||||
// Finally mix on to destination buffer
|
||||
size_t dstLength = Math::Min(length, bufferLen);
|
||||
SDL_MixAudioFormat(data, (const uint8 *)buffer, _format.format, (uint32)dstLength, mixVolume);
|
||||
|
||||
channel->UpdateOldVolume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resample the given buffer into _effectBuffer.
|
||||
* Assumes that srcBuffer is the same format as _format.
|
||||
*/
|
||||
size_t ApplyResample(ISDLAudioChannel * channel, const void * srcBuffer, sint32 srcSamples, sint32 dstSamples)
|
||||
{
|
||||
sint32 byteRate = _format.GetByteRate();
|
||||
|
||||
// Create resampler
|
||||
SpeexResamplerState * resampler = channel->GetResampler();
|
||||
if (resampler == nullptr)
|
||||
{
|
||||
resampler = speex_resampler_init(_format.channels, _format.freq, _format.freq, 0, 0);
|
||||
channel->SetResampler(resampler);
|
||||
}
|
||||
speex_resampler_set_rate(resampler, srcSamples, dstSamples);
|
||||
|
||||
// Ensure destination buffer is large enough
|
||||
size_t effectBufferReqLen = dstSamples * byteRate;
|
||||
_effectBuffer.EnsureCapacity(effectBufferReqLen);
|
||||
|
||||
uint32 inLen = srcSamples;
|
||||
uint32 outLen = dstSamples;
|
||||
speex_resampler_process_interleaved_int(
|
||||
resampler,
|
||||
(const spx_int16_t *)srcBuffer,
|
||||
&inLen,
|
||||
(spx_int16_t *)_effectBuffer.GetData(),
|
||||
&outLen);
|
||||
|
||||
return outLen * byteRate;
|
||||
}
|
||||
|
||||
void ApplyPan(const IAudioChannel * channel, void * buffer, size_t len, size_t sampleSize)
|
||||
{
|
||||
if (channel->GetPan() != 0.5f && _format.channels == 2)
|
||||
{
|
||||
switch (_format.format) {
|
||||
case AUDIO_S16SYS:
|
||||
EffectPanS16(channel, (sint16 *)buffer, (sint32)(len / sampleSize));
|
||||
break;
|
||||
case AUDIO_U8:
|
||||
EffectPanU8(channel, (uint8 *)buffer, (sint32)(len / sampleSize));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sint32 ApplyVolume(const IAudioChannel * channel, void * buffer, size_t len)
|
||||
{
|
||||
float volumeAdjust = _volume;
|
||||
volumeAdjust *= (gConfigSound.master_volume / 100.0f);
|
||||
switch (channel->GetGroup()) {
|
||||
case MIXER_GROUP_SOUND:
|
||||
volumeAdjust *= _adjustSoundVolume;
|
||||
|
||||
// Cap sound volume on title screen so music is more audible
|
||||
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)
|
||||
{
|
||||
volumeAdjust = Math::Min(volumeAdjust, 0.75f);
|
||||
}
|
||||
break;
|
||||
case MIXER_GROUP_RIDE_MUSIC:
|
||||
volumeAdjust *= _adjustMusicVolume;
|
||||
break;
|
||||
}
|
||||
|
||||
sint32 startVolume = (sint32)(channel->GetOldVolume() * volumeAdjust);
|
||||
sint32 endVolume = (sint32)(channel->GetVolume() * volumeAdjust);
|
||||
if (channel->IsStopping())
|
||||
{
|
||||
endVolume = 0;
|
||||
}
|
||||
|
||||
sint32 mixVolume = (sint32)(channel->GetVolume() * volumeAdjust);
|
||||
if (startVolume != endVolume)
|
||||
{
|
||||
// Set to max since we are adjusting the volume ourselves
|
||||
mixVolume = SDL_MIX_MAXVOLUME;
|
||||
|
||||
// Fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
|
||||
sint32 fadeLength = (sint32)len / _format.BytesPerSample();
|
||||
switch (_format.format) {
|
||||
case AUDIO_S16SYS:
|
||||
EffectFadeS16((sint16 *)buffer, fadeLength, startVolume, endVolume);
|
||||
break;
|
||||
case AUDIO_U8:
|
||||
EffectFadeU8((uint8 *)buffer, fadeLength, startVolume, endVolume);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mixVolume;
|
||||
}
|
||||
|
||||
static void EffectPanS16(const IAudioChannel * channel, sint16 * data, sint32 length)
|
||||
{
|
||||
const float dt = 1.0f / (length * 2);
|
||||
float volumeL = channel->GetOldVolumeL();
|
||||
float volumeR = channel->GetOldVolumeR();
|
||||
const float d_left = dt * (channel->GetVolumeL() - channel->GetOldVolumeL());
|
||||
const float d_right = dt * (channel->GetVolumeR() - channel->GetOldVolumeR());
|
||||
|
||||
for (sint32 i = 0; i < length * 2; i += 2)
|
||||
{
|
||||
data[i] = (sint16)(data[i] * volumeL);
|
||||
data[i + 1] = (sint16)(data[i + 1] * volumeR);
|
||||
volumeL += d_left;
|
||||
volumeR += d_right;
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectPanU8(const IAudioChannel * channel, uint8 * data, sint32 length)
|
||||
{
|
||||
float volumeL = channel->GetVolumeL();
|
||||
float volumeR = channel->GetVolumeR();
|
||||
float oldVolumeL = channel->GetOldVolumeL();
|
||||
float oldVolumeR = channel->GetOldVolumeR();
|
||||
|
||||
for (sint32 i = 0; i < length * 2; i += 2)
|
||||
{
|
||||
float t = (float)i / (length * 2);
|
||||
data[i] = (uint8)(data[i] * ((1.0 - t) * oldVolumeL + t * volumeL));
|
||||
data[i + 1] = (uint8)(data[i + 1] * ((1.0 - t) * oldVolumeR + t * volumeR));
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectFadeS16(sint16 * data, sint32 length, sint32 startvolume, sint32 endvolume)
|
||||
{
|
||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||
for (sint32 i = 0; i < length; i++)
|
||||
{
|
||||
float t = (float)i / length;
|
||||
data[i] = (sint16)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectFadeU8(uint8* data, sint32 length, sint32 startvolume, sint32 endvolume)
|
||||
{
|
||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||
for (sint32 i = 0; i < length; i++)
|
||||
{
|
||||
float t = (float)i / length;
|
||||
data[i] = (uint8)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||
}
|
||||
}
|
||||
|
||||
bool Convert(SDL_AudioCVT * cvt, const void * src, size_t len)
|
||||
{
|
||||
// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
|
||||
bool result = false;
|
||||
if (len != 0 && cvt->len_mult != 0)
|
||||
{
|
||||
size_t reqConvertBufferCapacity = len * cvt->len_mult;
|
||||
_convertBuffer.EnsureCapacity(reqConvertBufferCapacity);
|
||||
Memory::Copy(_convertBuffer.GetData(), src, len);
|
||||
|
||||
cvt->len = (sint32)len;
|
||||
cvt->buf = (uint8 *)_convertBuffer.GetData();
|
||||
if (SDL_ConvertAudio(cvt) >= 0)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioMixer * AudioMixer::Create()
|
||||
{
|
||||
return new AudioMixerImpl();
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,210 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
/**
|
||||
* An audio source where raw PCM data is streamed directly from
|
||||
* a file.
|
||||
*/
|
||||
class FileAudioSource final : public ISDLAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = { 0 };
|
||||
SDL_RWops * _rw = nullptr;
|
||||
uint64 _dataBegin = 0;
|
||||
uint64 _dataLength = 0;
|
||||
|
||||
public:
|
||||
~FileAudioSource()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
uint64 GetLength() const override
|
||||
{
|
||||
return _dataLength;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() const override
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
size_t bytesRead = 0;
|
||||
sint64 currentPosition = SDL_RWtell(_rw);
|
||||
if (currentPosition != -1)
|
||||
{
|
||||
size_t bytesToRead = (size_t)Math::Min<uint64>(len, _dataLength - offset);
|
||||
sint64 dataOffset = _dataBegin + offset;
|
||||
if (currentPosition != dataOffset)
|
||||
{
|
||||
sint64 newPosition = SDL_RWseek(_rw, dataOffset, SEEK_SET);
|
||||
if (newPosition == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
bytesRead = SDL_RWread(_rw, dst, 1, bytesToRead);
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(SDL_RWops * rw)
|
||||
{
|
||||
const uint32 DATA = 0x61746164;
|
||||
const uint32 FMT = 0x20746D66;
|
||||
const uint32 RIFF = 0x46464952;
|
||||
const uint32 WAVE = 0x45564157;
|
||||
const uint16 pcmformat = 0x0001;
|
||||
|
||||
Unload();
|
||||
|
||||
if (rw == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_rw = rw;
|
||||
|
||||
uint32 chunkId = SDL_ReadLE32(rw);
|
||||
if (chunkId != RIFF)
|
||||
{
|
||||
log_verbose("Not a WAV file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and discard chunk size
|
||||
SDL_ReadLE32(rw);
|
||||
uint32 chunkFormat = SDL_ReadLE32(rw);
|
||||
if (chunkFormat != WAVE)
|
||||
{
|
||||
log_verbose("Not in WAVE format");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 fmtChunkSize = FindChunk(rw, FMT);
|
||||
if (!fmtChunkSize)
|
||||
{
|
||||
log_verbose("Could not find FMT chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 chunkStart = SDL_RWtell(rw);
|
||||
|
||||
WaveFormat waveFormat;
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
SDL_RWseek(rw, chunkStart + fmtChunkSize, RW_SEEK_SET);
|
||||
if (waveFormat.encoding != pcmformat) {
|
||||
log_verbose("Not in proper format");
|
||||
return false;
|
||||
}
|
||||
|
||||
_format.freq = waveFormat.frequency;
|
||||
switch (waveFormat.bitspersample) {
|
||||
case 8:
|
||||
_format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
_format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
log_verbose("Invalid bits per sample");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
uint32 dataChunkSize = FindChunk(rw, DATA);
|
||||
if (dataChunkSize == 0)
|
||||
{
|
||||
log_verbose("Could not find DATA chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
_dataLength = dataChunkSize;
|
||||
_dataBegin = SDL_RWtell(rw);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 FindChunk(SDL_RWops * rw, uint32 wantedId)
|
||||
{
|
||||
uint32 subchunkId = SDL_ReadLE32(rw);
|
||||
uint32 subchunkSize = SDL_ReadLE32(rw);
|
||||
if (subchunkId == wantedId)
|
||||
{
|
||||
return subchunkSize;
|
||||
}
|
||||
const uint32 FACT = 0x74636166;
|
||||
const uint32 LIST = 0x5453494c;
|
||||
const uint32 BEXT = 0x74786562;
|
||||
const uint32 JUNK = 0x4B4E554A;
|
||||
while (subchunkId == FACT || subchunkId == LIST || subchunkId == BEXT || subchunkId == JUNK)
|
||||
{
|
||||
SDL_RWseek(rw, subchunkSize, RW_SEEK_CUR);
|
||||
subchunkId = SDL_ReadLE32(rw);
|
||||
subchunkSize = SDL_ReadLE32(rw);
|
||||
if (subchunkId == wantedId)
|
||||
{
|
||||
return subchunkSize;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Unload()
|
||||
{
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
_dataBegin = 0;
|
||||
_dataLength = 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateStreamFromWAV(const std::string &path)
|
||||
{
|
||||
IAudioSource * source = nullptr;
|
||||
SDL_RWops* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(rw);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource * AudioSource::CreateStreamFromWAV(SDL_RWops * rw)
|
||||
{
|
||||
auto source = new FileAudioSource();
|
||||
if (!source->LoadWAV(rw))
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,241 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL.h>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/audio/AudioMixer.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
/**
|
||||
* An audio source where raw PCM data is initially loaded into RAM from
|
||||
* a file and then streamed.
|
||||
*/
|
||||
class MemoryAudioSource final : public ISDLAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = { 0 };
|
||||
uint8 * _data = nullptr;
|
||||
size_t _length = 0;
|
||||
bool _isSDLWav = false;
|
||||
|
||||
public:
|
||||
~MemoryAudioSource()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
uint64 GetLength() const override
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() const override
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
size_t bytesToRead = 0;
|
||||
if (offset < _length)
|
||||
{
|
||||
bytesToRead = (size_t)Math::Min<uint64>(len, _length - offset);
|
||||
Memory::Copy<void>(dst, _data + offset, bytesToRead);
|
||||
}
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(const utf8 * path)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadWAV(%s)", path);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
SDL_AudioSpec audiospec = { 0 };
|
||||
uint32 audioLen;
|
||||
SDL_AudioSpec * spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_data, &audioLen);
|
||||
if (spec != nullptr)
|
||||
{
|
||||
_format.freq = spec->freq;
|
||||
_format.format = spec->format;
|
||||
_format.channels = spec->channels;
|
||||
_length = audioLen;
|
||||
_isSDLWav = true;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s, unsupported WAV format", path);
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LoadCSS1(const utf8 * path, size_t index)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadCSS1(%s, %d)", path, index);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
uint32 numSounds;
|
||||
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
|
||||
if (index < numSounds)
|
||||
{
|
||||
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
|
||||
|
||||
uint32 pcmOffset;
|
||||
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
uint32 pcmSize;
|
||||
SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1);
|
||||
_length = pcmSize;
|
||||
|
||||
WaveFormatEx waveFormat;
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
_format.freq = waveFormat.frequency;
|
||||
_format.format = AUDIO_S16LSB;
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
_data = new (std::nothrow) uint8[_length];
|
||||
if (_data != nullptr)
|
||||
{
|
||||
SDL_RWread(rw, _data, _length, 1);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to allocate data");
|
||||
}
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to load %s", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Convert(const AudioFormat * format)
|
||||
{
|
||||
if (*format != _format)
|
||||
{
|
||||
SDL_AudioCVT cvt;
|
||||
if (SDL_BuildAudioCVT(&cvt, _format.format, _format.channels, _format.freq, format->format, format->channels, format->freq) >= 0)
|
||||
{
|
||||
cvt.len = (sint32)_length;
|
||||
cvt.buf = new uint8[cvt.len * cvt.len_mult];
|
||||
Memory::Copy(cvt.buf, _data, _length);
|
||||
if (SDL_ConvertAudio(&cvt) >= 0)
|
||||
{
|
||||
Unload();
|
||||
_data = cvt.buf;
|
||||
_length = cvt.len_cvt;
|
||||
_format = *format;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] cvt.buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void Unload()
|
||||
{
|
||||
if (_data != nullptr)
|
||||
{
|
||||
if (_isSDLWav)
|
||||
{
|
||||
SDL_FreeWAV(_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] _data;
|
||||
}
|
||||
_data = nullptr;
|
||||
}
|
||||
_isSDLWav = false;
|
||||
_length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateMemoryFromCSS1(const std::string &path, size_t index, const AudioFormat * targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadCSS1(path.c_str(), index))
|
||||
{
|
||||
if (targetFormat != nullptr)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource * AudioSource::CreateMemoryFromWAV(const std::string &path, const AudioFormat * targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadWAV(path.c_str()))
|
||||
{
|
||||
if (targetFormat != nullptr)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
} }
|
|
@ -0,0 +1,36 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openrct2/common.h>
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
namespace Drawing
|
||||
{
|
||||
interface IDrawingEngine;
|
||||
}
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
Drawing::IDrawingEngine * CreateSoftwareDrawingEngine(IUiContext * uiContext);
|
||||
Drawing::IDrawingEngine * CreateHardwareDisplayDrawingEngine(IUiContext * uiContext);
|
||||
#ifndef DISABLE_OPENGL
|
||||
Drawing::IDrawingEngine * CreateOpenGLDrawingEngine(IUiContext * uiContext);
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -14,27 +14,35 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "../../core/Guard.hpp"
|
||||
#include "../../core/Math.hpp"
|
||||
#include "../../core/Memory.hpp"
|
||||
#include "../IDrawingContext.h"
|
||||
#include "../IDrawingEngine.h"
|
||||
#include "../Rain.h"
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include <openrct2/core/Guard.hpp>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/drawing/IDrawingContext.h>
|
||||
#include <openrct2/drawing/IDrawingEngine.h>
|
||||
#include <openrct2/drawing/Rain.h>
|
||||
#include "DrawingEngines.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../../config/Config.h"
|
||||
#include "../../game.h"
|
||||
#include "../../interface/screenshot.h"
|
||||
#include "../../interface/viewport.h"
|
||||
#include "../../interface/window.h"
|
||||
#include "../../intro.h"
|
||||
#include "../../platform/platform.h"
|
||||
#include "../../rct2.h"
|
||||
#include "../drawing.h"
|
||||
#include "../lightfx.h"
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/game.h>
|
||||
#include <openrct2/interface/screenshot.h>
|
||||
#include <openrct2/interface/viewport.h>
|
||||
#include <openrct2/interface/window.h>
|
||||
#include <openrct2/intro.h>
|
||||
#include <openrct2/platform/platform.h>
|
||||
#include <openrct2/rct2.h>
|
||||
#include <openrct2/drawing/drawing.h>
|
||||
#include <openrct2/drawing/lightfx.h>
|
||||
}
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
class SoftwareDrawingEngine;
|
||||
|
||||
struct DirtyGrid
|
||||
|
@ -184,8 +192,10 @@ public:
|
|||
class SoftwareDrawingEngine final : public IDrawingEngine
|
||||
{
|
||||
private:
|
||||
bool _hardwareDisplay;
|
||||
IUiContext * const _uiContext;
|
||||
bool const _hardwareDisplay;
|
||||
|
||||
SDL_Window * _window = nullptr;
|
||||
SDL_Surface * _surface = nullptr;
|
||||
SDL_Surface * _RGBASurface = nullptr;
|
||||
SDL_Palette * _palette = nullptr;
|
||||
|
@ -220,9 +230,11 @@ private:
|
|||
SoftwareDrawingContext * _drawingContext;
|
||||
|
||||
public:
|
||||
explicit SoftwareDrawingEngine(bool hardwareDisplay)
|
||||
explicit SoftwareDrawingEngine(IUiContext * uiContext, bool hardwareDisplay)
|
||||
: _uiContext(uiContext),
|
||||
_hardwareDisplay(hardwareDisplay)
|
||||
{
|
||||
_hardwareDisplay = hardwareDisplay;
|
||||
UNUSED(_uiContext); // Will be used in due course to retrieve window information
|
||||
_drawingContext = new SoftwareDrawingContext(this);
|
||||
#ifdef __ENABLE_LIGHTFX__
|
||||
_lastLightFXenabled = (gConfigGeneral.enable_light_fx != 0);
|
||||
|
@ -244,6 +256,7 @@ public:
|
|||
|
||||
void Initialise(SDL_Window * window) override
|
||||
{
|
||||
_window = window;
|
||||
if (_hardwareDisplay)
|
||||
{
|
||||
// Try to create the accelerated renderer.
|
||||
|
@ -308,7 +321,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void SetPalette(SDL_Color * palette) override
|
||||
void SetPalette(const rct_palette_entry * palette) override
|
||||
{
|
||||
if (_sdlRenderer != nullptr)
|
||||
{
|
||||
|
@ -316,7 +329,7 @@ public:
|
|||
{
|
||||
for (sint32 i = 0; i < 256; i++)
|
||||
{
|
||||
_paletteHWMapped[i] = SDL_MapRGB(_screenTextureFormat, palette[i].r, palette[i].g, palette[i].b);
|
||||
_paletteHWMapped[i] = SDL_MapRGB(_screenTextureFormat, palette[i].red, palette[i].green, palette[i].blue);
|
||||
}
|
||||
|
||||
#ifdef __ENABLE_LIGHTFX__
|
||||
|
@ -333,17 +346,17 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow);
|
||||
if (windowSurface == nullptr)
|
||||
SDL_Surface * windowSurface = SDL_GetWindowSurface(_window);
|
||||
if (windowSurface != nullptr && _palette != nullptr)
|
||||
{
|
||||
log_fatal("SDL_GetWindowSurface failed %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (_palette != nullptr && SDL_SetPaletteColors(_palette, palette, 0, 256))
|
||||
{
|
||||
log_fatal("SDL_SetPaletteColors failed %s", SDL_GetError());
|
||||
exit(1);
|
||||
SDL_Colour colours[256];
|
||||
for (sint32 i = 0; i < 256; i++) {
|
||||
colours[i].r = palette[i].red;
|
||||
colours[i].g = palette[i].green;
|
||||
colours[i].b = palette[i].blue;
|
||||
colours[i].a = palette[i].alpha;
|
||||
}
|
||||
SDL_SetPaletteColors(_palette, colours, 0, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -646,8 +659,8 @@ private:
|
|||
// Determine region in pixels
|
||||
uint32 left = Math::Max<uint32>(0, x * _dirtyGrid.BlockWidth);
|
||||
uint32 top = Math::Max<uint32>(0, y * _dirtyGrid.BlockHeight);
|
||||
uint32 right = Math::Min((uint32)gScreenWidth, left + (columns * _dirtyGrid.BlockWidth));
|
||||
uint32 bottom = Math::Min((uint32)gScreenHeight, top + (rows * _dirtyGrid.BlockHeight));
|
||||
uint32 right = Math::Min(_width, left + (columns * _dirtyGrid.BlockWidth));
|
||||
uint32 bottom = Math::Min(_height, top + (rows * _dirtyGrid.BlockHeight));
|
||||
if (right <= left || bottom <= top)
|
||||
{
|
||||
return;
|
||||
|
@ -681,7 +694,7 @@ private:
|
|||
// Copy the surface to the window
|
||||
if (gConfigGeneral.window_scale == 1 || gConfigGeneral.window_scale <= 0)
|
||||
{
|
||||
SDL_Surface * windowSurface = SDL_GetWindowSurface(gWindow);
|
||||
SDL_Surface * windowSurface = SDL_GetWindowSurface(_window);
|
||||
if (SDL_BlitSurface(_surface, nullptr, windowSurface, nullptr))
|
||||
{
|
||||
log_fatal("SDL_BlitSurface %s", SDL_GetError());
|
||||
|
@ -699,13 +712,13 @@ private:
|
|||
|
||||
// then scale to window size. Without changing to RGBA first, SDL complains
|
||||
// about blit configurations being incompatible.
|
||||
if (SDL_BlitScaled(_RGBASurface, nullptr, SDL_GetWindowSurface(gWindow), nullptr))
|
||||
if (SDL_BlitScaled(_RGBASurface, nullptr, SDL_GetWindowSurface(_window), nullptr))
|
||||
{
|
||||
log_fatal("SDL_BlitScaled %s", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (SDL_UpdateWindowSurface(gWindow))
|
||||
if (SDL_UpdateWindowSurface(_window))
|
||||
{
|
||||
log_fatal("SDL_UpdateWindowSurface %s", SDL_GetError());
|
||||
exit(1);
|
||||
|
@ -726,14 +739,15 @@ private:
|
|||
}
|
||||
SDL_RenderCopy(_sdlRenderer, _screenTexture, nullptr, nullptr);
|
||||
|
||||
if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
|
||||
bool isSteamOverlayActive = GetContext()->GetUiContext()->IsSteamOverlayActive();
|
||||
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
|
||||
{
|
||||
OverlayPreRenderCheck();
|
||||
}
|
||||
|
||||
SDL_RenderPresent(_sdlRenderer);
|
||||
|
||||
if (gSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
|
||||
if (isSteamOverlayActive && gConfigGeneral.steam_overlay_pause)
|
||||
{
|
||||
OverlayPostRenderCheck();
|
||||
}
|
||||
|
@ -826,14 +840,14 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
IDrawingEngine * DrawingEngineFactory::CreateSoftware()
|
||||
IDrawingEngine * OpenRCT2::Ui::CreateSoftwareDrawingEngine(IUiContext * uiContext)
|
||||
{
|
||||
return new SoftwareDrawingEngine(false);
|
||||
return new SoftwareDrawingEngine(uiContext, false);
|
||||
}
|
||||
|
||||
IDrawingEngine * DrawingEngineFactory::CreateSoftwareWithHardwareDisplay()
|
||||
IDrawingEngine * OpenRCT2::Ui::CreateHardwareDisplayDrawingEngine(IUiContext * uiContext)
|
||||
{
|
||||
return new SoftwareDrawingEngine(true);
|
||||
return new SoftwareDrawingEngine(uiContext, true);
|
||||
}
|
||||
|
||||
SoftwareDrawingContext::SoftwareDrawingContext(SoftwareDrawingEngine * engine)
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include "OpenGLAPI.h"
|
||||
#include "GLSLTypes.h"
|
||||
#include "TextureCache.h"
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include <SDL_video.h>
|
||||
|
||||
#include "../../../core/Console.hpp"
|
||||
#include <openrct2/core/Console.hpp>
|
||||
|
||||
template <typename T>
|
||||
static inline bool SetProc(T * func, const char * name)
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
|
||||
#if OPENGL_NO_LINK
|
||||
|
|
@ -14,21 +14,32 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifdef DISABLE_OPENGL
|
||||
|
||||
#include "../../IDrawingEngine.h"
|
||||
|
||||
IDrawingEngine * DrawingEngineFactory::CreateOpenGL()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
#ifndef DISABLE_OPENGL
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <SDL_platform.h>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/core/Console.hpp>
|
||||
#include <openrct2/core/Exception.hpp>
|
||||
#include <openrct2/core/Math.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/drawing/IDrawingContext.h>
|
||||
#include <openrct2/drawing/IDrawingEngine.h>
|
||||
#include <openrct2/drawing/Rain.h>
|
||||
#include <openrct2/config/Config.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <openrct2/interface/screenshot.h>
|
||||
#include <openrct2/interface/window.h>
|
||||
#include <openrct2/intro.h>
|
||||
#include <openrct2/drawing/drawing.h>
|
||||
}
|
||||
|
||||
#include "../DrawingEngines.h"
|
||||
#include "GLSLTypes.h"
|
||||
#include "OpenGLAPI.h"
|
||||
#include "OpenGLFramebuffer.h"
|
||||
|
@ -40,23 +51,9 @@ IDrawingEngine * DrawingEngineFactory::CreateOpenGL()
|
|||
#include "TextureCache.h"
|
||||
#include "DrawCommands.h"
|
||||
|
||||
#include "../../../core/Console.hpp"
|
||||
#include "../../../core/Exception.hpp"
|
||||
#include "../../../core/Math.hpp"
|
||||
#include "../../../core/Memory.hpp"
|
||||
#include "../../IDrawingContext.h"
|
||||
#include "../../IDrawingEngine.h"
|
||||
#include "../../Rain.h"
|
||||
#include "../../../config/Config.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../../../config/Config.h"
|
||||
#include "../../../interface/screenshot.h"
|
||||
#include "../../../interface/window.h"
|
||||
#include "../../../intro.h"
|
||||
#include "../../drawing.h"
|
||||
}
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
struct OpenGLVersion
|
||||
{
|
||||
|
@ -229,8 +226,9 @@ public:
|
|||
class OpenGLDrawingEngine : public IDrawingEngine
|
||||
{
|
||||
private:
|
||||
SDL_Window * _window = nullptr;
|
||||
SDL_GLContext _context = nullptr;
|
||||
IUiContext * const _uiContext = nullptr;
|
||||
SDL_Window * _window = nullptr;
|
||||
SDL_GLContext _context = nullptr;
|
||||
|
||||
uint32 _width = 0;
|
||||
uint32 _height = 0;
|
||||
|
@ -250,8 +248,10 @@ public:
|
|||
SDL_Color Palette[256];
|
||||
vec4f GLPalette[256];
|
||||
|
||||
OpenGLDrawingEngine()
|
||||
OpenGLDrawingEngine(IUiContext * uiContext)
|
||||
: _uiContext(uiContext)
|
||||
{
|
||||
UNUSED(_uiContext); // Will be used in due course to retrieve window information
|
||||
_drawingContext = new OpenGLDrawingContext(this);
|
||||
}
|
||||
|
||||
|
@ -305,11 +305,14 @@ public:
|
|||
_drawingContext->Resize(width, height);
|
||||
}
|
||||
|
||||
void SetPalette(SDL_Color * palette) override
|
||||
void SetPalette(const rct_palette_entry * palette) override
|
||||
{
|
||||
for (sint32 i = 0; i < 256; i++)
|
||||
{
|
||||
SDL_Color colour = palette[i];
|
||||
SDL_Color colour;
|
||||
colour.r = palette[i].red;
|
||||
colour.g = palette[i].green;
|
||||
colour.b = palette[i].blue;
|
||||
colour.a = i == 0 ? 0 : 255;
|
||||
|
||||
Palette[i] = colour;
|
||||
|
@ -498,9 +501,9 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
IDrawingEngine * DrawingEngineFactory::CreateOpenGL()
|
||||
IDrawingEngine * OpenRCT2::Ui::CreateOpenGLDrawingEngine(IUiContext * uiContext)
|
||||
{
|
||||
return new OpenGLDrawingEngine();
|
||||
return new OpenGLDrawingEngine(uiContext);
|
||||
}
|
||||
|
||||
OpenGLDrawingContext::OpenGLDrawingContext(OpenGLDrawingEngine * engine)
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
#ifndef DISABLE_OPENGL
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include <SDL_video.h>
|
||||
#include "../../../core/Memory.hpp"
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include "OpenGLFramebuffer.h"
|
||||
|
||||
constexpr GLuint BACKBUFFER_ID = 0;
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include "OpenGLAPI.h"
|
||||
|
||||
struct SDL_Window;
|
|
@ -16,17 +16,17 @@
|
|||
|
||||
#ifndef DISABLE_OPENGL
|
||||
|
||||
#include "../../../core/Console.hpp"
|
||||
#include "../../../core/Exception.hpp"
|
||||
#include "../../../core/FileStream.hpp"
|
||||
#include "../../../core/Memory.hpp"
|
||||
#include "../../../core/Path.hpp"
|
||||
#include "../../../core/String.hpp"
|
||||
#include <openrct2/core/Console.hpp>
|
||||
#include <openrct2/core/Exception.hpp>
|
||||
#include <openrct2/core/FileStream.hpp>
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include <openrct2/core/Path.hpp>
|
||||
#include <openrct2/core/String.hpp>
|
||||
#include "OpenGLShaderProgram.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../../../platform/platform.h"
|
||||
#include <openrct2/platform/platform.h>
|
||||
}
|
||||
|
||||
OpenGLShader::OpenGLShader(const char * name, GLenum type)
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include "OpenGLAPI.h"
|
||||
|
||||
class OpenGLShader final
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include "OpenGLAPI.h"
|
||||
|
||||
class CopyFramebufferShader;
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include "../../../core/Memory.hpp"
|
||||
#include <openrct2/core/Memory.hpp>
|
||||
#include "TextureCache.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../../drawing.h"
|
||||
#include <openrct2/drawing/drawing.h>
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache()
|
|
@ -20,7 +20,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <SDL_pixels.h>
|
||||
#include "../../../common.h"
|
||||
#include <openrct2/common.h>
|
||||
#include "OpenGLAPI.h"
|
||||
#include "GLSLTypes.h"
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<SolutionDir Condition="'$(SolutionDir)'==''">..\..\</SolutionDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="audio\AudioChannel.cpp" />
|
||||
<ClCompile Include="audio\AudioContext.cpp" />
|
||||
<ClCompile Include="audio\AudioMixer.cpp" />
|
||||
<ClCompile Include="audio\FileAudioSource.cpp" />
|
||||
<ClCompile Include="audio\MemoryAudioSource.cpp" />
|
||||
<ClCompile Include="CursorData.cpp" />
|
||||
<ClCompile Include="CursorRepository.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\CopyFramebufferShader.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\DrawImageShader.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\DrawLineShader.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\FillRectShader.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\OpenGLAPI.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\OpenGLDrawingEngine.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\OpenGLFramebuffer.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\OpenGLShaderProgram.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\SwapFramebuffer.cpp" />
|
||||
<ClCompile Include="drawing\engines\opengl\TextureCache.cpp" />
|
||||
<ClCompile Include="drawing\engines\SoftwareDrawingEngine.cpp" />
|
||||
<ClCompile Include="TextComposition.cpp" />
|
||||
<ClCompile Include="Ui.cpp" />
|
||||
<ClCompile Include="UiContext.cpp" />
|
||||
<ClCompile Include="UiContext.Linux.cpp" />
|
||||
<ClCompile Include="UiContext.Win32.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="audio\AudioContext.h" />
|
||||
<ClInclude Include="audio\AudioFormat.h" />
|
||||
<ClInclude Include="CursorRepository.h" />
|
||||
<ClInclude Include="drawing\engines\DrawingEngines.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\CopyFramebufferShader.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\DrawCommands.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\DrawImageShader.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\DrawLineShader.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\FillRectShader.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\GLSLTypes.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\OpenGLAPI.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\OpenGLFramebuffer.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\OpenGLShaderProgram.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\SwapFramebuffer.h" />
|
||||
<ClInclude Include="drawing\engines\opengl\TextureCache.h" />
|
||||
<ClInclude Include="SDLException.h" />
|
||||
<ClInclude Include="TextComposition.h" />
|
||||
<ClInclude Include="UiContext.h" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8DD8AB7D-2EA6-44E3-8265-BAF08E832951}</ProjectGuid>
|
||||
<RootNamespace>openrct2-ui</RootNamespace>
|
||||
<ProjectName>libopenrct2ui</ProjectName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<!-- Debug|Win32 is reserved for RCT2 interop builds, this means it has to be a DLL -->
|
||||
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\openrct2.common.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>openrct2</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>__ENABLE_LIGHTFX__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Breakpad)'=='true'">USE_BREAKPAD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<ObjectFileName>$(IntDir)\%(RelativeDir)</ObjectFileName>
|
||||
<AdditionalOptions>$(OPENRCT2_CL_ADDITIONALOPTIONS) %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<TargetMachine Condition="'$(Platform)'=='Win32'">MachineX86</TargetMachine>
|
||||
<TargetMachine Condition="'$(Platform)'=='x64'">MachineX64</TargetMachine>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,327 @@
|
|||
# CMAKE project for libopenrct2 (core OpenRCT2 component)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif ()
|
||||
|
||||
# CMake dependencies
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(FindPkgConfig)
|
||||
|
||||
# Options
|
||||
option(STATIC "Create a static build.")
|
||||
option(USE_MMAP "Use mmap to try loading rct2's data segment into memory.")
|
||||
option(DISABLE_RCT2 "Build a standalone version, without using code and data segments from vanilla. On by default." ON)
|
||||
|
||||
option(DISABLE_HTTP_TWITCH "Disable HTTP and Twitch support.")
|
||||
option(DISABLE_NETWORK "Disable multiplayer functionality. Mainly for testing.")
|
||||
option(DISABLE_TTF "Disable support for TTF provided by SDL2_ttf.")
|
||||
option(ENABLE_LIGHTFX "Enable lighting effects." ON)
|
||||
|
||||
if (NOT DISABLE_RCT2)
|
||||
if (WIN32)
|
||||
message(FATAL_ERROR "DISABLE_RCT2 not supported for Windows")
|
||||
endif ()
|
||||
if (NOT FORCE32)
|
||||
message(FATAL_ERROR "DISABLE_RCT2 requires FORCE32")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Needed for linking with non-broken OpenSSL on Apple platforms
|
||||
if (APPLE)
|
||||
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/opt/openssl/lib/pkgconfig")
|
||||
endif ()
|
||||
|
||||
# Third party libraries
|
||||
PKG_CHECK_MODULES(JANSSON REQUIRED jansson>=2.5)
|
||||
PKG_CHECK_MODULES(LIBZIP REQUIRED libzip>=1.0)
|
||||
PKG_CHECK_MODULES(ZLIB REQUIRED zlib)
|
||||
|
||||
PKG_CHECK_MODULES(PNG libpng>=1.6)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG libpng16)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG libpng>=1.2)
|
||||
if (NOT PNG_FOUND)
|
||||
PKG_CHECK_MODULES(PNG REQUIRED libpng12)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Third party libraries (optional)
|
||||
if (NOT DISABLE_HTTP_TWITCH OR NOT DISABLE_NETWORK)
|
||||
PKG_CHECK_MODULES(LIBCURL REQUIRED libcurl)
|
||||
endif ()
|
||||
if (NOT DISABLE_NETWORK)
|
||||
find_package(OpenSSL 1.0.0 REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Third party libraries (which we want to eventually remove from libopenrct2)
|
||||
PKG_CHECK_MODULES(SDL2 REQUIRED sdl2)
|
||||
PKG_CHECK_MODULES(SPEEX REQUIRED speexdsp)
|
||||
if (NOT DISABLE_TTF)
|
||||
if (STATIC)
|
||||
# FreeType is required by SDL2_ttf, but not wired up properly in package
|
||||
PKG_CHECK_MODULES(FREETYPE REQUIRED freetype2)
|
||||
endif ()
|
||||
if (UNIX AND NOT APPLE)
|
||||
PKG_CHECK_MODULES(FONTCONFIG REQUIRED fontconfig)
|
||||
endif ()
|
||||
PKG_CHECK_MODULES(SDL2_TTF REQUIRED SDL2_ttf)
|
||||
endif ()
|
||||
|
||||
# Sources
|
||||
file(GLOB_RECURSE OPENRCT2_CORE_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.c"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/*.hpp")
|
||||
if (APPLE)
|
||||
file(GLOB_RECURSE OPENRCT2_CORE_MM_SOURCES "${CMAKE_CURRENT_LIST_DIR}/*.m")
|
||||
set_source_files_properties(${OPENRCT2_CORE_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-x objective-c -fmodules")
|
||||
endif ()
|
||||
|
||||
# Handle creating the rct2 text and data files on macOS and Linux
|
||||
# See details in src/openrct2/rct2/interop.c:rct2_interop_setup_segment for how the values
|
||||
# were derived.
|
||||
if (NOT DISABLE_RCT2 AND UNIX)
|
||||
set(OPENRCT2_EXE "${ROOT_DIR}/openrct2.exe")
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_text
|
||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_text" bs=4096 skip=1 count=1187
|
||||
DEPENDS ${OPENRCT2_EXE}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_data
|
||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1188 count=318
|
||||
COMMAND dd if=/dev/zero of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 seek=318 count=2630 conv=notrunc
|
||||
COMMAND dd if="${OPENRCT2_EXE}" of="${CMAKE_BINARY_DIR}/openrct2_data" bs=4096 skip=1506 seek=2948 count=1 conv=notrunc
|
||||
DEPENDS ${OPENRCT2_EXE}
|
||||
)
|
||||
add_custom_target(segfiles DEPENDS openrct2_text openrct2_data)
|
||||
if (NOT USE_MMAP)
|
||||
set(OBJ_FORMAT "elf32-i386")
|
||||
set(LINKER_SCRIPT "ld_script_i386.xc")
|
||||
if (APPLE)
|
||||
set(RCT2_SEGMENT_LINKER_FLAGS "-sectcreate rct2_text __text ${CMAKE_BINARY_DIR}/openrct2_text -sectcreate rct2_data __data ${CMAKE_BINARY_DIR}/openrct2_data -segaddr rct2_data 0x8a4000 -segprot rct2_data rwx rwx -segaddr rct2_text 0x401000 -segprot rct2_text rwx rwx -segaddr __TEXT 0x2000000 -read_only_relocs suppress")
|
||||
else ()
|
||||
# For Linux we have to use objcopy to wrap regular binaries into a linkable
|
||||
# format. We use specific section names which are then referenced in a
|
||||
# bespoke linker script so they can be placed at predefined VMAs.
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_text_section.o
|
||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_text openrct2_text_section.o --rename-section .data=.rct2_text,contents,alloc,load,readonly,code
|
||||
DEPENDS segfiles
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT openrct2_data_section.o
|
||||
COMMAND objcopy --input binary --output ${OBJ_FORMAT} --binary-architecture i386 openrct2_data openrct2_data_section.o --rename-section .data=.rct2_data,contents,alloc,load,readonly,data
|
||||
DEPENDS segfiles
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(linkable_sections DEPENDS openrct2_text_section.o openrct2_data_section.o)
|
||||
set_source_files_properties(
|
||||
openrct2_text_section.o openrct2_data_section.o
|
||||
PROPERTIES
|
||||
EXTERNAL_OBJECT true
|
||||
GENERATED true
|
||||
)
|
||||
# can't use GLOB here, as the files don't exist yet at cmake-time
|
||||
set(RCT2_SECTIONS "${CMAKE_BINARY_DIR}/openrct2_data_section.o" "${CMAKE_BINARY_DIR}/openrct2_text_section.o")
|
||||
set(RCT2_SEGMENT_LINKER_FLAGS "-Wl,-T,\"${ROOT_DIR}/distribution/linux/${LINKER_SCRIPT}\"")
|
||||
endif ()
|
||||
endif ()
|
||||
elseif (USE_MMAP)
|
||||
# No dd here, can't extract data segment
|
||||
message(WARNING "Sorry, your platform is not supported, you have to extract data segment manually")
|
||||
endif ()
|
||||
|
||||
# Outputs
|
||||
set(PROJECT libopenrct2)
|
||||
project(${PROJECT})
|
||||
add_library(${PROJECT} SHARED ${OPENRCT2_CORE_SOURCES} ${OPENRCT2_CORE_MM_SOURCES} ${RCT2_SECTIONS})
|
||||
set_target_properties(${PROJECT} PROPERTIES PREFIX "")
|
||||
set_target_properties(${PROJECT} PROPERTIES COMPILE_FLAGS "-Wundef")
|
||||
|
||||
if (NOT DISABLE_RCT2)
|
||||
add_dependencies(${PROJECT} segfiles)
|
||||
if (NOT USE_MMAP)
|
||||
if (NOT APPLE)
|
||||
add_dependencies(${PROJECT} linkable_sections)
|
||||
endif ()
|
||||
set_target_properties(${PROJECT} PROPERTIES LINK_FLAGS ${RCT2_SEGMENT_LINKER_FLAGS})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Libraries
|
||||
if (STATIC)
|
||||
target_link_libraries(${PROJECT} ${SDL2_STATIC_LIBRARIES}
|
||||
${JANSSON_STATIC_LIBRARIES}
|
||||
${SPEEX_STATIC_LIBRARIES}
|
||||
${PNG_STATIC_LIBRARIES}
|
||||
${ZLIB_STATIC_LIBRARIES}
|
||||
${LIBZIP_STATIC_LIBRARIES})
|
||||
else ()
|
||||
target_link_libraries(${PROJECT} ${SDL2_LIBRARIES}
|
||||
${JANSSON_LIBRARIES}
|
||||
${SPEEX_LIBRARIES}
|
||||
${PNG_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (UNIX AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
# Include libdl for dlopen
|
||||
target_link_libraries(${PROJECT} dl)
|
||||
endif ()
|
||||
|
||||
if (NOT DISABLE_NETWORK)
|
||||
if (WIN32)
|
||||
target_link_libraries(${PROJECT} ws2_32)
|
||||
endif ()
|
||||
if (STATIC)
|
||||
target_link_libraries(${PROJECT} ${LIBCURL_STATIC_LIBRARIES}
|
||||
${SSL_STATIC_LIBRARIES})
|
||||
else ()
|
||||
target_link_libraries(${PROJECT} ${LIBCURL_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (NOT DISABLE_TTF)
|
||||
if (STATIC)
|
||||
target_link_libraries(${PROJECT} ${FREETYPE_STATIC_LIBRARIES}
|
||||
${SDL2_TTF_STATIC_LIBRARIES})
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(${PROJECT} ${FONTCONFIG_STATIC_LIBRARIES})
|
||||
endif ()
|
||||
else ()
|
||||
target_link_libraries(${PROJECT} ${SDL2_TTF_LIBRARIES})
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(${PROJECT} ${FONTCONFIG_LIBRARIES})
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (APPLE OR STATIC OR ${CMAKE_SYSTEM_NAME} MATCHES "BSD")
|
||||
find_library(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c)
|
||||
target_link_libraries(${PROJECT} ${ICONV_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Includes
|
||||
target_include_directories(${PROJECT} SYSTEM PRIVATE ${LIBZIP_INCLUDE_DIRS})
|
||||
target_include_directories(${PROJECT} PRIVATE ${SDL2_INCLUDE_DIRS}
|
||||
${JANSSON_INCLUDE_DIRS}
|
||||
${SPEEX_INCLUDE_DIRS}
|
||||
${PNG_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIRS})
|
||||
if (NOT DISABLE_HTTP_TWITCH OR NOT DISABLE_NETWORK)
|
||||
target_include_directories(${PROJECT} PRIVATE ${LIBCURL_INCLUDE_DIRS})
|
||||
endif ()
|
||||
if (NOT DISABLE_NETWORK)
|
||||
target_include_directories(${PROJECT} PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
endif ()
|
||||
if (NOT DISABLE_TTF AND UNIX AND NOT APPLE)
|
||||
target_include_directories(${PROJECT} PRIVATE ${FONTCONFIG_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
# macOS builds fail on the use of tmpnam otherwise (#4959)
|
||||
if(APPLE)
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -Wno-error=deprecated-declarations -Wno-error=objc-method-access")
|
||||
endif()
|
||||
|
||||
# Compiler flags
|
||||
set(DEBUG_LEVEL 0 CACHE STRING "Select debug level for compilation. Use value in range 0–3.")
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fstrict-aliasing -Werror -Wundef -Wmissing-declarations -Winit-self -Wall -Wno-unknown-pragmas -Wno-unused-function -Wno-missing-braces ")
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -Wno-comment -Wshadow -Wmissing-declarations -Wnonnull")
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
|
||||
# On mingw all code is already PIC, this will avoid compiler error on redefining this option
|
||||
if (NOT MINGW)
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fPIC")
|
||||
endif ()
|
||||
|
||||
if (APPLE AND NOT USE_MMAP)
|
||||
set(PIE_FLAG "-fno-pie")
|
||||
else ()
|
||||
set(PIE_FLAG "-fpie")
|
||||
endif ()
|
||||
|
||||
if (FORCE32)
|
||||
set(TARGET_M "-m32")
|
||||
endif ()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 ${COMMON_COMPILE_OPTIONS} ${TARGET_M} -Wimplicit")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14 ${COMMON_COMPILE_OPTIONS} ${TARGET_M}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TARGET_M}")
|
||||
|
||||
function (ADD_CHECK_C_COMPILER_FLAG _CFLAGS _CACHE_VAR _FLAG)
|
||||
CHECK_C_COMPILER_FLAG("${_FLAG}" "${_CACHE_VAR}")
|
||||
if (${_CACHE_VAR})
|
||||
set(${_CFLAGS} "${${_CFLAGS}} ${_FLAG}" PARENT_SCOPE)
|
||||
else ()
|
||||
message(STATUS "Unsupported CFLAG: ${_FLAG}")
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
function (ADD_CHECK_CXX_COMPILER_FLAG _CXXFLAGS _CACHE_VAR _FLAG)
|
||||
CHECK_CXX_COMPILER_FLAG("${_FLAG}" "${_CACHE_VAR}")
|
||||
if (${_CACHE_VAR})
|
||||
set(${_CXXFLAGS} "${${_CXXFLAGS}} ${_FLAG}" PARENT_SCOPE)
|
||||
else ()
|
||||
message(STATUS "Unsupported CXXFLAG: ${_FLAG}")
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
# Launchpad turns on -Wdate-time for compilers that support it, this shouldn't break our build
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_WRITE_STRINGS -Wno-error=date-time)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_WRITE_STRINGS -Wno-error=date-time)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_NULL_DEREFERENCE -Wnull-dereference)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_FINAL_TYPES -Wsuggest-final-types)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_FINAL_METHODS -Wsuggest-final-methods)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_OVERRIDE -Wsuggest-override)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_DUPLICATED_COND -Wduplicated-cond)
|
||||
if (MINGW)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_SUGGEST_OVERRIDE -Wno-error=redundant-decls)
|
||||
endif ()
|
||||
|
||||
# Items below are not supported by ICC
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_REDUNDANT_DECLS -Wredundant-decls)
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_IGNORED_QUALIFIERS -Wignored-qualifiers)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_REDUNDANT_DECLS -Wredundant-decls)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_IGNORED_QUALIFIERS -Wignored-qualifiers)
|
||||
|
||||
# -Wstrict-overflow is only active when -fstrict-overflow is enabled, but -fstrict-overflow
|
||||
# is enabled on -O2, -O3, -Os. This should help catch bugs locally before they reach Travis
|
||||
# As of 2a435bf -Wstrict-overflow=1 passes, but higher values do not.
|
||||
set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fstrict-overflow")
|
||||
ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WARN_STRICT_OVERFLOW -Wstrict-overflow=1)
|
||||
ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS CXX_WARN_STRICT_OVERFLOW -Wstrict-overflow=1)
|
||||
|
||||
# Defines
|
||||
if (USE_MMAP)
|
||||
add_definitions(-DUSE_MMAP)
|
||||
endif ()
|
||||
if (DISABLE_NETWORK)
|
||||
add_definitions(-DDISABLE_NETWORK)
|
||||
endif ()
|
||||
if (DISABLE_HTTP_TWITCH)
|
||||
add_definitions(-DDISABLE_HTTP)
|
||||
add_definitions(-DDISABLE_TWITCH)
|
||||
endif ()
|
||||
if (DISABLE_TTF)
|
||||
add_definitions(-DNO_TTF)
|
||||
endif ()
|
||||
if (ENABLE_LIGHTFX)
|
||||
add_definitions(-D__ENABLE_LIGHTFX__)
|
||||
endif ()
|
||||
if (DISABLE_RCT2)
|
||||
add_definitions(-DNO_RCT2)
|
||||
endif ()
|
||||
|
||||
if (NOT DISABLE_RCT2)
|
||||
# Disable optimizations for addresses.c for all compilers, to allow optimized
|
||||
# builds without need for -fno-omit-frame-pointer
|
||||
set_source_files_properties(${CMAKE_CURRENT_LIST_DIR}/rct2/addresses.c PROPERTIES COMPILE_FLAGS -O0)
|
||||
endif ()
|
|
@ -0,0 +1,702 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "Context.h"
|
||||
#include "ui/UiContext.h"
|
||||
#include "core/Console.hpp"
|
||||
#include "core/File.h"
|
||||
#include "core/FileStream.hpp"
|
||||
#include "core/Guard.hpp"
|
||||
#include "core/String.hpp"
|
||||
#include "FileClassifier.h"
|
||||
#include "network/network.h"
|
||||
#include "object/ObjectRepository.h"
|
||||
#include "OpenRCT2.h"
|
||||
#include "ParkImporter.h"
|
||||
#include "platform/crash.h"
|
||||
#include "PlatformEnvironment.h"
|
||||
#include "ride/TrackDesignRepository.h"
|
||||
#include "scenario/ScenarioRepository.h"
|
||||
#include "title/TitleScreen.h"
|
||||
#include "title/TitleSequenceManager.h"
|
||||
#include "Version.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "audio/audio.h"
|
||||
#include "config/Config.h"
|
||||
#include "editor.h"
|
||||
#include "game.h"
|
||||
#include "interface/chat.h"
|
||||
#include "interface/themes.h"
|
||||
#include "intro.h"
|
||||
#include "localisation/localisation.h"
|
||||
#include "network/http.h"
|
||||
#include "object_list.h"
|
||||
#include "rct1.h"
|
||||
#include "rct2.h"
|
||||
#include "rct2/interop.h"
|
||||
}
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
class Context : public IContext
|
||||
{
|
||||
private:
|
||||
// The game update inverval in milliseconds, (1000 / 40fps) = 25ms
|
||||
constexpr static uint32 UPDATE_TIME_MS = 25;
|
||||
|
||||
// Dependencies
|
||||
IAudioContext * const _audioContext = nullptr;
|
||||
IUiContext * const _uiContext = nullptr;
|
||||
|
||||
// Services
|
||||
IPlatformEnvironment * _env = nullptr;
|
||||
IObjectRepository * _objectRepository = nullptr;
|
||||
ITrackDesignRepository * _trackDesignRepository = nullptr;
|
||||
IScenarioRepository * _scenarioRepository = nullptr;
|
||||
|
||||
bool _isWindowMinimised = false;
|
||||
uint32 _lastTick = 0;
|
||||
uint32 _uncapTick = 0;
|
||||
|
||||
/** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to false. */
|
||||
bool _finished = false;
|
||||
|
||||
public:
|
||||
// Singleton of Context.
|
||||
// Remove this when GetContext() is no longer called so that
|
||||
// multiple instances can be created in parallel
|
||||
static Context * Instance;
|
||||
|
||||
public:
|
||||
Context(IAudioContext * audioContext, IUiContext * uiContext)
|
||||
: _audioContext(audioContext),
|
||||
_uiContext(uiContext)
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
~Context() override
|
||||
{
|
||||
network_close();
|
||||
http_dispose();
|
||||
language_close_all();
|
||||
rct2_dispose();
|
||||
config_release();
|
||||
#ifndef DISABLE_NETWORK
|
||||
EVP_MD_CTX_destroy(gHashCTX);
|
||||
#endif // DISABLE_NETWORK
|
||||
rct2_interop_dispose();
|
||||
platform_free();
|
||||
Instance = nullptr;
|
||||
}
|
||||
|
||||
IAudioContext * GetAudioContext() override
|
||||
{
|
||||
return _audioContext;
|
||||
}
|
||||
|
||||
IUiContext * GetUiContext() override
|
||||
{
|
||||
return _uiContext;
|
||||
}
|
||||
|
||||
sint32 RunOpenRCT2(int argc, char * * argv) override
|
||||
{
|
||||
Initialise();
|
||||
Launch();
|
||||
return gExitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the OpenRCT2 game loop to finish.
|
||||
*/
|
||||
void Finish() override
|
||||
{
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool Initialise()
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
gHashCTX = EVP_MD_CTX_create();
|
||||
Guard::Assert(gHashCTX != nullptr, "EVP_MD_CTX_create failed");
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
crash_init();
|
||||
|
||||
// Sets up the environment OpenRCT2 is running in, e.g. directory paths
|
||||
_env = SetupEnvironment();
|
||||
if (_env == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rct2_interop_setup_segment())
|
||||
{
|
||||
log_fatal("Unable to load RCT2 data sector");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION))
|
||||
{
|
||||
gOpenRCT2ShowChangelog = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
gOpenRCT2ShowChangelog = true;
|
||||
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
|
||||
config_save_default();
|
||||
}
|
||||
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
GetContext()->GetUiContext()->CreateWindow();
|
||||
}
|
||||
|
||||
// TODO add configuration option to allow multiple instances
|
||||
// if (!gOpenRCT2Headless && !platform_lock_single_instance()) {
|
||||
// log_fatal("OpenRCT2 is already running.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
_objectRepository = CreateObjectRepository(_env);
|
||||
_trackDesignRepository = CreateTrackDesignRepository(_env);
|
||||
_scenarioRepository = CreateScenarioRepository(_env);
|
||||
|
||||
if (!language_open(gConfigGeneral.language))
|
||||
{
|
||||
log_error("Failed to open configured language...");
|
||||
if (!language_open(LANGUAGE_ENGLISH_UK))
|
||||
{
|
||||
log_fatal("Failed to open fallback language...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Ideally we want to delay this until we show the title so that we can
|
||||
// still open the game window and draw a progress screen for the creation
|
||||
// of the object cache.
|
||||
_objectRepository->LoadOrConstruct();
|
||||
|
||||
// TODO Like objects, this can take a while if there are a lot of track designs
|
||||
// its also really something really we might want to do in the background
|
||||
// as its not required until the player wants to place a new ride.
|
||||
_trackDesignRepository->Scan();
|
||||
|
||||
_scenarioRepository->Scan();
|
||||
TitleSequenceManager::Scan();
|
||||
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
audio_init();
|
||||
audio_populate_devices();
|
||||
}
|
||||
|
||||
http_init();
|
||||
theme_manager_initialise();
|
||||
|
||||
rct2_interop_setup_hooks();
|
||||
|
||||
if (!rct2_init())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chat_init();
|
||||
|
||||
rct2_copy_original_user_files_over();
|
||||
return true;
|
||||
}
|
||||
|
||||
IPlatformEnvironment * SetupEnvironment()
|
||||
{
|
||||
utf8 userPath[MAX_PATH];
|
||||
platform_resolve_openrct_data_path();
|
||||
platform_resolve_user_data_path();
|
||||
platform_get_user_directory(userPath, NULL, sizeof(userPath));
|
||||
if (!platform_ensure_directory_exists(userPath))
|
||||
{
|
||||
Console::Error::WriteLine("Could not create user directory (do you have write access to your documents folder?)");
|
||||
return nullptr;
|
||||
}
|
||||
platform_get_exe_path(gExePath, sizeof(gExePath));
|
||||
log_verbose("Setting exe path to %s", gExePath);
|
||||
|
||||
config_set_defaults();
|
||||
if (!config_open_default())
|
||||
{
|
||||
if (!config_find_or_browse_install_directory())
|
||||
{
|
||||
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
|
||||
config_save_default();
|
||||
utf8 path[MAX_PATH];
|
||||
config_get_default_path(path, sizeof(path));
|
||||
Console::Error::WriteLine("An RCT2 install directory must be specified! Please edit \"game_path\" in %s.", path);
|
||||
return nullptr;
|
||||
}
|
||||
config_save_default();
|
||||
}
|
||||
|
||||
if (!rct2_init_directories())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (!rct2_startup_checks())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
utf8 path[260];
|
||||
std::string basePaths[4];
|
||||
basePaths[(size_t)DIRBASE::RCT1] = String::ToStd(gConfigGeneral.rct1_path);
|
||||
basePaths[(size_t)DIRBASE::RCT2] = String::ToStd(gConfigGeneral.rct2_path);
|
||||
platform_get_openrct_data_path(path, sizeof(path));
|
||||
basePaths[(size_t)DIRBASE::OPENRCT2] = std::string(path);
|
||||
platform_get_user_directory(path, nullptr, sizeof(path));
|
||||
basePaths[(size_t)DIRBASE::USER] = std::string(path);
|
||||
|
||||
IPlatformEnvironment * env = CreatePlatformEnvironment(basePaths);
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the game, after command line arguments have been parsed and processed.
|
||||
*/
|
||||
void Launch()
|
||||
{
|
||||
gIntroState = INTRO_STATE_NONE;
|
||||
if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro)
|
||||
{
|
||||
gOpenRCT2StartupAction = STARTUP_ACTION_INTRO;
|
||||
}
|
||||
|
||||
switch (gOpenRCT2StartupAction) {
|
||||
case STARTUP_ACTION_INTRO:
|
||||
gIntroState = INTRO_STATE_PUBLISHER_BEGIN;
|
||||
title_load();
|
||||
break;
|
||||
case STARTUP_ACTION_TITLE:
|
||||
title_load();
|
||||
break;
|
||||
case STARTUP_ACTION_OPEN:
|
||||
{
|
||||
bool parkLoaded = false;
|
||||
// A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL
|
||||
// This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled
|
||||
if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr)
|
||||
{
|
||||
#ifndef DISABLE_HTTP
|
||||
// Download park and open it using its temporary filename
|
||||
char tmpPath[MAX_PATH];
|
||||
if (!http_download_park(gOpenRCT2StartupActionPath, tmpPath))
|
||||
{
|
||||
title_load();
|
||||
break;
|
||||
}
|
||||
|
||||
parkLoaded = OpenParkAutoDetectFormat(tmpPath);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
parkLoaded = rct2_open_file(gOpenRCT2StartupActionPath);
|
||||
}
|
||||
|
||||
if (!parkLoaded)
|
||||
{
|
||||
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
|
||||
title_load();
|
||||
break;
|
||||
}
|
||||
|
||||
gScreenFlags = SCREEN_FLAGS_PLAYING;
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (gNetworkStart == NETWORK_MODE_SERVER)
|
||||
{
|
||||
if (gNetworkStartPort == 0)
|
||||
{
|
||||
gNetworkStartPort = gConfigNetwork.default_port;
|
||||
}
|
||||
|
||||
if (String::IsNullOrEmpty(gCustomPassword))
|
||||
{
|
||||
network_set_password(gConfigNetwork.default_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
network_set_password(gCustomPassword);
|
||||
}
|
||||
network_begin_server(gNetworkStartPort);
|
||||
}
|
||||
#endif // DISABLE_NETWORK
|
||||
break;
|
||||
}
|
||||
case STARTUP_ACTION_EDIT:
|
||||
if (String::SizeOf(gOpenRCT2StartupActionPath) == 0)
|
||||
{
|
||||
editor_load();
|
||||
}
|
||||
else if (!editor_load_landscape(gOpenRCT2StartupActionPath))
|
||||
{
|
||||
title_load();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (gNetworkStart == NETWORK_MODE_CLIENT)
|
||||
{
|
||||
if (gNetworkStartPort == 0)
|
||||
{
|
||||
gNetworkStartPort = gConfigNetwork.default_port;
|
||||
}
|
||||
network_begin_client(gNetworkStartHost, gNetworkStartPort);
|
||||
}
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
RunGameLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the main game loop until the finished flag is set.
|
||||
*/
|
||||
void RunGameLoop()
|
||||
{
|
||||
log_verbose("begin openrct2 loop");
|
||||
_finished = false;
|
||||
do
|
||||
{
|
||||
if (ShouldRunVariableFrame())
|
||||
{
|
||||
RunVariableFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunFixedFrame();
|
||||
}
|
||||
} while (!_finished);
|
||||
log_verbose("finish openrct2 loop");
|
||||
}
|
||||
|
||||
bool ShouldRunVariableFrame()
|
||||
{
|
||||
if (!gConfigGeneral.uncap_fps) return false;
|
||||
if (gGameSpeed > 4) return false;
|
||||
if (gOpenRCT2Headless) return false;
|
||||
if (_uiContext->IsMinimised()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunFixedFrame()
|
||||
{
|
||||
_uncapTick = 0;
|
||||
uint32 currentTick = platform_get_ticks();
|
||||
uint32 ticksElapsed = currentTick - _lastTick;
|
||||
if (ticksElapsed < UPDATE_TIME_MS)
|
||||
{
|
||||
platform_sleep(UPDATE_TIME_MS - ticksElapsed);
|
||||
_lastTick += UPDATE_TIME_MS;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastTick = currentTick;
|
||||
}
|
||||
GetContext()->GetUiContext()->ProcessMessages();
|
||||
rct2_update();
|
||||
if (!_isWindowMinimised)
|
||||
{
|
||||
platform_draw();
|
||||
}
|
||||
}
|
||||
|
||||
void RunVariableFrame()
|
||||
{
|
||||
uint32 currentTick = platform_get_ticks();
|
||||
if (_uncapTick == 0)
|
||||
{
|
||||
_uncapTick = currentTick;
|
||||
sprite_position_tween_reset();
|
||||
}
|
||||
|
||||
// Limit number of updates per loop (any long pauses or debugging can make this update for a very long time)
|
||||
if (currentTick - _uncapTick > UPDATE_TIME_MS * 60)
|
||||
{
|
||||
_uncapTick = currentTick - UPDATE_TIME_MS - 1;
|
||||
}
|
||||
|
||||
GetContext()->GetUiContext()->ProcessMessages();
|
||||
|
||||
while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS)
|
||||
{
|
||||
// Get the original position of each sprite
|
||||
sprite_position_tween_store_a();
|
||||
|
||||
// Update the game so the sprite positions update
|
||||
rct2_update();
|
||||
|
||||
// Get the next position of each sprite
|
||||
sprite_position_tween_store_b();
|
||||
|
||||
_uncapTick += UPDATE_TIME_MS;
|
||||
}
|
||||
|
||||
// Tween the position of each sprite from the last position to the new position based on the time between the last
|
||||
// tick and the next tick.
|
||||
float nudge = 1 - ((float)(currentTick - _uncapTick) / UPDATE_TIME_MS);
|
||||
sprite_position_tween_all(nudge);
|
||||
|
||||
platform_draw();
|
||||
|
||||
sprite_position_tween_restore();
|
||||
}
|
||||
|
||||
bool OpenParkAutoDetectFormat(const utf8 * path)
|
||||
{
|
||||
ClassifiedFile info;
|
||||
if (TryClassifyFile(path, &info))
|
||||
{
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME ||
|
||||
info.Type == FILE_TYPE::SCENARIO)
|
||||
{
|
||||
std::unique_ptr<IParkImporter> parkImporter;
|
||||
if (info.Version <= 2)
|
||||
{
|
||||
parkImporter.reset(ParkImporter::CreateS4());
|
||||
}
|
||||
else
|
||||
{
|
||||
parkImporter.reset(ParkImporter::CreateS6());
|
||||
}
|
||||
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME)
|
||||
{
|
||||
try
|
||||
{
|
||||
parkImporter->LoadSavedGame(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
game_load_init();
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Error loading saved game.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
parkImporter->LoadScenario(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
scenario_begin();
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Error loading scenario.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Invalid file type.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Unable to detect file type.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Context * Context::Instance = nullptr;
|
||||
|
||||
IContext * CreateContext()
|
||||
{
|
||||
return new Context(nullptr, nullptr);
|
||||
}
|
||||
|
||||
IContext * CreateContext(Audio::IAudioContext * audioContext, IUiContext * uiContext)
|
||||
{
|
||||
return new Context(audioContext, uiContext);
|
||||
}
|
||||
|
||||
IContext * GetContext()
|
||||
{
|
||||
return Context::Instance;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize)
|
||||
{
|
||||
String::Set(buffer, bufferSize, gVersionInfoFull);
|
||||
}
|
||||
|
||||
void openrct2_finish()
|
||||
{
|
||||
GetContext()->Finish();
|
||||
}
|
||||
|
||||
void context_setcurrentcursor(sint32 cursor)
|
||||
{
|
||||
GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor);
|
||||
}
|
||||
|
||||
void context_hide_cursor()
|
||||
{
|
||||
GetContext()->GetUiContext()->SetCursorVisible(false);
|
||||
}
|
||||
|
||||
void context_show_cursor()
|
||||
{
|
||||
GetContext()->GetUiContext()->SetCursorVisible(true);
|
||||
}
|
||||
|
||||
void context_get_cursor_position(sint32 * x, sint32 * y)
|
||||
{
|
||||
GetContext()->GetUiContext()->GetCursorPosition(x, y);
|
||||
}
|
||||
|
||||
void context_get_cursor_position_scaled(sint32 * x, sint32 * y)
|
||||
{
|
||||
context_get_cursor_position(x, y);
|
||||
|
||||
// Compensate for window scaling.
|
||||
*x = (sint32)ceilf(*x / gConfigGeneral.window_scale);
|
||||
*y = (sint32)ceilf(*y / gConfigGeneral.window_scale);
|
||||
}
|
||||
|
||||
void context_set_cursor_position(sint32 x, sint32 y)
|
||||
{
|
||||
GetContext()->GetUiContext()->SetCursorPosition(x, y);
|
||||
}
|
||||
|
||||
const CursorState * context_get_cursor_state()
|
||||
{
|
||||
return GetContext()->GetUiContext()->GetCursorState();
|
||||
}
|
||||
|
||||
const uint8 * context_get_keys_state()
|
||||
{
|
||||
return GetContext()->GetUiContext()->GetKeysState();
|
||||
}
|
||||
|
||||
const uint8 * context_get_keys_pressed()
|
||||
{
|
||||
return GetContext()->GetUiContext()->GetKeysPressed();
|
||||
}
|
||||
|
||||
TextInputSession * context_start_text_input(utf8 * buffer, size_t maxLength)
|
||||
{
|
||||
return GetContext()->GetUiContext()->StartTextInput(buffer, maxLength);
|
||||
}
|
||||
|
||||
void context_stop_text_input()
|
||||
{
|
||||
GetContext()->GetUiContext()->StopTextInput();
|
||||
}
|
||||
|
||||
bool context_is_input_active()
|
||||
{
|
||||
return GetContext()->GetUiContext()->IsTextInputActive();
|
||||
}
|
||||
|
||||
void context_trigger_resize()
|
||||
{
|
||||
return GetContext()->GetUiContext()->TriggerResize();
|
||||
}
|
||||
|
||||
void context_set_fullscreen_mode(sint32 mode)
|
||||
{
|
||||
return GetContext()->GetUiContext()->SetFullscreenMode((FULLSCREEN_MODE)mode);
|
||||
}
|
||||
|
||||
sint32 context_get_resolutions(Resolution * * outResolutions)
|
||||
{
|
||||
auto resolutions = GetContext()->GetUiContext()->GetFullscreenResolutions();
|
||||
sint32 count = (sint32)resolutions.size();
|
||||
*outResolutions = Memory::AllocateArray<Resolution>(count);
|
||||
Memory::CopyArray(*outResolutions, resolutions.data(), count);
|
||||
return count;
|
||||
}
|
||||
|
||||
sint32 context_get_width()
|
||||
{
|
||||
return GetContext()->GetUiContext()->GetWidth();
|
||||
}
|
||||
|
||||
sint32 context_get_height()
|
||||
{
|
||||
return GetContext()->GetUiContext()->GetHeight();
|
||||
}
|
||||
|
||||
bool context_has_focus()
|
||||
{
|
||||
return GetContext()->GetUiContext()->HasFocus();
|
||||
}
|
||||
|
||||
void context_set_cursor_trap(bool value)
|
||||
{
|
||||
GetContext()->GetUiContext()->SetCursorTrap(value);
|
||||
}
|
||||
|
||||
bool platform_open_common_file_dialog(utf8 * outFilename, file_dialog_desc * desc, size_t outSize)
|
||||
{
|
||||
FileDialogDesc desc2;
|
||||
desc2.Type = (FILE_DIALOG_TYPE)desc->type;
|
||||
desc2.Title = String::ToStd(desc->title);
|
||||
desc2.InitialDirectory = String::ToStd(desc->initial_directory);
|
||||
desc2.DefaultFilename = String::ToStd(desc->default_filename);
|
||||
for (const auto &filter : desc->filters)
|
||||
{
|
||||
if (filter.name != nullptr)
|
||||
{
|
||||
desc2.Filters.push_back({ String::ToStd(filter.name), String::ToStd(filter.pattern) });
|
||||
}
|
||||
}
|
||||
std::string result = GetContext()->GetUiContext()->ShowFileDialog(desc2);
|
||||
String::Set(outFilename, outSize, result.c_str());
|
||||
return !result.empty();
|
||||
}
|
||||
|
||||
utf8 * platform_open_directory_browser(const utf8 * title)
|
||||
{
|
||||
std::string result = GetContext()->GetUiContext()->ShowDirectoryDialog(title);
|
||||
return String::Duplicate(result.c_str());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct CursorState
|
||||
{
|
||||
sint32 x, y;
|
||||
uint8 left, middle, right, any;
|
||||
sint32 wheel;
|
||||
sint32 old;
|
||||
bool touch, touchIsDouble;
|
||||
uint32 touchDownTimestamp;
|
||||
} CursorState;
|
||||
|
||||
typedef struct TextInputSession
|
||||
{
|
||||
utf8 * Buffer; // UTF-8 stream
|
||||
size_t BufferSize; // Maximum number of bytes (excluding null terminator)
|
||||
size_t Size; // Number of bytes (excluding null terminator)
|
||||
size_t Length; // Number of codepoints
|
||||
size_t SelectionStart; // Selection start, in bytes
|
||||
size_t SelectionSize; // Selection length in bytes
|
||||
|
||||
const utf8 * ImeBuffer; // IME UTF-8 stream
|
||||
} TextInputSession;
|
||||
|
||||
struct Resolution
|
||||
{
|
||||
sint32 Width;
|
||||
sint32 Height;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CURSOR_UP = 0,
|
||||
CURSOR_DOWN = 1,
|
||||
CURSOR_CHANGED = 2,
|
||||
CURSOR_RELEASED = CURSOR_UP | CURSOR_CHANGED,
|
||||
CURSOR_PRESSED = CURSOR_DOWN | CURSOR_CHANGED,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
interface IAudioContext;
|
||||
}
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
interface IUiContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an instance of OpenRCT2 and can be used to get various services.
|
||||
*/
|
||||
interface IContext
|
||||
{
|
||||
virtual ~IContext() = default;
|
||||
|
||||
virtual Audio::IAudioContext * GetAudioContext() abstract;
|
||||
virtual Ui::IUiContext * GetUiContext() abstract;
|
||||
|
||||
virtual sint32 RunOpenRCT2(int argc, char * * argv) abstract;
|
||||
virtual void Finish() abstract;
|
||||
};
|
||||
|
||||
IContext * CreateContext();
|
||||
IContext * CreateContext(Audio::IAudioContext * audioContext, Ui::IUiContext * uiContext);
|
||||
IContext * GetContext();
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
void context_setcurrentcursor(sint32 cursor);
|
||||
void context_hide_cursor();
|
||||
void context_show_cursor();
|
||||
void context_get_cursor_position(sint32 * x, sint32 * y);
|
||||
void context_get_cursor_position_scaled(sint32 * x, sint32 * y);
|
||||
void context_set_cursor_position(sint32 x, sint32 y);
|
||||
const CursorState * context_get_cursor_state();
|
||||
const uint8 * context_get_keys_state();
|
||||
const uint8 * context_get_keys_pressed();
|
||||
TextInputSession * context_start_text_input(utf8 * buffer, size_t maxLength);
|
||||
void context_stop_text_input();
|
||||
bool context_is_input_active();
|
||||
void context_trigger_resize();
|
||||
void context_set_fullscreen_mode(sint32 mode);
|
||||
sint32 context_get_resolutions(struct Resolution * * outResolutions);
|
||||
sint32 context_get_width();
|
||||
sint32 context_get_height();
|
||||
bool context_has_focus();
|
||||
void context_set_cursor_trap(bool value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -14,47 +14,18 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "core/Console.hpp"
|
||||
#include "core/File.h"
|
||||
#include "core/FileStream.hpp"
|
||||
#include "core/Guard.hpp"
|
||||
#include "core/String.hpp"
|
||||
#include "FileClassifier.h"
|
||||
#include "network/network.h"
|
||||
#include "object/ObjectRepository.h"
|
||||
#include "OpenRCT2.h"
|
||||
#include "ParkImporter.h"
|
||||
#include "platform/crash.h"
|
||||
#include "PlatformEnvironment.h"
|
||||
#include "ride/TrackDesignRepository.h"
|
||||
#include "scenario/ScenarioRepository.h"
|
||||
#include "title/TitleScreen.h"
|
||||
#include "title/TitleSequenceManager.h"
|
||||
#include "Version.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "audio/audio.h"
|
||||
#include "config/Config.h"
|
||||
#include "editor.h"
|
||||
#include "game.h"
|
||||
#include "interface/chat.h"
|
||||
#include "interface/themes.h"
|
||||
#include "intro.h"
|
||||
#include "localisation/localisation.h"
|
||||
#include "network/http.h"
|
||||
#include "object_list.h"
|
||||
#include "platform/platform.h"
|
||||
#include "rct1.h"
|
||||
#include "rct2.h"
|
||||
#include "rct2/interop.h"
|
||||
}
|
||||
|
||||
// The game update interval in milliseconds, (1000 / 40fps) = 25ms
|
||||
constexpr uint32 UPDATE_TIME_MS = 25;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
sint32 gExitCode;
|
||||
|
@ -76,252 +47,6 @@ extern "C"
|
|||
// OpenSSL's message digest context used for calculating sprite checksums
|
||||
EVP_MD_CTX * gHashCTX = nullptr;
|
||||
#endif // DISABLE_NETWORK
|
||||
}
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
static IPlatformEnvironment * _env = nullptr;
|
||||
static bool _isWindowMinimised;
|
||||
static uint32 _isWindowMinimisedLastCheckTick;
|
||||
static uint32 _lastTick;
|
||||
static uint32 _uncapTick;
|
||||
|
||||
/** If set, will end the OpenRCT2 game loop. Intentionally private to this module so that the flag can not be set back to false. */
|
||||
static bool _finished;
|
||||
|
||||
static bool ShouldRunVariableFrame();
|
||||
static void RunGameLoop();
|
||||
static void RunFixedFrame();
|
||||
static void RunVariableFrame();
|
||||
|
||||
static bool OpenParkAutoDetectFormat(const utf8 * path);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize)
|
||||
{
|
||||
String::Set(buffer, bufferSize, gVersionInfoFull);
|
||||
}
|
||||
|
||||
static void openrct2_set_exe_path()
|
||||
{
|
||||
platform_get_exe_path(gExePath, sizeof(gExePath));
|
||||
log_verbose("Setting exe path to %s", gExePath);
|
||||
}
|
||||
|
||||
bool openrct2_initialise()
|
||||
{
|
||||
#ifndef DISABLE_NETWORK
|
||||
gHashCTX = EVP_MD_CTX_create();
|
||||
Guard::Assert(gHashCTX != nullptr, "EVP_MD_CTX_create failed");
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
crash_init();
|
||||
|
||||
// Sets up the environment OpenRCT2 is running in, e.g. directory paths
|
||||
OpenRCT2::_env = OpenRCT2::SetupEnvironment();
|
||||
if (OpenRCT2::_env == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rct2_interop_setup_segment())
|
||||
{
|
||||
log_fatal("Unable to load RCT2 data sector");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION))
|
||||
{
|
||||
gOpenRCT2ShowChangelog = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
gOpenRCT2ShowChangelog = true;
|
||||
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
|
||||
config_save_default();
|
||||
}
|
||||
|
||||
// TODO add configuration option to allow multiple instances
|
||||
// if (!gOpenRCT2Headless && !platform_lock_single_instance()) {
|
||||
// log_fatal("OpenRCT2 is already running.");
|
||||
// return false;
|
||||
// }
|
||||
|
||||
IObjectRepository * objRepo = CreateObjectRepository(OpenRCT2::_env);
|
||||
ITrackDesignRepository * tdRepo = CreateTrackDesignRepository(OpenRCT2::_env);
|
||||
IScenarioRepository * scenarioRepo = CreateScenarioRepository(OpenRCT2::_env);
|
||||
|
||||
if (!language_open(gConfigGeneral.language))
|
||||
{
|
||||
log_error("Failed to open configured language...");
|
||||
if (!language_open(LANGUAGE_ENGLISH_UK))
|
||||
{
|
||||
log_fatal("Failed to open fallback language...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Ideally we want to delay this until we show the title so that we can
|
||||
// still open the game window and draw a progress screen for the creation
|
||||
// of the object cache.
|
||||
objRepo->LoadOrConstruct();
|
||||
|
||||
// TODO Like objects, this can take a while if there are a lot of track designs
|
||||
// it's also really something we might want to do in the background
|
||||
// as it's not required until the player wants to place a new ride.
|
||||
tdRepo->Scan();
|
||||
|
||||
scenarioRepo->Scan();
|
||||
TitleSequenceManager::Scan();
|
||||
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
audio_init();
|
||||
audio_populate_devices();
|
||||
}
|
||||
|
||||
http_init();
|
||||
theme_manager_initialise();
|
||||
|
||||
rct2_interop_setup_hooks();
|
||||
|
||||
if (!rct2_init())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
chat_init();
|
||||
|
||||
rct2_copy_original_user_files_over();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the game, after command line arguments have been parsed and processed.
|
||||
*/
|
||||
void openrct2_launch()
|
||||
{
|
||||
if (openrct2_initialise())
|
||||
{
|
||||
gIntroState = INTRO_STATE_NONE;
|
||||
if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro)
|
||||
{
|
||||
gOpenRCT2StartupAction = STARTUP_ACTION_INTRO;
|
||||
}
|
||||
|
||||
switch (gOpenRCT2StartupAction) {
|
||||
case STARTUP_ACTION_INTRO:
|
||||
gIntroState = INTRO_STATE_PUBLISHER_BEGIN;
|
||||
title_load();
|
||||
break;
|
||||
case STARTUP_ACTION_TITLE:
|
||||
title_load();
|
||||
break;
|
||||
case STARTUP_ACTION_OPEN:
|
||||
{
|
||||
bool parkLoaded = false;
|
||||
// A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL
|
||||
// This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled
|
||||
if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr)
|
||||
{
|
||||
#ifndef DISABLE_HTTP
|
||||
// Download park and open it using its temporary filename
|
||||
char tmpPath[MAX_PATH];
|
||||
if (!http_download_park(gOpenRCT2StartupActionPath, tmpPath))
|
||||
{
|
||||
title_load();
|
||||
break;
|
||||
}
|
||||
|
||||
parkLoaded = OpenRCT2::OpenParkAutoDetectFormat(tmpPath);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
parkLoaded = rct2_open_file(gOpenRCT2StartupActionPath);
|
||||
}
|
||||
|
||||
if (!parkLoaded)
|
||||
{
|
||||
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
|
||||
title_load();
|
||||
break;
|
||||
}
|
||||
|
||||
gScreenFlags = SCREEN_FLAGS_PLAYING;
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (gNetworkStart == NETWORK_MODE_SERVER)
|
||||
{
|
||||
if (gNetworkStartPort == 0)
|
||||
{
|
||||
gNetworkStartPort = gConfigNetwork.default_port;
|
||||
}
|
||||
|
||||
if (String::IsNullOrEmpty(gCustomPassword))
|
||||
{
|
||||
network_set_password(gConfigNetwork.default_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
network_set_password(gCustomPassword);
|
||||
}
|
||||
network_begin_server(gNetworkStartPort);
|
||||
}
|
||||
#endif // DISABLE_NETWORK
|
||||
break;
|
||||
}
|
||||
case STARTUP_ACTION_EDIT:
|
||||
if (String::SizeOf(gOpenRCT2StartupActionPath) == 0)
|
||||
{
|
||||
editor_load();
|
||||
}
|
||||
else if (!editor_load_landscape(gOpenRCT2StartupActionPath))
|
||||
{
|
||||
title_load();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
if (gNetworkStart == NETWORK_MODE_CLIENT)
|
||||
{
|
||||
if (gNetworkStartPort == 0)
|
||||
{
|
||||
gNetworkStartPort = gConfigNetwork.default_port;
|
||||
}
|
||||
network_begin_client(gNetworkStartHost, gNetworkStartPort);
|
||||
}
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
OpenRCT2::RunGameLoop();
|
||||
}
|
||||
openrct2_dispose();
|
||||
}
|
||||
|
||||
void openrct2_dispose()
|
||||
{
|
||||
network_close();
|
||||
http_dispose();
|
||||
language_close_all();
|
||||
rct2_dispose();
|
||||
config_release();
|
||||
#ifndef DISABLE_NETWORK
|
||||
EVP_MD_CTX_destroy(gHashCTX);
|
||||
#endif // DISABLE_NETWORK
|
||||
rct2_interop_dispose();
|
||||
platform_free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the OpenRCT2 game loop to finish.
|
||||
*/
|
||||
void openrct2_finish()
|
||||
{
|
||||
OpenRCT2::_finished = true;
|
||||
}
|
||||
|
||||
bool check_file_path(sint32 pathId)
|
||||
{
|
||||
|
@ -353,229 +78,3 @@ extern "C"
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
IPlatformEnvironment * SetupEnvironment()
|
||||
{
|
||||
utf8 userPath[MAX_PATH];
|
||||
platform_resolve_openrct_data_path();
|
||||
platform_resolve_user_data_path();
|
||||
platform_get_user_directory(userPath, NULL, sizeof(userPath));
|
||||
if (!platform_ensure_directory_exists(userPath))
|
||||
{
|
||||
Console::Error::WriteLine("Could not create user directory (do you have write access to your documents folder?)");
|
||||
return nullptr;
|
||||
}
|
||||
openrct2_set_exe_path();
|
||||
|
||||
config_set_defaults();
|
||||
if (!config_open_default())
|
||||
{
|
||||
if (!config_find_or_browse_install_directory())
|
||||
{
|
||||
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
|
||||
config_save_default();
|
||||
utf8 path[MAX_PATH];
|
||||
config_get_default_path(path, sizeof(path));
|
||||
Console::Error::WriteLine("An RCT2 install directory must be specified! Please edit \"game_path\" in %s.", path);
|
||||
return nullptr;
|
||||
}
|
||||
config_save_default();
|
||||
}
|
||||
|
||||
if (!rct2_init_directories())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (!rct2_startup_checks())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
utf8 path[260];
|
||||
std::string basePaths[4];
|
||||
basePaths[(size_t)DIRBASE::RCT1] = String::ToStd(gConfigGeneral.rct1_path);
|
||||
basePaths[(size_t)DIRBASE::RCT2] = String::ToStd(gConfigGeneral.rct2_path);
|
||||
platform_get_openrct_data_path(path, sizeof(path));
|
||||
basePaths[(size_t)DIRBASE::OPENRCT2] = std::string(path);
|
||||
platform_get_user_directory(path, nullptr, sizeof(path));
|
||||
basePaths[(size_t)DIRBASE::USER] = std::string(path);
|
||||
|
||||
IPlatformEnvironment * env = CreatePlatformEnvironment(basePaths);
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the main game loop until the finished flag is set.
|
||||
*/
|
||||
static void RunGameLoop()
|
||||
{
|
||||
log_verbose("begin openrct2 loop");
|
||||
_finished = false;
|
||||
do
|
||||
{
|
||||
if (ShouldRunVariableFrame())
|
||||
{
|
||||
RunVariableFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunFixedFrame();
|
||||
}
|
||||
}
|
||||
while (!_finished);
|
||||
log_verbose("finish openrct2 loop");
|
||||
}
|
||||
|
||||
static bool IsMinimised()
|
||||
{
|
||||
// Don't check if window is minimised too frequently (every second is fine)
|
||||
if (_lastTick > _isWindowMinimisedLastCheckTick + 1000)
|
||||
{
|
||||
uint32 windowFlags = SDL_GetWindowFlags(gWindow);
|
||||
_isWindowMinimised = (windowFlags & SDL_WINDOW_MINIMIZED) ||
|
||||
(windowFlags & SDL_WINDOW_HIDDEN);
|
||||
}
|
||||
return _isWindowMinimised;
|
||||
}
|
||||
|
||||
static bool ShouldRunVariableFrame()
|
||||
{
|
||||
if (!gConfigGeneral.uncap_fps) return false;
|
||||
if (gGameSpeed > 4) return false;
|
||||
if (gOpenRCT2Headless) return false;
|
||||
if (IsMinimised()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RunFixedFrame()
|
||||
{
|
||||
_uncapTick = 0;
|
||||
uint32 currentTick = SDL_GetTicks();
|
||||
uint32 ticksElapsed = currentTick - _lastTick;
|
||||
if (ticksElapsed < UPDATE_TIME_MS)
|
||||
{
|
||||
SDL_Delay(UPDATE_TIME_MS - ticksElapsed);
|
||||
_lastTick += UPDATE_TIME_MS;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastTick = currentTick;
|
||||
}
|
||||
platform_process_messages();
|
||||
rct2_update();
|
||||
if (!_isWindowMinimised)
|
||||
{
|
||||
platform_draw();
|
||||
}
|
||||
}
|
||||
|
||||
static void RunVariableFrame()
|
||||
{
|
||||
uint32 currentTick = SDL_GetTicks();
|
||||
if (_uncapTick == 0)
|
||||
{
|
||||
_uncapTick = currentTick;
|
||||
sprite_position_tween_reset();
|
||||
}
|
||||
|
||||
// Limit number of updates per loop (any long pauses or debugging can make this update for a very long time)
|
||||
if (currentTick - _uncapTick > UPDATE_TIME_MS * 60)
|
||||
{
|
||||
_uncapTick = currentTick - UPDATE_TIME_MS - 1;
|
||||
}
|
||||
|
||||
platform_process_messages();
|
||||
|
||||
while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS)
|
||||
{
|
||||
// Get the original position of each sprite
|
||||
sprite_position_tween_store_a();
|
||||
|
||||
// Update the game so the sprite positions update
|
||||
rct2_update();
|
||||
|
||||
// Get the next position of each sprite
|
||||
sprite_position_tween_store_b();
|
||||
|
||||
_uncapTick += UPDATE_TIME_MS;
|
||||
}
|
||||
|
||||
// Tween the position of each sprite from the last position to the new position based on the time between the last
|
||||
// tick and the next tick.
|
||||
float nudge = 1 - ((float)(currentTick - _uncapTick) / UPDATE_TIME_MS);
|
||||
sprite_position_tween_all(nudge);
|
||||
|
||||
platform_draw();
|
||||
|
||||
sprite_position_tween_restore();
|
||||
}
|
||||
|
||||
static bool OpenParkAutoDetectFormat(const utf8 * path)
|
||||
{
|
||||
ClassifiedFile info;
|
||||
if (TryClassifyFile(path, &info))
|
||||
{
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME ||
|
||||
info.Type == FILE_TYPE::SCENARIO)
|
||||
{
|
||||
std::unique_ptr<IParkImporter> parkImporter;
|
||||
if (info.Version <= 2)
|
||||
{
|
||||
parkImporter.reset(ParkImporter::CreateS4());
|
||||
}
|
||||
else
|
||||
{
|
||||
parkImporter.reset(ParkImporter::CreateS6());
|
||||
}
|
||||
|
||||
if (info.Type == FILE_TYPE::SAVED_GAME)
|
||||
{
|
||||
try
|
||||
{
|
||||
parkImporter->LoadSavedGame(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
game_load_init();
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Error loading saved game.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
parkImporter->LoadScenario(path);
|
||||
parkImporter->Import();
|
||||
game_fix_save_vars();
|
||||
sprite_position_tween_reset();
|
||||
gScreenAge = 0;
|
||||
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
|
||||
scenario_begin();
|
||||
return true;
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
Console::Error::WriteLine("Error loading scenario.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Invalid file type.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::Error::WriteLine("Unable to detect file type.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ extern "C"
|
|||
{
|
||||
#endif
|
||||
#include "platform/platform.h"
|
||||
#undef CreateWindow
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -40,17 +41,6 @@ enum STARTUP_ACTION
|
|||
STARTUP_ACTION_EDIT
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
interface IPlatformEnvironment;
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
IPlatformEnvironment * SetupEnvironment();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
@ -80,17 +70,10 @@ extern "C"
|
|||
#endif
|
||||
|
||||
void openrct2_write_full_version_info(utf8 * buffer, size_t bufferSize);
|
||||
bool openrct2_initialise();
|
||||
void openrct2_launch();
|
||||
void openrct2_dispose();
|
||||
void openrct2_finish();
|
||||
|
||||
sint32 cmdline_run(const char * * argv, sint32 argc);
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
int RunOpenRCT2(int argc, char * * argv);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <cmath>
|
||||
#include <speex/speex_resampler.h>
|
||||
#include "../core/Math.hpp"
|
||||
#include "AudioChannel.h"
|
||||
#include "AudioSource.h"
|
||||
|
||||
class AudioChannelImpl : public IAudioChannel
|
||||
{
|
||||
private:
|
||||
IAudioSource * _source = nullptr;
|
||||
SpeexResamplerState * _resampler = nullptr;
|
||||
|
||||
sint32 _group = MIXER_GROUP_SOUND;
|
||||
double _rate = 0;
|
||||
uint64 _offset = 0;
|
||||
sint32 _loop = 0;
|
||||
|
||||
sint32 _volume = 1;
|
||||
float _volume_l = 0.f;
|
||||
float _volume_r = 0.f;
|
||||
float _oldvolume_l = 0.f;
|
||||
float _oldvolume_r = 0.f;
|
||||
sint32 _oldvolume = 0;
|
||||
float _pan = 0;
|
||||
|
||||
bool _stopping = false;
|
||||
bool _done = true;
|
||||
bool _deleteondone = false;
|
||||
bool _deletesourceondone = false;
|
||||
|
||||
public:
|
||||
AudioChannelImpl()
|
||||
{
|
||||
SetRate(1);
|
||||
SetVolume(SDL_MIX_MAXVOLUME);
|
||||
SetPan(0.5f);
|
||||
}
|
||||
|
||||
~AudioChannelImpl() override
|
||||
{
|
||||
if (_resampler != nullptr)
|
||||
{
|
||||
speex_resampler_destroy(_resampler);
|
||||
_resampler = nullptr;
|
||||
}
|
||||
if (_deletesourceondone)
|
||||
{
|
||||
delete _source;
|
||||
}
|
||||
}
|
||||
|
||||
IAudioSource * GetSource() const override
|
||||
{
|
||||
return _source;
|
||||
}
|
||||
|
||||
SpeexResamplerState * GetResampler() const override
|
||||
{
|
||||
return _resampler;
|
||||
}
|
||||
|
||||
void SetResampler(SpeexResamplerState * value) override
|
||||
{
|
||||
_resampler = value;
|
||||
}
|
||||
|
||||
sint32 GetGroup() const override
|
||||
{
|
||||
return _group;
|
||||
}
|
||||
|
||||
void SetGroup(sint32 group) override
|
||||
{
|
||||
_group = group;
|
||||
}
|
||||
|
||||
double GetRate() const override
|
||||
{
|
||||
return _rate;
|
||||
}
|
||||
|
||||
void SetRate(double rate) override
|
||||
{
|
||||
_rate = Math::Max(0.001, rate);
|
||||
}
|
||||
|
||||
uint64 GetOffset() const override
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
|
||||
bool SetOffset(uint64 offset) override
|
||||
{
|
||||
if (_source != nullptr && offset < _source->GetLength())
|
||||
{
|
||||
AudioFormat format = _source->GetFormat();
|
||||
sint32 samplesize = format.channels * format.BytesPerSample();
|
||||
_offset = (offset / samplesize) * samplesize;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual sint32 GetLoop() const override
|
||||
{
|
||||
return _loop;
|
||||
}
|
||||
|
||||
virtual void SetLoop(sint32 value) override
|
||||
{
|
||||
_loop = value;
|
||||
}
|
||||
|
||||
sint32 GetVolume() const override
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
|
||||
float GetVolumeL() const override
|
||||
{
|
||||
return _volume_l;
|
||||
}
|
||||
|
||||
float GetVolumeR() const override
|
||||
{
|
||||
return _volume_r;
|
||||
}
|
||||
|
||||
float GetOldVolumeL() const override
|
||||
{
|
||||
return _oldvolume_l;
|
||||
}
|
||||
|
||||
float GetOldVolumeR() const override
|
||||
{
|
||||
return _oldvolume_r;
|
||||
}
|
||||
|
||||
sint32 GetOldVolume() const override
|
||||
{
|
||||
return _oldvolume;
|
||||
}
|
||||
|
||||
void SetVolume(sint32 volume) override
|
||||
{
|
||||
_volume = Math::Clamp(0, volume, SDL_MIX_MAXVOLUME);
|
||||
}
|
||||
|
||||
float GetPan() const override
|
||||
{
|
||||
return _pan;
|
||||
}
|
||||
|
||||
void SetPan(float pan) override
|
||||
{
|
||||
_pan = Math::Clamp(0.0f, pan, 1.0f);
|
||||
double decibels = (std::abs(_pan - 0.5) * 2.0) * 100.0;
|
||||
double attenuation = pow(10, decibels / 20.0);
|
||||
if (_pan <= 0.5)
|
||||
{
|
||||
_volume_l = 1.0;
|
||||
_volume_r = (float)(1.0 / attenuation);
|
||||
}
|
||||
else
|
||||
{
|
||||
_volume_r = 1.0;
|
||||
_volume_l = (float)(1.0 / attenuation);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStopping() const override
|
||||
{
|
||||
return _stopping;
|
||||
}
|
||||
|
||||
void SetStopping(bool value) override
|
||||
{
|
||||
_stopping = value;
|
||||
}
|
||||
|
||||
bool IsDone() const override
|
||||
{
|
||||
return _done;
|
||||
}
|
||||
|
||||
void SetDone(bool value) override
|
||||
{
|
||||
_done = value;
|
||||
}
|
||||
|
||||
bool DeleteOnDone() const override
|
||||
{
|
||||
return _deleteondone;
|
||||
}
|
||||
|
||||
void SetDeleteOnDone(bool value) override
|
||||
{
|
||||
_deleteondone = value;
|
||||
}
|
||||
|
||||
void SetDeleteSourceOnDone(bool value) override
|
||||
{
|
||||
_deletesourceondone = value;
|
||||
}
|
||||
|
||||
bool IsPlaying() const override
|
||||
{
|
||||
return !_done;
|
||||
}
|
||||
|
||||
void Play(IAudioSource * source, sint32 loop) override
|
||||
{
|
||||
_source = source;
|
||||
_loop = loop;
|
||||
_offset = 0;
|
||||
_done = false;
|
||||
}
|
||||
|
||||
void UpdateOldVolume() override
|
||||
{
|
||||
_oldvolume = _volume;
|
||||
_oldvolume_l = _volume_l;
|
||||
_oldvolume_r = _volume_r;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() const override
|
||||
{
|
||||
AudioFormat result = { 0 };
|
||||
if (_source != nullptr)
|
||||
{
|
||||
result = _source->GetFormat();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, size_t len) override
|
||||
{
|
||||
size_t bytesRead = 0;
|
||||
size_t bytesToRead = len;
|
||||
while (bytesToRead > 0 && !_done)
|
||||
{
|
||||
size_t readLen = _source->Read(dst, _offset, bytesToRead);
|
||||
if (readLen > 0)
|
||||
{
|
||||
dst = (void *)((uintptr_t)dst + readLen);
|
||||
bytesToRead -= readLen;
|
||||
bytesRead += readLen;
|
||||
_offset += readLen;
|
||||
}
|
||||
if (_offset >= _source->GetLength())
|
||||
{
|
||||
if (_loop == 0)
|
||||
{
|
||||
_done = true;
|
||||
}
|
||||
else if (_loop == MIXER_LOOP_INFINITE)
|
||||
{
|
||||
_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loop--;
|
||||
_offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioChannel * AudioChannel::Create()
|
||||
{
|
||||
return new (std::nothrow) AudioChannelImpl();
|
||||
}
|
|
@ -16,70 +16,61 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <speex/speex_resampler.h>
|
||||
#include "../common.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioMixer.h"
|
||||
|
||||
interface IAudioSource;
|
||||
|
||||
/**
|
||||
* Represents an audio channel that represents an audio source
|
||||
* and a number of properties such as volume, pan and loop information.
|
||||
*/
|
||||
interface IAudioChannel
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
virtual ~IAudioChannel() = default;
|
||||
interface IAudioSource;
|
||||
|
||||
virtual IAudioSource * GetSource() const abstract;
|
||||
/**
|
||||
* Represents an audio channel that represents an audio source
|
||||
* and a number of properties such as volume, pan and loop information.
|
||||
*/
|
||||
interface IAudioChannel
|
||||
{
|
||||
virtual ~IAudioChannel() = default;
|
||||
|
||||
virtual SpeexResamplerState * GetResampler() const abstract;
|
||||
virtual void SetResampler(SpeexResamplerState * value) abstract;
|
||||
virtual IAudioSource * GetSource() const abstract;
|
||||
|
||||
virtual sint32 GetGroup() const abstract;
|
||||
virtual void SetGroup(sint32 group) abstract;
|
||||
virtual sint32 GetGroup() const abstract;
|
||||
virtual void SetGroup(sint32 group) abstract;
|
||||
|
||||
virtual double GetRate() const abstract;
|
||||
virtual void SetRate(double rate) abstract;
|
||||
virtual double GetRate() const abstract;
|
||||
virtual void SetRate(double rate) abstract;
|
||||
|
||||
virtual uint64 GetOffset() const abstract;
|
||||
virtual bool SetOffset(uint64 offset) abstract;
|
||||
virtual uint64 GetOffset() const abstract;
|
||||
virtual bool SetOffset(uint64 offset) abstract;
|
||||
|
||||
virtual sint32 GetLoop() const abstract;
|
||||
virtual void SetLoop(sint32 value) abstract;
|
||||
virtual sint32 GetLoop() const abstract;
|
||||
virtual void SetLoop(sint32 value) abstract;
|
||||
|
||||
virtual sint32 GetVolume() const abstract;
|
||||
virtual float GetVolumeL() const abstract;
|
||||
virtual float GetVolumeR() const abstract;
|
||||
virtual float GetOldVolumeL() const abstract;
|
||||
virtual float GetOldVolumeR() const abstract;
|
||||
virtual sint32 GetOldVolume() const abstract;
|
||||
virtual void SetVolume(sint32 volume) abstract;
|
||||
virtual sint32 GetVolume() const abstract;
|
||||
virtual float GetVolumeL() const abstract;
|
||||
virtual float GetVolumeR() const abstract;
|
||||
virtual float GetOldVolumeL() const abstract;
|
||||
virtual float GetOldVolumeR() const abstract;
|
||||
virtual sint32 GetOldVolume() const abstract;
|
||||
virtual void SetVolume(sint32 volume) abstract;
|
||||
|
||||
virtual float GetPan() const abstract;
|
||||
virtual void SetPan(float pan) abstract;
|
||||
virtual float GetPan() const abstract;
|
||||
virtual void SetPan(float pan) abstract;
|
||||
|
||||
virtual bool IsStopping() const abstract;
|
||||
virtual void SetStopping(bool value) abstract;
|
||||
virtual bool IsStopping() const abstract;
|
||||
virtual void SetStopping(bool value) abstract;
|
||||
|
||||
virtual bool IsDone() const abstract;
|
||||
virtual void SetDone(bool value) abstract;
|
||||
virtual bool IsDone() const abstract;
|
||||
virtual void SetDone(bool value) abstract;
|
||||
|
||||
virtual bool DeleteOnDone() const abstract;
|
||||
virtual void SetDeleteOnDone(bool value) abstract;
|
||||
virtual bool DeleteOnDone() const abstract;
|
||||
virtual void SetDeleteOnDone(bool value) abstract;
|
||||
|
||||
virtual void SetDeleteSourceOnDone(bool value) abstract;
|
||||
virtual void SetDeleteSourceOnDone(bool value) abstract;
|
||||
|
||||
virtual bool IsPlaying() const abstract;
|
||||
virtual bool IsPlaying() const abstract;
|
||||
|
||||
virtual void Play(IAudioSource * source, sint32 loop = MIXER_LOOP_NONE) abstract;
|
||||
virtual void UpdateOldVolume() abstract;
|
||||
virtual void Play(IAudioSource * source, sint32 loop = 0) abstract;
|
||||
virtual void UpdateOldVolume() abstract;
|
||||
|
||||
virtual AudioFormat GetFormat() const abstract;
|
||||
virtual size_t Read(void * dst, size_t len) abstract;
|
||||
};
|
||||
|
||||
namespace AudioChannel
|
||||
{
|
||||
IAudioChannel * Create();
|
||||
}
|
||||
virtual size_t Read(void * dst, size_t len) abstract;
|
||||
};
|
||||
} }
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../common.h"
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
namespace Audio
|
||||
{
|
||||
interface IAudioChannel;
|
||||
interface IAudioMixer;
|
||||
interface IAudioSource;
|
||||
|
||||
/**
|
||||
* Audio services for playing music and sound effects.
|
||||
*/
|
||||
interface IAudioContext
|
||||
{
|
||||
virtual ~IAudioContext() = default;
|
||||
|
||||
virtual IAudioMixer * GetMixer() abstract;
|
||||
|
||||
virtual std::vector<std::string> GetOutputDevices() abstract;
|
||||
virtual void SetOutputDevice(const std::string &deviceName) abstract;
|
||||
|
||||
virtual IAudioSource * CreateStreamFromWAV(const std::string &path) abstract;
|
||||
|
||||
|
||||
virtual void StartTitleMusic() abstract;
|
||||
|
||||
virtual IAudioChannel * PlaySound(sint32 soundId, sint32 volume, sint32 pan) abstract;
|
||||
virtual IAudioChannel * PlaySoundAtLocation(sint32 soundId, sint16 x, sint16 y, sint16 z) abstract;
|
||||
virtual IAudioChannel * PlaySoundPanned(sint32 soundId, sint32 pan, sint16 x, sint16 y, sint16 z) abstract;
|
||||
|
||||
virtual void ToggleAllSounds() abstract;
|
||||
virtual void PauseSounds() abstract;
|
||||
virtual void UnpauseSounds() abstract;
|
||||
|
||||
virtual void StopAll() abstract;
|
||||
virtual void StopCrowdSound() abstract;
|
||||
virtual void StopRainSound() abstract;
|
||||
virtual void StopRideMusic() abstract;
|
||||
virtual void StopTitleMusic() abstract;
|
||||
virtual void StopVehicleSounds() abstract;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,549 +1,56 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <list>
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../core/Math.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/Util.hpp"
|
||||
#include <cmath>
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "audio.h"
|
||||
#include "AudioChannel.h"
|
||||
#include "AudioContext.h"
|
||||
#include "AudioMixer.h"
|
||||
#include "AudioSource.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../config/Config.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../platform/platform.h"
|
||||
#include "../rct2.h"
|
||||
#include "audio.h"
|
||||
}
|
||||
|
||||
IAudioMixer * gMixer;
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Audio;
|
||||
|
||||
struct Buffer
|
||||
static IAudioMixer * GetMixer()
|
||||
{
|
||||
private:
|
||||
void * _data = nullptr;
|
||||
size_t _capacity = 0;
|
||||
|
||||
public:
|
||||
void * GetData() const { return _data; }
|
||||
void * GetData() { return _data; }
|
||||
size_t GetCapacity() const { return _capacity; }
|
||||
|
||||
~Buffer()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
void Free()
|
||||
{
|
||||
Memory::Free(_data);
|
||||
_data = nullptr;
|
||||
_capacity = 0;
|
||||
}
|
||||
|
||||
void EnsureCapacity(size_t capacity)
|
||||
{
|
||||
if (_capacity < capacity)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_data = Memory::Reallocate(_data, capacity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AudioMixer final : public IAudioMixer
|
||||
{
|
||||
private:
|
||||
IAudioSource * _nullSource = nullptr;
|
||||
|
||||
SDL_AudioDeviceID _deviceId = 0;
|
||||
AudioFormat _format = { 0 };
|
||||
std::list<IAudioChannel *> _channels;
|
||||
float _volume = 1.0f;
|
||||
float _adjustSoundVolume = 0.0f;
|
||||
float _adjustMusicVolume = 0.0f;
|
||||
uint8 _settingSoundVolume = 0xFF;
|
||||
uint8 _settingMusicVolume = 0xFF;
|
||||
|
||||
IAudioSource * _css1Sources[SOUND_MAXID] = { nullptr };
|
||||
IAudioSource * _musicSources[PATH_ID_END] = { nullptr };
|
||||
|
||||
Buffer _channelBuffer;
|
||||
Buffer _convertBuffer;
|
||||
Buffer _effectBuffer;
|
||||
|
||||
public:
|
||||
AudioMixer()
|
||||
{
|
||||
_nullSource = AudioSource::CreateNull();
|
||||
}
|
||||
|
||||
~AudioMixer()
|
||||
{
|
||||
Close();
|
||||
delete _nullSource;
|
||||
}
|
||||
|
||||
void Init(const char* device) override
|
||||
{
|
||||
Close();
|
||||
|
||||
SDL_AudioSpec want = { 0 };
|
||||
want.freq = 44100;
|
||||
want.format = AUDIO_S16SYS;
|
||||
want.channels = 2;
|
||||
want.samples = 1024;
|
||||
want.callback = [](void * arg, uint8 * dst, sint32 length) -> void
|
||||
{
|
||||
auto mixer = static_cast<AudioMixer *>(arg);
|
||||
mixer->GetNextAudioChunk(dst, (size_t)length);
|
||||
};
|
||||
want.userdata = this;
|
||||
|
||||
SDL_AudioSpec have;
|
||||
_deviceId = SDL_OpenAudioDevice(device, 0, &want, &have, 0);
|
||||
_format.format = have.format;
|
||||
_format.channels = have.channels;
|
||||
_format.freq = have.freq;
|
||||
|
||||
LoadAllSounds();
|
||||
|
||||
SDL_PauseAudioDevice(_deviceId, 0);
|
||||
}
|
||||
|
||||
void Close() override
|
||||
{
|
||||
// Free channels
|
||||
Lock();
|
||||
for (IAudioChannel * channel : _channels)
|
||||
{
|
||||
delete channel;
|
||||
}
|
||||
_channels.clear();
|
||||
Unlock();
|
||||
|
||||
SDL_CloseAudioDevice(_deviceId);
|
||||
|
||||
// Free sources
|
||||
for (size_t i = 0; i < Util::CountOf(_css1Sources); i++)
|
||||
{
|
||||
if (_css1Sources[i] != _nullSource)
|
||||
{
|
||||
SafeDelete(_css1Sources[i]);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < Util::CountOf(_musicSources); i++)
|
||||
{
|
||||
if (_musicSources[i] != _nullSource)
|
||||
{
|
||||
SafeDelete(_musicSources[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Free buffers
|
||||
_channelBuffer.Free();
|
||||
_convertBuffer.Free();
|
||||
_effectBuffer.Free();
|
||||
}
|
||||
|
||||
void Lock() override
|
||||
{
|
||||
SDL_LockAudioDevice(_deviceId);
|
||||
}
|
||||
|
||||
void Unlock() override
|
||||
{
|
||||
SDL_UnlockAudioDevice(_deviceId);
|
||||
}
|
||||
|
||||
IAudioChannel * Play(IAudioSource * source, sint32 loop, bool deleteondone, bool deletesourceondone) override
|
||||
{
|
||||
Lock();
|
||||
IAudioChannel * channel = AudioChannel::Create();
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->Play(source, loop);
|
||||
channel->SetDeleteOnDone(deleteondone);
|
||||
channel->SetDeleteSourceOnDone(deletesourceondone);
|
||||
_channels.push_back(channel);
|
||||
}
|
||||
Unlock();
|
||||
return channel;
|
||||
}
|
||||
|
||||
void Stop(IAudioChannel * channel) override
|
||||
{
|
||||
Lock();
|
||||
channel->SetStopping(true);
|
||||
Unlock();
|
||||
}
|
||||
|
||||
bool LoadMusic(size_t pathId) override
|
||||
{
|
||||
bool result = false;
|
||||
if (pathId < Util::CountOf(_musicSources))
|
||||
{
|
||||
IAudioSource * source = _musicSources[pathId];
|
||||
if (source == nullptr)
|
||||
{
|
||||
const utf8 * path = get_file_path((sint32)pathId);
|
||||
source = AudioSource::CreateMemoryFromWAV(path, &_format);
|
||||
if (source == nullptr)
|
||||
{
|
||||
source = _nullSource;
|
||||
}
|
||||
_musicSources[pathId] = source;
|
||||
}
|
||||
result = source != _nullSource;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SetVolume(float volume) override
|
||||
{
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
IAudioSource * GetSoundSource(sint32 id) override
|
||||
{
|
||||
return _css1Sources[id];
|
||||
}
|
||||
|
||||
IAudioSource * GetMusicSource(sint32 id) override
|
||||
{
|
||||
return _musicSources[id];
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadAllSounds()
|
||||
{
|
||||
const utf8 * css1Path = get_file_path(PATH_ID_CSS1);
|
||||
for (size_t i = 0; i < Util::CountOf(_css1Sources); i++)
|
||||
{
|
||||
auto source = AudioSource::CreateMemoryFromCSS1(css1Path, i, &_format);
|
||||
if (source == nullptr)
|
||||
{
|
||||
source = _nullSource;
|
||||
}
|
||||
_css1Sources[i] = source;
|
||||
}
|
||||
}
|
||||
|
||||
void GetNextAudioChunk(uint8 * dst, size_t length)
|
||||
{
|
||||
UpdateAdjustedSound();
|
||||
|
||||
// Zero the output buffer
|
||||
Memory::Set(dst, 0, length);
|
||||
|
||||
// Mix channels onto output buffer
|
||||
std::list<IAudioChannel *>::iterator it = _channels.begin();
|
||||
while (it != _channels.end())
|
||||
{
|
||||
IAudioChannel * channel = *it;
|
||||
|
||||
sint32 group = channel->GetGroup();
|
||||
if (group != MIXER_GROUP_SOUND || gConfigSound.sound_enabled)
|
||||
{
|
||||
MixChannel(channel, dst, length);
|
||||
}
|
||||
if ((channel->IsDone() && channel->DeleteOnDone()) || channel->IsStopping())
|
||||
{
|
||||
delete channel;
|
||||
it = _channels.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAdjustedSound()
|
||||
{
|
||||
// Did the volume level get changed? Recalculate level in this case.
|
||||
if (_settingSoundVolume != gConfigSound.sound_volume)
|
||||
{
|
||||
_settingSoundVolume = gConfigSound.sound_volume;
|
||||
_adjustSoundVolume = powf(_settingSoundVolume / 100.f, 10.f / 6.f);
|
||||
}
|
||||
if (_settingMusicVolume != gConfigSound.ride_music_volume)
|
||||
{
|
||||
_settingMusicVolume = gConfigSound.ride_music_volume;
|
||||
_adjustMusicVolume = powf(_settingMusicVolume / 100.f, 10.f / 6.f);
|
||||
}
|
||||
}
|
||||
|
||||
void MixChannel(IAudioChannel * channel, uint8 * data, size_t length)
|
||||
{
|
||||
sint32 byteRate = _format.GetByteRate();
|
||||
sint32 numSamples = (sint32)(length / byteRate);
|
||||
double rate = 1;
|
||||
if (_format.format == AUDIO_S16SYS)
|
||||
{
|
||||
rate = channel->GetRate();
|
||||
}
|
||||
|
||||
bool mustConvert = false;
|
||||
SDL_AudioCVT cvt;
|
||||
cvt.len_ratio = 1;
|
||||
AudioFormat streamformat = channel->GetFormat();
|
||||
if (streamformat != _format)
|
||||
{
|
||||
if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, _format.format, _format.channels, _format.freq) == -1)
|
||||
{
|
||||
// Unable to convert channel data
|
||||
return;
|
||||
}
|
||||
mustConvert = true;
|
||||
}
|
||||
|
||||
// Read raw PCM from channel
|
||||
sint32 readSamples = (sint32)(numSamples * rate);
|
||||
size_t readLength = (size_t)(readSamples / cvt.len_ratio) * byteRate;
|
||||
_channelBuffer.EnsureCapacity(readLength);
|
||||
size_t bytesRead = channel->Read(_channelBuffer.GetData(), readLength);
|
||||
|
||||
// Convert data to required format if necessary
|
||||
void * buffer = nullptr;
|
||||
size_t bufferLen = 0;
|
||||
if (mustConvert)
|
||||
{
|
||||
if (Convert(&cvt, _channelBuffer.GetData(), bytesRead))
|
||||
{
|
||||
buffer = cvt.buf;
|
||||
bufferLen = cvt.len_cvt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = _channelBuffer.GetData();
|
||||
bufferLen = bytesRead;
|
||||
}
|
||||
|
||||
// Apply effects
|
||||
if (rate != 1)
|
||||
{
|
||||
sint32 srcSamples = (sint32)(bufferLen / byteRate);
|
||||
sint32 dstSamples = numSamples;
|
||||
bufferLen = ApplyResample(channel, buffer, srcSamples, dstSamples);
|
||||
buffer = _effectBuffer.GetData();
|
||||
}
|
||||
|
||||
// Apply panning and volume
|
||||
ApplyPan(channel, buffer, bufferLen, byteRate);
|
||||
sint32 mixVolume = ApplyVolume(channel, buffer, bufferLen);
|
||||
|
||||
// Finally mix on to destination buffer
|
||||
size_t dstLength = Math::Min(length, bufferLen);
|
||||
SDL_MixAudioFormat(data, (const uint8 *)buffer, _format.format, (uint32)dstLength, mixVolume);
|
||||
|
||||
channel->UpdateOldVolume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resample the given buffer into _effectBuffer.
|
||||
* Assumes that srcBuffer is the same format as _format.
|
||||
*/
|
||||
size_t ApplyResample(IAudioChannel * channel, const void * srcBuffer, sint32 srcSamples, sint32 dstSamples)
|
||||
{
|
||||
sint32 byteRate = _format.GetByteRate();
|
||||
|
||||
// Create resampler
|
||||
SpeexResamplerState * resampler = channel->GetResampler();
|
||||
if (resampler == nullptr)
|
||||
{
|
||||
resampler = speex_resampler_init(_format.channels, _format.freq, _format.freq, 0, 0);
|
||||
channel->SetResampler(resampler);
|
||||
}
|
||||
speex_resampler_set_rate(resampler, srcSamples, dstSamples);
|
||||
|
||||
// Ensure destination buffer is large enough
|
||||
size_t effectBufferReqLen = dstSamples * byteRate;
|
||||
_effectBuffer.EnsureCapacity(effectBufferReqLen);
|
||||
|
||||
uint32 inLen = srcSamples;
|
||||
uint32 outLen = dstSamples;
|
||||
speex_resampler_process_interleaved_int(
|
||||
resampler,
|
||||
(const spx_int16_t *)srcBuffer,
|
||||
&inLen,
|
||||
(spx_int16_t *)_effectBuffer.GetData(),
|
||||
&outLen);
|
||||
|
||||
return outLen * byteRate;
|
||||
}
|
||||
|
||||
void ApplyPan(const IAudioChannel * channel, void * buffer, size_t len, size_t sampleSize)
|
||||
{
|
||||
if (channel->GetPan() != 0.5f && _format.channels == 2)
|
||||
{
|
||||
switch (_format.format) {
|
||||
case AUDIO_S16SYS:
|
||||
EffectPanS16(channel, (sint16 *)buffer, (sint32)(len / sampleSize));
|
||||
break;
|
||||
case AUDIO_U8:
|
||||
EffectPanU8(channel, (uint8 *)buffer, (sint32)(len / sampleSize));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sint32 ApplyVolume(const IAudioChannel * channel, void * buffer, size_t len)
|
||||
{
|
||||
float volumeAdjust = _volume;
|
||||
volumeAdjust *= (gConfigSound.master_volume / 100.0f);
|
||||
switch (channel->GetGroup()) {
|
||||
case MIXER_GROUP_SOUND:
|
||||
volumeAdjust *= _adjustSoundVolume;
|
||||
|
||||
// Cap sound volume on title screen so music is more audible
|
||||
if (gScreenFlags & SCREEN_FLAGS_TITLE_DEMO)
|
||||
{
|
||||
volumeAdjust = Math::Min(volumeAdjust, 0.75f);
|
||||
}
|
||||
break;
|
||||
case MIXER_GROUP_RIDE_MUSIC:
|
||||
volumeAdjust *= _adjustMusicVolume;
|
||||
break;
|
||||
}
|
||||
|
||||
sint32 startVolume = (sint32)(channel->GetOldVolume() * volumeAdjust);
|
||||
sint32 endVolume = (sint32)(channel->GetVolume() * volumeAdjust);
|
||||
if (channel->IsStopping())
|
||||
{
|
||||
endVolume = 0;
|
||||
}
|
||||
|
||||
sint32 mixVolume = (sint32)(channel->GetVolume() * volumeAdjust);
|
||||
if (startVolume != endVolume)
|
||||
{
|
||||
// Set to max since we are adjusting the volume ourselves
|
||||
mixVolume = SDL_MIX_MAXVOLUME;
|
||||
|
||||
// Fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
|
||||
sint32 fadeLength = (sint32)len / _format.BytesPerSample();
|
||||
switch (_format.format) {
|
||||
case AUDIO_S16SYS:
|
||||
EffectFadeS16((sint16 *)buffer, fadeLength, startVolume, endVolume);
|
||||
break;
|
||||
case AUDIO_U8:
|
||||
EffectFadeU8((uint8 *)buffer, fadeLength, startVolume, endVolume);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mixVolume;
|
||||
}
|
||||
|
||||
static void EffectPanS16(const IAudioChannel * channel, sint16 * data, sint32 length)
|
||||
{
|
||||
const float dt = 1.0f / (length * 2);
|
||||
float volumeL = channel->GetOldVolumeL();
|
||||
float volumeR = channel->GetOldVolumeR();
|
||||
const float d_left = dt * (channel->GetVolumeL() - channel->GetOldVolumeL());
|
||||
const float d_right = dt * (channel->GetVolumeR() - channel->GetOldVolumeR());
|
||||
|
||||
for (sint32 i = 0; i < length * 2; i += 2)
|
||||
{
|
||||
data[i] = (sint16)(data[i] * volumeL);
|
||||
data[i + 1] = (sint16)(data[i + 1] * volumeR);
|
||||
volumeL += d_left;
|
||||
volumeR += d_right;
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectPanU8(const IAudioChannel * channel, uint8 * data, sint32 length)
|
||||
{
|
||||
float volumeL = channel->GetVolumeL();
|
||||
float volumeR = channel->GetVolumeR();
|
||||
float oldVolumeL = channel->GetOldVolumeL();
|
||||
float oldVolumeR = channel->GetOldVolumeR();
|
||||
|
||||
for (sint32 i = 0; i < length * 2; i += 2)
|
||||
{
|
||||
float t = (float)i / (length * 2);
|
||||
data[i] = (uint8)(data[i] * ((1.0 - t) * oldVolumeL + t * volumeL));
|
||||
data[i + 1] = (uint8)(data[i + 1] * ((1.0 - t) * oldVolumeR + t * volumeR));
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectFadeS16(sint16 * data, sint32 length, sint32 startvolume, sint32 endvolume)
|
||||
{
|
||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||
for (sint32 i = 0; i < length; i++)
|
||||
{
|
||||
float t = (float)i / length;
|
||||
data[i] = (sint16)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||
}
|
||||
}
|
||||
|
||||
static void EffectFadeU8(uint8* data, sint32 length, sint32 startvolume, sint32 endvolume)
|
||||
{
|
||||
float startvolume_f = (float)startvolume / SDL_MIX_MAXVOLUME;
|
||||
float endvolume_f = (float)endvolume / SDL_MIX_MAXVOLUME;
|
||||
for (sint32 i = 0; i < length; i++)
|
||||
{
|
||||
float t = (float)i / length;
|
||||
data[i] = (uint8)(data[i] * ((1 - t) * startvolume_f + t * endvolume_f));
|
||||
}
|
||||
}
|
||||
|
||||
bool Convert(SDL_AudioCVT * cvt, const void * src, size_t len)
|
||||
{
|
||||
// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
|
||||
bool result = false;
|
||||
if (len != 0 && cvt->len_mult != 0)
|
||||
{
|
||||
size_t reqConvertBufferCapacity = len * cvt->len_mult;
|
||||
_convertBuffer.EnsureCapacity(reqConvertBufferCapacity);
|
||||
Memory::Copy(_convertBuffer.GetData(), src, len);
|
||||
|
||||
cvt->len = (sint32)len;
|
||||
cvt->buf = (uint8 *)_convertBuffer.GetData();
|
||||
if (SDL_ConvertAudio(cvt) >= 0)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
IAudioContext * audioContext = GetContext()->GetAudioContext();
|
||||
return audioContext->GetMixer();
|
||||
}
|
||||
|
||||
void Mixer_Init(const char * device)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
IAudioContext * audioContext = GetContext()->GetAudioContext();
|
||||
if (device == nullptr)
|
||||
{
|
||||
gMixer = new AudioMixer();
|
||||
gMixer->Init(device);
|
||||
device = "";
|
||||
}
|
||||
audioContext->SetOutputDevice(std::string(device));
|
||||
}
|
||||
|
||||
void * Mixer_Play_Effect(size_t id, sint32 loop, sint32 volume, float pan, double rate, sint32 deleteondone)
|
||||
{
|
||||
IAudioChannel * channel = nullptr;
|
||||
if (!gOpenRCT2Headless && gConfigSound.sound_enabled)
|
||||
if (gConfigSound.sound_enabled)
|
||||
{
|
||||
if (id >= SOUND_MAXID)
|
||||
{
|
||||
|
@ -551,7 +58,7 @@ void * Mixer_Play_Effect(size_t id, sint32 loop, sint32 volume, float pan, doubl
|
|||
}
|
||||
else
|
||||
{
|
||||
IAudioMixer * mixer = gMixer;
|
||||
IAudioMixer * mixer = GetMixer();
|
||||
mixer->Lock();
|
||||
IAudioSource * source = mixer->GetSoundSource((sint32)id);
|
||||
channel = mixer->Play(source, loop, deleteondone != 0, false);
|
||||
|
@ -569,124 +76,103 @@ void * Mixer_Play_Effect(size_t id, sint32 loop, sint32 volume, float pan, doubl
|
|||
|
||||
void Mixer_Stop_Channel(void * channel)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
gMixer->Stop(static_cast<IAudioChannel*>(channel));
|
||||
}
|
||||
GetMixer()->Stop(static_cast<IAudioChannel*>(channel));
|
||||
}
|
||||
|
||||
void Mixer_Channel_Volume(void * channel, sint32 volume)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
gMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetVolume(volume);
|
||||
gMixer->Unlock();
|
||||
}
|
||||
IAudioMixer * audioMixer = GetMixer();
|
||||
audioMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetVolume(volume);
|
||||
audioMixer->Unlock();
|
||||
}
|
||||
|
||||
void Mixer_Channel_Pan(void * channel, float pan)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
gMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetPan(pan);
|
||||
gMixer->Unlock();
|
||||
}
|
||||
IAudioMixer * audioMixer = GetMixer();
|
||||
audioMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetPan(pan);
|
||||
audioMixer->Unlock();
|
||||
}
|
||||
|
||||
void Mixer_Channel_Rate(void* channel, double rate)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
gMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetRate(rate);
|
||||
gMixer->Unlock();
|
||||
}
|
||||
IAudioMixer * audioMixer = GetMixer();
|
||||
audioMixer->Lock();
|
||||
static_cast<IAudioChannel*>(channel)->SetRate(rate);
|
||||
audioMixer->Unlock();
|
||||
}
|
||||
|
||||
sint32 Mixer_Channel_IsPlaying(void * channel)
|
||||
{
|
||||
bool isPlaying = false;
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
isPlaying = static_cast<IAudioChannel*>(channel)->IsPlaying();
|
||||
}
|
||||
return isPlaying;
|
||||
return static_cast<IAudioChannel*>(channel)->IsPlaying();
|
||||
}
|
||||
|
||||
uint64 Mixer_Channel_GetOffset(void * channel)
|
||||
{
|
||||
uint64 offset = 0;
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
offset = static_cast<IAudioChannel*>(channel)->GetOffset();
|
||||
}
|
||||
return offset;
|
||||
return static_cast<IAudioChannel*>(channel)->GetOffset();
|
||||
}
|
||||
|
||||
sint32 Mixer_Channel_SetOffset(void * channel, uint64 offset)
|
||||
{
|
||||
sint32 result = 0;
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
result = static_cast<IAudioChannel*>(channel)->SetOffset(offset);
|
||||
}
|
||||
return result;
|
||||
return static_cast<IAudioChannel*>(channel)->SetOffset(offset);
|
||||
}
|
||||
|
||||
void Mixer_Channel_SetGroup(void * channel, sint32 group)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
static_cast<IAudioChannel *>(channel)->SetGroup(group);
|
||||
}
|
||||
static_cast<IAudioChannel *>(channel)->SetGroup(group);
|
||||
}
|
||||
|
||||
void * Mixer_Play_Music(sint32 pathId, sint32 loop, sint32 streaming)
|
||||
{
|
||||
IAudioChannel * channel = nullptr;
|
||||
if (!gOpenRCT2Headless)
|
||||
IAudioMixer * mixer = GetMixer();
|
||||
if (streaming)
|
||||
{
|
||||
IAudioMixer * mixer = gMixer;
|
||||
if (streaming)
|
||||
{
|
||||
const utf8 * filename = get_file_path(pathId);
|
||||
const utf8 * path = get_file_path(pathId);
|
||||
|
||||
SDL_RWops* rw = SDL_RWFromFile(filename, "rb");
|
||||
if (rw != nullptr)
|
||||
IAudioContext * audioContext = GetContext()->GetAudioContext();
|
||||
IAudioSource * source = audioContext->CreateStreamFromWAV(path);
|
||||
if (source != nullptr)
|
||||
{
|
||||
channel = mixer->Play(source, loop, false, true);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
auto source = AudioSource::CreateStreamFromWAV(rw);
|
||||
if (source != nullptr)
|
||||
{
|
||||
channel = mixer->Play(source, loop, false, true);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
delete source;
|
||||
}
|
||||
}
|
||||
delete source;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mixer->LoadMusic(pathId))
|
||||
{
|
||||
if (mixer->LoadMusic(pathId))
|
||||
{
|
||||
IAudioSource * source = mixer->GetMusicSource(pathId);
|
||||
channel = mixer->Play(source, MIXER_LOOP_INFINITE, false, false);
|
||||
}
|
||||
}
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->SetGroup(MIXER_GROUP_RIDE_MUSIC);
|
||||
IAudioSource * source = mixer->GetMusicSource(pathId);
|
||||
channel = mixer->Play(source, MIXER_LOOP_INFINITE, false, false);
|
||||
}
|
||||
}
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->SetGroup(MIXER_GROUP_RIDE_MUSIC);
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
void Mixer_SetVolume(float volume)
|
||||
{
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
gMixer->SetVolume(volume);
|
||||
}
|
||||
GetMixer()->SetVolume(volume);
|
||||
}
|
||||
|
||||
sint32 DStoMixerVolume(sint32 volume)
|
||||
{
|
||||
return (sint32)(SDL_MIX_MAXVOLUME * (std::pow(10.0f, (float)volume / 2000)));
|
||||
}
|
||||
|
||||
float DStoMixerPan(sint32 pan)
|
||||
{
|
||||
return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2;
|
||||
}
|
||||
|
||||
double DStoMixerRate(sint32 frequency)
|
||||
{
|
||||
return (double)frequency / 22050;
|
||||
}
|
||||
|
|
|
@ -14,21 +14,11 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifndef _MIXER_H_
|
||||
#define _MIXER_H_
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif // __cplusplus
|
||||
|
||||
#include "../platform/platform.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
#define SDL_MIX_MAXVOLUME 128
|
||||
|
||||
#define MIXER_LOOP_NONE 0
|
||||
#define MIXER_LOOP_INFINITE -1
|
||||
|
@ -42,59 +32,61 @@ enum MIXER_GROUP
|
|||
|
||||
#ifdef __cplusplus
|
||||
|
||||
interface IAudioSource;
|
||||
interface IAudioChannel;
|
||||
|
||||
/**
|
||||
* Provides an audio stream by mixing multiple audio channels together.
|
||||
*/
|
||||
interface IAudioMixer
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
virtual ~IAudioMixer() = default;
|
||||
interface IAudioSource;
|
||||
interface IAudioChannel;
|
||||
|
||||
virtual void Init(const char * device) abstract;
|
||||
virtual void Close() abstract;
|
||||
virtual void Lock() abstract;
|
||||
virtual void Unlock() abstract;
|
||||
virtual IAudioChannel * Play(IAudioSource * source, sint32 loop, bool deleteondone, bool deletesourceondone) abstract;
|
||||
virtual void Stop(IAudioChannel * channel) abstract;
|
||||
virtual bool LoadMusic(size_t pathid) abstract;
|
||||
virtual void SetVolume(float volume) abstract;
|
||||
/**
|
||||
* Provides an audio stream by mixing multiple audio channels together.
|
||||
*/
|
||||
interface IAudioMixer
|
||||
{
|
||||
virtual ~IAudioMixer() = default;
|
||||
|
||||
virtual IAudioSource * GetSoundSource(sint32 id) abstract;
|
||||
virtual IAudioSource * GetMusicSource(sint32 id) abstract;
|
||||
};
|
||||
virtual void Init(const char * device) abstract;
|
||||
virtual void Close() abstract;
|
||||
virtual void Lock() abstract;
|
||||
virtual void Unlock() abstract;
|
||||
virtual IAudioChannel * Play(IAudioSource * source, sint32 loop, bool deleteondone, bool deletesourceondone) abstract;
|
||||
virtual void Stop(IAudioChannel * channel) abstract;
|
||||
virtual bool LoadMusic(size_t pathid) abstract;
|
||||
virtual void SetVolume(float volume) abstract;
|
||||
|
||||
virtual IAudioSource * GetSoundSource(sint32 id) abstract;
|
||||
virtual IAudioSource * GetMusicSource(sint32 id) abstract;
|
||||
};
|
||||
} }
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#ifndef DSBPAN_LEFT
|
||||
#define DSBPAN_LEFT -10000
|
||||
#endif
|
||||
#ifndef DSBPAN_RIGHT
|
||||
#define DSBPAN_RIGHT 10000
|
||||
#endif
|
||||
|
||||
#ifndef DSBPAN_LEFT
|
||||
#define DSBPAN_LEFT -10000
|
||||
#endif
|
||||
#ifndef DSBPAN_RIGHT
|
||||
#define DSBPAN_RIGHT 10000
|
||||
#endif
|
||||
|
||||
void Mixer_Init(const char* device);
|
||||
void* Mixer_Play_Effect(size_t id, sint32 loop, sint32 volume, float pan, double rate, sint32 deleteondone);
|
||||
void Mixer_Stop_Channel(void* channel);
|
||||
void Mixer_Channel_Volume(void* channel, sint32 volume);
|
||||
void Mixer_Channel_Pan(void* channel, float pan);
|
||||
void Mixer_Channel_Rate(void* channel, double rate);
|
||||
sint32 Mixer_Channel_IsPlaying(void* channel);
|
||||
uint64 Mixer_Channel_GetOffset(void* channel);
|
||||
sint32 Mixer_Channel_SetOffset(void* channel, uint64 offset);
|
||||
void Mixer_Channel_SetGroup(void* channel, sint32 group);
|
||||
void* Mixer_Play_Music(sint32 pathId, sint32 loop, sint32 streaming);
|
||||
void Mixer_SetVolume(float volume);
|
||||
|
||||
static inline sint32 DStoMixerVolume(sint32 volume) { return (sint32)(SDL_MIX_MAXVOLUME * (SDL_pow(10, (float)volume / 2000))); }
|
||||
static inline float DStoMixerPan(sint32 pan) { return (((float)pan + -DSBPAN_LEFT) / DSBPAN_RIGHT) / 2; }
|
||||
static inline double DStoMixerRate(sint32 frequency) { return (double)frequency / 22050; }
|
||||
void Mixer_Init(const char * device);
|
||||
void* Mixer_Play_Effect(size_t id, sint32 loop, sint32 volume, float pan, double rate, sint32 deleteondone);
|
||||
void Mixer_Stop_Channel(void* channel);
|
||||
void Mixer_Channel_Volume(void* channel, sint32 volume);
|
||||
void Mixer_Channel_Pan(void* channel, float pan);
|
||||
void Mixer_Channel_Rate(void* channel, double rate);
|
||||
sint32 Mixer_Channel_IsPlaying(void* channel);
|
||||
uint64 Mixer_Channel_GetOffset(void* channel);
|
||||
sint32 Mixer_Channel_SetOffset(void* channel, uint64 offset);
|
||||
void Mixer_Channel_SetGroup(void* channel, sint32 group);
|
||||
void* Mixer_Play_Music(sint32 pathId, sint32 loop, sint32 streaming);
|
||||
void Mixer_SetVolume(float volume);
|
||||
|
||||
sint32 DStoMixerVolume(sint32 volume);
|
||||
float DStoMixerPan(sint32 pan);
|
||||
double DStoMixerRate(sint32 frequency);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,25 +17,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "AudioMixer.h"
|
||||
|
||||
/**
|
||||
* Represents a readable source of audio PCM data.
|
||||
*/
|
||||
interface IAudioSource
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
virtual ~IAudioSource() = default;
|
||||
/**
|
||||
* Represents a readable source of audio PCM data.
|
||||
*/
|
||||
interface IAudioSource
|
||||
{
|
||||
virtual ~IAudioSource() = default;
|
||||
|
||||
virtual uint64 GetLength() abstract;
|
||||
virtual AudioFormat GetFormat() abstract;
|
||||
virtual size_t Read(void * dst, uint64 offset, size_t len) abstract;
|
||||
};
|
||||
virtual uint64 GetLength() const abstract;
|
||||
// virtual AudioFormat GetFormat() abstract;
|
||||
virtual size_t Read(void * dst, uint64 offset, size_t len) abstract;
|
||||
};
|
||||
|
||||
namespace AudioSource
|
||||
{
|
||||
IAudioSource * CreateNull();
|
||||
IAudioSource * CreateMemoryFromCSS1(const utf8 * path, size_t index, const AudioFormat * targetFormat = nullptr);
|
||||
IAudioSource * CreateMemoryFromWAV(const utf8 * path, const AudioFormat * targetFormat = nullptr);
|
||||
IAudioSource * CreateStreamFromWAV(SDL_RWops * rw);
|
||||
}
|
||||
namespace AudioSource
|
||||
{
|
||||
IAudioSource * CreateNull();
|
||||
}
|
||||
} }
|
||||
|
|
|
@ -1,204 +0,0 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "../core/Math.hpp"
|
||||
#include "AudioSource.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WaveFormat
|
||||
{
|
||||
uint16 encoding;
|
||||
uint16 channels;
|
||||
uint32 frequency;
|
||||
uint32 byterate;
|
||||
uint16 blockalign;
|
||||
uint16 bitspersample;
|
||||
};
|
||||
assert_struct_size(WaveFormat, 16);
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* An audio source where raw PCM data is streamed directly from
|
||||
* a file.
|
||||
*/
|
||||
class FileAudioSource final : public IAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = { 0 };
|
||||
SDL_RWops * _rw = nullptr;
|
||||
uint64 _dataBegin = 0;
|
||||
uint64 _dataLength = 0;
|
||||
|
||||
public:
|
||||
~FileAudioSource()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
uint64 GetLength() override
|
||||
{
|
||||
return _dataLength;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() override
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
size_t bytesRead = 0;
|
||||
sint64 currentPosition = SDL_RWtell(_rw);
|
||||
if (currentPosition != -1)
|
||||
{
|
||||
size_t bytesToRead = (size_t)Math::Min<uint64>(len, _dataLength - offset);
|
||||
sint64 dataOffset = _dataBegin + offset;
|
||||
if (currentPosition != dataOffset)
|
||||
{
|
||||
sint64 newPosition = SDL_RWseek(_rw, dataOffset, SEEK_SET);
|
||||
if (newPosition == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
bytesRead = SDL_RWread(_rw, dst, 1, bytesToRead);
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(SDL_RWops * rw)
|
||||
{
|
||||
const uint32 DATA = 0x61746164;
|
||||
const uint32 FMT = 0x20746D66;
|
||||
const uint32 RIFF = 0x46464952;
|
||||
const uint32 WAVE = 0x45564157;
|
||||
const uint16 pcmformat = 0x0001;
|
||||
|
||||
Unload();
|
||||
|
||||
if (rw == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_rw = rw;
|
||||
|
||||
uint32 chunkId = SDL_ReadLE32(rw);
|
||||
if (chunkId != RIFF)
|
||||
{
|
||||
log_verbose("Not a WAV file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and discard chunk size
|
||||
SDL_ReadLE32(rw);
|
||||
uint32 chunkFormat = SDL_ReadLE32(rw);
|
||||
if (chunkFormat != WAVE)
|
||||
{
|
||||
log_verbose("Not in WAVE format");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 fmtChunkSize = FindChunk(rw, FMT);
|
||||
if (!fmtChunkSize)
|
||||
{
|
||||
log_verbose("Could not find FMT chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 chunkStart = SDL_RWtell(rw);
|
||||
|
||||
WaveFormat waveFormat;
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
SDL_RWseek(rw, chunkStart + fmtChunkSize, RW_SEEK_SET);
|
||||
if (waveFormat.encoding != pcmformat) {
|
||||
log_verbose("Not in proper format");
|
||||
return false;
|
||||
}
|
||||
|
||||
_format.freq = waveFormat.frequency;
|
||||
switch (waveFormat.bitspersample) {
|
||||
case 8:
|
||||
_format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
_format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
log_verbose("Invalid bits per sample");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
uint32 dataChunkSize = FindChunk(rw, DATA);
|
||||
if (dataChunkSize == 0)
|
||||
{
|
||||
log_verbose("Could not find DATA chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
_dataLength = dataChunkSize;
|
||||
_dataBegin = SDL_RWtell(rw);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 FindChunk(SDL_RWops * rw, uint32 wantedId)
|
||||
{
|
||||
uint32 subchunkId = SDL_ReadLE32(rw);
|
||||
uint32 subchunkSize = SDL_ReadLE32(rw);
|
||||
if (subchunkId == wantedId)
|
||||
{
|
||||
return subchunkSize;
|
||||
}
|
||||
const uint32 FACT = 0x74636166;
|
||||
const uint32 LIST = 0x5453494c;
|
||||
const uint32 BEXT = 0x74786562;
|
||||
const uint32 JUNK = 0x4B4E554A;
|
||||
while (subchunkId == FACT || subchunkId == LIST || subchunkId == BEXT || subchunkId == JUNK)
|
||||
{
|
||||
SDL_RWseek(rw, subchunkSize, RW_SEEK_CUR);
|
||||
subchunkId = SDL_ReadLE32(rw);
|
||||
subchunkSize = SDL_ReadLE32(rw);
|
||||
if (subchunkId == wantedId)
|
||||
{
|
||||
return subchunkSize;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Unload()
|
||||
{
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
_dataBegin = 0;
|
||||
_dataLength = 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateStreamFromWAV(SDL_RWops * rw)
|
||||
{
|
||||
auto source = new FileAudioSource();
|
||||
if (!source->LoadWAV(rw))
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
return source;
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "../core/Math.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "AudioMixer.h"
|
||||
#include "AudioSource.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WaveFormatEx
|
||||
{
|
||||
uint16 encoding;
|
||||
uint16 channels;
|
||||
uint32 frequency;
|
||||
uint32 byterate;
|
||||
uint16 blockalign;
|
||||
uint16 bitspersample;
|
||||
uint16 extrasize;
|
||||
};
|
||||
assert_struct_size(WaveFormatEx, 18);
|
||||
#pragma pack(pop)
|
||||
|
||||
/**
|
||||
* An audio source where raw PCM data is initially loaded into RAM from
|
||||
* a file and then streamed.
|
||||
*/
|
||||
class MemoryAudioSource final : public IAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = { 0 };
|
||||
uint8 * _data = nullptr;
|
||||
size_t _length = 0;
|
||||
bool _isSDLWav = false;
|
||||
|
||||
public:
|
||||
~MemoryAudioSource()
|
||||
{
|
||||
Unload();
|
||||
}
|
||||
|
||||
uint64 GetLength() override
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() override
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
size_t bytesToRead = 0;
|
||||
if (offset < _length)
|
||||
{
|
||||
bytesToRead = (size_t)Math::Min<uint64>(len, _length - offset);
|
||||
Memory::Copy<void>(dst, _data + offset, bytesToRead);
|
||||
}
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(const utf8 * path)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadWAV(%s)", path);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
SDL_AudioSpec audiospec = { 0 };
|
||||
uint32 audioLen;
|
||||
SDL_AudioSpec * spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_data, &audioLen);
|
||||
if (spec != nullptr)
|
||||
{
|
||||
_format.freq = spec->freq;
|
||||
_format.format = spec->format;
|
||||
_format.channels = spec->channels;
|
||||
_length = audioLen;
|
||||
_isSDLWav = true;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s, unsupported WAV format", path);
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LoadCSS1(const utf8 * path, size_t index)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadCSS1(%s, %d)", path, index);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
uint32 numSounds;
|
||||
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
|
||||
if (index < numSounds)
|
||||
{
|
||||
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
|
||||
|
||||
uint32 pcmOffset;
|
||||
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
uint32 pcmSize;
|
||||
SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1);
|
||||
_length = pcmSize;
|
||||
|
||||
WaveFormatEx waveFormat;
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
_format.freq = waveFormat.frequency;
|
||||
_format.format = AUDIO_S16LSB;
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
_data = new (std::nothrow) uint8[_length];
|
||||
if (_data != nullptr)
|
||||
{
|
||||
SDL_RWread(rw, _data, _length, 1);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to allocate data");
|
||||
}
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to load %s", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Convert(const AudioFormat * format)
|
||||
{
|
||||
if (*format != _format)
|
||||
{
|
||||
SDL_AudioCVT cvt;
|
||||
if (SDL_BuildAudioCVT(&cvt, _format.format, _format.channels, _format.freq, format->format, format->channels, format->freq) >= 0)
|
||||
{
|
||||
cvt.len = (sint32)_length;
|
||||
cvt.buf = new uint8[cvt.len * cvt.len_mult];
|
||||
Memory::Copy(cvt.buf, _data, _length);
|
||||
if (SDL_ConvertAudio(&cvt) >= 0)
|
||||
{
|
||||
Unload();
|
||||
_data = cvt.buf;
|
||||
_length = cvt.len_cvt;
|
||||
_format = *format;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] cvt.buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void Unload()
|
||||
{
|
||||
if (_data != nullptr)
|
||||
{
|
||||
if (_isSDLWav)
|
||||
{
|
||||
SDL_FreeWAV(_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] _data;
|
||||
}
|
||||
_data = nullptr;
|
||||
}
|
||||
_isSDLWav = false;
|
||||
_length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateMemoryFromCSS1(const utf8 * path, size_t index, const AudioFormat * targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadCSS1(path, index))
|
||||
{
|
||||
if (targetFormat != nullptr)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource * AudioSource::CreateMemoryFromWAV(const utf8 * path, const AudioFormat * targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadWAV(path))
|
||||
{
|
||||
if (targetFormat != nullptr)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeDelete(source);
|
||||
}
|
||||
return source;
|
||||
}
|
|
@ -16,29 +16,27 @@
|
|||
|
||||
#include "AudioSource.h"
|
||||
|
||||
/**
|
||||
* An audio source representing silence.
|
||||
*/
|
||||
class NullAudioSource : public IAudioSource
|
||||
namespace OpenRCT2 { namespace Audio
|
||||
{
|
||||
public:
|
||||
uint64 GetLength() override
|
||||
/**
|
||||
* An audio source representing silence.
|
||||
*/
|
||||
class NullAudioSource : public IAudioSource
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
public:
|
||||
uint64 GetLength() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
AudioFormat GetFormat() override
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateNull()
|
||||
{
|
||||
return { 0 };
|
||||
return new NullAudioSource();
|
||||
}
|
||||
|
||||
size_t Read(void * dst, uint64 offset, size_t len) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource * AudioSource::CreateNull()
|
||||
{
|
||||
return new NullAudioSource();
|
||||
}
|
||||
} }
|
||||
|
|
|
@ -14,193 +14,199 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "../core/Collections.hpp"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileStream.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/String.hpp"
|
||||
#include "../core/Util.hpp"
|
||||
#include "../localisation/string_ids.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../ui/UiContext.h"
|
||||
#include "audio.h"
|
||||
#include "AudioContext.h"
|
||||
#include "AudioMixer.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../config/Config.h"
|
||||
#include "../interface/viewport.h"
|
||||
#include "../intro.h"
|
||||
#include "../localisation/language.h"
|
||||
#include "../util/util.h"
|
||||
#include "audio.h"
|
||||
#include "../interface/viewport.h"
|
||||
#include "../intro.h"
|
||||
#include "../localisation/language.h"
|
||||
#include "../ride/ride.h"
|
||||
#include "../util/util.h"
|
||||
}
|
||||
|
||||
typedef struct rct_audio_params {
|
||||
bool in_range;
|
||||
sint32 volume;
|
||||
sint32 pan;
|
||||
} rct_audio_params;
|
||||
using namespace OpenRCT2::Audio;
|
||||
|
||||
audio_device *gAudioDevices = NULL;
|
||||
sint32 gAudioDeviceCount;
|
||||
sint32 gAudioCurrentDevice = -1;
|
||||
void *gCrowdSoundChannel = 0;
|
||||
bool gGameSoundsOff = false;
|
||||
void *gRainSoundChannel = 0;
|
||||
rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC];
|
||||
rct_ride_music_params gRideMusicParamsList[6];
|
||||
rct_ride_music_params *gRideMusicParamsListEnd;
|
||||
void *gTitleMusicChannel = 0;
|
||||
rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
rct_vehicle_sound_params *gVehicleSoundParamsListEnd;
|
||||
sint32 gVolumeAdjustZoom = 0;
|
||||
|
||||
sint32 _volumeAdjust[SOUND_MAXID] = {
|
||||
0, // SOUND_LIFT_1
|
||||
0, // SOUND_TRACK_FRICTION_1
|
||||
0, // SOUND_LIFT_2
|
||||
0, // SOUND_SCREAM_1
|
||||
0, // SOUND_CLICK_1
|
||||
0, // SOUND_CLICK_2
|
||||
0, // SOUND_PLACE_ITEM
|
||||
0, // SOUND_SCREAM_2
|
||||
0, // SOUND_SCREAM_3
|
||||
0, // SOUND_SCREAM_4
|
||||
0, // SOUND_SCREAM_5
|
||||
0, // SOUND_SCREAM_6
|
||||
0, // SOUND_LIFT_3
|
||||
-400, // SOUND_PURCHASE
|
||||
0, // SOUND_CRASH
|
||||
0, // SOUND_LAYING_OUT_WATER
|
||||
0, // SOUND_WATER_1
|
||||
0, // SOUND_WATER_2
|
||||
0, // SOUND_TRAIN_WHISTLE
|
||||
0, // SOUND_TRAIN_CHUGGING
|
||||
-1000, // SOUND_WATER_SPLASH
|
||||
0, // SOUND_HAMMERING
|
||||
-800, // SOUND_RIDE_LAUNCH_1
|
||||
-1700, // SOUND_RIDE_LAUNCH_2
|
||||
-700, // SOUND_COUGH_1
|
||||
-700, // SOUND_COUGH_2
|
||||
-700, // SOUND_COUGH_3
|
||||
-700, // SOUND_COUGH_4
|
||||
0, // SOUND_RAIN_1
|
||||
0, // SOUND_THUNDER_1
|
||||
0, // SOUND_THUNDER_2
|
||||
0, // SOUND_RAIN_2
|
||||
0, // SOUND_RAIN_3
|
||||
0, // SOUND_BALLOON_POP
|
||||
-700, // SOUND_MECHANIC_FIX
|
||||
0, // SOUND_SCREAM_7
|
||||
-2500, // SOUND_TOILET_FLUSH original value: -1000
|
||||
0, // SOUND_CLICK_3
|
||||
0, // SOUND_QUACK
|
||||
0, // SOUND_NEWS_ITEM
|
||||
0, // SOUND_WINDOW_OPEN
|
||||
-900, // SOUND_LAUGH_1
|
||||
-900, // SOUND_LAUGH_2
|
||||
-900, // SOUND_LAUGH_3
|
||||
0, // SOUND_APPLAUSE
|
||||
-600, // SOUND_HAUNTED_HOUSE_SCARE
|
||||
-700, // SOUND_HAUNTED_HOUSE_SCREAM_1
|
||||
-700, // SOUND_HAUNTED_HOUSE_SCREAM_2
|
||||
-2550, // SOUND_48
|
||||
-2900, // SOUND_49
|
||||
0, // SOUND_ERROR
|
||||
-3400, // SOUND_51
|
||||
0, // SOUND_LIFT_4
|
||||
0, // SOUND_LIFT_5
|
||||
0, // SOUND_TRACK_FRICTION_2
|
||||
0, // SOUND_LIFT_6
|
||||
0, // SOUND_LIFT_7
|
||||
0, // SOUND_TRACK_FRICTION_3
|
||||
0, // SOUND_SCREAM_8
|
||||
0, // SOUND_TRAM
|
||||
-2000, // SOUND_DOOR_OPEN
|
||||
-2700, // SOUND_DOOR_CLOSE
|
||||
-700 // SOUND_62
|
||||
struct AudioParams
|
||||
{
|
||||
bool in_range;
|
||||
sint32 volume;
|
||||
sint32 pan;
|
||||
};
|
||||
|
||||
rct_audio_params audio_get_params_from_location(sint32 soundId, const rct_xyz16 *location);
|
||||
void audio_stop_channel(void **channel);
|
||||
audio_device * gAudioDevices = nullptr;
|
||||
sint32 gAudioDeviceCount;
|
||||
sint32 gAudioCurrentDevice = -1;
|
||||
|
||||
bool gGameSoundsOff = false;
|
||||
sint32 gVolumeAdjustZoom = 0;
|
||||
|
||||
void * gTitleMusicChannel = nullptr;
|
||||
void * gRainSoundChannel = nullptr;
|
||||
|
||||
rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC];
|
||||
rct_ride_music_params gRideMusicParamsList[6];
|
||||
rct_ride_music_params * gRideMusicParamsListEnd;
|
||||
|
||||
rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
rct_vehicle_sound_params * gVehicleSoundParamsListEnd;
|
||||
|
||||
static sint32 SoundVolumeAdjust[SOUND_MAXID] =
|
||||
{
|
||||
0, // SOUND_LIFT_1
|
||||
0, // SOUND_TRACK_FRICTION_1
|
||||
0, // SOUND_LIFT_2
|
||||
0, // SOUND_SCREAM_1
|
||||
0, // SOUND_CLICK_1
|
||||
0, // SOUND_CLICK_2
|
||||
0, // SOUND_PLACE_ITEM
|
||||
0, // SOUND_SCREAM_2
|
||||
0, // SOUND_SCREAM_3
|
||||
0, // SOUND_SCREAM_4
|
||||
0, // SOUND_SCREAM_5
|
||||
0, // SOUND_SCREAM_6
|
||||
0, // SOUND_LIFT_3
|
||||
-400, // SOUND_PURCHASE
|
||||
0, // SOUND_CRASH
|
||||
0, // SOUND_LAYING_OUT_WATER
|
||||
0, // SOUND_WATER_1
|
||||
0, // SOUND_WATER_2
|
||||
0, // SOUND_TRAIN_WHISTLE
|
||||
0, // SOUND_TRAIN_CHUGGING
|
||||
-1000, // SOUND_WATER_SPLASH
|
||||
0, // SOUND_HAMMERING
|
||||
-800, // SOUND_RIDE_LAUNCH_1
|
||||
-1700, // SOUND_RIDE_LAUNCH_2
|
||||
-700, // SOUND_COUGH_1
|
||||
-700, // SOUND_COUGH_2
|
||||
-700, // SOUND_COUGH_3
|
||||
-700, // SOUND_COUGH_4
|
||||
0, // SOUND_RAIN_1
|
||||
0, // SOUND_THUNDER_1
|
||||
0, // SOUND_THUNDER_2
|
||||
0, // SOUND_RAIN_2
|
||||
0, // SOUND_RAIN_3
|
||||
0, // SOUND_BALLOON_POP
|
||||
-700, // SOUND_MECHANIC_FIX
|
||||
0, // SOUND_SCREAM_7
|
||||
-2500, // SOUND_TOILET_FLUSH original value: -1000
|
||||
0, // SOUND_CLICK_3
|
||||
0, // SOUND_QUACK
|
||||
0, // SOUND_NEWS_ITEM
|
||||
0, // SOUND_WINDOW_OPEN
|
||||
-900, // SOUND_LAUGH_1
|
||||
-900, // SOUND_LAUGH_2
|
||||
-900, // SOUND_LAUGH_3
|
||||
0, // SOUND_APPLAUSE
|
||||
-600, // SOUND_HAUNTED_HOUSE_SCARE
|
||||
-700, // SOUND_HAUNTED_HOUSE_SCREAM_1
|
||||
-700, // SOUND_HAUNTED_HOUSE_SCREAM_2
|
||||
-2550, // SOUND_48
|
||||
-2900, // SOUND_49
|
||||
0, // SOUND_ERROR
|
||||
-3400, // SOUND_51
|
||||
0, // SOUND_LIFT_4
|
||||
0, // SOUND_LIFT_5
|
||||
0, // SOUND_TRACK_FRICTION_2
|
||||
0, // SOUND_LIFT_6
|
||||
0, // SOUND_LIFT_7
|
||||
0, // SOUND_TRACK_FRICTION_3
|
||||
0, // SOUND_SCREAM_8
|
||||
0, // SOUND_TRAM
|
||||
-2000, // SOUND_DOOR_OPEN
|
||||
-2700, // SOUND_DOOR_CLOSE
|
||||
-700 // SOUND_62
|
||||
};
|
||||
|
||||
AudioParams audio_get_params_from_location(sint32 soundId, const rct_xyz16 *location);
|
||||
|
||||
void audio_init()
|
||||
{
|
||||
sint32 result = SDL_Init(SDL_INIT_AUDIO);
|
||||
if (result < 0) {
|
||||
log_error("SDL_Init %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
if (str_is_null_or_empty(gConfigSound.device))
|
||||
{
|
||||
Mixer_Init(NULL);
|
||||
gAudioCurrentDevice = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mixer_Init(gConfigSound.device);
|
||||
|
||||
if (str_is_null_or_empty(gConfigSound.device)) {
|
||||
Mixer_Init(NULL);
|
||||
gAudioCurrentDevice = 0;
|
||||
} else {
|
||||
Mixer_Init(gConfigSound.device);
|
||||
for (sint32 i = 0; i < gAudioDeviceCount; i++) {
|
||||
if (strcmp(gAudioDevices[i].name, gConfigSound.device) == 0) {
|
||||
gAudioCurrentDevice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_quit()
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
audio_populate_devices();
|
||||
for (sint32 i = 0; i < gAudioDeviceCount; i++)
|
||||
{
|
||||
if (String::Equals(gAudioDevices[i].name, gConfigSound.device))
|
||||
{
|
||||
gAudioCurrentDevice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_populate_devices()
|
||||
{
|
||||
if (gAudioDevices != NULL)
|
||||
free(gAudioDevices);
|
||||
if (gAudioDevices != nullptr)
|
||||
{
|
||||
Memory::Free(gAudioDevices);
|
||||
gAudioDevices = nullptr;
|
||||
}
|
||||
|
||||
gAudioDeviceCount = SDL_GetNumAudioDevices(SDL_FALSE);
|
||||
if (gAudioDeviceCount <= 0)
|
||||
return;
|
||||
IAudioContext * audioContext = OpenRCT2::GetContext()->GetAudioContext();
|
||||
std::vector<std::string> devices = audioContext->GetOutputDevices();
|
||||
|
||||
audio_device * systemAudioDevices = Memory::AllocateArray<audio_device>(gAudioDeviceCount);
|
||||
for (sint32 i = 0; i < gAudioDeviceCount; i++) {
|
||||
const char *utf8Name = SDL_GetAudioDeviceName(i, SDL_FALSE);
|
||||
if (utf8Name == NULL)
|
||||
utf8Name = language_get_string(STR_OPTIONS_SOUND_VALUE_UNKNOWN);
|
||||
// Replace blanks with localised unknown string
|
||||
for (size_t i = 0; i < devices.size(); i++)
|
||||
{
|
||||
if (devices[i].empty())
|
||||
{
|
||||
devices[i] = language_get_string(STR_OPTIONS_SOUND_VALUE_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
safe_strcpy(systemAudioDevices[i].name, utf8Name, AUDIO_DEVICE_NAME_SIZE);
|
||||
}
|
||||
#ifndef __LINUX__
|
||||
gAudioDeviceCount++;
|
||||
gAudioDevices = Memory::AllocateArray<audio_device>(gAudioDeviceCount);
|
||||
safe_strcpy(gAudioDevices[0].name, language_get_string(STR_OPTIONS_SOUND_VALUE_DEFAULT), AUDIO_DEVICE_NAME_SIZE);
|
||||
Memory::CopyArray(&gAudioDevices[1], systemAudioDevices, gAudioDeviceCount - 1);
|
||||
#else
|
||||
gAudioDevices = Memory::AllocateArray<audio_device>(gAudioDeviceCount);
|
||||
Memory::CopyArray(gAudioDevices, systemAudioDevices, gAudioDeviceCount);
|
||||
#endif // __LINUX__
|
||||
// The first device is always system default on Windows and macOS
|
||||
std::string defaultDevice = language_get_string(STR_OPTIONS_SOUND_VALUE_DEFAULT);
|
||||
devices.insert(devices.begin(), defaultDevice);
|
||||
#endif
|
||||
|
||||
free(systemAudioDevices);
|
||||
}
|
||||
|
||||
sint32 audio_play_sound_panned(sint32 soundId, sint32 pan, sint16 x, sint16 y, sint16 z)
|
||||
{
|
||||
if (pan == AUDIO_PLAY_AT_LOCATION)
|
||||
return audio_play_sound_at_location(soundId, x, y, z);
|
||||
|
||||
return audio_play_sound(soundId, 0, pan);
|
||||
gAudioDeviceCount = (sint32)devices.size();
|
||||
gAudioDevices = Memory::AllocateArray<audio_device>(gAudioDeviceCount);
|
||||
for (sint32 i = 0; i < gAudioDeviceCount; i++)
|
||||
{
|
||||
auto device = &gAudioDevices[i];
|
||||
String::Set(device->name, sizeof(device->name), devices[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
sint32 audio_play_sound_at_location(sint32 soundId, sint16 x, sint16 y, sint16 z)
|
||||
{
|
||||
if (gGameSoundsOff)
|
||||
return 0;
|
||||
if (gGameSoundsOff)
|
||||
return 0;
|
||||
|
||||
rct_xyz16 location;
|
||||
location.x = x;
|
||||
location.y = y;
|
||||
location.z = z;
|
||||
rct_xyz16 location;
|
||||
location.x = x;
|
||||
location.y = y;
|
||||
location.z = z;
|
||||
|
||||
rct_audio_params params = audio_get_params_from_location(soundId, &location);
|
||||
if (!params.in_range)
|
||||
return soundId;
|
||||
|
||||
return audio_play_sound(soundId, params.volume, params.pan);
|
||||
AudioParams params = audio_get_params_from_location(soundId, &location);
|
||||
if (params.in_range)
|
||||
{
|
||||
soundId = audio_play_sound(soundId, params.volume, params.pan);
|
||||
}
|
||||
return soundId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,234 +215,249 @@ sint32 audio_play_sound_at_location(sint32 soundId, sint16 x, sint16 y, sint16 z
|
|||
* @param location The location at which the sound effect is to be played.
|
||||
* @return The audio parameters to be used when playing this sound effect.
|
||||
*/
|
||||
rct_audio_params audio_get_params_from_location(sint32 soundId, const rct_xyz16 *location)
|
||||
AudioParams audio_get_params_from_location(sint32 soundId, const rct_xyz16 *location)
|
||||
{
|
||||
sint32 volumeDown = 0;
|
||||
rct_audio_params params;
|
||||
params.in_range = true;
|
||||
params.volume = 0;
|
||||
params.pan = 0;
|
||||
sint32 volumeDown = 0;
|
||||
AudioParams params;
|
||||
params.in_range = true;
|
||||
params.volume = 0;
|
||||
params.pan = 0;
|
||||
|
||||
rct_map_element *element = map_get_surface_element_at(location->x / 32, location->y / 32);
|
||||
if (element && (element->base_height * 8) - 5 > location->z)
|
||||
volumeDown = 10;
|
||||
rct_map_element * element = map_get_surface_element_at(location->x >> 5, location->y >> 5);
|
||||
if (element && (element->base_height * 8) - 5 > location->z)
|
||||
{
|
||||
volumeDown = 10;
|
||||
}
|
||||
|
||||
uint8 rotation = get_current_rotation();
|
||||
rct_xy16 pos2 = coordinate_3d_to_2d(location, rotation);
|
||||
rct_window *window = gWindowNextSlot;
|
||||
while (true) {
|
||||
window--;
|
||||
if (window < g_window_list)
|
||||
break;
|
||||
uint8 rotation = get_current_rotation();
|
||||
rct_xy16 pos2 = coordinate_3d_to_2d(location, rotation);
|
||||
rct_window * window = gWindowNextSlot;
|
||||
while (true)
|
||||
{
|
||||
window--;
|
||||
if (window < g_window_list)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
rct_viewport *viewport = window->viewport;
|
||||
if (!viewport || !(viewport->flags & VIEWPORT_FLAG_SOUND_ON))
|
||||
continue;
|
||||
rct_viewport * viewport = window->viewport;
|
||||
if (viewport != nullptr && (viewport->flags & VIEWPORT_FLAG_SOUND_ON))
|
||||
{
|
||||
sint16 vy = pos2.y - viewport->view_y;
|
||||
sint16 vx = pos2.x - viewport->view_x;
|
||||
params.pan = viewport->x + (vx >> viewport->zoom);
|
||||
params.volume = SoundVolumeAdjust[soundId] + ((-1024 * viewport->zoom - 1) << volumeDown) + 1;
|
||||
|
||||
sint16 vy = pos2.y - viewport->view_y;
|
||||
sint16 vx = pos2.x - viewport->view_x;
|
||||
params.pan = viewport->x + (vx >> viewport->zoom);
|
||||
params.volume = _volumeAdjust[soundId] + ((-1024 * viewport->zoom - 1) << volumeDown) + 1;
|
||||
if (vy < 0 || vy >= viewport->view_height || vx < 0 || vx >= viewport->view_width || params.volume < -10000)
|
||||
{
|
||||
params.in_range = false;
|
||||
return params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vy < 0 || vy >= viewport->view_height || vx < 0 || vx >= viewport->view_width || params.volume < -10000) {
|
||||
params.in_range = false;
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
return params;
|
||||
}
|
||||
|
||||
sint32 audio_play_sound(sint32 soundId, sint32 volume, sint32 pan)
|
||||
{
|
||||
if (gGameSoundsOff)
|
||||
return 0;
|
||||
if (gGameSoundsOff)
|
||||
return 0;
|
||||
|
||||
sint32 mixerPan = 0;
|
||||
if (pan != AUDIO_PLAY_AT_CENTRE) {
|
||||
sint32 x2 = pan << 16;
|
||||
uint16 screenWidth = Math::Max(64, gScreenWidth);
|
||||
mixerPan = ((x2 / screenWidth) - 0x8000) >> 4;
|
||||
}
|
||||
sint32 mixerPan = 0;
|
||||
if (pan != AUDIO_PLAY_AT_CENTRE)
|
||||
{
|
||||
sint32 x2 = pan << 16;
|
||||
uint16 screenWidth = Math::Max<sint32>(64, OpenRCT2::GetContext()->GetUiContext()->GetWidth());
|
||||
mixerPan = ((x2 / screenWidth) - 0x8000) >> 4;
|
||||
}
|
||||
|
||||
Mixer_Play_Effect(soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1);
|
||||
return 0;
|
||||
Mixer_Play_Effect(soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_start_title_music()
|
||||
{
|
||||
if (gGameSoundsOff || !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) || gIntroState != INTRO_STATE_NONE) {
|
||||
audio_stop_title_music();
|
||||
return;
|
||||
}
|
||||
if (gGameSoundsOff || !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) || gIntroState != INTRO_STATE_NONE)
|
||||
{
|
||||
audio_stop_title_music();
|
||||
return;
|
||||
}
|
||||
|
||||
if (gTitleMusicChannel)
|
||||
return;
|
||||
if (gTitleMusicChannel != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sint32 pathId;
|
||||
switch (gConfigSound.title_music) {
|
||||
case 1:
|
||||
pathId = PATH_ID_CSS50;
|
||||
break;
|
||||
case 2:
|
||||
pathId = PATH_ID_CSS17;
|
||||
break;
|
||||
case 3:
|
||||
if (util_rand() & 1)
|
||||
pathId = PATH_ID_CSS50;
|
||||
else
|
||||
pathId = PATH_ID_CSS17;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
sint32 pathId;
|
||||
switch (gConfigSound.title_music) {
|
||||
case 1:
|
||||
pathId = PATH_ID_CSS50;
|
||||
break;
|
||||
case 2:
|
||||
pathId = PATH_ID_CSS17;
|
||||
break;
|
||||
case 3:
|
||||
pathId = (util_rand() & 1) ? PATH_ID_CSS50 :
|
||||
PATH_ID_CSS17;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
gTitleMusicChannel = Mixer_Play_Music(pathId, MIXER_LOOP_INFINITE, true);
|
||||
if (gTitleMusicChannel != NULL) {
|
||||
Mixer_Channel_SetGroup(gTitleMusicChannel, MIXER_GROUP_TITLE_MUSIC);
|
||||
}
|
||||
gTitleMusicChannel = Mixer_Play_Music(pathId, MIXER_LOOP_INFINITE, true);
|
||||
if (gTitleMusicChannel != nullptr)
|
||||
{
|
||||
Mixer_Channel_SetGroup(gTitleMusicChannel, MIXER_GROUP_TITLE_MUSIC);
|
||||
}
|
||||
}
|
||||
|
||||
void audio_stop_ride_music()
|
||||
{
|
||||
for (sint32 i = 0; i < AUDIO_MAX_RIDE_MUSIC; i++) {
|
||||
rct_ride_music *rideMusic = &gRideMusicList[i];
|
||||
if (rideMusic->ride_id == (uint8)-1)
|
||||
continue;
|
||||
|
||||
if (rideMusic->sound_channel)
|
||||
Mixer_Stop_Channel(rideMusic->sound_channel);
|
||||
|
||||
rideMusic->ride_id = -1;
|
||||
}
|
||||
for (sint32 i = 0; i < AUDIO_MAX_RIDE_MUSIC; i++)
|
||||
{
|
||||
rct_ride_music * rideMusic = &gRideMusicList[i];
|
||||
if (rideMusic->ride_id != RIDE_ID_NULL)
|
||||
{
|
||||
rideMusic->ride_id = RIDE_ID_NULL;
|
||||
if (rideMusic->sound_channel != nullptr)
|
||||
{
|
||||
Mixer_Stop_Channel(rideMusic->sound_channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_stop_all_music_and_sounds()
|
||||
{
|
||||
audio_stop_title_music();
|
||||
audio_stop_vehicle_sounds();
|
||||
audio_stop_ride_music();
|
||||
audio_stop_crowd_sound();
|
||||
audio_stop_rain_sound();
|
||||
}
|
||||
|
||||
void audio_stop_crowd_sound()
|
||||
{
|
||||
audio_stop_channel(&gCrowdSoundChannel);
|
||||
audio_stop_title_music();
|
||||
audio_stop_vehicle_sounds();
|
||||
audio_stop_ride_music();
|
||||
peep_stop_crowd_noise();
|
||||
audio_stop_rain_sound();
|
||||
}
|
||||
|
||||
void audio_stop_title_music()
|
||||
{
|
||||
audio_stop_channel(&gTitleMusicChannel);
|
||||
if (gTitleMusicChannel != nullptr)
|
||||
{
|
||||
Mixer_Stop_Channel(gTitleMusicChannel);
|
||||
gTitleMusicChannel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_stop_rain_sound()
|
||||
{
|
||||
audio_stop_channel(&gRainSoundChannel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the specified audio channel from playing.
|
||||
* @param channel The channel to stop.
|
||||
*/
|
||||
void audio_stop_channel(void **channel)
|
||||
{
|
||||
if (!*channel)
|
||||
return;
|
||||
|
||||
Mixer_Stop_Channel(*channel);
|
||||
*channel = 0;
|
||||
if (gRainSoundChannel != nullptr)
|
||||
{
|
||||
Mixer_Stop_Channel(gRainSoundChannel);
|
||||
gRainSoundChannel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_init_ride_sounds_and_info()
|
||||
{
|
||||
sint32 deviceNum = 0;
|
||||
audio_init_ride_sounds(deviceNum);
|
||||
sint32 deviceNum = 0;
|
||||
audio_init_ride_sounds(deviceNum);
|
||||
|
||||
for (size_t m = 0; m < Util::CountOf(gRideMusicInfoList); m++) {
|
||||
rct_ride_music_info *rideMusicInfo = gRideMusicInfoList[m];
|
||||
const utf8 *path = get_file_path(rideMusicInfo->path_id);
|
||||
if (File::Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
uint32 head = fs.ReadValue<uint32>();
|
||||
if (head == 0x78787878) {
|
||||
rideMusicInfo->length = 0;
|
||||
}
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t m = 0; m < Util::CountOf(gRideMusicInfoList); m++)
|
||||
{
|
||||
rct_ride_music_info *rideMusicInfo = gRideMusicInfoList[m];
|
||||
const utf8 *path = get_file_path(rideMusicInfo->path_id);
|
||||
if (File::Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
auto fs = FileStream(path, FILE_MODE_OPEN);
|
||||
uint32 head = fs.ReadValue<uint32>();
|
||||
if (head == 0x78787878)
|
||||
{
|
||||
rideMusicInfo->length = 0;
|
||||
}
|
||||
}
|
||||
catch (const Exception &)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_init_ride_sounds(sint32 device)
|
||||
{
|
||||
audio_close();
|
||||
for (sint32 i = 0; i < AUDIO_MAX_VEHICLE_SOUNDS; i++) {
|
||||
rct_vehicle_sound *vehicleSound = &gVehicleSoundList[i];
|
||||
vehicleSound->id = -1;
|
||||
}
|
||||
audio_close();
|
||||
for (sint32 i = 0; i < AUDIO_MAX_VEHICLE_SOUNDS; i++)
|
||||
{
|
||||
rct_vehicle_sound * vehicleSound = &gVehicleSoundList[i];
|
||||
vehicleSound->id = 0xFFFF;
|
||||
}
|
||||
|
||||
gAudioCurrentDevice = device;
|
||||
config_save_default();
|
||||
for (sint32 i = 0; i < AUDIO_MAX_RIDE_MUSIC; i++) {
|
||||
rct_ride_music *rideMusic = &gRideMusicList[i];
|
||||
rideMusic->ride_id = -1;
|
||||
}
|
||||
gAudioCurrentDevice = device;
|
||||
config_save_default();
|
||||
for (sint32 i = 0; i < AUDIO_MAX_RIDE_MUSIC; i++)
|
||||
{
|
||||
rct_ride_music * rideMusic = &gRideMusicList[i];
|
||||
rideMusic->ride_id = RIDE_ID_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void audio_close()
|
||||
{
|
||||
audio_stop_crowd_sound();
|
||||
audio_stop_title_music();
|
||||
audio_stop_ride_music();
|
||||
audio_stop_rain_sound();
|
||||
gAudioCurrentDevice = -1;
|
||||
peep_stop_crowd_noise();
|
||||
audio_stop_title_music();
|
||||
audio_stop_ride_music();
|
||||
audio_stop_rain_sound();
|
||||
gAudioCurrentDevice = -1;
|
||||
}
|
||||
|
||||
void audio_toggle_all_sounds()
|
||||
{
|
||||
if (gGameSoundsOff) {
|
||||
audio_unpause_sounds();
|
||||
} else {
|
||||
audio_pause_sounds();
|
||||
}
|
||||
gConfigSound.sound_enabled = !gConfigSound.sound_enabled;
|
||||
if (gConfigSound.sound_enabled)
|
||||
{
|
||||
audio_unpause_sounds();
|
||||
}
|
||||
else
|
||||
{
|
||||
audio_stop_title_music();
|
||||
audio_pause_sounds();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_pause_sounds()
|
||||
{
|
||||
gGameSoundsOff = true;
|
||||
audio_stop_vehicle_sounds();
|
||||
audio_stop_ride_music();
|
||||
audio_stop_crowd_sound();
|
||||
audio_stop_rain_sound();
|
||||
gGameSoundsOff = true;
|
||||
audio_stop_vehicle_sounds();
|
||||
audio_stop_ride_music();
|
||||
peep_stop_crowd_noise();
|
||||
audio_stop_rain_sound();
|
||||
}
|
||||
|
||||
void audio_unpause_sounds()
|
||||
{
|
||||
gGameSoundsOff = false;
|
||||
gGameSoundsOff = false;
|
||||
}
|
||||
|
||||
void audio_stop_vehicle_sounds()
|
||||
{
|
||||
if (gOpenRCT2Headless || gAudioCurrentDevice == -1)
|
||||
return;
|
||||
if (gAudioCurrentDevice == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < Util::CountOf(gVehicleSoundList); i++) {
|
||||
rct_vehicle_sound *vehicleSound = &gVehicleSoundList[i];
|
||||
if (vehicleSound->id == 0xFFFF)
|
||||
continue;
|
||||
|
||||
if (vehicleSound->sound1_id != 0xFFFF)
|
||||
Mixer_Stop_Channel(vehicleSound->sound1_channel);
|
||||
|
||||
if (vehicleSound->sound2_id != 0xFFFF)
|
||||
Mixer_Stop_Channel(vehicleSound->sound2_channel);
|
||||
|
||||
vehicleSound->id = 0xFFFF;
|
||||
}
|
||||
for (size_t i = 0; i < Util::CountOf(gVehicleSoundList); i++)
|
||||
{
|
||||
rct_vehicle_sound * vehicleSound = &gVehicleSoundList[i];
|
||||
if (vehicleSound->id != 0xFFFF)
|
||||
{
|
||||
vehicleSound->id = 0xFFFF;
|
||||
if (vehicleSound->sound1_id != 0xFFFF)
|
||||
{
|
||||
Mixer_Stop_Channel(vehicleSound->sound1_channel);
|
||||
}
|
||||
if (vehicleSound->sound2_id != 0xFFFF)
|
||||
{
|
||||
Mixer_Stop_Channel(vehicleSound->sound2_channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,167 +14,167 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#ifndef _AUDIO_H_
|
||||
#define _AUDIO_H_
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../world/sprite.h"
|
||||
|
||||
#define AUDIO_DEVICE_NAME_SIZE 256
|
||||
#define AUDIO_MAX_RIDE_MUSIC 2
|
||||
#define AUDIO_MAX_VEHICLE_SOUNDS 14
|
||||
#define NUM_DEFAULT_MUSIC_TRACKS 46
|
||||
#define AUDIO_PLAY_AT_CENTRE 0x8000
|
||||
#define AUDIO_PLAY_AT_LOCATION 0x8001
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef struct audio_device {
|
||||
char name[AUDIO_DEVICE_NAME_SIZE];
|
||||
#define AUDIO_DEVICE_NAME_SIZE 256
|
||||
#define AUDIO_MAX_RIDE_MUSIC 2
|
||||
#define AUDIO_MAX_VEHICLE_SOUNDS 14
|
||||
#define NUM_DEFAULT_MUSIC_TRACKS 46
|
||||
#define AUDIO_PLAY_AT_CENTRE 0x8000
|
||||
#define AUDIO_PLAY_AT_LOCATION 0x8001
|
||||
|
||||
typedef struct audio_device
|
||||
{
|
||||
char name[AUDIO_DEVICE_NAME_SIZE];
|
||||
} audio_device;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct rct_ride_music {
|
||||
uint8 ride_id;
|
||||
uint8 tune_id;
|
||||
sint16 volume;
|
||||
sint16 pan;
|
||||
uint16 frequency;
|
||||
void* sound_channel;
|
||||
typedef struct rct_ride_music
|
||||
{
|
||||
uint8 ride_id;
|
||||
uint8 tune_id;
|
||||
sint16 volume;
|
||||
sint16 pan;
|
||||
uint16 frequency;
|
||||
void* sound_channel;
|
||||
} rct_ride_music;
|
||||
#ifdef PLATFORM_32BIT
|
||||
assert_struct_size(rct_ride_music, 12);
|
||||
#endif
|
||||
|
||||
typedef struct rct_ride_music_info {
|
||||
uint32 length;
|
||||
uint32 offset;
|
||||
uint8 path_id;
|
||||
uint8 var_9;
|
||||
typedef struct rct_ride_music_info
|
||||
{
|
||||
uint32 length;
|
||||
uint32 offset;
|
||||
uint8 path_id;
|
||||
uint8 var_9;
|
||||
} rct_ride_music_info;
|
||||
assert_struct_size(rct_ride_music_info, 10);
|
||||
|
||||
typedef struct rct_ride_music_params {
|
||||
uint8 ride_id;
|
||||
uint8 tune_id;
|
||||
sint32 offset;
|
||||
sint16 volume;
|
||||
sint16 pan;
|
||||
uint16 frequency;
|
||||
typedef struct rct_ride_music_params
|
||||
{
|
||||
uint8 ride_id;
|
||||
uint8 tune_id;
|
||||
sint32 offset;
|
||||
sint16 volume;
|
||||
sint16 pan;
|
||||
uint16 frequency;
|
||||
} rct_ride_music_params;
|
||||
assert_struct_size(rct_ride_music_params, 12);
|
||||
|
||||
typedef struct rct_vehicle_sound {
|
||||
uint16 id;
|
||||
sint16 volume;
|
||||
uint16 sound1_id;
|
||||
sint16 sound1_volume;
|
||||
sint16 sound1_pan;
|
||||
uint16 sound1_freq;
|
||||
uint16 sound2_id;
|
||||
sint16 sound2_volume;
|
||||
sint16 sound2_pan;
|
||||
uint16 sound2_freq;
|
||||
void* sound1_channel;
|
||||
void* sound2_channel;
|
||||
typedef struct rct_vehicle_sound
|
||||
{
|
||||
uint16 id;
|
||||
sint16 volume;
|
||||
uint16 sound1_id;
|
||||
sint16 sound1_volume;
|
||||
sint16 sound1_pan;
|
||||
uint16 sound1_freq;
|
||||
uint16 sound2_id;
|
||||
sint16 sound2_volume;
|
||||
sint16 sound2_pan;
|
||||
uint16 sound2_freq;
|
||||
void* sound1_channel;
|
||||
void* sound2_channel;
|
||||
} rct_vehicle_sound;
|
||||
#ifdef PLATFORM_32BIT
|
||||
assert_struct_size(rct_vehicle_sound, 28);
|
||||
#endif
|
||||
|
||||
typedef struct rct_vehicle_sound_params {
|
||||
uint16 id;
|
||||
sint16 pan_x;
|
||||
sint16 pan_y;
|
||||
uint16 frequency;
|
||||
sint16 volume;
|
||||
uint16 var_A;
|
||||
typedef struct rct_vehicle_sound_params
|
||||
{
|
||||
uint16 id;
|
||||
sint16 pan_x;
|
||||
sint16 pan_y;
|
||||
uint16 frequency;
|
||||
sint16 volume;
|
||||
uint16 var_A;
|
||||
} rct_vehicle_sound_params;
|
||||
assert_struct_size(rct_vehicle_sound_params, 12);
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
typedef enum RCT2_SOUND {
|
||||
SOUND_LIFT_1 = 0,
|
||||
SOUND_TRACK_FRICTION_1 = 1,
|
||||
SOUND_LIFT_2 = 2,
|
||||
SOUND_SCREAM_1 = 3,
|
||||
SOUND_CLICK_1 = 4,
|
||||
SOUND_CLICK_2 = 5,
|
||||
SOUND_PLACE_ITEM = 6,
|
||||
SOUND_SCREAM_2 = 7,
|
||||
SOUND_SCREAM_3 = 8,
|
||||
SOUND_SCREAM_4 = 9,
|
||||
SOUND_SCREAM_5 = 10,
|
||||
SOUND_SCREAM_6 = 11,
|
||||
SOUND_LIFT_3 = 12,
|
||||
SOUND_PURCHASE = 13,
|
||||
SOUND_CRASH = 14,
|
||||
SOUND_LAYING_OUT_WATER = 15,
|
||||
SOUND_WATER_1 = 16,
|
||||
SOUND_WATER_2 = 17,
|
||||
SOUND_TRAIN_WHISTLE = 18,
|
||||
SOUND_TRAIN_CHUGGING = 19,
|
||||
SOUND_WATER_SPLASH = 20,
|
||||
SOUND_HAMMERING = 21,
|
||||
SOUND_RIDE_LAUNCH_1 = 22,
|
||||
SOUND_RIDE_LAUNCH_2 = 23,
|
||||
SOUND_COUGH_1 = 24,
|
||||
SOUND_COUGH_2 = 25,
|
||||
SOUND_COUGH_3 = 26,
|
||||
SOUND_COUGH_4 = 27,
|
||||
SOUND_RAIN_1 = 28,
|
||||
SOUND_THUNDER_1 = 29,
|
||||
SOUND_THUNDER_2 = 30,
|
||||
SOUND_RAIN_2 = 31,
|
||||
SOUND_RAIN_3 = 32,
|
||||
SOUND_BALLOON_POP = 33,
|
||||
SOUND_MECHANIC_FIX = 34,
|
||||
SOUND_SCREAM_7 = 35,
|
||||
SOUND_TOILET_FLUSH = 36,
|
||||
SOUND_CLICK_3 = 37,
|
||||
SOUND_QUACK = 38,
|
||||
SOUND_NEWS_ITEM = 39,
|
||||
SOUND_WINDOW_OPEN = 40,
|
||||
SOUND_LAUGH_1 = 41,
|
||||
SOUND_LAUGH_2 = 42,
|
||||
SOUND_LAUGH_3 = 43,
|
||||
SOUND_APPLAUSE = 44,
|
||||
SOUND_HAUNTED_HOUSE_SCARE = 45,
|
||||
SOUND_HAUNTED_HOUSE_SCREAM_1 = 46,
|
||||
SOUND_HAUNTED_HOUSE_SCREAM_2 = 47,
|
||||
SOUND_48 = 48,
|
||||
SOUND_49 = 49,
|
||||
SOUND_ERROR = 50,
|
||||
SOUND_51 = 51,
|
||||
SOUND_LIFT_4 = 52,
|
||||
SOUND_LIFT_5 = 53,
|
||||
SOUND_TRACK_FRICTION_2 = 54,
|
||||
SOUND_LIFT_6 = 55,
|
||||
SOUND_LIFT_7 = 56,
|
||||
SOUND_TRACK_FRICTION_3 = 57,
|
||||
SOUND_SCREAM_8 = 58,
|
||||
SOUND_TRAM = 59,
|
||||
SOUND_DOOR_OPEN = 60,
|
||||
SOUND_DOOR_CLOSE = 61,
|
||||
SOUND_62 = 62,
|
||||
SOUND_MAXID
|
||||
typedef enum RCT2_SOUND
|
||||
{
|
||||
SOUND_LIFT_1,
|
||||
SOUND_TRACK_FRICTION_1,
|
||||
SOUND_LIFT_2,
|
||||
SOUND_SCREAM_1,
|
||||
SOUND_CLICK_1,
|
||||
SOUND_CLICK_2,
|
||||
SOUND_PLACE_ITEM,
|
||||
SOUND_SCREAM_2,
|
||||
SOUND_SCREAM_3,
|
||||
SOUND_SCREAM_4,
|
||||
SOUND_SCREAM_5,
|
||||
SOUND_SCREAM_6,
|
||||
SOUND_LIFT_3,
|
||||
SOUND_PURCHASE,
|
||||
SOUND_CRASH,
|
||||
SOUND_LAYING_OUT_WATER,
|
||||
SOUND_WATER_1,
|
||||
SOUND_WATER_2,
|
||||
SOUND_TRAIN_WHISTLE,
|
||||
SOUND_TRAIN_CHUGGING,
|
||||
SOUND_WATER_SPLASH,
|
||||
SOUND_HAMMERING,
|
||||
SOUND_RIDE_LAUNCH_1,
|
||||
SOUND_RIDE_LAUNCH_2,
|
||||
SOUND_COUGH_1,
|
||||
SOUND_COUGH_2,
|
||||
SOUND_COUGH_3,
|
||||
SOUND_COUGH_4,
|
||||
SOUND_RAIN_1,
|
||||
SOUND_THUNDER_1,
|
||||
SOUND_THUNDER_2,
|
||||
SOUND_RAIN_2,
|
||||
SOUND_RAIN_3,
|
||||
SOUND_BALLOON_POP,
|
||||
SOUND_MECHANIC_FIX,
|
||||
SOUND_SCREAM_7,
|
||||
SOUND_TOILET_FLUSH,
|
||||
SOUND_CLICK_3,
|
||||
SOUND_QUACK,
|
||||
SOUND_NEWS_ITEM,
|
||||
SOUND_WINDOW_OPEN,
|
||||
SOUND_LAUGH_1,
|
||||
SOUND_LAUGH_2,
|
||||
SOUND_LAUGH_3,
|
||||
SOUND_APPLAUSE,
|
||||
SOUND_HAUNTED_HOUSE_SCARE,
|
||||
SOUND_HAUNTED_HOUSE_SCREAM_1,
|
||||
SOUND_HAUNTED_HOUSE_SCREAM_2,
|
||||
SOUND_48,
|
||||
SOUND_49,
|
||||
SOUND_ERROR,
|
||||
SOUND_51,
|
||||
SOUND_LIFT_4,
|
||||
SOUND_LIFT_5,
|
||||
SOUND_TRACK_FRICTION_2,
|
||||
SOUND_LIFT_6,
|
||||
SOUND_LIFT_7,
|
||||
SOUND_TRACK_FRICTION_3,
|
||||
SOUND_SCREAM_8,
|
||||
SOUND_TRAM,
|
||||
SOUND_DOOR_OPEN,
|
||||
SOUND_DOOR_CLOSE,
|
||||
SOUND_62,
|
||||
SOUND_MAXID
|
||||
} RCT2_SOUND;
|
||||
|
||||
extern audio_device *gAudioDevices;
|
||||
extern sint32 gAudioDeviceCount;
|
||||
extern sint32 gAudioCurrentDevice;
|
||||
extern void *gCrowdSoundChannel;
|
||||
extern bool gGameSoundsOff;
|
||||
extern void *gRainSoundChannel;
|
||||
extern rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC];
|
||||
extern rct_ride_music_info *gRideMusicInfoList[NUM_DEFAULT_MUSIC_TRACKS];
|
||||
extern rct_ride_music_params gRideMusicParamsList[6];
|
||||
extern rct_ride_music_params *gRideMusicParamsListEnd;
|
||||
extern void *gTitleMusicChannel;
|
||||
extern rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
extern rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
extern rct_vehicle_sound_params *gVehicleSoundParamsListEnd;
|
||||
extern sint32 gVolumeAdjustZoom;
|
||||
extern audio_device * gAudioDevices;
|
||||
extern sint32 gAudioDeviceCount;
|
||||
extern sint32 gAudioCurrentDevice;
|
||||
|
||||
extern bool gGameSoundsOff;
|
||||
extern sint32 gVolumeAdjustZoom;
|
||||
|
||||
extern void * gTitleMusicChannel;
|
||||
extern void * gRainSoundChannel;
|
||||
|
||||
extern rct_ride_music gRideMusicList[AUDIO_MAX_RIDE_MUSIC];
|
||||
extern rct_ride_music_info * gRideMusicInfoList[NUM_DEFAULT_MUSIC_TRACKS];
|
||||
extern rct_ride_music_params gRideMusicParamsList[6];
|
||||
extern rct_ride_music_params * gRideMusicParamsListEnd;
|
||||
|
||||
extern rct_vehicle_sound gVehicleSoundList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
extern rct_vehicle_sound_params gVehicleSoundParamsList[AUDIO_MAX_VEHICLE_SOUNDS];
|
||||
extern rct_vehicle_sound_params * gVehicleSoundParamsListEnd;
|
||||
|
||||
/**
|
||||
* Deregisters the audio device.
|
||||
|
@ -219,39 +219,15 @@ sint32 audio_play_sound(sint32 soundId, sint32 volume, sint32 pan);
|
|||
*/
|
||||
sint32 audio_play_sound_at_location(sint32 soundId, sint16 x, sint16 y, sint16 z);
|
||||
/**
|
||||
* rct2: 0x006BB76E
|
||||
* @deprecated Use audio_play_sound_at_location or audio_play_sound instead.
|
||||
* Plays the specified sound effect at a location specified by the pan parameter.
|
||||
* @param soundId (eax) The sound effect to play.
|
||||
* @param pan (ebx) If set to AUDIO_PLAY_AT_LOCATION, play the sound at the specified location; if set to AUDIO_PLAY_AT_CENTRE,
|
||||
* play the sound at the centre of the viewport; if set to anything else, use the value of pan as a relative position to the
|
||||
* centre of the viewport.
|
||||
* @param x (cx) The x coordinate of the location.
|
||||
* @param y (dx) The y coordinate of the location.
|
||||
* @param z (bp) The z coordinate of the location.
|
||||
* @return 0 if the sound was not out of range; otherwise, soundId.
|
||||
*/
|
||||
sint32 audio_play_sound_panned(sint32 soundId, sint32 pan, sint16 x, sint16 y, sint16 z);
|
||||
/**
|
||||
* Populates the gAudioDevices array with the available audio devices.
|
||||
*/
|
||||
void audio_populate_devices();
|
||||
/**
|
||||
* Terminates the audio subsystem.
|
||||
* This appears to be unused.
|
||||
*/
|
||||
void audio_quit();
|
||||
/**
|
||||
* Starts playing the title music.
|
||||
* rct2: 0x006BD0F8
|
||||
*/
|
||||
void audio_start_title_music();
|
||||
/**
|
||||
* Stops the crowd sound effect from playing.
|
||||
* rct2: 0x006BD07F
|
||||
*/
|
||||
void audio_stop_crowd_sound();
|
||||
/**
|
||||
* Stops the rain sound effect from playing.
|
||||
*/
|
||||
void audio_stop_rain_sound();
|
||||
|
@ -283,4 +259,6 @@ void audio_unpause_sounds();
|
|||
|
||||
void audio_stop_all_music_and_sounds();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -103,11 +103,11 @@ exitcode_t CommandLine::HandleCommandConvert(CommandLineArgEnumerator * enumerat
|
|||
WriteConvertFromAndToMessage(sourceFileType, destinationFileType);
|
||||
|
||||
gOpenRCT2Headless = true;
|
||||
if (!openrct2_initialise())
|
||||
{
|
||||
Console::Error::WriteLine("Error while initialising OpenRCT2.");
|
||||
return EXITCODE_FAIL;
|
||||
}
|
||||
// if (!openrct2_initialise())
|
||||
// {
|
||||
// Console::Error::WriteLine("Error while initialising OpenRCT2.");
|
||||
// return EXITCODE_FAIL;
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -389,9 +389,9 @@ static exitcode_t HandleCommandScanObjects(CommandLineArgEnumerator * enumerator
|
|||
return result;
|
||||
}
|
||||
|
||||
IPlatformEnvironment * env = OpenRCT2::SetupEnvironment();
|
||||
IObjectRepository * objectRepository = CreateObjectRepository(env);
|
||||
objectRepository->Construct();
|
||||
// IPlatformEnvironment * env = OpenRCT2::SetupEnvironment();
|
||||
// IObjectRepository * objectRepository = CreateObjectRepository(env);
|
||||
// objectRepository->Construct();
|
||||
return EXITCODE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
#include "cmdline_sprite.h"
|
||||
#include "drawing/drawing.h"
|
||||
#include "Imaging.h"
|
||||
#include "localisation/localisation.h"
|
||||
#include "OpenRCT2.h"
|
||||
#include "platform/platform.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#define MODE_DEFAULT 0
|
||||
|
@ -61,6 +61,22 @@ rct_sprite_file_header spriteFileHeader;
|
|||
rct_g1_element *spriteFileEntries;
|
||||
uint8 *spriteFileData;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static FILE * fopen_utf8(const char * path, const char * mode)
|
||||
{
|
||||
wchar_t * pathW = utf8_to_widechar(path);
|
||||
wchar_t * modeW = utf8_to_widechar(mode);
|
||||
FILE * file = _wfopen(pathW, modeW);
|
||||
free(pathW);
|
||||
free(modeW);
|
||||
return file;
|
||||
}
|
||||
|
||||
#define fopen fopen_utf8
|
||||
|
||||
#endif
|
||||
|
||||
static void sprite_file_load_palette(sint32 spriteIndex)
|
||||
{
|
||||
rct_g1_element *g1 = &spriteFileEntries[spriteIndex];
|
||||
|
@ -90,14 +106,12 @@ static void sprite_entries_make_relative()
|
|||
|
||||
static bool sprite_file_open(const utf8 *path)
|
||||
{
|
||||
SDL_RWops *file;
|
||||
|
||||
file = SDL_RWFromFile(path, "rb");
|
||||
FILE * file = fopen(path, "rb");
|
||||
if (file == NULL)
|
||||
return false;
|
||||
|
||||
if (SDL_RWread(file, &spriteFileHeader, sizeof(rct_sprite_file_header), 1) != 1) {
|
||||
SDL_RWclose(file);
|
||||
if (fread(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -105,21 +119,21 @@ static bool sprite_file_open(const utf8 *path)
|
|||
sint32 openEntryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element_32bit);
|
||||
rct_g1_element_32bit * openElements = (rct_g1_element_32bit *)malloc(openEntryTableSize);
|
||||
if (openElements == NULL) {
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SDL_RWread(file, openElements, openEntryTableSize, 1) != 1) {
|
||||
if (fread(openElements, openEntryTableSize, 1, file) != 1) {
|
||||
free(openElements);
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
spriteFileData = malloc(spriteFileHeader.total_size);
|
||||
if (SDL_RWread(file, spriteFileData, spriteFileHeader.total_size, 1) != 1) {
|
||||
if (fread(spriteFileData, spriteFileHeader.total_size, 1, file) != 1) {
|
||||
free(spriteFileData);
|
||||
free(openElements);
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -141,18 +155,18 @@ static bool sprite_file_open(const utf8 *path)
|
|||
free(openElements);
|
||||
}
|
||||
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sprite_file_save(const char *path)
|
||||
{
|
||||
SDL_RWops *file = SDL_RWFromFile(path, "wb");
|
||||
FILE * file = fopen(path, "wb");
|
||||
if (file == NULL)
|
||||
return false;
|
||||
|
||||
if (SDL_RWwrite(file, &spriteFileHeader, sizeof(rct_sprite_file_header), 1) != 1) {
|
||||
SDL_RWclose(file);
|
||||
if (fwrite(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -160,7 +174,7 @@ static bool sprite_file_save(const char *path)
|
|||
sint32 saveEntryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element_32bit);
|
||||
rct_g1_element_32bit * saveElements = (rct_g1_element_32bit *)malloc(saveEntryTableSize);
|
||||
if (saveElements == NULL) {
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -177,20 +191,20 @@ static bool sprite_file_save(const char *path)
|
|||
outElement->zoomed_offset = inElement->zoomed_offset;
|
||||
}
|
||||
|
||||
if (SDL_RWwrite(file, saveElements, saveEntryTableSize, 1) != 1) {
|
||||
if (fwrite(saveElements, saveEntryTableSize, 1, file) != 1) {
|
||||
free(saveElements);
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
free(saveElements);
|
||||
|
||||
if (SDL_RWwrite(file, spriteFileData, spriteFileHeader.total_size, 1) != 1) {
|
||||
SDL_RWclose(file);
|
||||
if (fwrite(spriteFileData, spriteFileHeader.total_size, 1, file) != 1) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RWclose(file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma endregion
|
||||
|
||||
#include <memory>
|
||||
#include "../Context.h"
|
||||
#include "../core/Console.hpp"
|
||||
#include "../core/Exception.hpp"
|
||||
#include "../core/FileStream.hpp"
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include "../interface/window.h"
|
||||
#include "../network/network.h"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../ui/UiContext.h"
|
||||
#include "Config.h"
|
||||
#include "IniReader.hpp"
|
||||
#include "IniWriter.hpp"
|
||||
|
@ -38,6 +40,9 @@ extern "C"
|
|||
#include "../scenario/scenario.h"
|
||||
}
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace Config
|
||||
{
|
||||
#pragma region Enums
|
||||
|
@ -155,6 +160,7 @@ namespace Config
|
|||
model->window_height = reader->GetSint32("window_height", -1);
|
||||
model->window_snap_proximity = reader->GetSint32("window_snap_proximity", 5);
|
||||
model->window_width = reader->GetSint32("window_width", -1);
|
||||
model->default_display = reader->GetSint32("default_display", 0);
|
||||
model->drawing_engine = reader->GetEnum<sint32>("drawing_engine", DRAWING_ENGINE_SOFTWARE, Enum_DrawingEngine);
|
||||
model->uncap_fps = reader->GetBoolean("uncap_fps", false);
|
||||
|
||||
|
@ -228,6 +234,7 @@ namespace Config
|
|||
writer->WriteSint32("window_height", model->window_height);
|
||||
writer->WriteSint32("window_snap_proximity", model->window_snap_proximity);
|
||||
writer->WriteSint32("window_width", model->window_width);
|
||||
writer->WriteSint32("default_display", model->default_display);
|
||||
writer->WriteEnum<sint32>("drawing_engine", model->drawing_engine, Enum_DrawingEngine);
|
||||
writer->WriteBoolean("uncap_fps", model->uncap_fps);
|
||||
writer->WriteBoolean("test_unfinished_tracks", model->test_unfinished_tracks);
|
||||
|
@ -688,7 +695,8 @@ extern "C"
|
|||
}
|
||||
while (1)
|
||||
{
|
||||
platform_show_messagebox("OpenRCT2 needs files from the original RollerCoaster Tycoon 2 in order to work. Please select the directory where you installed RollerCoaster Tycoon 2.");
|
||||
IUiContext * uiContext = GetContext()->GetUiContext();
|
||||
uiContext->ShowMessageBox("OpenRCT2 needs files from the original RollerCoaster Tycoon 2 in order to work. Please select the directory where you installed RollerCoaster Tycoon 2.");
|
||||
utf8 * installPath = platform_open_directory_browser("Please select your RCT2 directory");
|
||||
if (installPath == nullptr)
|
||||
{
|
||||
|
@ -703,9 +711,8 @@ extern "C"
|
|||
return true;
|
||||
}
|
||||
|
||||
utf8 message[MAX_PATH];
|
||||
snprintf(message, MAX_PATH, "Could not find %s" PATH_SEPARATOR "Data" PATH_SEPARATOR "g1.dat at this path", installPath);
|
||||
platform_show_messagebox(message);
|
||||
std::string message = String::StdFormat("Could not find %s" PATH_SEPARATOR "Data" PATH_SEPARATOR "g1.dat at this path", installPath);
|
||||
uiContext->ShowMessageBox(message);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -25,6 +25,7 @@ typedef struct GeneralConfiguration
|
|||
utf8 * rct2_path;
|
||||
|
||||
// Display
|
||||
sint32 default_display;
|
||||
sint32 window_width;
|
||||
sint32 window_height;
|
||||
sint32 fullscreen_mode;
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace OpenRCT2
|
||||
{
|
||||
/**
|
||||
* Represents a registration of some service which when deleted will be
|
||||
* unregistered.
|
||||
*/
|
||||
interface IRegistration
|
||||
{
|
||||
virtual ~IRegistration() = default;
|
||||
};
|
||||
|
||||
class Registration
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Class which can wrap a function in an IRegistration.
|
||||
*/
|
||||
template<typename T>
|
||||
struct CallbackRegistration : public IRegistration
|
||||
{
|
||||
private:
|
||||
T _callback;
|
||||
|
||||
public:
|
||||
CallbackRegistration(T callback) :
|
||||
_callback(callback)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CallbackRegistration() override
|
||||
{
|
||||
_callback();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new IRegistration which when deleted, calls the given
|
||||
* function.
|
||||
*/
|
||||
template<typename T>
|
||||
static IRegistration * Create(T unregisterCallback)
|
||||
{
|
||||
return new CallbackRegistration<T>(unregisterCallback);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -47,6 +47,30 @@ namespace String
|
|||
return returnValue;
|
||||
}
|
||||
|
||||
std::string ToUtf8(const std::wstring &s)
|
||||
{
|
||||
std::string result;
|
||||
utf8 * cstr = widechar_to_utf8(s.c_str());
|
||||
if (cstr != nullptr)
|
||||
{
|
||||
result = std::string(cstr);
|
||||
}
|
||||
free(cstr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring ToUtf16(const std::string &s)
|
||||
{
|
||||
std::wstring result;
|
||||
wchar_t * wcstr = utf8_to_widechar(s.c_str());
|
||||
if (wcstr != nullptr)
|
||||
{
|
||||
result = std::wstring(wcstr);
|
||||
}
|
||||
free(wcstr);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsNullOrEmpty(const utf8 * str)
|
||||
{
|
||||
return str == nullptr || str[0] == '\0';
|
||||
|
|
|
@ -24,8 +24,10 @@ namespace String
|
|||
{
|
||||
constexpr const utf8 * Empty = "";
|
||||
|
||||
std::string ToStd(const utf8 * str);
|
||||
std::string StdFormat(const utf8 * format, ...);
|
||||
std::string ToStd(const utf8 * str);
|
||||
std::string StdFormat(const utf8 * format, ...);
|
||||
std::string ToUtf8(const std::wstring &s);
|
||||
std::wstring ToUtf16(const std::string &s);
|
||||
|
||||
bool IsNullOrEmpty(const utf8 * str);
|
||||
sint32 Compare(const std::string &a, const std::string &b, bool ignoreCase = false);
|
||||
|
|
|
@ -18,24 +18,28 @@
|
|||
|
||||
#include "../common.h"
|
||||
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#include "drawing.h"
|
||||
}
|
||||
|
||||
interface IDrawingEngine;
|
||||
|
||||
interface IDrawingContext
|
||||
namespace OpenRCT2 { namespace Drawing
|
||||
{
|
||||
virtual ~IDrawingContext() { }
|
||||
interface IDrawingEngine;
|
||||
|
||||
virtual IDrawingEngine * GetEngine() abstract;
|
||||
interface IDrawingContext
|
||||
{
|
||||
virtual ~IDrawingContext() { }
|
||||
|
||||
virtual void Clear(uint8 paletteIndex) abstract;
|
||||
virtual void FillRect(uint32 colour, sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void FilterRect(FILTER_PALETTE_ID palette, sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 x2, sint32 y2) abstract;
|
||||
virtual void DrawSprite(uint32 image, sint32 x, sint32 y, uint32 tertiaryColour) abstract;
|
||||
virtual void DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskImage, uint32 colourImage) abstract;
|
||||
virtual void DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) abstract;
|
||||
virtual void DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) abstract;
|
||||
};
|
||||
virtual OpenRCT2::Drawing::IDrawingEngine * GetEngine() abstract;
|
||||
|
||||
virtual void Clear(uint8 paletteIndex) abstract;
|
||||
virtual void FillRect(uint32 colour, sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void FilterRect(FILTER_PALETTE_ID palette, sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 x2, sint32 y2) abstract;
|
||||
virtual void DrawSprite(uint32 image, sint32 x, sint32 y, uint32 tertiaryColour) abstract;
|
||||
virtual void DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskImage, uint32 colourImage) abstract;
|
||||
virtual void DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) abstract;
|
||||
virtual void DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) abstract;
|
||||
};
|
||||
} }
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
#include "../common.h"
|
||||
|
||||
#include <SDL_video.h>
|
||||
|
||||
enum DRAWING_ENGINE
|
||||
{
|
||||
DRAWING_ENGINE_NONE = -1,
|
||||
DRAWING_ENGINE_SOFTWARE,
|
||||
DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY,
|
||||
DRAWING_ENGINE_OPENGL,
|
||||
DRAWING_ENGINE_COUNT,
|
||||
};
|
||||
|
||||
enum DRAWING_ENGINE_FLAGS
|
||||
|
@ -41,47 +40,46 @@ enum DRAWING_ENGINE_FLAGS
|
|||
#ifdef __cplusplus
|
||||
|
||||
struct rct_drawpixelinfo;
|
||||
interface IDrawingContext;
|
||||
struct rct_palette_entry;
|
||||
struct SDL_Window;
|
||||
|
||||
interface IDrawingEngine
|
||||
namespace OpenRCT2 { namespace Drawing
|
||||
{
|
||||
virtual ~IDrawingEngine() { }
|
||||
interface IDrawingContext;
|
||||
|
||||
virtual void Initialise(SDL_Window * window) abstract;
|
||||
virtual void Resize(uint32 width, uint32 height) abstract;
|
||||
virtual void SetPalette(SDL_Color * colours) abstract;
|
||||
interface IDrawingEngine
|
||||
{
|
||||
virtual ~IDrawingEngine() { }
|
||||
|
||||
virtual void SetUncappedFrameRate(bool uncapped) abstract;
|
||||
virtual void Initialise(SDL_Window * window) abstract;
|
||||
virtual void Resize(uint32 width, uint32 height) abstract;
|
||||
virtual void SetPalette(const rct_palette_entry * colours) abstract;
|
||||
|
||||
virtual void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void Draw() abstract;
|
||||
virtual void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) abstract;
|
||||
virtual sint32 Screenshot() abstract;
|
||||
virtual void SetUncappedFrameRate(bool uncapped) abstract;
|
||||
|
||||
virtual IDrawingContext * GetDrawingContext(rct_drawpixelinfo * dpi) abstract;
|
||||
virtual rct_drawpixelinfo * GetDrawingPixelInfo() abstract;
|
||||
virtual void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) abstract;
|
||||
virtual void Draw() abstract;
|
||||
virtual void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) abstract;
|
||||
virtual sint32 Screenshot() abstract;
|
||||
|
||||
virtual DRAWING_ENGINE_FLAGS GetFlags() abstract;
|
||||
virtual IDrawingContext * GetDrawingContext(rct_drawpixelinfo * dpi) abstract;
|
||||
virtual rct_drawpixelinfo * GetDrawingPixelInfo() abstract;
|
||||
|
||||
virtual void InvalidateImage(uint32 image) abstract;
|
||||
};
|
||||
virtual DRAWING_ENGINE_FLAGS GetFlags() abstract;
|
||||
|
||||
namespace DrawingEngineFactory
|
||||
{
|
||||
IDrawingEngine * CreateSoftware();
|
||||
IDrawingEngine * CreateSoftwareWithHardwareDisplay();
|
||||
IDrawingEngine * CreateOpenGL();
|
||||
}
|
||||
virtual void InvalidateImage(uint32 image) abstract;
|
||||
};
|
||||
|
||||
interface IRainDrawer
|
||||
{
|
||||
virtual ~IRainDrawer() { }
|
||||
virtual void Draw(sint32 x,
|
||||
sint32 y,
|
||||
sint32 width,
|
||||
sint32 height,
|
||||
sint32 xStart,
|
||||
sint32 yStart) abstract;
|
||||
};
|
||||
interface IRainDrawer
|
||||
{
|
||||
virtual ~IRainDrawer() { }
|
||||
virtual void Draw(sint32 x,
|
||||
sint32 y,
|
||||
sint32 width,
|
||||
sint32 height,
|
||||
sint32 xStart,
|
||||
sint32 yStart) abstract;
|
||||
};
|
||||
} }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <stdexcept>
|
||||
#include "../Context.h"
|
||||
#include "../ui/UiContext.h"
|
||||
#include "../core/Exception.hpp"
|
||||
#include "../core/Registration.hpp"
|
||||
#include "IDrawingContext.h"
|
||||
#include "IDrawingEngine.h"
|
||||
#include "NewDrawing.h"
|
||||
|
@ -29,8 +33,12 @@ extern "C"
|
|||
#include "../rct2.h"
|
||||
}
|
||||
|
||||
static sint32 _drawingEngineType = DRAWING_ENGINE_SOFTWARE;
|
||||
static IDrawingEngine * _drawingEngine = nullptr;
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
static sint32 _drawingEngineType = DRAWING_ENGINE_SOFTWARE;
|
||||
static IDrawingEngine * _drawingEngine = nullptr;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -69,20 +77,11 @@ extern "C"
|
|||
{
|
||||
assert(_drawingEngine == nullptr);
|
||||
|
||||
IDrawingEngine * drawingEngine = nullptr;
|
||||
_drawingEngineType = gConfigGeneral.drawing_engine;
|
||||
switch (_drawingEngineType) {
|
||||
case DRAWING_ENGINE_SOFTWARE:
|
||||
drawingEngine = DrawingEngineFactory::CreateSoftware();
|
||||
break;
|
||||
case DRAWING_ENGINE_SOFTWARE_WITH_HARDWARE_DISPLAY:
|
||||
drawingEngine = DrawingEngineFactory::CreateSoftwareWithHardwareDisplay();
|
||||
break;
|
||||
case DRAWING_ENGINE_OPENGL:
|
||||
drawingEngine = DrawingEngineFactory::CreateOpenGL();
|
||||
break;
|
||||
}
|
||||
|
||||
IContext * context = GetContext();
|
||||
IUiContext * uiContext = context->GetUiContext();
|
||||
IDrawingEngine * drawingEngine = uiContext->CreateDrawingEngine((DRAWING_ENGINE_TYPE)_drawingEngineType);
|
||||
if (drawingEngine == nullptr)
|
||||
{
|
||||
if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE)
|
||||
|
@ -105,7 +104,7 @@ extern "C"
|
|||
{
|
||||
try
|
||||
{
|
||||
drawingEngine->Initialise(gWindow);
|
||||
drawingEngine->Initialise(uiContext->GetWindow());
|
||||
drawingEngine->SetUncappedFrameRate(gConfigGeneral.uncap_fps == 1);
|
||||
_drawingEngine = drawingEngine;
|
||||
}
|
||||
|
@ -140,10 +139,12 @@ extern "C"
|
|||
{
|
||||
drawing_engine_init();
|
||||
}
|
||||
_drawingEngine->Resize(gScreenWidth, gScreenHeight);
|
||||
|
||||
IUiContext * uiContext = GetContext()->GetUiContext();
|
||||
_drawingEngine->Resize(uiContext->GetWidth(), uiContext->GetHeight());
|
||||
}
|
||||
|
||||
void drawing_engine_set_palette(SDL_Color * colours)
|
||||
void drawing_engine_set_palette(const rct_palette_entry * colours)
|
||||
{
|
||||
if (_drawingEngine != nullptr)
|
||||
{
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <SDL_video.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
@ -32,7 +30,7 @@ sint32 drawing_engine_get_type();
|
|||
bool drawing_engine_requires_restart(sint32 srcEngine, sint32 dstEngine);
|
||||
void drawing_engine_init();
|
||||
void drawing_engine_resize();
|
||||
void drawing_engine_set_palette(SDL_Color * colours);
|
||||
void drawing_engine_set_palette(const rct_palette_entry * colours);
|
||||
void drawing_engine_draw();
|
||||
void drawing_engine_copy_rect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy);
|
||||
void drawing_engine_dispose();
|
||||
|
|
|
@ -26,6 +26,8 @@ extern "C"
|
|||
#include "Rain.h"
|
||||
#include "../core/Math.hpp"
|
||||
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
typedef void (* DrawRainFunc)(IRainDrawer * rainDrawer, sint32 left, sint32 top, sint32 width, sint32 height);
|
||||
|
||||
static void DrawLightRain(IRainDrawer * rainDrawer, sint32 left, sint32 top, sint32 width, sint32 height);
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
#include "../common.h"
|
||||
|
||||
interface IRainDrawer;
|
||||
namespace OpenRCT2 { namespace Drawing
|
||||
{
|
||||
interface IRainDrawer;
|
||||
} }
|
||||
|
||||
void DrawRain(rct_drawpixelinfo * dpi, IRainDrawer * rainDrawer);
|
||||
void DrawRain(rct_drawpixelinfo * dpi, OpenRCT2::Drawing::IRainDrawer * rainDrawer);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma endregion
|
||||
|
||||
#include "../common.h"
|
||||
#include "../Context.h"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../interface/window.h"
|
||||
#include "../localisation/localisation.h"
|
||||
|
@ -546,7 +547,7 @@ void load_palette(){
|
|||
*/
|
||||
void gfx_invalidate_screen()
|
||||
{
|
||||
gfx_set_dirty_blocks(0, 0, gScreenWidth, gScreenHeight);
|
||||
gfx_set_dirty_blocks(0, 0, context_get_width(), context_get_height());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -253,6 +253,7 @@ typedef struct rct_palette {
|
|||
extern sint16 gCurrentFontSpriteBase;
|
||||
extern uint16 gCurrentFontFlags;
|
||||
|
||||
extern rct_palette_entry gPalette[256];
|
||||
extern uint8 gGamePalette[256 * 4];
|
||||
extern uint32 gPaletteEffectFrame;
|
||||
extern const FILTER_PALETTE_ID GlassPaletteIds[COLOUR_COUNT];
|
||||
|
@ -337,7 +338,7 @@ void FASTCALL gfx_draw_sprite_raw_masked_software(rct_drawpixelinfo *dpi, sint32
|
|||
// string
|
||||
sint32 clip_text(char *buffer, sint32 width);
|
||||
sint32 gfx_wrap_string(char* buffer, sint32 width, sint32* num_lines, sint32* font_height);
|
||||
sint32 gfx_get_string_width(char *buffer);
|
||||
sint32 gfx_get_string_width(const utf8 * buffer);
|
||||
sint32 gfx_get_string_width_new_lined(char* buffer);
|
||||
void gfx_draw_string(rct_drawpixelinfo *dpi, char *buffer, sint32 colour, sint32 x, sint32 y);
|
||||
void gfx_draw_string_left(rct_drawpixelinfo *dpi, rct_string_id format, void *args, sint32 colour, sint32 x, sint32 y);
|
||||
|
|
|
@ -16,21 +16,26 @@
|
|||
|
||||
#include <memory>
|
||||
#include "../common.h"
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "../core/File.h"
|
||||
#include "../core/FileStream.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../core/Util.hpp"
|
||||
#include "../OpenRCT2.h"
|
||||
#include "../sprites.h"
|
||||
#include "../ui/UiContext.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../config/Config.h"
|
||||
#include "../rct2/addresses.h"
|
||||
#include "../util/util.h"
|
||||
#include "drawing.h"
|
||||
}
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
static void * _g1Buffer = nullptr;
|
||||
|
@ -116,7 +121,8 @@ extern "C"
|
|||
log_fatal("Unable to load g1 graphics");
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
platform_show_messagebox("Unable to load g1.dat. Your RollerCoaster Tycoon 2 path may be incorrectly set.");
|
||||
IUiContext * uiContext = GetContext()->GetUiContext();
|
||||
uiContext->ShowMessageBox("Unable to load g1.dat. Your RollerCoaster Tycoon 2 path may be incorrectly set.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -174,7 +180,8 @@ extern "C"
|
|||
log_fatal("Unable to load g2 graphics");
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
platform_show_messagebox("Unable to load g2.dat");
|
||||
IUiContext * uiContext = GetContext()->GetUiContext();
|
||||
uiContext->ShowMessageBox("Unable to load g2.dat");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -101,7 +101,7 @@ sint32 gfx_get_string_width_new_lined(utf8 *text)
|
|||
* rct2: 0x006C2321
|
||||
* buffer (esi)
|
||||
*/
|
||||
sint32 gfx_get_string_width(char* buffer)
|
||||
sint32 gfx_get_string_width(const utf8 * buffer)
|
||||
{
|
||||
return ttf_get_string_width(buffer);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "audio/audio.h"
|
||||
#include "cheats.h"
|
||||
#include "config/Config.h"
|
||||
#include "Context.h"
|
||||
#include "editor.h"
|
||||
#include "game.h"
|
||||
#include "input.h"
|
||||
|
@ -138,7 +139,7 @@ void game_create_windows()
|
|||
window_main_open();
|
||||
window_top_toolbar_open();
|
||||
window_game_bottom_toolbar_open();
|
||||
window_resize_gui(gScreenWidth, gScreenHeight);
|
||||
window_resize_gui(context_get_width(), context_get_height());
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <SDL_keycode.h>
|
||||
#include "audio/audio.h"
|
||||
#include "config/Config.h"
|
||||
#include "Context.h"
|
||||
#include "game.h"
|
||||
#include "input.h"
|
||||
#include "interface/chat.h"
|
||||
|
@ -137,8 +138,10 @@ void game_handle_input()
|
|||
if (_inputFlags & INPUT_FLAG_5) {
|
||||
game_handle_input_mouse(x, y, state);
|
||||
} else if (x != 0x80000000) {
|
||||
x = clamp(0, x, gScreenWidth - 1);
|
||||
y = clamp(0, y, gScreenHeight - 1);
|
||||
sint32 screenWidth = context_get_width();
|
||||
sint32 screenHeight = context_get_height();
|
||||
x = clamp(0, x, screenWidth - 1);
|
||||
y = clamp(0, y, screenHeight - 1);
|
||||
|
||||
game_handle_input_mouse(x, y, state);
|
||||
process_mouse_over(x, y);
|
||||
|
@ -158,8 +161,9 @@ static sint32 game_get_next_input(sint32 *x, sint32 *y)
|
|||
{
|
||||
rct_mouse_data *input = get_mouse_input();
|
||||
if (input == NULL) {
|
||||
*x = gCursorState.x;
|
||||
*y = gCursorState.y;
|
||||
const CursorState * cursorState = context_get_cursor_state();
|
||||
*x = cursorState->x;
|
||||
*y = cursorState->y;
|
||||
return 0;
|
||||
} else {
|
||||
*x = input->x;
|
||||
|
@ -199,7 +203,7 @@ static void input_scroll_drag_begin(sint32 x, sint32 y, rct_window* w, rct_widge
|
|||
_ticksSinceDragStart = 0;
|
||||
|
||||
_dragScrollIndex = window_get_scroll_data_index(w, widgetIndex);
|
||||
platform_hide_cursor();
|
||||
context_hide_cursor();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,7 +244,7 @@ static void input_scroll_drag_continue(sint32 x, sint32 y, rct_window* w)
|
|||
sint32 fixedCursorPositionX = (sint32) ceilf(gInputDragLastX * gConfigGeneral.window_scale);
|
||||
sint32 fixedCursorPositionY = (sint32) ceilf(gInputDragLastY * gConfigGeneral.window_scale);
|
||||
|
||||
platform_set_cursor_position(fixedCursorPositionX, fixedCursorPositionY);
|
||||
context_set_cursor_position(fixedCursorPositionX, fixedCursorPositionY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,7 +255,7 @@ static void input_scroll_right(sint32 x, sint32 y, sint32 state)
|
|||
{
|
||||
rct_window* w = window_find_by_number(_dragWidget.window_classification, _dragWidget.window_number);
|
||||
if (w == NULL) {
|
||||
platform_show_cursor();
|
||||
context_show_cursor();
|
||||
_inputState = INPUT_STATE_RESET;
|
||||
return;
|
||||
}
|
||||
|
@ -266,7 +270,7 @@ static void input_scroll_right(sint32 x, sint32 y, sint32 state)
|
|||
break;
|
||||
case MOUSE_STATE_RIGHT_RELEASE:
|
||||
_inputState = INPUT_STATE_RESET;
|
||||
platform_show_cursor();
|
||||
context_show_cursor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -468,7 +472,7 @@ static void input_window_resize_begin(rct_window *w, rct_widgetindex widgetIndex
|
|||
|
||||
static void input_window_resize_continue(rct_window *w, sint32 x, sint32 y)
|
||||
{
|
||||
if (y < (sint32)gScreenHeight - 2) {
|
||||
if (y < (sint32)context_get_height() - 2) {
|
||||
sint32 dx, dy, targetWidth, targetHeight;
|
||||
dx = x - gInputDragLastX;
|
||||
dy = y - gInputDragLastY;
|
||||
|
@ -501,8 +505,8 @@ static void input_viewport_drag_begin(rct_window *w, sint32 x, sint32 y)
|
|||
_dragWidget.window_classification = w->classification;
|
||||
_dragWidget.window_number = w->number;
|
||||
_ticksSinceDragStart = 0;
|
||||
platform_get_cursor_position(&gInputDragLastX, &gInputDragLastY);
|
||||
platform_hide_cursor();
|
||||
context_get_cursor_position(&gInputDragLastX, &gInputDragLastY);
|
||||
context_hide_cursor();
|
||||
|
||||
// gInputFlags |= INPUT_FLAG_5;
|
||||
}
|
||||
|
@ -513,7 +517,7 @@ static void input_viewport_drag_continue()
|
|||
rct_window *w;
|
||||
rct_viewport *viewport;
|
||||
|
||||
platform_get_cursor_position(&newDragX, &newDragY);
|
||||
context_get_cursor_position(&newDragX, &newDragY);
|
||||
|
||||
dx = newDragX - gInputDragLastX;
|
||||
dy = newDragY - gInputDragLastY;
|
||||
|
@ -529,7 +533,7 @@ static void input_viewport_drag_continue()
|
|||
viewport = w->viewport;
|
||||
_ticksSinceDragStart += gTicksSinceLastUpdate;
|
||||
if (viewport == NULL) {
|
||||
platform_show_cursor();
|
||||
context_show_cursor();
|
||||
_inputState = INPUT_STATE_RESET;
|
||||
} else if (dx != 0 || dy != 0) {
|
||||
if (!(w->flags & WF_NO_SCROLLING)) {
|
||||
|
@ -551,13 +555,13 @@ static void input_viewport_drag_continue()
|
|||
}
|
||||
}
|
||||
|
||||
platform_set_cursor_position(gInputDragLastX, gInputDragLastY);
|
||||
context_set_cursor_position(gInputDragLastX, gInputDragLastY);
|
||||
}
|
||||
|
||||
static void input_viewport_drag_end()
|
||||
{
|
||||
_inputState = INPUT_STATE_RESET;
|
||||
platform_show_cursor();
|
||||
context_show_cursor();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
@ -1013,7 +1017,7 @@ static void input_widget_left(sint32 x, sint32 y, rct_window *w, rct_widgetindex
|
|||
break;
|
||||
default:
|
||||
if (widget_is_enabled(w, widgetIndex) && !widget_is_disabled(w, widgetIndex)) {
|
||||
audio_play_sound_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2, 0, 0, 0);
|
||||
audio_play_sound(SOUND_CLICK_1, 0, w->x + ((widget->left + widget->right) / 2));
|
||||
|
||||
// Set new cursor down widget
|
||||
gPressedWidget.window_classification = windowClass;
|
||||
|
@ -1267,7 +1271,7 @@ void input_state_widget_pressed(sint32 x, sint32 y, sint32 state, rct_widgetinde
|
|||
break;
|
||||
|
||||
sint32 mid_point_x = (widget->left + widget->right) / 2 + w->x;
|
||||
audio_play_sound_panned(5, mid_point_x, 0, 0, 0);
|
||||
audio_play_sound(SOUND_CLICK_2, 0, mid_point_x);
|
||||
if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex)
|
||||
break;
|
||||
|
||||
|
@ -1410,14 +1414,15 @@ void title_handle_keyboard_input()
|
|||
if (!gConsoleOpen) {
|
||||
// Handle modifier keys and key scrolling
|
||||
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
|
||||
if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT])
|
||||
const uint8 * keysState = context_get_keys_state();
|
||||
if (keysState[SDL_SCANCODE_LSHIFT] || keysState[SDL_SCANCODE_RSHIFT])
|
||||
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_SHIFT_Z;
|
||||
if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL])
|
||||
if (keysState[SDL_SCANCODE_LCTRL] || keysState[SDL_SCANCODE_RCTRL])
|
||||
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_COPY_Z;
|
||||
if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT])
|
||||
if (keysState[SDL_SCANCODE_LALT] || keysState[SDL_SCANCODE_RALT])
|
||||
gInputPlaceObjectModifier |= 4;
|
||||
#ifdef __MACOSX__
|
||||
if (gKeysState[SDL_SCANCODE_LGUI] || gKeysState[SDL_SCANCODE_RGUI]) {
|
||||
if (keysState[SDL_SCANCODE_LGUI] || keysState[SDL_SCANCODE_RGUI]) {
|
||||
gInputPlaceObjectModifier |= 8;
|
||||
}
|
||||
#endif
|
||||
|
@ -1429,7 +1434,7 @@ void title_handle_keyboard_input()
|
|||
|
||||
// Reserve backtick for console
|
||||
if (key == SDL_SCANCODE_GRAVE) {
|
||||
if ((gConfigGeneral.debugging_tools && !platform_is_input_active()) || gConsoleOpen) {
|
||||
if ((gConfigGeneral.debugging_tools && !context_is_input_active()) || gConsoleOpen) {
|
||||
window_cancel_textbox();
|
||||
console_toggle();
|
||||
}
|
||||
|
@ -1475,17 +1480,18 @@ void game_handle_keyboard_input()
|
|||
|
||||
// Handle modifier keys and key scrolling
|
||||
gInputPlaceObjectModifier = PLACE_OBJECT_MODIFIER_NONE;
|
||||
if (gKeysState[SDL_SCANCODE_LSHIFT] || gKeysState[SDL_SCANCODE_RSHIFT]) {
|
||||
const uint8 * keysState = context_get_keys_state();
|
||||
if (keysState[SDL_SCANCODE_LSHIFT] || keysState[SDL_SCANCODE_RSHIFT]) {
|
||||
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_SHIFT_Z;
|
||||
}
|
||||
if (gKeysState[SDL_SCANCODE_LCTRL] || gKeysState[SDL_SCANCODE_RCTRL]) {
|
||||
if (keysState[SDL_SCANCODE_LCTRL] || keysState[SDL_SCANCODE_RCTRL]) {
|
||||
gInputPlaceObjectModifier |= PLACE_OBJECT_MODIFIER_COPY_Z;
|
||||
}
|
||||
if (gKeysState[SDL_SCANCODE_LALT] || gKeysState[SDL_SCANCODE_RALT]) {
|
||||
if (keysState[SDL_SCANCODE_LALT] || keysState[SDL_SCANCODE_RALT]) {
|
||||
gInputPlaceObjectModifier |= 4;
|
||||
}
|
||||
#ifdef __MACOSX__
|
||||
if (gKeysState[SDL_SCANCODE_LGUI] || gKeysState[SDL_SCANCODE_RGUI]) {
|
||||
if (keysState[SDL_SCANCODE_LGUI] || keysState[SDL_SCANCODE_RGUI]) {
|
||||
gInputPlaceObjectModifier |= 8;
|
||||
}
|
||||
#endif
|
||||
|
@ -1500,7 +1506,7 @@ void game_handle_keyboard_input()
|
|||
|
||||
// Reserve backtick for console
|
||||
if (key == SDL_SCANCODE_GRAVE) {
|
||||
if ((gConfigGeneral.debugging_tools && !platform_is_input_active()) || gConsoleOpen) {
|
||||
if ((gConfigGeneral.debugging_tools && !context_is_input_active()) || gConsoleOpen) {
|
||||
window_cancel_textbox();
|
||||
console_toggle();
|
||||
}
|
||||
|
@ -1535,10 +1541,10 @@ void game_handle_keyboard_input()
|
|||
*/
|
||||
sint32 get_next_key()
|
||||
{
|
||||
sint32 i;
|
||||
for (i = 0; i < 221; i++) {
|
||||
if (gKeysPressed[i]) {
|
||||
gKeysPressed[i] = 0;
|
||||
uint8 * keysPressed = (uint8 *)context_get_keys_pressed();
|
||||
for (sint32 i = 0; i < 221; i++) {
|
||||
if (keysPressed[i]) {
|
||||
keysPressed[i] = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
@ -1557,7 +1563,7 @@ void sub_6ED990(uint8 cursor_id)
|
|||
if (_inputState == INPUT_STATE_RESIZING) {
|
||||
cursor_id = CURSOR_DIAGONAL_ARROWS;
|
||||
}
|
||||
cursors_setcurrentcursor(cursor_id);
|
||||
context_setcurrentcursor(cursor_id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1607,24 +1613,23 @@ void game_handle_edge_scroll()
|
|||
return;
|
||||
if (mainWindow->viewport == NULL)
|
||||
return;
|
||||
|
||||
uint32 window_flags = SDL_GetWindowFlags(gWindow);
|
||||
if ((window_flags & SDL_WINDOW_INPUT_FOCUS) == 0)
|
||||
if (!context_has_focus())
|
||||
return;
|
||||
|
||||
scrollX = 0;
|
||||
scrollY = 0;
|
||||
|
||||
// Scroll left / right
|
||||
if (gCursorState.x == 0)
|
||||
const CursorState * cursorState = context_get_cursor_state();
|
||||
if (cursorState->x == 0)
|
||||
scrollX = -1;
|
||||
else if (gCursorState.x >= gScreenWidth - 1)
|
||||
else if (cursorState->x >= context_get_width() - 1)
|
||||
scrollX = 1;
|
||||
|
||||
// Scroll up / down
|
||||
if (gCursorState.y == 0)
|
||||
if (cursorState->y == 0)
|
||||
scrollY = -1;
|
||||
else if (gCursorState.y >= gScreenHeight - 1)
|
||||
else if (cursorState->y >= context_get_height() - 1)
|
||||
scrollY = 1;
|
||||
|
||||
// Scroll viewport
|
||||
|
@ -1660,25 +1665,26 @@ void game_handle_key_scroll()
|
|||
scrollX = 0;
|
||||
scrollY = 0;
|
||||
|
||||
const uint8 * keysState = context_get_keys_state();
|
||||
for (sint32 shortcutId = SHORTCUT_SCROLL_MAP_UP; shortcutId <= SHORTCUT_SCROLL_MAP_RIGHT; shortcutId++) {
|
||||
uint16 shortcutKey = gShortcutKeys[shortcutId];
|
||||
uint8 scancode = shortcutKey & 0xFF;
|
||||
|
||||
if (shortcutKey == 0xFFFF) continue;
|
||||
if (!gKeysState[scancode]) continue;
|
||||
if (!keysState[scancode]) continue;
|
||||
|
||||
if (shortcutKey & SHIFT) {
|
||||
if (!gKeysState[SDL_SCANCODE_LSHIFT] && !gKeysState[SDL_SCANCODE_RSHIFT]) continue;
|
||||
if (!keysState[SDL_SCANCODE_LSHIFT] && !keysState[SDL_SCANCODE_RSHIFT]) continue;
|
||||
}
|
||||
if (shortcutKey & CTRL) {
|
||||
if (!gKeysState[SDL_SCANCODE_LCTRL] && !gKeysState[SDL_SCANCODE_RCTRL]) continue;
|
||||
if (!keysState[SDL_SCANCODE_LCTRL] && !keysState[SDL_SCANCODE_RCTRL]) continue;
|
||||
}
|
||||
if (shortcutKey & ALT) {
|
||||
if (!gKeysState[SDL_SCANCODE_LALT] && !gKeysState[SDL_SCANCODE_RALT]) continue;
|
||||
if (!keysState[SDL_SCANCODE_LALT] && !keysState[SDL_SCANCODE_RALT]) continue;
|
||||
}
|
||||
#ifdef __MACOSX__
|
||||
if (shortcutKey & CMD) {
|
||||
if (!gKeysState[SDL_SCANCODE_LGUI] && !gKeysState[SDL_SCANCODE_RGUI]) continue;
|
||||
if (!keysState[SDL_SCANCODE_LGUI] && !keysState[SDL_SCANCODE_RGUI]) continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
||||
/*****************************************************************************
|
||||
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
||||
*
|
||||
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
|
||||
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* A full copy of the GNU General Public License can be found in licence.txt
|
||||
*****************************************************************************/
|
||||
#pragma endregion
|
||||
|
||||
#include <SDL_mouse.h>
|
||||
#include "../core/Guard.hpp"
|
||||
#include "Cursors.h"
|
||||
|
||||
namespace Cursors
|
||||
{
|
||||
constexpr sint32 CURSOR_WIDTH = 32;
|
||||
constexpr sint32 CURSOR_HEIGHT = 32;
|
||||
|
||||
static SDL_Cursor * _loadedCursors[CURSOR_COUNT];
|
||||
static bool _initialised = false;
|
||||
static CURSOR_ID _currentCursor = CURSOR_UNDEFINED;
|
||||
|
||||
static SDL_Cursor * Create(const CursorData * cursorInfo)
|
||||
{
|
||||
SDL_Cursor * cursor = SDL_CreateCursor(
|
||||
cursorInfo->Data,
|
||||
cursorInfo->Mask,
|
||||
CURSOR_WIDTH,
|
||||
CURSOR_HEIGHT,
|
||||
cursorInfo->HotSpot.X,
|
||||
cursorInfo->HotSpot.Y);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
void Initialise()
|
||||
{
|
||||
Guard::Assert(!_initialised, "Cursors have already been initialised.");
|
||||
_initialised = true;
|
||||
|
||||
// Using system cursors
|
||||
_loadedCursors[CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
|
||||
_loadedCursors[CURSOR_HAND_POINT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
|
||||
|
||||
// Using custom cursors
|
||||
for (size_t i = 0; i < CURSOR_COUNT; i++)
|
||||
{
|
||||
const CursorData * cursorData = GetCursorData((CURSOR_ID)i);
|
||||
if (cursorData != nullptr)
|
||||
{
|
||||
_loadedCursors[i] = Create(cursorData);
|
||||
}
|
||||
}
|
||||
|
||||
_currentCursor = CURSOR_UNDEFINED;
|
||||
SetCurrentCursor(CURSOR_ARROW);
|
||||
}
|
||||
|
||||
void Dispose()
|
||||
{
|
||||
if (_initialised)
|
||||
{
|
||||
for (size_t i = 0; i < CURSOR_COUNT; i++)
|
||||
{
|
||||
SDL_FreeCursor(_loadedCursors[i]);
|
||||
_loadedCursors[i] = nullptr;
|
||||
}
|
||||
_currentCursor = CURSOR_UNDEFINED;
|
||||
_initialised = false;
|
||||
}
|
||||
}
|
||||
|
||||
CURSOR_ID GetCurrentCursor()
|
||||
{
|
||||
return _currentCursor;
|
||||
}
|
||||
|
||||
void SetCurrentCursor(CURSOR_ID cursorId)
|
||||
{
|
||||
if (_currentCursor != cursorId)
|
||||
{
|
||||
SDL_Cursor * cursor = _loadedCursors[cursorId];
|
||||
SDL_SetCursor(cursor);
|
||||
_currentCursor = cursorId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void cursors_initialise()
|
||||
{
|
||||
Cursors::Initialise();
|
||||
}
|
||||
|
||||
void cursors_dispose()
|
||||
{
|
||||
Cursors::Dispose();
|
||||
}
|
||||
|
||||
sint32 cursors_getcurrentcursor()
|
||||
{
|
||||
return Cursors::GetCurrentCursor();
|
||||
}
|
||||
|
||||
void cursors_setcurrentcursor(sint32 cursorId)
|
||||
{
|
||||
Cursors::SetCurrentCursor((CURSOR_ID)cursorId);
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ enum CURSOR_ID
|
|||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace Cursors
|
||||
namespace OpenRCT2 { namespace Ui
|
||||
{
|
||||
struct CursorData
|
||||
{
|
||||
|
@ -65,27 +65,6 @@ namespace Cursors
|
|||
uint8 Data[32 * 4];
|
||||
uint8 Mask[32 * 4];
|
||||
};
|
||||
|
||||
const CursorData * GetCursorData(CURSOR_ID cursorId);
|
||||
|
||||
void Initialise();
|
||||
void Dispose();
|
||||
CURSOR_ID GetCurrentCursor();
|
||||
void SetCurrentCursor(CURSOR_ID cursorId);
|
||||
}
|
||||
} }
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
void cursors_initialise();
|
||||
void cursors_dispose();
|
||||
sint32 cursors_getcurrentcursor();
|
||||
void cursors_setcurrentcursor(sint32 cursorId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../audio/audio.h"
|
||||
#include "../audio/AudioMixer.h"
|
||||
#include "../Context.h"
|
||||
#include "../interface/themes.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "../network/network.h"
|
||||
|
@ -25,17 +26,18 @@
|
|||
#include "chat.h"
|
||||
|
||||
bool gChatOpen = false;
|
||||
char _chatCurrentLine[CHAT_MAX_MESSAGE_LENGTH];
|
||||
char _chatHistory[CHAT_HISTORY_SIZE][CHAT_INPUT_SIZE];
|
||||
uint32 _chatHistoryTime[CHAT_HISTORY_SIZE];
|
||||
uint32 _chatHistoryIndex = 0;
|
||||
uint32 _chatCaretTicks = 0;
|
||||
sint32 _chatLeft;
|
||||
sint32 _chatTop;
|
||||
sint32 _chatRight;
|
||||
sint32 _chatBottom;
|
||||
sint32 _chatWidth;
|
||||
sint32 _chatHeight;
|
||||
static char _chatCurrentLine[CHAT_MAX_MESSAGE_LENGTH];
|
||||
static char _chatHistory[CHAT_HISTORY_SIZE][CHAT_INPUT_SIZE];
|
||||
static uint32 _chatHistoryTime[CHAT_HISTORY_SIZE];
|
||||
static uint32 _chatHistoryIndex = 0;
|
||||
static uint32 _chatCaretTicks = 0;
|
||||
static sint32 _chatLeft;
|
||||
static sint32 _chatTop;
|
||||
static sint32 _chatRight;
|
||||
static sint32 _chatBottom;
|
||||
static sint32 _chatWidth;
|
||||
static sint32 _chatHeight;
|
||||
static TextInputSession * _chatTextInputSession;
|
||||
|
||||
static const char* chat_history_get(uint32 index);
|
||||
static uint32 chat_history_get_time(uint32 index);
|
||||
|
@ -44,13 +46,13 @@ static void chat_clear_input();
|
|||
void chat_open()
|
||||
{
|
||||
gChatOpen = true;
|
||||
platform_start_text_input(_chatCurrentLine, sizeof(_chatCurrentLine));
|
||||
_chatTextInputSession = context_start_text_input(_chatCurrentLine, sizeof(_chatCurrentLine));
|
||||
}
|
||||
|
||||
void chat_close()
|
||||
{
|
||||
gChatOpen = false;
|
||||
platform_stop_text_input();
|
||||
context_stop_text_input();
|
||||
}
|
||||
|
||||
void chat_toggle()
|
||||
|
@ -82,9 +84,9 @@ void chat_draw(rct_drawpixelinfo * dpi)
|
|||
}
|
||||
|
||||
_chatLeft = 10;
|
||||
_chatRight = min((gScreenWidth - 10), CHAT_MAX_WINDOW_WIDTH);
|
||||
_chatRight = min((context_get_width() - 10), CHAT_MAX_WINDOW_WIDTH);
|
||||
_chatWidth = _chatRight - _chatLeft;
|
||||
_chatBottom = gScreenHeight - 45;
|
||||
_chatBottom = context_get_height() - 45;
|
||||
_chatTop = _chatBottom - 10;
|
||||
|
||||
char lineBuffer[CHAT_INPUT_SIZE + 10];
|
||||
|
@ -160,8 +162,8 @@ void chat_draw(rct_drawpixelinfo * dpi)
|
|||
|
||||
// TODO: Show caret if the input text has multiple lines
|
||||
if (_chatCaretTicks < 15 && gfx_get_string_width(lineBuffer) < (_chatWidth - 10)) {
|
||||
memcpy(lineBuffer, _chatCurrentLine, gTextInput.selection_offset);
|
||||
lineBuffer[gTextInput.selection_offset] = 0;
|
||||
memcpy(lineBuffer, _chatCurrentLine, _chatTextInputSession->SelectionStart);
|
||||
lineBuffer[_chatTextInputSession->SelectionStart] = 0;
|
||||
sint32 caretX = x + gfx_get_string_width(lineBuffer);
|
||||
sint32 caretY = y + 14;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <SDL_scancode.h>
|
||||
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "../drawing/drawing.h"
|
||||
#include "../game.h"
|
||||
#include "../input.h"
|
||||
|
@ -63,6 +64,7 @@ static sint32 _consoleCaretTicks;
|
|||
static utf8 _consolePrintfBuffer[CONSOLE_BUFFER_2_SIZE];
|
||||
static utf8 _consoleErrorBuffer[CONSOLE_BUFFER_2_SIZE];
|
||||
static sint32 _consoleScrollPos = 0;
|
||||
static TextInputSession * _consoleTextInputSession;
|
||||
|
||||
static utf8 _consoleHistory[CONSOLE_HISTORY_SIZE][CONSOLE_INPUT_SIZE];
|
||||
static sint32 _consoleHistoryIndex = 0;
|
||||
|
@ -91,14 +93,14 @@ void console_open()
|
|||
_consoleScrollPos = 0;
|
||||
console_refresh_caret();
|
||||
console_update_scroll();
|
||||
platform_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine));
|
||||
_consoleTextInputSession = context_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine));
|
||||
}
|
||||
|
||||
void console_close()
|
||||
{
|
||||
gConsoleOpen = false;
|
||||
console_invalidate();
|
||||
platform_stop_text_input();
|
||||
context_stop_text_input();
|
||||
}
|
||||
|
||||
void console_toggle()
|
||||
|
@ -125,7 +127,7 @@ void console_update()
|
|||
|
||||
_consoleLeft = 0;
|
||||
_consoleTop = 0;
|
||||
_consoleRight = gScreenWidth;
|
||||
_consoleRight = context_get_width();
|
||||
_consoleBottom = 322;
|
||||
|
||||
if (gConsoleOpen) {
|
||||
|
@ -235,8 +237,8 @@ void console_draw(rct_drawpixelinfo *dpi)
|
|||
|
||||
// Draw caret
|
||||
if (_consoleCaretTicks < 15) {
|
||||
memcpy(lineBuffer, _consoleCurrentLine, gTextInput.selection_offset);
|
||||
lineBuffer[gTextInput.selection_offset] = 0;
|
||||
memcpy(lineBuffer, _consoleCurrentLine, _consoleTextInputSession->SelectionStart);
|
||||
lineBuffer[_consoleTextInputSession->SelectionStart] = 0;
|
||||
sint32 caretX = x + gfx_get_string_width(lineBuffer);
|
||||
sint32 caretY = y + lineHeight;
|
||||
|
||||
|
@ -270,15 +272,17 @@ void console_input(sint32 c)
|
|||
_consoleHistoryIndex--;
|
||||
memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], 256);
|
||||
}
|
||||
textinputbuffer_recalculate_length(&gTextInput);
|
||||
gTextInput.selection_offset = strlen(_consoleCurrentLine);
|
||||
_consoleTextInputSession->Size = strlen(_consoleTextInputSession->Buffer);
|
||||
_consoleTextInputSession->Length = utf8_length(_consoleTextInputSession->Buffer);
|
||||
_consoleTextInputSession->SelectionStart = strlen(_consoleCurrentLine);
|
||||
break;
|
||||
case SDL_SCANCODE_DOWN:
|
||||
if (_consoleHistoryIndex < _consoleHistoryCount - 1) {
|
||||
_consoleHistoryIndex++;
|
||||
memcpy(_consoleCurrentLine, _consoleHistory[_consoleHistoryIndex], 256);
|
||||
textinputbuffer_recalculate_length(&gTextInput);
|
||||
gTextInput.selection_offset = strlen(_consoleCurrentLine);
|
||||
_consoleTextInputSession->Size = strlen(_consoleTextInputSession->Buffer);
|
||||
_consoleTextInputSession->Length = utf8_length(_consoleTextInputSession->Buffer);
|
||||
_consoleTextInputSession->SelectionStart = strlen(_consoleCurrentLine);
|
||||
} else {
|
||||
_consoleHistoryIndex = _consoleHistoryCount;
|
||||
console_clear_input();
|
||||
|
@ -412,7 +416,7 @@ static void console_clear_input()
|
|||
{
|
||||
_consoleCurrentLine[0] = 0;
|
||||
if (gConsoleOpen) {
|
||||
platform_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine));
|
||||
context_start_text_input(_consoleCurrentLine, sizeof(_consoleCurrentLine));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1005,7 +1009,7 @@ static sint32 cc_set(const utf8 **argv, sint32 argc)
|
|||
gConfigGeneral.window_scale = clamp(newScale, 0.5f, 5.0f);
|
||||
config_save_default();
|
||||
gfx_invalidate_screen();
|
||||
platform_trigger_resize();
|
||||
context_trigger_resize();
|
||||
console_execute_silent("get window_scale");
|
||||
}
|
||||
else if (strcmp(argv[0], "window_limit") == 0 && invalidArguments(&invalidArgs, int_valid[0])) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "../audio/audio.h"
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "../drawing/drawing.h"
|
||||
#include "../game.h"
|
||||
#include "../Imaging.h"
|
||||
|
@ -46,7 +47,7 @@ void screenshot_check()
|
|||
screenshotIndex = screenshot_dump();
|
||||
|
||||
if (screenshotIndex != -1) {
|
||||
audio_play_sound(SOUND_WINDOW_OPEN, 100, gScreenWidth / 2);
|
||||
audio_play_sound(SOUND_WINDOW_OPEN, 100, context_get_width() / 2);
|
||||
} else {
|
||||
window_error_open(STR_SCREENSHOT_FAILED, STR_NONE);
|
||||
}
|
||||
|
@ -58,13 +59,7 @@ void screenshot_check()
|
|||
|
||||
static void screenshot_get_rendered_palette(rct_palette* palette) {
|
||||
for (sint32 i = 0; i < 256; i++) {
|
||||
const SDL_Color *renderedEntry = &gPalette[i];
|
||||
rct_palette_entry *entry = &palette->entries[i];
|
||||
|
||||
entry->red = renderedEntry->r;
|
||||
entry->green = renderedEntry->g;
|
||||
entry->blue = renderedEntry->b;
|
||||
entry->alpha = renderedEntry->a;
|
||||
palette->entries[i] = gPalette[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +283,7 @@ sint32 cmdline_for_screenshot(const char **argv, sint32 argc)
|
|||
}
|
||||
|
||||
gOpenRCT2Headless = true;
|
||||
if (openrct2_initialise()) {
|
||||
// if (openrct2_initialise()) {
|
||||
drawing_engine_init();
|
||||
rct2_open_file(inputPath);
|
||||
|
||||
|
@ -373,7 +368,7 @@ sint32 cmdline_for_screenshot(const char **argv, sint32 argc)
|
|||
|
||||
free(dpi.bits);
|
||||
drawing_engine_dispose();
|
||||
}
|
||||
openrct2_dispose();
|
||||
// }
|
||||
// openrct2_dispose();
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma endregion
|
||||
|
||||
#include "../config/Config.h"
|
||||
#include "../Context.h"
|
||||
#include "../drawing/drawing.h"
|
||||
#include "../game.h"
|
||||
#include "../input.h"
|
||||
|
@ -424,8 +425,8 @@ static void viewport_move(sint16 x, sint16 y, rct_window* w, rct_viewport* viewp
|
|||
if (w->flags & WF_7){
|
||||
sint32 left = max(viewport->x, 0);
|
||||
sint32 top = max(viewport->y, 0);
|
||||
sint32 right = min(viewport->x + viewport->width, gScreenWidth);
|
||||
sint32 bottom = min(viewport->y + viewport->height, gScreenHeight);
|
||||
sint32 right = min(viewport->x + viewport->width, context_get_width());
|
||||
sint32 bottom = min(viewport->y + viewport->height, context_get_height());
|
||||
|
||||
if (left >= right) return;
|
||||
if (top >= bottom) return;
|
||||
|
@ -447,7 +448,7 @@ static void viewport_move(sint16 x, sint16 y, rct_window* w, rct_viewport* viewp
|
|||
viewport->x = 0;
|
||||
}
|
||||
|
||||
sint32 eax = viewport->x + viewport->width - gScreenWidth;
|
||||
sint32 eax = viewport->x + viewport->width - context_get_width();
|
||||
if (eax > 0){
|
||||
viewport->width -= eax;
|
||||
viewport->view_width -= eax * zoom;
|
||||
|
@ -465,7 +466,7 @@ static void viewport_move(sint16 x, sint16 y, rct_window* w, rct_viewport* viewp
|
|||
viewport->y = 0;
|
||||
}
|
||||
|
||||
eax = viewport->y + viewport->height - gScreenHeight;
|
||||
eax = viewport->y + viewport->height - context_get_height();
|
||||
if (eax > 0){
|
||||
viewport->height -= eax;
|
||||
viewport->view_height -= eax * zoom;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "../platform/platform.h"
|
||||
#include "../localisation/localisation.h"
|
||||
#include "../util/util.h"
|
||||
#include "../Context.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
@ -1104,15 +1105,15 @@ static void widget_text_box_draw(rct_drawpixelinfo *dpi, rct_window *w, rct_widg
|
|||
|
||||
// Make a copy of the string for measuring the width.
|
||||
char temp_string[TEXT_INPUT_SIZE] = { 0 };
|
||||
memcpy(temp_string, wrapped_string, min(string_length, gTextInput.selection_offset));
|
||||
memcpy(temp_string, wrapped_string, min(string_length, gTextInput->SelectionStart));
|
||||
sint32 cur_x = l + gfx_get_string_width(temp_string) + 3;
|
||||
|
||||
sint32 width = 6;
|
||||
if ((uint32)gTextInput.selection_offset < strlen(gTextBoxInput)){
|
||||
if ((uint32)gTextInput->SelectionStart < strlen(gTextBoxInput)){
|
||||
// Make a new 1 character wide string for measuring the width
|
||||
// of the character that the cursor is under.
|
||||
temp_string[1] = '\0';
|
||||
temp_string[0] = gTextBoxInput[gTextInput.selection_offset];
|
||||
temp_string[0] = gTextBoxInput[gTextInput->SelectionStart];
|
||||
width = max(gfx_get_string_width(temp_string) - 2, 4);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma endregion
|
||||
|
||||
#include "../audio/audio.h"
|
||||
#include "../Context.h"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../drawing/drawing.h"
|
||||
#include "../editor.h"
|
||||
|
@ -47,6 +48,7 @@ char gTextBoxInput[TEXT_INPUT_SIZE] = { 0 };
|
|||
sint32 gMaxTextBoxInputLength = 0;
|
||||
sint32 gTextBoxFrameNo = 0;
|
||||
bool gUsingWidgetTextBox = 0;
|
||||
TextInputSession * gTextInput;
|
||||
|
||||
uint16 gWindowUpdateTicks;
|
||||
uint8 gToolbarDirtyFlags;
|
||||
|
@ -295,7 +297,7 @@ static bool window_other_wheel_input(rct_window *w, rct_widgetindex widgetIndex,
|
|||
static void window_all_wheel_input()
|
||||
{
|
||||
// Get wheel value
|
||||
sint32 raw = gCursorState.wheel;
|
||||
sint32 raw = context_get_cursor_state()->wheel;
|
||||
sint32 wheel = 0;
|
||||
while (1) {
|
||||
raw -= 120;
|
||||
|
@ -311,14 +313,17 @@ static void window_all_wheel_input()
|
|||
wheel += 17;
|
||||
}
|
||||
raw -= 120;
|
||||
gCursorState.wheel = raw;
|
||||
|
||||
// TODO do something about this hack
|
||||
CursorState * cursorState = (CursorState *)context_get_cursor_state();
|
||||
cursorState->wheel = raw;
|
||||
|
||||
if (wheel == 0)
|
||||
return;
|
||||
|
||||
// Check window cursor is over
|
||||
if (!(input_test_flag(INPUT_FLAG_5))) {
|
||||
rct_window *w = window_find_from_point(gCursorState.x, gCursorState.y);
|
||||
rct_window *w = window_find_from_point(cursorState->x, cursorState->y);
|
||||
if (w != NULL) {
|
||||
// Check if main window
|
||||
if (w->classification == WC_MAIN_WINDOW || w->classification == WC_VIEWPORT) {
|
||||
|
@ -327,7 +332,7 @@ static void window_all_wheel_input()
|
|||
}
|
||||
|
||||
// Check scroll view, cursor is over
|
||||
rct_widgetindex widgetIndex = window_find_widget_from_point(w, gCursorState.x, gCursorState.y);
|
||||
rct_widgetindex widgetIndex = window_find_widget_from_point(w, cursorState->x, cursorState->y);
|
||||
if (widgetIndex != -1) {
|
||||
rct_widget *widget = &w->widgets[widgetIndex];
|
||||
if (widget->type == WWT_SCROLL) {
|
||||
|
@ -451,7 +456,7 @@ rct_window *window_create(sint32 x, sint32 y, sint32 width, sint32 height, rct_w
|
|||
// Play sounds and flash the window
|
||||
if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))){
|
||||
w->flags |= WF_WHITE_BORDER_MASK;
|
||||
audio_play_sound_panned(SOUND_WINDOW_OPEN, x + (width / 2), 0, 0, 0);
|
||||
audio_play_sound(SOUND_WINDOW_OPEN, 0, x + (width / 2));
|
||||
}
|
||||
|
||||
w->number = 0;
|
||||
|
@ -495,8 +500,8 @@ rct_window *window_create(sint32 x, sint32 y, sint32 width, sint32 height, rct_w
|
|||
*/
|
||||
static bool sub_6EA8EC(sint32 x, sint32 y, sint32 width, sint32 height)
|
||||
{
|
||||
uint16 screenWidth = gScreenWidth;
|
||||
uint16 screenHeight = gScreenHeight;
|
||||
uint16 screenWidth = context_get_width();
|
||||
uint16 screenHeight = context_get_height();
|
||||
sint32 unk;
|
||||
|
||||
unk = -(width / 4);
|
||||
|
@ -522,8 +527,8 @@ static bool sub_6EA934(sint32 x, sint32 y, sint32 width, sint32 height)
|
|||
{
|
||||
if (x < 0) return false;
|
||||
if (y < 28) return false;
|
||||
if (x + width > gScreenWidth) return false;
|
||||
if (y + height > gScreenHeight) return false;
|
||||
if (x + width > context_get_width()) return false;
|
||||
if (y + height > context_get_height()) return false;
|
||||
return sub_6EA95D(x, y, width, height);
|
||||
}
|
||||
|
||||
|
@ -564,8 +569,8 @@ static bool sub_6EA95D(sint32 x, sint32 y, sint32 width, sint32 height)
|
|||
*/
|
||||
rct_window *window_create_auto_pos(sint32 width, sint32 height, rct_window_event_list *event_handlers, rct_windowclass cls, uint16 flags)
|
||||
{
|
||||
uint16 screenWidth = gScreenWidth;
|
||||
uint16 screenHeight = gScreenHeight;
|
||||
uint16 screenWidth = context_get_width();
|
||||
uint16 screenHeight = context_get_height();
|
||||
|
||||
// TODO dead code, looks like it is cascading the new window offset from an existing window
|
||||
// we will have to re-implement this in our own way.
|
||||
|
@ -574,11 +579,11 @@ rct_window *window_create_auto_pos(sint32 width, sint32 height, rct_window_event
|
|||
// cls &= ~0x80;
|
||||
// rct_window *w = window_find_by_number(0, 0);
|
||||
// if (w != NULL) {
|
||||
// if (w->x > -60 && w->x < gScreenWidth - 20) {
|
||||
// if (w->y < gScreenHeight - 20) {
|
||||
// if (w->x > -60 && w->x < screenWidth - 20) {
|
||||
// if (w->y < screenHeight - 20) {
|
||||
// sint32 x = w->x;
|
||||
// if (w->x + width > gScreenWidth)
|
||||
// x = gScreenWidth - 20 - width;
|
||||
// if (w->x + width > screenWidth)
|
||||
// x = screenWidth - 20 - width;
|
||||
// sint32 y = w->y;
|
||||
// return window_create(x + 10, y + 10, width, height, event_handlers, cls, flags);
|
||||
// }
|
||||
|
@ -684,12 +689,13 @@ foundSpace:
|
|||
return window_create(x, y, width, height, event_handlers, cls, flags);
|
||||
}
|
||||
|
||||
rct_window *window_create_centred(sint32 width, sint32 height, rct_window_event_list *event_handlers, rct_windowclass cls, uint16 flags)
|
||||
rct_window * window_create_centred(sint32 width, sint32 height, rct_window_event_list *event_handlers, rct_windowclass cls, uint16 flags)
|
||||
{
|
||||
sint32 x, y;
|
||||
sint32 screenWidth = context_get_width();
|
||||
sint32 screenHeight = context_get_height();
|
||||
|
||||
x = (gScreenWidth - width) / 2;
|
||||
y = max(28, (gScreenHeight - height) / 2);
|
||||
sint32 x = (screenWidth - width) / 2;
|
||||
sint32 y = max(28, (screenHeight - height) / 2);
|
||||
return window_create(x, y, width, height, event_handlers, cls, flags);
|
||||
}
|
||||
|
||||
|
@ -1228,7 +1234,7 @@ void window_push_others_right(rct_window* window)
|
|||
continue;
|
||||
|
||||
window_invalidate(w);
|
||||
if (window->x + window->width + 13 >= gScreenWidth)
|
||||
if (window->x + window->width + 13 >= context_get_width())
|
||||
continue;
|
||||
uint16 push_amount = window->x + window->width - w->x + 3;
|
||||
w->x += push_amount;
|
||||
|
@ -1263,7 +1269,7 @@ void window_push_others_below(rct_window *w1)
|
|||
continue;
|
||||
|
||||
// Check if there is room to push it down
|
||||
if (w1->y + w1->height + 80 >= gScreenHeight)
|
||||
if (w1->y + w1->height + 80 >= context_get_height())
|
||||
continue;
|
||||
|
||||
// Invalidate the window's current area
|
||||
|
@ -1471,7 +1477,7 @@ void window_viewport_get_map_coords_by_cursor(rct_window *w, sint16 *map_x, sint
|
|||
{
|
||||
// Get mouse position to offset against.
|
||||
sint32 mouse_x, mouse_y;
|
||||
platform_get_cursor_position_scaled(&mouse_x, &mouse_y);
|
||||
context_get_cursor_position_scaled(&mouse_x, &mouse_y);
|
||||
|
||||
// Compute map coordinate by mouse position.
|
||||
get_map_coordinates_from_pos(mouse_x, mouse_y, VIEWPORT_INTERACTION_MASK_NONE, map_x, map_y, NULL, NULL, NULL);
|
||||
|
@ -1499,7 +1505,7 @@ void window_viewport_centre_tile_around_cursor(rct_window *w, sint16 map_x, sint
|
|||
|
||||
// Get mouse position to offset against.
|
||||
sint32 mouse_x, mouse_y;
|
||||
platform_get_cursor_position_scaled(&mouse_x, &mouse_y);
|
||||
context_get_cursor_position_scaled(&mouse_x, &mouse_y);
|
||||
|
||||
// Rebase mouse position onto centre of window, and compensate for zoom level.
|
||||
sint32 rebased_x = ((w->width >> 1) - mouse_x) * (1 << w->viewport->zoom),
|
||||
|
@ -2397,7 +2403,7 @@ static void window_snap_right(rct_window *w, sint32 proximity)
|
|||
leftMost = min(leftMost, w2->x);
|
||||
}
|
||||
|
||||
screenWidth = gScreenWidth;
|
||||
screenWidth = context_get_width();
|
||||
if (screenWidth >= wLeftProximity && screenWidth <= wRightProximity)
|
||||
leftMost = min(leftMost, screenWidth);
|
||||
|
||||
|
@ -2430,7 +2436,7 @@ static void window_snap_bottom(rct_window *w, sint32 proximity)
|
|||
topMost = min(topMost, w2->y);
|
||||
}
|
||||
|
||||
screenHeight = gScreenHeight;
|
||||
screenHeight = context_get_height();
|
||||
if (screenHeight >= wTopProximity && screenHeight <= wBottomProximity)
|
||||
topMost = min(topMost, screenHeight);
|
||||
|
||||
|
@ -2443,7 +2449,7 @@ void window_move_and_snap(rct_window *w, sint32 newWindowX, sint32 newWindowY, s
|
|||
sint32 originalX = w->x;
|
||||
sint32 originalY = w->y;
|
||||
|
||||
newWindowY = clamp(29, newWindowY, gScreenHeight - 34);
|
||||
newWindowY = clamp(29, newWindowY, context_get_height() - 34);
|
||||
|
||||
if (snapProximity > 0) {
|
||||
w->x = newWindowX;
|
||||
|
@ -2507,7 +2513,7 @@ void window_start_textbox(rct_window *call_w, rct_widgetindex call_widget, rct_s
|
|||
// from crashing the game.
|
||||
gTextBoxInput[maxLength - 1] = '\0';
|
||||
|
||||
platform_start_text_input(gTextBoxInput, maxLength);
|
||||
context_start_text_input(gTextBoxInput, maxLength);
|
||||
}
|
||||
|
||||
void window_cancel_textbox()
|
||||
|
@ -2520,7 +2526,7 @@ void window_cancel_textbox()
|
|||
window_event_textinput_call(w, gCurrentTextBox.widget_index, NULL);
|
||||
gCurrentTextBox.window.classification = WC_NULL;
|
||||
gCurrentTextBox.window.number = 0;
|
||||
platform_stop_text_input();
|
||||
context_stop_text_input();
|
||||
gUsingWidgetTextBox = false;
|
||||
widget_invalidate(w, gCurrentTextBox.widget_index);
|
||||
gCurrentTextBox.widget_index = WWT_LAST;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue