diff --git a/.github/build.sh b/.github/build.sh
index a57d9802d..d3777e24a 100755
--- a/.github/build.sh
+++ b/.github/build.sh
@@ -43,6 +43,28 @@ aarch64-android-bionic-static) ;;
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
esac
+if [[ -z ${BSH_NO_PACKAGES-} ]]; then
+ case $BSH_BUILD_PLATFORM in
+ linux)
+ sudo apt update
+ if [[ $BSH_STATIC_DYNAMIC == static ]]; then
+ sudo apt install libc6-dev libc6-dev-i386
+ else
+ sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
+ fi
+ if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
+ sudo apt install g++-mingw-w64-x86-64
+ fi
+ ;;
+ darwin)
+ brew install pkg-config binutils
+ if [[ $BSH_STATIC_DYNAMIC != static ]]; then
+ brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
+ fi
+ ;;
+ esac
+fi
+
function inplace_sed() {
local subst=$1
local path=$2
@@ -163,7 +185,6 @@ meson_configure+=$'\t'-Dapp_data=$APP_DATA
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
meson_configure+=$'\t'-Db_strip=false
meson_configure+=$'\t'-Db_staticpic=false
-meson_configure+=$'\t'-Dinstall_check=true
meson_configure+=$'\t'-Dmod_id=$MOD_ID
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
x86_64-linux-gnu-debug) ;&
@@ -174,6 +195,12 @@ x86_64-darwin-macos-debug)
meson_configure+=$'\t'-Dbuild_font=true
;;
esac
+if [[ $PACKAGE_MODE == nohttp ]]; then
+ meson_configure+=$'\t'-Dhttp=false
+fi
+if [[ $PACKAGE_MODE == nolua ]]; then
+ meson_configure+=$'\t'-Dlua=none
+fi
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
meson_configure+=$'\t'-Dstatic=prebuilt
if [[ $BSH_HOST_PLATFORM == windows ]]; then
@@ -212,9 +239,15 @@ fi
if [[ $RELEASE_TYPE == stable ]]; then
stable_or_beta=yes
fi
-save_version=$(grep -w src/Config.template.h -e "#define SAVE_VERSION" | cut -d ' ' -f 3)
-minor_version=$(grep -w src/Config.template.h -e "#define MINOR_VERSION" | cut -d ' ' -f 3)
-build_num=$(grep -w src/Config.template.h -e "#define BUILD_NUM" | cut -d ' ' -f 3)
+set +e
+save_version=$(cat src/Config.template.h | sed -n 's/constexpr int SAVE_VERSION * = \([^;]*\);/\1/p')
+minor_version=$(cat src/Config.template.h | sed -n 's/constexpr int MINOR_VERSION * = \([^;]*\);/\1/p')
+build_num=$(cat src/Config.template.h | sed -n 's/constexpr int BUILD_NUM * = \([^;]*\);/\1/p')
+if [[ -z ${save_version-} ]] || [[ -z ${minor_version-} ]] || [[ -z ${build_num-} ]]; then
+ >&2 echo "failed to extract version from Config.template.h"
+ exit 1
+fi
+set -e
if [[ $stable_or_beta == yes ]] && [[ $MOD_ID != 0 ]]; then
save_version=$(echo $RELEASE_NAME | cut -d '.' -f 1)
minor_version=$(echo $RELEASE_NAME | cut -d '.' -f 2)
@@ -252,9 +285,14 @@ if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini
fi
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
- if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
- >&2 echo "GITHUB_REPOSITORY_OWNER not set"
- exit 1
+ if [[ -z ${TPTLIBSREMOTE-} ]]; then
+ if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
+ >&2 echo "GITHUB_REPOSITORY_OWNER not set"
+ exit 1
+ fi
+ tptlibsremote=https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs
+ else
+ tptlibsremote=$TPTLIBSREMOTE
fi
if [[ "$BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "x86_64-windows-mingw-dynamic linux" ]]; then
>&2 echo "this configuration is not supported in tptlibsdev mode"
@@ -262,10 +300,13 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] ||
exit 0
fi
tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH
+ if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
+ rm -rf build-tpt-libs
+ fi
if [[ ! -d build-tpt-libs/tpt-libs ]]; then
mkdir -p build-tpt-libs
cd build-tpt-libs
- git clone https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs --branch $tptlibsbranch --depth 1
+ git clone $tptlibsremote --branch $tptlibsbranch --depth 1
cd ..
fi
tpt_libs_vtag=v00000000000000
@@ -331,7 +372,7 @@ if [[ $BSH_HOST_PLATFORM == android ]]; then
fi
if [[ $PACKAGE_MODE == appimage ]]; then
# so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later
- meson configure -Dinstall_check=false -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
+ meson configure -Dcan_install=no -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
strip_target=$APP_EXE
fi
if [[ $BSH_BUILD_PLATFORM == windows ]]; then
@@ -341,6 +382,14 @@ if [[ $BSH_BUILD_PLATFORM == windows ]]; then
set -e
cat $APP_EXE.exe.rsp
[[ $ninja_code == 0 ]];
+ echo # rsps don't usually have a newline at the end
+ if [[ "$BSH_HOST_PLATFORM-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "windows-dynamic windows" ]]; then
+ # on windows we provide the dynamic dependencies also; makes sense to check for their presence
+ # msys ldd works fine but only on windows build machines
+ if ldd $APP_EXE | grep "not found"; then
+ exit 1 # ldd | grep will have printed missing deps
+ fi
+ fi
else
ninja -v
fi
@@ -383,8 +432,7 @@ if [[ $PACKAGE_MODE == dmg ]]; then
mv $appdir dmgroot/$appdir
cp ../LICENSE dmgroot/LICENSE
cp ../README.md dmgroot/README.md
- hdiutil create uncompressed.dmg -ov -volname $APP_NAME -fs HFS+ -srcfolder dmgroot
- hdiutil convert uncompressed.dmg -format UDZO -o $ASSET_PATH
+ hdiutil create -format UDZO -volname $APP_NAME -fs HFS+ -srcfolder dmgroot -o $ASSET_PATH
elif [[ $PACKAGE_MODE == appimage ]]; then
# so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later
case $BSH_HOST_ARCH in
diff --git a/.github/prepare.py b/.github/prepare.py
index 6da65fd0c..a02ed2f99 100755
--- a/.github/prepare.py
+++ b/.github/prepare.py
@@ -78,11 +78,13 @@ build_matrix = []
publish_matrix = []
# consider disabling line wrapping to edit this monstrosity
for arch, platform, libc, statdyn, bplatform, runson, suffix, publish, artifact, dbgsuffix, mode, starcatcher, dbgrel in [
- ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ),
- ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ),
- ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, True, '.dbg', 'appimage', None, 'release' ),
- ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ),
- ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'release' ),
+ ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
+ ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ),
+ ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', 'appimage', None, 'release' ),
+ ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
+ ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug' ),
+ ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug' ),
+ ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release' ),
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
@@ -107,14 +109,14 @@ for arch, platform, libc, statdyn, bplatform, runson, suff
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release' ),
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
- ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
- ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ),
- ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
- ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ),
- ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
- ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ),
- ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
- ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ),
+ ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
+ ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ),
+ ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
+ ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ),
+ ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
+ ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ),
+ ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
+ ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ),
]:
if not mode:
mode = 'default'
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 450247501..a5533bbeb 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -31,6 +31,7 @@ jobs:
app_vendor: ${{ steps.prepare.outputs.app_vendor }}
do_publish: ${{ steps.prepare.outputs.do_publish }}
steps:
+ - run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
@@ -58,21 +59,14 @@ jobs:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
steps:
+ - run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- run: python -m pip install meson ninja
- if: matrix.bsh_build_platform == 'darwin'
- run: brew install pkg-config coreutils binutils bash
- - if: matrix.bsh_build_platform == 'darwin' && matrix.bsh_static_dynamic != 'static'
- run: brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
- - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_host_libc == 'mingw'
- run: sudo apt update && sudo apt install g++-mingw-w64-x86-64
- - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic != 'static'
- run: sudo apt update && sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
- - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic == 'static'
- run: sudo apt update && sudo apt install libc6-dev libc6-dev-i386
+ run: brew install bash coreutils
- run: bash -c './.github/build.sh'
env:
BSH_HOST_ARCH: ${{ matrix.bsh_host_arch }}
@@ -130,6 +124,7 @@ jobs:
matrix: ${{ fromJSON(needs.prepare.outputs.publish_matrix) }}
if: needs.prepare.outputs.do_publish == 'yes'
steps:
+ - run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
@@ -147,6 +142,7 @@ jobs:
needs: [build, publish, prepare]
if: needs.prepare.outputs.do_publish == 'yes'
steps:
+ - run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- run: ./.github/starcatcher-release.sh
env:
diff --git a/README.md b/README.md
index 7efc608fa..af48ad012 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-The Powder Toy - December 2022
+The Powder Toy - January 2023
==========================
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
@@ -25,7 +25,7 @@ Thanks
* Skresanov Savely
* cracker64
* Catelite
-* Bryan Hoyle
+* Victoria Hoyle
* Nathan Cousins
* jacksonmj
* Felix Wallin
@@ -111,15 +111,16 @@ Controls
Command Line
---------------------------------------------------------------------------
-| Command | Description | Example |
-| --------------------- | ------------------------------------------------ | --------------------------------- |
-| `scale:SIZE` | Change window scale factor | `scale:2` |
-| `kiosk` | Fullscreen mode | |
-| `proxy:SERVER[:PORT]` | Proxy server to use | `proxy:wwwcache.lancs.ac.uk:8080` |
-| `open FILE` | Opens the file as a stamp or game save | |
-| `ddir DIRECTORY` | Directory used for saving stamps and preferences | |
-| `ptsave:SAVEID` | Open online save, used by ptsave: URLs | `ptsave:2198` |
-| `disable-network` | Disables internet connections | |
-| `redirect` | Redirects output to stdout.txt / stderr.txt | |
+| Command | Description | Example |
+| --------------------- | ------------------------------------------------ | --------------------------------------------|
+| `scale:SIZE` | Change window scale factor | `scale:2` |
+| `kiosk` | Fullscreen mode | |
+| `proxy:SERVER[:PORT]` | Proxy server to use | `proxy:wwwcache.lancs.ac.uk:8080` |
+| `open FILE` | Opens the file as a stamp or game save | |
+| `ddir DIRECTORY` | Directory used for saving stamps and preferences | |
+| `ptsave:SAVEID` | Open online save, used by ptsave: URLs | `ptsave:2198` |
+| `disable-network` | Disables internet connections | |
+| `disable-bluescreen` | Disable bluescreen handler | |
+| `redirect` | Redirects output to stdout.txt / stderr.txt | |
| `cafile:CAFILE` | Set certificate bundle path | `cafile:/etc/ssl/certs/ca-certificates.crt` |
-| `capath:CAPATH` | Set certificate directory path | `capath:/etc/ssl/certs` |
+| `capath:CAPATH` | Set certificate directory path | `capath:/etc/ssl/certs` |
diff --git a/meson.build b/meson.build
index 513351ee2..ee9f0c862 100644
--- a/meson.build
+++ b/meson.build
@@ -37,6 +37,9 @@ host_arch = host_machine.cpu_family()
host_platform = host_machine.system()
# educated guesses follow, PRs welcome
if c_compiler.get_id() in [ 'msvc' ]
+ if host_platform != 'windows'
+ error('this seems fishy')
+ endif
host_libc = 'msvc'
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
host_libc = 'mingw'
@@ -49,6 +52,11 @@ elif host_platform in [ 'android' ]
host_platform = 'android'
host_libc = 'bionic'
else
+ if host_platform != 'linux'
+ # TODO: maybe use 'default' in place of 'linux', or use something other than host_platform where details such as desktop integration are concerned
+ warning('host platform is not linux but we will pretend that it is')
+ host_platform = 'linux'
+ endif
host_libc = 'gnu'
endif
@@ -64,11 +72,7 @@ endif
is_static = static_variant != 'none'
is_debug = get_option('optimization') in [ '0', 'g' ]
-enforce_https = get_option('enforce_https')
-
-if not is_debug and not enforce_https
- error('refusing to build a release binary with enforce_https=false')
-endif
+app_exe = get_option('app_exe')
tpt_libs_static = 'none'
if static_variant == 'prebuilt'
@@ -81,7 +85,7 @@ tpt_libs_debug = is_debug ? 'debug' : 'release'
tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static)
tpt_libs_vtag = get_option('tpt_libs_vtag')
if tpt_libs_vtag == ''
- tpt_libs_vtag = 'v20221223184203'
+ tpt_libs_vtag = 'v20230205154205'
endif
if tpt_libs_static != 'none'
if tpt_libs_variant not in [
@@ -122,10 +126,6 @@ else
endif
endif
-conf_data = configuration_data()
-conf_data.set('CURL_STATICLIB', false)
-conf_data.set('ZLIB_WINAPI', false)
-
x86_sse_level_str = get_option('x86_sse')
if x86_sse_level_str == 'auto'
x86_sse_level = 20
@@ -179,9 +179,7 @@ if host_platform == 'android'
endif
curl_dep = enable_http ? dependency('libcurl', static: is_static) : []
-enable_gravfft = get_option('gravfft')
-fftw_dep = enable_gravfft ? dependency('fftw3f', static: is_static) : []
-
+fftw_dep = dependency('fftw3f', static: is_static)
threads_dep = dependency('threads')
zlib_dep = dependency('zlib', static: is_static)
png_dep = dependency('libpng16', static: is_static)
@@ -278,13 +276,11 @@ endif
if host_platform == 'windows'
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501' ]
- project_c_args += args_ccomp_win
- project_cpp_args += args_ccomp_win
windows_mod = import('windows')
if is_static
- conf_data.set('CURL_STATICLIB', true)
+ args_ccomp_win += [ '-DCURL_STATICLIB' ]
if host_arch == 'x86_64'
- conf_data.set('ZLIB_WINAPI', true)
+ args_ccomp_win += [ '-DZLIB_WINAPI' ]
endif
else
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
@@ -302,6 +298,8 @@ if host_platform == 'windows'
endif
endforeach
endif
+ project_c_args += args_ccomp_win
+ project_cpp_args += args_ccomp_win
endif
project_inc = include_directories([ 'src', 'resources' ])
@@ -316,43 +314,6 @@ else
ident_platform = 'UNKNOWN'
endif
-app_exe = get_option('app_exe')
-app_id = get_option('app_id')
-
-conf_data.set('LIN', host_platform == 'linux')
-conf_data.set('AND', host_platform == 'android')
-conf_data.set('WIN', host_platform == 'windows')
-conf_data.set('MACOSX', host_platform == 'darwin')
-conf_data.set('X86', is_x86)
-conf_data.set('X86_SSE3', x86_sse_level >= 30)
-conf_data.set('X86_SSE2', x86_sse_level >= 20)
-conf_data.set('X86_SSE', x86_sse_level >= 10)
-conf_data.set('_64BIT', is_64bit)
-conf_data.set('BETA', get_option('beta'))
-conf_data.set('NO_INSTALL_CHECK', not get_option('install_check'))
-conf_data.set('IGNORE_UPDATES', get_option('ignore_updates'))
-conf_data.set('MOD_ID', get_option('mod_id'))
-conf_data.set('DEBUG', is_debug)
-conf_data.set('SNAPSHOT', get_option('snapshot'))
-conf_data.set('SNAPSHOT_ID', get_option('snapshot_id'))
-conf_data.set('SERVER', '"@0@"'.format(get_option('server')))
-conf_data.set('STATICSERVER', '"@0@"'.format(get_option('static_server')))
-conf_data.set('IDENT_PLATFORM', '"@0@"'.format(ident_platform))
-conf_data.set('IDENT', '"@0@-@1@-@2@"'.format(host_arch, host_platform, host_libc).to_upper())
-conf_data.set('ENFORCE_HTTPS', enforce_https)
-conf_data.set('APPNAME', get_option('app_name'))
-conf_data.set('APPCOMMENT', get_option('app_comment'))
-conf_data.set('APPEXE', app_exe)
-conf_data.set('APPID', app_id)
-conf_data.set('APPDATA', get_option('app_data'))
-conf_data.set('APPVENDOR', get_option('app_vendor'))
-
-if get_option('update_server') != ''
- conf_data.set('UPDATESERVER', '"@0@"'.format(get_option('update_server')))
-else
- conf_data.set('UPDATESERVER', false)
-endif
-
data_files = []
subdir('src')
@@ -378,7 +339,7 @@ if get_option('build_powder')
powder_sha = shared_library(
app_exe,
sources: powder_files,
- include_directories: [ project_inc, powder_inc ],
+ include_directories: project_inc,
c_args: project_c_args,
cpp_args: project_cpp_args,
link_args: project_link_args,
@@ -389,12 +350,13 @@ if get_option('build_powder')
executable(
app_exe,
sources: powder_files,
- include_directories: [ project_inc, powder_inc ],
+ include_directories: project_inc,
c_args: project_c_args,
cpp_args: project_cpp_args,
win_subsystem: is_debug ? 'console' : 'windows',
link_args: project_link_args,
dependencies: powder_deps,
+ install: true,
)
endif
endif
@@ -414,7 +376,7 @@ if get_option('build_render')
executable(
'render',
sources: render_files,
- include_directories: [ project_inc, render_inc ],
+ include_directories: project_inc,
c_args: project_c_args,
cpp_args: project_cpp_args,
link_args: render_link_args,
@@ -434,7 +396,7 @@ if get_option('build_font')
executable(
'font',
sources: font_files,
- include_directories: [ project_inc, font_inc ],
+ include_directories: project_inc,
c_args: project_c_args,
cpp_args: project_cpp_args,
link_args: project_link_args,
diff --git a/meson_options.txt b/meson_options.txt
index cfffdafee..6cc15b608 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -18,10 +18,11 @@ option(
description: 'Don\'t show notifications about available updates'
)
option(
- 'install_check',
- type: 'boolean',
- value: false,
- description: 'Do install check on startup'
+ 'can_install',
+ type: 'combo',
+ choices: [ 'no', 'yes', 'yes_check', 'auto' ],
+ value: 'auto',
+ description: 'Disable (\'no\') or enable (\'yes\') setting up file and URL associations, or even offer to do it at startup (\'yes_check\')'
)
option(
'http',
@@ -29,12 +30,6 @@ option(
value: true,
description: 'Enable HTTP via libcurl'
)
-option(
- 'gravfft',
- type: 'boolean',
- value: true,
- description: 'Enable FFT gravity via libfftw3'
-)
option(
'snapshot',
type: 'boolean',
@@ -207,6 +202,12 @@ option(
value: true,
description: 'Enforce encrypted HTTP traffic, may be disabled for debugging'
)
+option(
+ 'secure_ciphers_only',
+ type: 'boolean',
+ value: false,
+ description: 'Use only secure ciphers for encrypted HTTP traffic, please review cipher list before enabling'
+)
option(
'prepare',
type: 'boolean',
diff --git a/resources/Info.template.plist b/resources/Info.template.plist
index 835cc446c..0022bb960 100644
--- a/resources/Info.template.plist
+++ b/resources/Info.template.plist
@@ -18,7 +18,7 @@
CFBundleIconFile
icon_exe.icns
NSHumanReadableCopyright
- Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2022 Simon Robertshaw, Copyright © 2016-2022 jacob1
+ Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1
CFBundleDevelopmentRegion
English
CFBundleDocumentTypes
diff --git a/resources/font.bz2 b/resources/font.bz2
index a26ce218d..63da2c61a 100644
Binary files a/resources/font.bz2 and b/resources/font.bz2 differ
diff --git a/resources/meson.build b/resources/meson.build
index 48dd45de3..bf8b39532 100644
--- a/resources/meson.build
+++ b/resources/meson.build
@@ -60,6 +60,7 @@ if host_platform == 'windows'
],
depend_files: [
'resource.h',
+ 'winutf8.xml',
],
)
elif host_platform == 'darwin'
diff --git a/resources/powder-res.rc b/resources/powder-res.rc
index a591330f8..7bb0c594e 100644
--- a/resources/powder-res.rc
+++ b/resources/powder-res.rc
@@ -1,4 +1,6 @@
#include "resource.h"
+#include
IDI_ICON ICON DISCARDABLE "icon_exe.ico"
IDI_DOC_ICON ICON DISCARDABLE "icon_cps.ico"
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "winutf8.xml"
diff --git a/resources/resource.h b/resources/resource.h
index d6086ac90..2c039a4af 100644
--- a/resources/resource.h
+++ b/resources/resource.h
@@ -1,5 +1,3 @@
-#define LUASCRIPT 256
-
#define IDI_ICON 101
#define IDI_DOC_ICON 102
diff --git a/resources/winutf8.xml b/resources/winutf8.xml
new file mode 100644
index 000000000..522749a1d
--- /dev/null
+++ b/resources/winutf8.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ UTF-8
+
+
+
diff --git a/src/Activity.h b/src/Activity.h
index d11418c59..fa7c79294 100644
--- a/src/Activity.h
+++ b/src/Activity.h
@@ -1,6 +1,4 @@
#pragma once
-#include "Config.h"
-
#include "gui/interface/Window.h"
class Activity
diff --git a/src/Config.template.h b/src/Config.template.h
index b7c206970..9ef9dea16 100644
--- a/src/Config.template.h
+++ b/src/Config.template.h
@@ -1,158 +1,46 @@
-#ifndef CONFIG_H
-#define CONFIG_H
+#pragma once
-#mesondefine CURL_STATICLIB
-#mesondefine ZLIB_WINAPI
+constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
+constexpr bool DEBUG = @DEBUG@;
+constexpr bool X86 = @X86@;
+constexpr bool BETA = @BETA@;
+constexpr bool SNAPSHOT = @SNAPSHOT@;
+constexpr bool MOD = @MOD@;
+constexpr bool NOHTTP = @NOHTTP@;
+constexpr bool LUACONSOLE = @LUACONSOLE@;
+constexpr bool ALLOW_FAKE_NEWER_VERSION = @ALLOW_FAKE_NEWER_VERSION@;
+constexpr bool USE_UPDATESERVER = @USE_UPDATESERVER@;
+constexpr bool CAN_INSTALL = @CAN_INSTALL@;
+constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
+constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
+constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@;
+constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@;
+constexpr char PATH_SEP_CHAR = '@PATH_SEP_CHAR@';
-#mesondefine LUACONSOLE
-#mesondefine NOHTTP
-#mesondefine GRAVFFT
-#mesondefine RENDERER
-#mesondefine FONTEDITOR
+constexpr char SERVER[] = "@SERVER@";
+constexpr char STATICSERVER[] = "@STATICSERVER@";
+constexpr char UPDATESERVER[] = "@UPDATESERVER@";
+constexpr char IDENT_PLATFORM[] = "@IDENT_PLATFORM@";
+constexpr char IDENT[] = "@IDENT@";
+constexpr char APPNAME[] = "@APPNAME@";
+constexpr char APPCOMMENT[] = "@APPCOMMENT@";
+constexpr char APPEXE[] = "@APPEXE@";
+constexpr char APPID[] = "@APPID@";
+constexpr char APPDATA[] = "@APPDATA@";
+constexpr char APPVENDOR[] = "@APPVENDOR@";
-#mesondefine BETA
-#mesondefine DEBUG
-#mesondefine IGNORE_UPDATES
-#mesondefine LIN
-#mesondefine AND
-#mesondefine NO_INSTALL_CHECK
-#mesondefine SNAPSHOT
-#mesondefine WIN
-#mesondefine MACOSX
-#mesondefine X86
-#mesondefine X86_SSE
-#mesondefine X86_SSE2
-#mesondefine X86_SSE3
-#mesondefine _64BIT
-#mesondefine SERVER
-#mesondefine STATICSERVER
-#mesondefine UPDATESERVER
-#mesondefine IDENT_PLATFORM
-#mesondefine IDENT
-#mesondefine ENFORCE_HTTPS
-#define APPNAME "@APPNAME@"
-#define APPCOMMENT "@APPCOMMENT@"
-#define APPEXE "@APPEXE@"
-#define APPID "@APPID@"
-#define APPDATA "@APPDATA@"
-#define APPVENDOR "@APPVENDOR@"
+constexpr int SAVE_VERSION = 97;
+constexpr int MINOR_VERSION = 0;
+constexpr int BUILD_NUM = 352;
+constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@;
+constexpr int MOD_ID = @MOD_ID@;
+constexpr int FUTURE_SAVE_VERSION = 98;
+constexpr int FUTURE_MINOR_VERSION = 0;
-#ifdef WIN
-# define PATH_SEP "\\"
-# define PATH_SEP_CHAR '\\'
-#else
-# define PATH_SEP "/"
-# define PATH_SEP_CHAR '/'
-#endif
+constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');
-//VersionInfoStart
-#define SAVE_VERSION 97
-#define MINOR_VERSION 0
-#define BUILD_NUM 351
-#mesondefine SNAPSHOT_ID
-#mesondefine MOD_ID
-#define FUTURE_SAVE_VERSION 97
-#define FUTURE_MINOR_VERSION 0
-
-#if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0)
-#undef FUTURE_SAVE_VERSION
-#undef FUTURE_MINOR_VERSION
-#endif
-//VersionInfoEnd
-
-#if !(defined(MACOSX) && defined(DEBUG))
-#define HIGH_QUALITY_RESAMPLE //High quality image resampling, slower but much higher quality than my terribad linear interpolation
-#endif
-
-#if defined(SNAPSHOT)
-#define IDENT_RELTYPE "S"
-#elif defined(BETA)
-#define IDENT_RELTYPE "B"
-#else
-#define IDENT_RELTYPE "R"
-#endif
-
-#if defined(X86_SSE3)
-#define IDENT_BUILD "SSE3"
-#elif defined(X86_SSE2)
-#define IDENT_BUILD "SSE2"
-#elif defined(X86_SSE)
-#define IDENT_BUILD "SSE"
-#else
-#define IDENT_BUILD "NO"
-#endif
-
-#define MTOS_EXPAND(str) #str
-#define MTOS(str) MTOS_EXPAND(str)
-
-#define SCHEME "https://"
-#define STATICSCHEME "https://"
-
-#define LOCAL_SAVE_DIR "Saves"
-
-#define STAMPS_DIR "stamps"
-
-#define BRUSH_DIR "Brushes"
-
-#ifndef M_GRAV
-#define M_GRAV 6.67300e-1
-#endif
-
-#ifdef RENDERER
-#define MENUSIZE 0
-#define BARSIZE 0
-#else
-#define MENUSIZE 40
-#define BARSIZE 17
-#endif
-#define XRES 612
-#define YRES 384
-#define NPART XRES*YRES
-
-#define XCNTR XRES/2
-#define YCNTR YRES/2
-
-#define WINDOWW (XRES+BARSIZE)
-#define WINDOWH (YRES+MENUSIZE)
-
-#define GRAV_DIFF
-
-#define MAXSIGNS 16
-
-//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
-#define CELL 4
-#define ISTP (CELL/2)
-#define CFDS (4.0f/CELL)
-#define SIM_MAXVELOCITY 1e4f
-
-//Air constants
-#define AIR_TSTEPP 0.3f
-#define AIR_TSTEPV 0.4f
-#define AIR_VADV 0.3f
-#define AIR_VLOSS 0.999f
-#define AIR_PLOSS 0.9999f
-
-#define NGOL 24
-
-#define CIRCLE_BRUSH 0
-#define SQUARE_BRUSH 1
-#define TRI_BRUSH 2
-#define BRUSH_NUM 3
-
-//Photon constants
-#define SURF_RANGE 10
-#define NORMAL_MIN_EST 3
-#define NORMAL_INTERP 20
-#define NORMAL_FRAC 16
-
-#define REFRACT 0x80000000
-
-/* heavy flint glass, for awesome refraction/dispersion
- this way you can make roof prisms easily */
-#define GLASS_IOR 1.9
-#define GLASS_DISP 0.07
-
-#define SDEUT
-#define R_TEMP 22
-
-#endif /* CONFIG_H */
+constexpr char SCHEME[] = "https://";
+constexpr char STATICSCHEME[] = "https://";
+constexpr char LOCAL_SAVE_DIR[] = "Saves";
+constexpr char STAMPS_DIR[] = "stamps";
+constexpr char BRUSH_DIR[] = "Brushes";
diff --git a/src/Controller.h b/src/Controller.h
index aae55b612..1b1e4841d 100644
--- a/src/Controller.h
+++ b/src/Controller.h
@@ -1,6 +1,4 @@
-#ifndef CONTROLLER_H_
-#define CONTROLLER_H_
-
+#pragma once
class Controller
{
private:
@@ -9,5 +7,3 @@ private:
virtual void Hide();
virtual ~Controller() = default;
};
-
-#endif /* CONTROLLER_H_ */
diff --git a/src/Format.cpp b/src/Format.cpp
index 6bbcc4b30..1d79b98c4 100644
--- a/src/Format.cpp
+++ b/src/Format.cpp
@@ -1,19 +1,15 @@
-#include "Format.h"
-
+#include
+#include
+#include
#include
-#include
#include
#include
-#include
-#include
-#include
-
+#include
+#include
+#include
+#include "Format.h"
#include "graphics/Graphics.h"
-#ifndef RENDERER
-# include "SDLCompat.h"
-#endif
-
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat)
{
struct tm * timeData;
@@ -99,30 +95,187 @@ String format::CleanString(String dirtyString, bool ascii, bool color, bool newl
return dirtyString;
}
-std::vector format::VideoBufferToPPM(const VideoBuffer & vidBuf)
+std::vector format::PixelsToPPM(PlaneAdapter> const &input)
{
std::vector data;
char buffer[256];
- sprintf(buffer, "P6\n%d %d\n255\n", vidBuf.Width, vidBuf.Height);
- data.insert(data.end(), buffer, buffer+strlen(buffer));
+ sprintf(buffer, "P6\n%d %d\n255\n", input.Size().X, input.Size().Y);
+ data.insert(data.end(), buffer, buffer + strlen(buffer));
- unsigned char * currentRow = new unsigned char[vidBuf.Width*3];
- for(int y = 0; y < vidBuf.Height; y++)
+ data.reserve(data.size() + input.Size().X * input.Size().Y * 3);
+
+ for (int i = 0; i < input.Size().X * input.Size().Y; i++)
{
- int rowPos = 0;
- for(int x = 0; x < vidBuf.Width; x++)
- {
- currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]);
- currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]);
- currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]);
- }
- data.insert(data.end(), currentRow, currentRow+(vidBuf.Width*3));
+ auto colour = RGB::Unpack(input.data()[i]);
+ data.push_back(colour.Red);
+ data.push_back(colour.Green);
+ data.push_back(colour.Blue);
}
- delete [] currentRow;
return data;
}
+static std::unique_ptr>> readPNG(
+ std::vector const &data,
+ // If omitted,
+ // RGB data is returned with A=0xFF
+ // RGBA data is returned as itself
+ // If specified
+ // RGB data is returned with A=0x00
+ // RGBA data is blended against the background and returned with A=0x00
+ std::optional> background
+)
+{
+ png_infop info = nullptr;
+ auto deleter = [&info](png_struct *png) {
+ png_destroy_read_struct(&png, &info, NULL);
+ };
+ auto png = std::unique_ptr(
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
+ [](png_structp png, png_const_charp msg) {
+ fprintf(stderr, "PNG error: %s\n", msg);
+ },
+ [](png_structp png, png_const_charp msg) {
+ fprintf(stderr, "PNG warning: %s\n", msg);
+ }
+ ), deleter
+ );
+ if (!png)
+ return nullptr;
+
+ // libpng might longjmp() here in case of error
+ // Every time we create an object with a non-trivial destructor we must call setjmp again
+ if (setjmp(png_jmpbuf(png.get())))
+ return nullptr;
+
+ info = png_create_info_struct(png.get());
+ if (!info)
+ return nullptr;
+
+ auto it = data.begin();
+ auto const end = data.end();
+ auto readFn = [&it, end](png_structp png, png_bytep data, size_t length) {
+ if (size_t(end - it) < length)
+ png_error(png, "Tried to read beyond the buffer");
+ std::copy_n(it, length, data);
+ it += length;
+ };
+
+ // See above
+ if (setjmp(png_jmpbuf(png.get())))
+ return nullptr;
+
+ png_set_read_fn(png.get(), static_cast(&readFn), [](png_structp png, png_bytep data, size_t length) {
+ (*static_cast(png_get_io_ptr(png)))(png, data, length);
+ });
+ png_set_user_limits(png.get(), RES.X, RES.Y); // Refuse to parse larger images
+ png_read_info(png.get(), info);
+
+ auto output = std::make_unique>>(
+ Vec2(png_get_image_width(png.get(), info), png_get_image_height(png.get(), info))
+ );
+
+ std::vector rowPointers(output->Size().Y);
+ for (int y = 0; y < output->Size().Y; y++)
+ rowPointers[y] = reinterpret_cast(&*output->RowIterator(Vec2(0, y)));
+
+ // See above
+ if (setjmp(png_jmpbuf(png.get())))
+ return nullptr;
+
+ png_set_filler(png.get(), background ? 0x00 : 0xFF, PNG_FILLER_AFTER);
+ png_set_bgr(png.get());
+
+ auto bitDepth = png_get_bit_depth(png.get(), info);
+ auto colorType = png_get_color_type(png.get(), info);
+ if (colorType == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png.get());
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
+ png_set_expand_gray_1_2_4_to_8(png.get());
+ if (bitDepth == 16)
+ png_set_scale_16(png.get());
+ if (png_get_valid(png.get(), info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png.get());
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png.get());
+ if (background)
+ {
+ png_color_16 colour;
+ colour.red = background->Red;
+ colour.green = background->Green;
+ colour.blue = background->Blue;
+ png_set_background(png.get(), &colour, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
+ }
+
+ png_read_image(png.get(), rowPointers.data());
+ return output;
+}
+
+std::unique_ptr>> format::PixelsFromPNG(std::vector const &data)
+{
+ return readPNG(data, std::nullopt);
+}
+
+std::unique_ptr>> format::PixelsFromPNG(std::vector const &data, RGB background)
+{
+ return readPNG(data, background);
+}
+
+std::unique_ptr> format::PixelsToPNG(PlaneAdapter> const &input)
+{
+ png_infop info = nullptr;
+ auto deleter = [&info](png_struct *png) {
+ png_destroy_write_struct(&png, &info);
+ };
+ auto png = std::unique_ptr(
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
+ [](png_structp png, png_const_charp msg) {
+ fprintf(stderr, "PNG error: %s\n", msg);
+ },
+ [](png_structp png, png_const_charp msg) {
+ fprintf(stderr, "PNG warning: %s\n", msg);
+ }
+ ), deleter
+ );
+ if (!png)
+ return nullptr;
+
+ // libpng might longjmp() here in case of error
+ // Every time we create an object with a non-trivial destructor we must call setjmp again
+ if (setjmp(png_jmpbuf(png.get())))
+ return nullptr;
+
+
+ info = png_create_info_struct(png.get());
+ if (!info)
+ return nullptr;
+
+ std::vector output;
+ auto writeFn = [&output](png_structp png, png_bytep data, size_t length) {
+ output.insert(output.end(), data, data + length);
+ };
+
+ std::vector rowPointers(input.Size().Y);
+ for (int y = 0; y < input.Size().Y; y++)
+ rowPointers[y] = reinterpret_cast(&*input.RowIterator(Vec2(0, y)));
+
+ // See above
+ if (setjmp(png_jmpbuf(png.get())))
+ return nullptr;
+
+ png_set_write_fn(png.get(), static_cast(&writeFn), [](png_structp png, png_bytep data, size_t length) {
+ (*static_cast(png_get_io_ptr(png)))(png, data, length);
+ }, NULL);
+ png_set_IHDR(png.get(), info, input.Size().X, input.Size().Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png.get(), info);
+ png_set_filler(png.get(), 0x00, PNG_FILLER_AFTER);
+ png_set_bgr(png.get());
+ png_write_image(png.get(), const_cast(rowPointers.data()));
+ png_write_end(png.get(), NULL);
+
+ return std::make_unique>(std::move(output));
+}
+
const static char hex[] = "0123456789ABCDEF";
ByteString format::URLEncode(ByteString source)
diff --git a/src/Format.h b/src/Format.h
index e395a673c..87c2aefab 100644
--- a/src/Format.h
+++ b/src/Format.h
@@ -1,8 +1,9 @@
#pragma once
-#include "Config.h"
-
-#include "common/String.h"
+#include
#include
+#include "common/String.h"
+#include "common/Plane.h"
+#include "graphics/Pixel.h"
class VideoBuffer;
@@ -13,7 +14,10 @@ namespace format
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"));
ByteString UnixtimeToDateMini(time_t unixtime);
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
- std::vector VideoBufferToPPM(const VideoBuffer & vidBuf);
+ std::vector PixelsToPPM(PlaneAdapter> const &);
+ std::unique_ptr> PixelsToPNG(PlaneAdapter> const &);
+ std::unique_ptr>> PixelsFromPNG(std::vector const &);
+ std::unique_ptr>> PixelsFromPNG(std::vector const &, RGB background);
void RenderTemperature(StringBuilder &sb, float temp, int scale);
float StringToTemperature(String str, int defaultScale);
}
diff --git a/src/Misc.cpp b/src/Misc.cpp
index 5b4c86212..668f94bbc 100644
--- a/src/Misc.cpp
+++ b/src/Misc.cpp
@@ -1,140 +1,10 @@
#include "Misc.h"
-
-#include "Config.h"
-
+#include "common/tpt-minmax.h"
+#include "common/String.h"
#include
#include
#include
-#include "common/tpt-minmax.h"
-#include "common/String.h"
-
-const static char hex[] = "0123456789ABCDEF";
-void strcaturl(char *dst, char *src)
-{
- char *d;
- unsigned char *s;
-
- for (d=dst; *d; d++) ;
-
- for (s=(unsigned char *)src; *s; s++)
- {
- if ((*s>='0' && *s<='9') ||
- (*s>='a' && *s<='z') ||
- (*s>='A' && *s<='Z'))
- *(d++) = *s;
- else
- {
- *(d++) = '%';
- *(d++) = hex[*s>>4];
- *(d++) = hex[*s&15];
- }
- }
- *d = 0;
-}
-
-void strappend(char *dst, const char *src)
-{
- char *d;
- unsigned char *s;
-
- for (d=dst; *d; d++) ;
-
- for (s=(unsigned char *)src; *s; s++)
- {
- *(d++) = *s;
- }
- *d = 0;
-}
-
-void *file_load(char *fn, int *size)
-{
- FILE *f = fopen(fn, "rb");
- void *s;
-
- if (!f)
- return NULL;
- fseek(f, 0, SEEK_END);
- *size = ftell(f);
- fseek(f, 0, SEEK_SET);
- s = malloc(*size);
- if (!s)
- {
- fclose(f);
- return NULL;
- }
- int readsize = fread(s, *size, 1, f);
- fclose(f);
- if (readsize != 1)
- {
- free(s);
- return NULL;
- }
- return s;
-}
-
-matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2)
-{
- matrix2d result = {
- m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d,
- m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d
- };
- return result;
-}
-vector2d m2d_multiply_v2d(matrix2d m, vector2d v)
-{
- vector2d result = {
- m.a*v.x+m.b*v.y,
- m.c*v.x+m.d*v.y
- };
- return result;
-}
-matrix2d m2d_multiply_float(matrix2d m, float s)
-{
- matrix2d result = {
- m.a*s, m.b*s,
- m.c*s, m.d*s,
- };
- return result;
-}
-
-vector2d v2d_multiply_float(vector2d v, float s)
-{
- vector2d result = {
- v.x*s,
- v.y*s
- };
- return result;
-}
-
-vector2d v2d_add(vector2d v1, vector2d v2)
-{
- vector2d result = {
- v1.x+v2.x,
- v1.y+v2.y
- };
- return result;
-}
-vector2d v2d_sub(vector2d v1, vector2d v2)
-{
- vector2d result = {
- v1.x-v2.x,
- v1.y-v2.y
- };
- return result;
-}
-
-matrix2d m2d_new(float me0, float me1, float me2, float me3)
-{
- matrix2d result = {me0,me1,me2,me3};
- return result;
-}
-vector2d v2d_new(float x, float y)
-{
- vector2d result = {x, y};
- return result;
-}
-
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB
{
float hh, ss, vv, c, x;
@@ -216,9 +86,6 @@ void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize)
}
}
-vector2d v2d_zero = {0,0};
-matrix2d m2d_identity = {1,0,0,1};
-
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size)
{
return str.size() == size && !memcmp(str.data(), data, size);
diff --git a/src/Misc.h b/src/Misc.h
index 02a7ed589..5faa307fb 100644
--- a/src/Misc.h
+++ b/src/Misc.h
@@ -1,12 +1,29 @@
-#ifndef UTILS_H
-#define UTILS_H
-#include "Config.h"
+#pragma once
#include
#include
#include
#include
#include
+template
+inline std::pair floorDiv(Signed a, Signed b)
+{
+ auto quo = a / b;
+ auto rem = a % b;
+ if (a < Signed(0) && rem)
+ {
+ quo -= Signed(1);
+ rem += b;
+ }
+ return { quo, rem };
+}
+
+template
+inline std::pair ceilDiv(Signed a, Signed b)
+{
+ return floorDiv(a + b - Signed(1), b);
+}
+
//Linear interpolation
template inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord)
{
@@ -55,52 +72,10 @@ inline float restrict_flt(float f, float min, float max)
return f;
}
-void save_presets(int do_update);
-
-void load_presets(void);
-
-void strcaturl(char *dst, const char *src);
-
-void strappend(char *dst, const char *src);
-
-void *file_load(char *fn, int *size);
-
-extern char *clipboard_text;
-
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b);
-
void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
-
void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
-// a b
-// c d
-
-struct matrix2d {
- float a,b,c,d;
-};
-typedef struct matrix2d matrix2d;
-
-// column vector
-struct vector2d {
- float x,y;
-};
-typedef struct vector2d vector2d;
-
-matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
-vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
-matrix2d m2d_multiply_float(matrix2d m, float s);
-vector2d v2d_multiply_float(vector2d v, float s);
-
-vector2d v2d_add(vector2d v1, vector2d v2);
-vector2d v2d_sub(vector2d v1, vector2d v2);
-
-matrix2d m2d_new(float me0, float me1, float me2, float me3);
-vector2d v2d_new(float x, float y);
-
-extern vector2d v2d_zero;
-extern matrix2d m2d_identity;
-
class ByteString;
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);
@@ -110,5 +85,3 @@ bool byteStringEqualsLiteral(const ByteString &str, const char (&lit)[N])
{
return byteStringEqualsString(str, lit, N - 1U);
}
-
-#endif
diff --git a/src/PowderToy.cpp b/src/PowderToy.cpp
new file mode 100644
index 000000000..897370a0a
--- /dev/null
+++ b/src/PowderToy.cpp
@@ -0,0 +1,534 @@
+#include "PowderToySDL.h"
+#include "Format.h"
+#include "X86KillDenormals.h"
+#include "prefs/GlobalPrefs.h"
+#include "client/Client.h"
+#include "client/GameSave.h"
+#include "client/SaveFile.h"
+#include "client/SaveInfo.h"
+#include "client/http/requestmanager/RequestManager.h"
+#include "client/http/GetSaveRequest.h"
+#include "client/http/GetSaveDataRequest.h"
+#include "common/platform/Platform.h"
+#include "graphics/Graphics.h"
+#include "simulation/SaveRenderer.h"
+#include "common/tpt-rand.h"
+#include "gui/game/Favorite.h"
+#include "gui/Style.h"
+#include "gui/game/GameController.h"
+#include "gui/game/GameView.h"
+#include "gui/dialogues/ConfirmPrompt.h"
+#include "gui/dialogues/ErrorMessage.h"
+#include "gui/interface/Engine.h"
+#include "Config.h"
+#include "SimulationConfig.h"
+#include
+#include
+#include
+#include
+#include
+
+void LoadWindowPosition()
+{
+ if (Client::Ref().IsFirstRun())
+ {
+ return;
+ }
+
+ auto &prefs = GlobalPrefs::Ref();
+ int savedWindowX = prefs.Get("WindowX", INT_MAX);
+ int savedWindowY = prefs.Get("WindowY", INT_MAX);
+
+ int borderTop, borderLeft;
+ SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
+ // Sometimes (Windows), the border size may not be reported for 200+ frames
+ // So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved
+ if (borderTop == 0)
+ borderTop = 5;
+
+ int numDisplays = SDL_GetNumVideoDisplays();
+ SDL_Rect displayBounds;
+ bool ok = false;
+ for (int i = 0; i < numDisplays; i++)
+ {
+ SDL_GetDisplayBounds(i, &displayBounds);
+ if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y &&
+ savedWindowX + borderTop < displayBounds.x + displayBounds.w &&
+ savedWindowY + borderLeft < displayBounds.y + displayBounds.h)
+ {
+ ok = true;
+ break;
+ }
+ }
+ if (ok)
+ SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop);
+}
+
+void SaveWindowPosition()
+{
+ int x, y;
+ SDL_GetWindowPosition(sdl_window, &x, &y);
+
+ int borderTop, borderLeft;
+ SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
+
+ auto &prefs = GlobalPrefs::Ref();
+ prefs.Set("WindowX", x - borderLeft);
+ prefs.Set("WindowY", y - borderTop);
+}
+
+void LargeScreenDialog()
+{
+ StringBuilder message;
+ message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: ";
+ message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required";
+ message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
+ if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
+ {
+ GlobalPrefs::Ref().Set("Scale", 1);
+ ui::Engine::Ref().SetScale(1);
+ }
+}
+
+void TickClient()
+{
+ Client::Ref().Tick();
+}
+
+void BlueScreen(String detailMessage)
+{
+ auto &engine = ui::Engine::Ref();
+ engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
+
+ String errorTitle = "ERROR";
+ String errorDetails = "Details: " + detailMessage;
+ String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
+
+ // We use the width of errorHelp to center, but heights of the individual texts for vertical spacing
+ auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100);
+ engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF));
+ pos.Y += 4 + Graphics::TextSize(errorTitle).Y;
+ engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF));
+ pos.Y += 4 + Graphics::TextSize(errorDetails).Y;
+ engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF));
+
+ //Death loop
+ SDL_Event event;
+ while(true)
+ {
+ while (SDL_PollEvent(&event))
+ if(event.type == SDL_QUIT)
+ exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
+ blit(engine.g->Data());
+ }
+}
+
+struct
+{
+ int sig;
+ const char *message;
+} signalMessages[] = {
+ { SIGSEGV, "Memory read/write error" },
+ { SIGFPE, "Floating point exception" },
+ { SIGILL, "Program execution exception" },
+ { SIGABRT, "Unexpected program abort" },
+ { 0, nullptr },
+};
+
+void SigHandler(int signal)
+{
+ const char *message = "Unknown signal";
+ for (auto *msg = signalMessages; msg->message; ++msg)
+ {
+ if (msg->sig == signal)
+ {
+ message = msg->message;
+ break;
+ }
+ }
+ BlueScreen(ByteString(message).FromUtf8());
+}
+
+constexpr int SCALE_MAXIMUM = 10;
+constexpr int SCALE_MARGIN = 30;
+
+int GuessBestScale()
+{
+ const int widthNoMargin = desktopWidth - SCALE_MARGIN;
+ const int widthGuess = widthNoMargin / WINDOWW;
+
+ const int heightNoMargin = desktopHeight - SCALE_MARGIN;
+ const int heightGuess = heightNoMargin / WINDOWH;
+
+ int guess = std::min(widthGuess, heightGuess);
+ if(guess < 1 || guess > SCALE_MAXIMUM)
+ guess = 1;
+
+ return guess;
+}
+
+struct ExplicitSingletons
+{
+ // These need to be listed in the order they are populated in main.
+ std::unique_ptr globalPrefs;
+ http::RequestManagerPtr requestManager;
+ std::unique_ptr client;
+ std::unique_ptr saveRenderer;
+ std::unique_ptr favorite;
+ std::unique_ptr engine;
+ std::unique_ptr gameController;
+};
+static std::unique_ptr explicitSingletons;
+
+int main(int argc, char * argv[])
+{
+ Platform::SetupCrt();
+ Platform::Atexit([]() {
+ // Unregister dodgy error handlers so they don't try to show the blue screen when the window is closed
+ for (auto *msg = signalMessages; msg->message; ++msg)
+ {
+ signal(msg->sig, SIG_DFL);
+ }
+ SDLClose();
+ explicitSingletons.reset();
+ });
+ explicitSingletons = std::make_unique();
+
+
+ // https://bugzilla.libsdl.org/show_bug.cgi?id=3796
+ if (SDL_Init(0) < 0)
+ {
+ fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ Platform::originalCwd = Platform::GetCwd();
+
+ using Argument = std::optional;
+ std::map arguments;
+
+ for (auto i = 1; i < argc; ++i)
+ {
+ auto str = ByteString(argv[i]);
+ if (str.BeginsWith("file://"))
+ {
+ arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) });
+ }
+ else if (str.BeginsWith("ptsave:"))
+ {
+ arguments.insert({ "ptsave", str });
+ }
+ else if (auto split = str.SplitBy(':'))
+ {
+ arguments.insert({ split.Before(), split.After() });
+ }
+ else if (auto split = str.SplitBy('='))
+ {
+ arguments.insert({ split.Before(), split.After() });
+ }
+ else if (str == "open" || str == "ptsave" || str == "ddir")
+ {
+ if (i + 1 < argc)
+ {
+ arguments.insert({ str, argv[i + 1] });
+ i += 1;
+ }
+ else
+ {
+ std::cerr << "no value provided for command line parameter " << str << std::endl;
+ }
+ }
+ else
+ {
+ arguments.insert({ str, "" }); // so .has_value() is true
+ }
+ }
+
+ auto ddirArg = arguments["ddir"];
+ if (ddirArg.has_value())
+ {
+ if (Platform::ChangeDir(ddirArg.value()))
+ Platform::sharedCwd = Platform::GetCwd();
+ else
+ perror("failed to chdir to requested ddir");
+ }
+ else
+ {
+ auto ddir = std::unique_ptr(SDL_GetPrefPath(NULL, APPDATA), SDL_free);
+ if (!Platform::FileExists("powder.pref"))
+ {
+ if (ddir)
+ {
+ if (!Platform::ChangeDir(ddir.get()))
+ {
+ perror("failed to chdir to default ddir");
+ ddir.reset();
+ }
+ }
+ }
+
+ if (ddir)
+ {
+ Platform::sharedCwd = ddir.get();
+ }
+ }
+ // We're now in the correct directory, time to get prefs.
+ explicitSingletons->globalPrefs = std::make_unique();
+
+ auto &prefs = GlobalPrefs::Ref();
+ scale = prefs.Get("Scale", 1);
+ auto graveExitsConsole = prefs.Get("GraveExitsConsole", true);
+ resizable = prefs.Get("Resizable", false);
+ fullscreen = prefs.Get("Fullscreen", false);
+ altFullscreen = prefs.Get("AltFullscreen", false);
+ forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
+ momentumScroll = prefs.Get("MomentumScroll", true);
+ showAvatars = prefs.Get("ShowAvatars", true);
+
+ auto true_string = [](ByteString str) {
+ str = str.ToLower();
+ return str == "true" ||
+ str == "t" ||
+ str == "on" ||
+ str == "yes" ||
+ str == "y" ||
+ str == "1" ||
+ str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments
+ };
+ auto true_arg = [&true_string](Argument arg) {
+ return arg.has_value() && true_string(arg.value());
+ };
+
+ auto kioskArg = arguments["kiosk"];
+ if (kioskArg.has_value())
+ {
+ fullscreen = true_string(kioskArg.value());
+ prefs.Set("Fullscreen", fullscreen);
+ }
+
+ if (true_arg(arguments["redirect"]))
+ {
+ FILE *new_stdout = freopen("stdout.log", "w", stdout);
+ FILE *new_stderr = freopen("stderr.log", "w", stderr);
+ if (!new_stdout || !new_stderr)
+ {
+ Platform::Exit(42);
+ }
+ }
+
+ auto scaleArg = arguments["scale"];
+ if (scaleArg.has_value())
+ {
+ try
+ {
+ scale = scaleArg.value().ToNumber();
+ prefs.Set("Scale", scale);
+ }
+ catch (const std::runtime_error &e)
+ {
+ std::cerr << "failed to set scale: " << e.what() << std::endl;
+ }
+ }
+
+ auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) {
+ ByteString value;
+ if (arg.has_value())
+ {
+ value = arg.value();
+ if (value == "")
+ {
+ value = defaultValue;
+ }
+ prefs.Set(name, value);
+ }
+ else
+ {
+ value = prefs.Get(name, defaultValue);
+ }
+ return value;
+ };
+ ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
+ ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
+ ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
+ bool disableNetwork = true_arg(arguments["disable-network"]);
+ explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
+
+ explicitSingletons->client = std::make_unique();
+ Client::Ref().Initialize();
+
+ explicitSingletons->saveRenderer = std::make_unique();
+ explicitSingletons->favorite = std::make_unique();
+ explicitSingletons->engine = std::make_unique();
+
+ // TODO: maybe bind the maximum allowed scale to screen size somehow
+ if(scale < 1 || scale > SCALE_MAXIMUM)
+ scale = 1;
+
+ SDLOpen();
+
+ if (Client::Ref().IsFirstRun())
+ {
+ scale = GuessBestScale();
+ if (scale > 1)
+ {
+ prefs.Set("Scale", scale);
+ SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
+ showLargeScreenDialog = true;
+ }
+ }
+
+ StopTextInput();
+
+ auto &engine = ui::Engine::Ref();
+ engine.g = new Graphics();
+ engine.Scale = scale;
+ engine.GraveExitsConsole = graveExitsConsole;
+ engine.SetResizable(resizable);
+ engine.Fullscreen = fullscreen;
+ engine.SetAltFullscreen(altFullscreen);
+ engine.SetForceIntegerScaling(forceIntegerScaling);
+ engine.MomentumScroll = momentumScroll;
+ engine.ShowAvatars = showAvatars;
+ engine.Begin();
+ engine.SetFastQuit(prefs.Get("FastQuit", true));
+
+ bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]);
+ if (enableBluescreen)
+ {
+ //Get ready to catch any dodgy errors
+ for (auto *msg = signalMessages; msg->message; ++msg)
+ {
+ signal(msg->sig, SigHandler);
+ }
+ }
+
+ if constexpr (X86)
+ {
+ X86KillDenormals();
+ }
+
+ auto wrapWithBluescreen = [&]() {
+ explicitSingletons->gameController = std::make_unique();
+ auto *gameController = explicitSingletons->gameController.get();
+ engine.ShowWindow(gameController->GetView());
+
+ auto openArg = arguments["open"];
+ if (openArg.has_value())
+ {
+ if constexpr (DEBUG)
+ {
+ std::cout << "Loading " << openArg.value() << std::endl;
+ }
+ if (Platform::FileExists(openArg.value()))
+ {
+ try
+ {
+ std::vector gameSaveData;
+ if (!Platform::ReadFile(gameSaveData, openArg.value()))
+ {
+ new ErrorMessage("Error", "Could not read file");
+ }
+ else
+ {
+ auto newFile = std::make_unique(openArg.value());
+ auto newSave = std::make_unique(std::move(gameSaveData));
+ newFile->SetGameSave(std::move(newSave));
+ gameController->LoadSaveFile(std::move(newFile));
+ }
+
+ }
+ catch (std::exception & e)
+ {
+ new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
+ }
+ }
+ else
+ {
+ new ErrorMessage("Error", "Could not open file");
+ }
+ }
+
+ auto ptsaveArg = arguments["ptsave"];
+ if (ptsaveArg.has_value())
+ {
+ engine.g->Clear();
+ engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
+ String loadingText = "Loading save...";
+ engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
+
+ blit(engine.g->Data());
+ try
+ {
+ ByteString saveIdPart;
+ if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
+ {
+ if (split.Before() != "ptsave")
+ throw std::runtime_error("Not a ptsave link");
+ saveIdPart = split.After().SplitBy('#').Before();
+ }
+ else
+ throw std::runtime_error("Invalid save link");
+
+ if (!saveIdPart.size())
+ throw std::runtime_error("No Save ID");
+ if constexpr (DEBUG)
+ {
+ std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
+ }
+ int saveId = saveIdPart.ToNumber();
+
+ auto getSave = std::make_unique(saveId, 0);
+ getSave->Start();
+ getSave->Wait();
+ std::unique_ptr newSave;
+ try
+ {
+ newSave = getSave->Finish();
+ }
+ catch (const http::RequestError &ex)
+ {
+ throw std::runtime_error("Could not load save info\n" + ByteString(ex.what()));
+ }
+ auto getSaveData = std::make_unique(saveId, 0);
+ getSaveData->Start();
+ getSaveData->Wait();
+ std::unique_ptr saveData;
+ try
+ {
+ saveData = std::make_unique(getSaveData->Finish());
+ }
+ catch (const http::RequestError &ex)
+ {
+ throw std::runtime_error("Could not load save\n" + ByteString(ex.what()));
+ }
+ newSave->SetGameSave(std::move(saveData));
+ gameController->LoadSave(std::move(newSave));
+ }
+ catch (std::exception & e)
+ {
+ new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
+ }
+ }
+
+ EngineProcess();
+ SaveWindowPosition();
+ };
+
+ if (enableBluescreen)
+ {
+ try
+ {
+ wrapWithBluescreen();
+ }
+ catch (const std::exception &e)
+ {
+ BlueScreen(ByteString(e.what()).FromUtf8());
+ }
+ }
+ else
+ {
+ wrapWithBluescreen();
+ }
+ Platform::Exit(0);
+ return 0;
+}
diff --git a/src/PowderToy.h b/src/PowderToy.h
deleted file mode 100644
index f59e8f58b..000000000
--- a/src/PowderToy.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include "common/String.h"
-
-void EngineProcess();
-void ClipboardPush(ByteString text);
-ByteString ClipboardPull();
-int GetModifiers();
-unsigned int GetTicks();
-void StartTextInput();
-void StopTextInput();
-void SetTextInputRect(int x, int y, int w, int h);
diff --git a/src/PowderToyFontEditor.cpp b/src/PowderToyFontEditor.cpp
index cb7a53e83..a4f140805 100644
--- a/src/PowderToyFontEditor.cpp
+++ b/src/PowderToyFontEditor.cpp
@@ -1,442 +1,45 @@
-#include "Config.h"
-#include
-#include
-#ifdef WIN
-#include
-#endif
-#include "SDLCompat.h"
-
-#ifdef X86_SSE
-#include
-#endif
-#ifdef X86_SSE3
-#include
-#endif
-
-#include
-#if defined(LIN)
-# include "icon_exe.png.h"
-#endif
-#include
-
-#ifndef WIN
-#include
-#endif
-#ifdef MACOSX
-# include "common/macosx.h"
-#endif
-
-#include "Format.h"
-#include "Misc.h"
-
+#include "PowderToySDL.h"
#include "graphics/Graphics.h"
-
-#include "client/SaveInfo.h"
-#include "client/GameSave.h"
-#include "client/SaveFile.h"
-
-#include "gui/game/GameController.h"
-#include "gui/game/GameView.h"
+#include "common/platform/Platform.h"
+#include "common/tpt-rand.h"
#include "gui/font/FontEditor.h"
-#include "gui/dialogues/ErrorMessage.h"
-#include "gui/dialogues/ConfirmPrompt.h"
-#include "gui/interface/Keys.h"
-#include "gui/Style.h"
#include "gui/interface/Engine.h"
+#include "Config.h"
+#include "SimulationConfig.h"
+#include
+#include
-#include "SDLCompat.h"
-
-int desktopWidth = 1280, desktopHeight = 1024;
-
-SDL_Window * sdl_window;
-SDL_Renderer * sdl_renderer;
-SDL_Texture * sdl_texture;
-int scale = 1;
-bool fullscreen = false;
-bool altFullscreen = false;
-bool forceIntegerScaling = true;
-bool resizable = false;
-
-
-void StartTextInput()
+void LoadWindowPosition()
{
- SDL_StartTextInput();
}
-void StopTextInput()
+void SaveWindowPosition()
{
- SDL_StopTextInput();
}
-void SetTextInputRect(int x, int y, int w, int h)
+void LargeScreenDialog()
{
- SDL_Rect rect;
- rect.x = x;
- rect.y = y;
- rect.w = w;
- rect.h = h;
- SDL_SetTextInputRect(&rect);
}
-void ClipboardPush(ByteString text)
+void TickClient()
{
- SDL_SetClipboardText(text.c_str());
}
-ByteString ClipboardPull()
+struct ExplicitSingletons
{
- return ByteString(SDL_GetClipboardText());
-}
-
-int GetModifiers()
-{
- return SDL_GetModState();
-}
-
-void CalculateMousePosition(int *x, int *y)
-{
- int globalMx, globalMy;
- SDL_GetGlobalMouseState(&globalMx, &globalMy);
- int windowX, windowY;
- SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
-
- if (x)
- *x = (globalMx - windowX) / scale;
- if (y)
- *y = (globalMy - windowY) / scale;
-}
-
-void blit(pixel * vid)
-{
- SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
- // need to clear the renderer if there are black edges (fullscreen, or resizable window)
- if (fullscreen || resizable)
- SDL_RenderClear(sdl_renderer);
- SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
- SDL_RenderPresent(sdl_renderer);
-}
-
-void RecreateWindow();
-int SDLOpen()
-{
- if (SDL_Init(SDL_INIT_VIDEO) < 0)
- {
- fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
- return 1;
- }
-
- RecreateWindow();
-
- int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
- if (displayIndex >= 0)
- {
- SDL_Rect rect;
- if (!SDL_GetDisplayUsableBounds(displayIndex, &rect))
- {
- desktopWidth = rect.w;
- desktopHeight = rect.h;
- }
- }
-
-#ifdef LIN
- std::vector imageData;
- int imgw, imgh;
- if (PngDataToPixels(imageData, imgw, imgh, reinterpret_cast(icon_exe_png), icon_exe_png_size, false))
- {
- SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(&imageData[0], imgw, imgh, 32, imgw * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
- SDL_SetWindowIcon(sdl_window, icon);
- SDL_FreeSurface(icon);
- }
-#endif
-
- return 0;
-}
-
-void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_)
-{
-// bool changingScale = scale != scale_;
- bool changingFullscreen = fullscreen_ != fullscreen || (altFullscreen_ != altFullscreen && fullscreen);
- bool changingResizable = resizable != resizable_;
- scale = scale_;
- fullscreen = fullscreen_;
- altFullscreen = altFullscreen_;
- resizable = resizable_;
- forceIntegerScaling = forceIntegerScaling_;
- // Recreate the window when toggling fullscreen, due to occasional issues
- // Also recreate it when enabling resizable windows, to fix bugs on windows,
- // see https://github.com/jacob1/The-Powder-Toy/issues/24
- if (changingFullscreen || (changingResizable && resizable && !fullscreen))
- {
- RecreateWindow();
- return;
- }
- if (changingResizable)
- SDL_RestoreWindow(sdl_window);
-
- SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
- SDL_RenderSetIntegerScale(sdl_renderer, forceIntegerScaling && fullscreen ? SDL_TRUE : SDL_FALSE);
- unsigned int flags = 0;
- if (fullscreen)
- flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
- SDL_SetWindowFullscreen(sdl_window, flags);
- if (fullscreen)
- SDL_RaiseWindow(sdl_window);
- SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE);
-}
-
-void RecreateWindow()
-{
- unsigned int flags = 0;
- if (fullscreen)
- flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
- if (resizable && !fullscreen)
- flags |= SDL_WINDOW_RESIZABLE;
-
- if (sdl_texture)
- SDL_DestroyTexture(sdl_texture);
- if (sdl_renderer)
- SDL_DestroyRenderer(sdl_renderer);
- if (sdl_window)
- {
- SDL_DestroyWindow(sdl_window);
- }
-
- sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOWW * scale, WINDOWH * scale,
- flags);
- sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
- SDL_RenderSetLogicalSize(sdl_renderer, WINDOWW, WINDOWH);
- if (forceIntegerScaling && fullscreen)
- SDL_RenderSetIntegerScale(sdl_renderer, SDL_TRUE);
- sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WINDOWW, WINDOWH);
- SDL_RaiseWindow(sdl_window);
- SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
- //Uncomment this to enable resizing
- //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
- //SDL_SetWindowResizable(sdl_window, SDL_TRUE);
-}
-
-unsigned int GetTicks()
-{
- return SDL_GetTicks();
-}
-
-int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
-unsigned int lastTick = 0;
-unsigned int lastFpsUpdate = 0;
-float fps = 0;
-ui::Engine * engine = NULL;
-bool showDoubleScreenDialog = false;
-float currentWidth, currentHeight;
-
-int mousex = 0, mousey = 0;
-int mouseButton = 0;
-bool mouseDown = false;
-
-bool calculatedInitialMouse = false, delay = false;
-bool hasMouseMoved = false;
-
-void EventProcess(SDL_Event event)
-{
- switch (event.type)
- {
- case SDL_QUIT:
- if (engine->GetFastQuit() || engine->CloseWindow())
- engine->Exit();
- break;
- case SDL_KEYDOWN:
- if (SDL_GetModState() & KMOD_GUI)
- {
- break;
- }
- if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL))
- engine->ConfirmExit();
- else
- engine->onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
- break;
- case SDL_KEYUP:
- if (SDL_GetModState() & KMOD_GUI)
- {
- break;
- }
- engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
- break;
- case SDL_TEXTINPUT:
- if (SDL_GetModState() & KMOD_GUI)
- {
- break;
- }
- engine->onTextInput(ByteString(event.text.text).FromUtf8());
- break;
- case SDL_TEXTEDITING:
- if (SDL_GetModState() & KMOD_GUI)
- {
- break;
- }
- engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start);
- break;
- case SDL_MOUSEWHEEL:
- {
- // int x = event.wheel.x;
- int y = event.wheel.y;
- if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
- {
- // x *= -1;
- y *= -1;
- }
-
- engine->onMouseWheel(mousex, mousey, y); // TODO: pass x?
- break;
- }
- case SDL_MOUSEMOTION:
- mousex = event.motion.x;
- mousey = event.motion.y;
- engine->onMouseMove(mousex, mousey);
-
- hasMouseMoved = true;
- break;
- case SDL_DROPFILE:
- engine->onFileDrop(event.drop.file);
- SDL_free(event.drop.file);
- break;
- case SDL_MOUSEBUTTONDOWN:
- // if mouse hasn't moved yet, sdl will send 0,0. We don't want that
- if (hasMouseMoved)
- {
- mousex = event.motion.x;
- mousey = event.motion.y;
- }
- mouseButton = event.button.button;
- engine->onMouseClick(event.motion.x, event.motion.y, mouseButton);
-
- mouseDown = true;
-#if !defined(NDEBUG) && !defined(DEBUG)
- SDL_CaptureMouse(SDL_TRUE);
-#endif
- break;
- case SDL_MOUSEBUTTONUP:
- // if mouse hasn't moved yet, sdl will send 0,0. We don't want that
- if (hasMouseMoved)
- {
- mousex = event.motion.x;
- mousey = event.motion.y;
- }
- mouseButton = event.button.button;
- engine->onMouseUnclick(mousex, mousey, mouseButton);
-
- mouseDown = false;
-#if !defined(NDEBUG) && !defined(DEBUG)
- SDL_CaptureMouse(SDL_FALSE);
-#endif
- break;
- case SDL_WINDOWEVENT:
- {
- switch (event.window.event)
- {
- case SDL_WINDOWEVENT_SHOWN:
- if (!calculatedInitialMouse)
- {
- //initial mouse coords, sdl won't tell us this if mouse hasn't moved
- CalculateMousePosition(&mousex, &mousey);
- engine->onMouseMove(mousex, mousey);
- calculatedInitialMouse = true;
- }
- break;
- // This event would be needed in certain glitchy cases of window resizing
- // But for all currently tested cases, it isn't needed
- /*case SDL_WINDOWEVENT_RESIZED:
- {
- float width = event.window.data1;
- float height = event.window.data2;
-
- currentWidth = width;
- currentHeight = height;
- // this "* scale" thing doesn't really work properly
- // currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active
- inputScaleH = (float)WINDOWW * scale / currentWidth;
- inputScaleV = (float)WINDOWH * scale / currentHeight;
- std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl;
- break;
- }*/
- // This would send a mouse up event when focus is lost
- // Not even sdl itself will know when the mouse was released if it happens in another window
- // So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all
- // This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out
- /*case SDL_WINDOWEVENT_FOCUS_LOST:
- if (mouseDown)
- {
- mouseDown = false;
- engine->onMouseUnclick(mousex, mousey, mouseButton);
- }
- break;*/
- }
- break;
- }
- }
-}
-
-void EngineProcess()
-{
- double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f;
- SDL_Event event;
- while(engine->Running())
- {
- int frameStart = SDL_GetTicks();
- if(engine->Broken()) { engine->UnBreak(); break; }
- event.type = 0;
- while (SDL_PollEvent(&event))
- {
- EventProcess(event);
- event.type = 0; //Clear last event
- }
- if(engine->Broken()) { engine->UnBreak(); break; }
-
- engine->Tick();
- engine->Draw();
-
- if (scale != engine->Scale || fullscreen != engine->Fullscreen ||
- altFullscreen != engine->GetAltFullscreen() ||
- forceIntegerScaling != engine->GetForceIntegerScaling() || resizable != engine->GetResizable())
- {
- SDLSetScreen(engine->Scale, engine->GetResizable(), engine->Fullscreen, engine->GetAltFullscreen(),
- engine->GetForceIntegerScaling());
- }
-
- blit(engine->g->vid);
-
- int frameTime = SDL_GetTicks() - frameStart;
- frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2;
- float fpsLimit = ui::Engine::Ref().FpsLimit;
- if(fpsLimit > 2)
- {
- double offset = 1000.0 / fpsLimit - frameTimeAvg;
- if(offset > 0)
- SDL_Delay(Uint32(offset + 0.5));
- }
- int correctedFrameTime = SDL_GetTicks() - frameStart;
- correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05;
- if (frameStart - lastFpsUpdate > 200)
- {
- engine->SetFps(1000.0 / correctedFrameTimeAvg);
- lastFpsUpdate = frameStart;
- }
- if (frameStart - lastTick > 100)
- {
- lastTick = frameStart;
- }
- if (showDoubleScreenDialog)
- {
- showDoubleScreenDialog = false;
- }
- }
-#ifdef DEBUG
- std::cout << "Breaking out of EngineProcess" << std::endl;
-#endif
-}
+ // These need to be listed in the order they are populated in main.
+ std::unique_ptr engine;
+};
+static std::unique_ptr explicitSingletons;
int main(int argc, char * argv[])
{
- currentWidth = WINDOWW;
- currentHeight = WINDOWH;
+ Platform::SetupCrt();
+ Platform::Atexit([]() {
+ SDLClose();
+ explicitSingletons.reset();
+ });
+ explicitSingletons = std::make_unique();
scale = 1;
if (argc >= 3)
@@ -457,6 +60,8 @@ int main(int argc, char * argv[])
if(scale < 1 || scale > 10)
scale = 1;
+ explicitSingletons->engine = std::make_unique();
+
SDLOpen();
StopTextInput();
@@ -468,38 +73,21 @@ int main(int argc, char * argv[])
ui::Engine::Ref().SetAltFullscreen(altFullscreen);
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
- engine = &ui::Engine::Ref();
- engine->SetMaxSize(desktopWidth, desktopHeight);
- engine->Begin(WINDOWW, WINDOWH);
- engine->SetFastQuit(true);
-
- GameController * gameController = NULL;
+ auto &engine = ui::Engine::Ref();
+ engine.Begin();
+ engine.SetFastQuit(true);
if (argc >= 2)
{
- engine->ShowWindow(new FontEditor(argv[1]));
+ engine.ShowWindow(new FontEditor(argv[1]));
}
else
{
std::cerr << "path to font.cpp not supplied" << std::endl;
- exit(1);
+ Platform::Exit(1);
}
EngineProcess();
- ui::Engine::Ref().CloseWindow();
- delete gameController;
- delete ui::Engine::Ref().g;
- if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
- {
- // * nvidia-460 egl registers callbacks with x11 that end up being called
- // after egl is unloaded unless we grab it here and release it after
- // sdl closes the display. this is an nvidia driver weirdness but
- // technically an sdl bug. glfw has this fixed:
- // https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
- SDL_GL_LoadLibrary(NULL);
- SDL_QuitSubSystem(SDL_INIT_VIDEO);
- SDL_GL_UnloadLibrary();
- }
- SDL_Quit();
+ Platform::Exit(0);
return 0;
}
diff --git a/src/PowderToyRenderer.cpp b/src/PowderToyRenderer.cpp
index 68c4d03f5..78a3d91f1 100644
--- a/src/PowderToyRenderer.cpp
+++ b/src/PowderToyRenderer.cpp
@@ -1,42 +1,17 @@
-#include "Config.h"
#include "graphics/Graphics.h"
#include "graphics/Renderer.h"
-
+#include "common/String.h"
+#include "common/tpt-rand.h"
+#include "Format.h"
+#include "gui/interface/Engine.h"
+#include "client/GameSave.h"
+#include "simulation/Simulation.h"
+#include "common/platform/Platform.h"
#include
#include
#include
#include
-#include "common/String.h"
-#include "Format.h"
-#include "gui/interface/Engine.h"
-
-#include "client/GameSave.h"
-#include "simulation/Simulation.h"
-
-
-void EngineProcess() {}
-void ClipboardPush(ByteString) {}
-ByteString ClipboardPull() { return ""; }
-int GetModifiers() { return 0; }
-void SetCursorEnabled(int enabled) {}
-unsigned int GetTicks() { return 0; }
-
-static bool ReadFile(std::vector &fileData, ByteString filename)
-{
- std::ifstream f(filename, std::ios::binary);
- if (f) f.seekg(0, std::ios::end);
- if (f) fileData.resize(f.tellg());
- if (f) f.seekg(0);
- if (f) f.read(&fileData[0], fileData.size());
- if (!f)
- {
- std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;
- return false;
- }
- return true;
-}
-
int main(int argc, char *argv[])
{
if (!argv[1] || !argv[2]) {
@@ -47,15 +22,15 @@ int main(int argc, char *argv[])
auto outputFilename = ByteString(argv[2]) + ".png";
std::vector fileData;
- if (!ReadFile(fileData, inputFilename))
+ if (!Platform::ReadFile(fileData, inputFilename))
{
return 1;
}
- GameSave * gameSave = NULL;
+ std::unique_ptr gameSave;
try
{
- gameSave = new GameSave(fileData);
+ gameSave = std::make_unique(fileData, false);
}
catch (ParseException &e)
{
@@ -65,11 +40,11 @@ int main(int argc, char *argv[])
}
Simulation * sim = new Simulation();
- Renderer * ren = new Renderer(new Graphics(), sim);
+ Renderer * ren = new Renderer(sim);
if (gameSave)
{
- sim->Load(gameSave, true);
+ sim->Load(gameSave.get(), true, { 0, 0 });
//Render save
ren->decorations_enable = true;
@@ -81,19 +56,19 @@ int main(int argc, char *argv[])
frame--;
ren->render_parts();
ren->render_fire();
- ren->clearScreen(1.0f);
+ ren->clearScreen();
}
}
else
{
- int w = Graphics::textwidth("Save file invalid")+16, x = (XRES-w)/2, y = (YRES-24)/2;
- ren->drawrect(x, y, w, 24, 192, 192, 192, 255);
- ren->drawtext(x+8, y+8, "Save file invalid", 192, 192, 240, 255);
+ int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
+ ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
+ ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));
}
ren->RenderBegin();
ren->RenderEnd();
- VideoBuffer screenBuffer = ren->DumpFrame();
- screenBuffer.WritePNG(outputFilename);
+ if (auto data = ren->DumpFrame().ToPNG())
+ Platform::WriteFile(*data, outputFilename);
}
diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp
index b416fb6e2..4965d4778 100644
--- a/src/PowderToySDL.cpp
+++ b/src/PowderToySDL.cpp
@@ -1,66 +1,17 @@
+#include "PowderToySDL.h"
+#include "SimulationConfig.h"
+#include "WindowIcon.h"
#include "Config.h"
-#include "common/tpt-minmax.h"
-
-#include