Merge branch 'master' into jgrpp

# Conflicts:
#	.github/workflows/ci-build.yml
#	.github/workflows/codeql.yml
#	.github/workflows/commit-checker.yml
#	.github/workflows/release-linux-legacy.yml
#	.github/workflows/release-linux.yml
#	.github/workflows/release-macos.yml
#	.github/workflows/release-windows-store.yml
#	.github/workflows/release-windows.yml
#	.github/workflows/upload-cdn.yml
#	.github/workflows/upload-gog.yml
#	.github/workflows/upload-steam.yml
#	src/console_cmds.cpp
#	src/core/math_func.hpp
#	src/fios.cpp
#	src/fios.h
#	src/intro_gui.cpp
#	src/network/network_server.cpp
#	src/openttd.cpp
#	src/settings.cpp
#	src/settings_gui.cpp
#	src/settings_internal.h
#	src/settings_table.cpp
#	src/settings_type.h
#	src/table/settings.h.preamble
#	src/table/settings/company_settings.ini
#	src/table/settings/currency_settings.ini
#	src/table/settings/difficulty_settings.ini
#	src/table/settings/economy_settings.ini
#	src/table/settings/game_settings.ini
#	src/table/settings/gui_settings.ini
#	src/table/settings/linkgraph_settings.ini
#	src/table/settings/locale_settings.ini
#	src/table/settings/misc_settings.ini
#	src/table/settings/multimedia_settings.ini
#	src/table/settings/network_private_settings.ini
#	src/table/settings/network_settings.ini
#	src/table/settings/news_display_settings.ini
#	src/table/settings/old_gameopt_settings.ini
#	src/table/settings/pathfinding_settings.ini
#	src/table/settings/script_settings.ini
#	src/table/settings/win32_settings.ini
#	src/table/settings/window_settings.ini
#	src/table/settings/world_settings.ini
#	src/viewport.cpp
#	src/viewport_func.h
#	src/window.cpp
pull/661/head
Jonathan G Rennison 2 months ago
commit 86a6f63e2f

@ -30,16 +30,16 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- name: Setup cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: /emsdk/upstream/emscripten/cache
key: 3.1.42-${{ runner.os }}
@ -120,16 +120,16 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -231,16 +231,16 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -302,16 +302,16 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -384,11 +384,11 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
@ -460,4 +460,4 @@ jobs:
steps:
- name: Check annotations
uses: OpenTTD/actions/annotation-check@v3
uses: OpenTTD/actions/annotation-check@v5

@ -30,11 +30,11 @@ jobs:
steps:
- name: Checkout
if: github.event_name != 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
@ -65,16 +65,16 @@ jobs:
echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: cpp
config-file: ./.github/codeql/codeql-config.yml
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: /language:cpp
upload: False
@ -92,6 +92,6 @@ jobs:
output: sarif-results/cpp.sarif
- name: Upload results
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: sarif-results/cpp.sarif

@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
@ -35,7 +35,7 @@ jobs:
git checkout -b pr${{ github.event.pull_request.number }}
- name: Setup cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: /emsdk/upstream/emscripten/cache
key: 3.1.42-${{ runner.os }}

@ -15,7 +15,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: internal-source
@ -78,7 +78,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: openttd-docs
path: build/bundles/*.tar.xz

@ -21,7 +21,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: internal-source
@ -36,7 +36,7 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -162,7 +162,7 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: openttd-linux-generic
path: build/bundles

@ -18,7 +18,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: internal-source
@ -33,7 +33,7 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -187,7 +187,7 @@ jobs:
mv _CPack_Packages/*/Bundle/openttd-*.zip bundles/
- name: Store bundles
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: openttd-macos-universal
path: build-x64/bundles

@ -30,14 +30,14 @@ jobs:
steps:
- name: Checkout (Release)
if: github.event_name == 'release'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# We generate a changelog; for this we need the full git log.
fetch-depth: 0
- name: Checkout (Manual)
if: github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
# We generate a changelog; for this we need the full git log.
@ -45,7 +45,7 @@ jobs:
- name: Checkout (Trigger)
if: github.event_name == 'repository_dispatch'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.client_payload.ref }}
# We generate a changelog; for this we need the full git log.
@ -193,14 +193,14 @@ jobs:
echo "::endgroup::"
- name: Store bundles
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: openttd-source
path: build/bundles/*
retention-days: 5
- name: Store source (for other jobs)
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: internal-source
path: source.tar.gz

@ -30,7 +30,7 @@ jobs:
steps:
- name: Download source
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: internal-source
@ -46,7 +46,7 @@ jobs:
uses: Swatinem/rust-cache@v2
- name: Setup vcpkg caching
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
@ -188,7 +188,7 @@ jobs:
WINDOWS_CERTIFICATE_COMMON_NAME: ${{ secrets.WINDOWS_CERTIFICATE_COMMON_NAME }}
- name: Store bundles
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: openttd-windows-${{ matrix.arch }}
path: build/bundles

@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Check for finding script functions that require company/deity mode enforcement/checks
run: |

@ -15,7 +15,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Check for unused strings
run: |

@ -508,6 +508,15 @@ Most types of add-on content can be downloaded within OpenTTD via the 'Check Onl
Add-on content can also be installed manually, but that's more complicated; the [OpenTTD wiki](https://wiki.openttd.org/) may offer help with that, or the [OpenTTD directory structure guide](./docs/directory_structure.md).
### 1.5.1) Social Integration
OpenTTD has the ability to load plugins to integrate with Social Platforms like Steam, Discord, etc.
To enable such integration, the plugin for the specific platform has to be downloaded and stored in the `social_integration` folder.
See [OpenTTD's website](https://www.openttd.org), under Downloads, for what plugins are available.
### 1.6) OpenTTD directories
OpenTTD uses its own directory structure to store game data, add-on content etc.
@ -588,7 +597,10 @@ The icu scriptrun implementation in `src/3rdparty/icu` is licensed under the Uni
See `src/3rdparty/icu/LICENSE` for the complete license text.
The monocypher implementation in `src/3rdparty/monocypher` is licensed under the 2-clause BSD and CC-0 license.
See src/3rdparty/monocypher/LICENSE.md` for the complete license text.
See `src/3rdparty/monocypher/LICENSE.md` for the complete license text.
The OpenTTD Social Integration API in `src/3rdparty/openttd_social_integration_api` is licensed under the MIT license.
See `src/3rdparty/openttd_social_integration_api/LICENSE` for the complete license text.
## 4.0 Credits

@ -1,3 +1,159 @@
13.4 (2023-07-29)
------------------------------------------------------------------------
Fix: Setting tree lines drawn incorrectly for RTL languages (#11070)
Fix #11043: Don't choose toolbar dropdown option if focus is lost (#11044)
Fix #10917: Pay loan interest before generating statistics (#11040)
Fix #11016: Use after free in network invalid packet error path (#11022)
Fix #10987: Double-close of dropdown stopped land-info tool working as default (#11000)
13.3 (2023-06-11)
------------------------------------------------------------------------
Fix: [Win32] use full monitor resolution for fullscreen (#10985)
13.2 (2023-06-10)
------------------------------------------------------------------------
Change: [Win32] position window in center of workspace of primary display (#10942)
Change: Automatically disable hardware acceleration when GPU driver crashed the game last attempt (#10928)
Change: [Linux] Default scroll mode to non-mouse-lock (#10920)
Change: Include font style in font name for Freetype (#10879)
Fix: Don't restore backed up vehicle name if it's no longer unique (#10979)
Fix #10975: Train name wrongly marked as unique when joining trains (#10976)
Fix: Crash when not even a single row fits for dropdowns on low resolution screens (#10934)
Fix: Crash with tooltip on low resolution screens (#10933)
Fix: Crash when window can't be placed on low resolution screens (#10932)
Fix #10502: Apply engine refit before attaching free wagons (#10926)
Fix: Wayland crash on startup due to Pango also using FontConfig (#10916)
Fix: When syncing width of GUI items, take padding into account (#10915)
Fix: Make dropdowns self-close when losing focus (#10912)
Fix: Land info window maximum width was not scaled (#10894)
Fix: Check max member count in squirrel classes (#10883)
Fix: Ask FontConfig for the face index when opening fonts (#10878)
Fix #10831: Level crossing parts left barred after crossing tile removal (#10874)
Fix: Rail waypoint selection window not closed when parent windows closed (#10873)
Fix #10846: [Script] Crash on trying to allocate an excessively large array (#10848)
Fix: [Win32] Text line breaking did not properly handle punctuation characters (#10775)
Fix: [Emscripten] Crash when saving games (#10758)
Fix: [Win32] Wrong multi-line text layout due to incorrect whitespace handling (#10752)
Fix #10741: Rail platforms left partially reserved after train crash (#10751)
Fix: Shaded engines in purchase list incorrectly shaded (#10736)
Fix #10735: [NewGRF] {POP_COLOUR} fails if string is drawn with extra flags (#10736)
Fix #8177: Ships with max speed overflow to near-zero speed (#10695)
Fix #10289: Don't silently fail when setting timetable start dates (#10690)
Fix #8302: Improve "Maintenance intervals are in percents" helptext (#10686)
Fix #10665: "No vehicles are available yet" message did not appear correctly on non-temperate climates (#10673)
Fix #10630: Don't allow shifting service date earlier than year 0 (#10643)
Fix #10637, #10638: Incorrect water infrastructure totals when building certain object types (#10639, #10640)
Fix: Abort loading savegame if road vehicle is on invalid road type (#10622)
13.1 (2023-04-10)
------------------------------------------------------------------------
Add: [NewGRF] Engine name callback for nested variants. (#10399)
Fix: Improve main toolbar tooltips (#10616)
Fix: [NewGRF] Additional validation for Action3 (+others) (#10601)
Fix: Clear button for editbox didn't take account of padding (#10583)
Fix: [Script] Access to enum/consts defined outside of main.nut (#10573)
Fix #10568: Bogus warning when loading a save with a NewGRFs on dedicated servers (#10572)
Fix #10554: Crash when scrolling in the autoreplace window with collapsed variants (#10555)
Fix: Network server highlight invisible with RTL languages. (#10551)
Fix: Client name was not being used as company manager name (#10535)
Fix: Prevent road vehicles on crossing from crashing into the side of a train (#10496)
Fix #10477: [macOS] Calculation for window sizes when using custom fonts was being rounded incorrectly (#10489)
Fix #10486: Crash in debug window when GS started before AIs (#10487)
Fix #10469: [Script] Negative numbers in League Table window were sorted incorrectly (#10471)
Fix #10465: Crash on timeout if user never enters a password for server (#10466)
Fix #10280, #10461: Crash on opening town windows as a spectator (#10462)
Fix #10059: Script config values stored in the config file could cause crashes (#10444)
13.0 (2023-02-05)
------------------------------------------------------------------------
Change #10077: Make maximum loan a positive multiple of the loan interval (#10355)
Fix #10361: [Script] Don't try to give saved data to a dead script (#10433)
Fix #10419: Water infrastructure accounting when building ship depots and docks (#10432)
13.0-RC2 (2023-01-28)
------------------------------------------------------------------------
Feature: Press Ctrl to build a diagonal area of trees (#10342)
Feature: Set a custom number of industries in map generation window (#10340)
Change: Display font status as aa/noaa instead of true/false (#10352)
Fix: [Script] Improved API documentation for scripts (#10413, #10412)
Fix #10255: Reduce basic thickness of linkgraph GUI lines (#10410)
Fix #10220: Don't select unselectable engine as default (#10404)
Fix #10395: When loading old saves, don't forcibly bar level crossings (#10400)
Fix #10377: Bad sorting of rail vehicles when primary variant is missing (#10378)
Fix #10368: Server restarting game caused clients to hit assertion (#10369)
Fix #10362: NewGRF bridges without speed limits (#10365)
Fix #10363: CargoDist setting helptext shouldn't suggest symmetric distribution for diamonds in subtropic (#10364)
Fix: [Script] Switch to OWNER_TOWN prevented OWNER_DEITY test during industry prospecting (#10360)
Fix #10009: Bad overflow protection when taking out loans (#10359)
Fix #9865: Removing files with the console always failed (#10357)
Fix #10057: FallbackParagraphLayout fails to properly wrap (#10356)
Fix #10177: Company list password padlock showed after switching to single player (#10354)
Fix: Various Wide River issues (#10348)
Fix: Link variants to parents when finalising engines (#10346)
Fix #10333: Only show industry prospecting errors to local company (#10338)
Fix #10335: Set initial scrollbar count for object GUI (#10336)
Fix #10331: Starting new company during load must happen after AI start (#10332)
Fix #10309: [SDL] Uninitialized width and height when turning off full screen (#10328)
Fix #10032: Capacities of articulated vehicles in build window (#10326)
Fix: Improve handling of corrupt NewGRF or image files (#10321, #10316)
Fix: [NewGRF] Don't assume engclass 2 should be elrail (#10315)
Fix: [Script] AIGroup.GetProfitLastYear could get values different than those displayed in GUI (#10227)
Fix #10304: [Scripts] Don't start GS in intro game (#10305)
Fix: [Script] Copy compat files for version 13 (#10303)
13.0-RC1 (2023-01-01)
------------------------------------------------------------------------
Feature: 'font' console command to configure fonts within game (#10278)
Feature: Ctrl-click to bulk edit timetable speeds/waiting times (#10265)
Feature: [NewGRF] Vehicle variants in expandable purchase list (#10220)
Feature: Expand all towns in the scenario editor (#10215)
Add: [NewGRF] Slope-aware and roadtype-specific one-way sprites (#10282)
Change: Display text files in black (#10291)
Change: Make vehicle list dropdown buttons resize to fit strings (#10286)
Change: [NewGRF] Support flipping shorter engines without explicit support (#10262)
Change: Separate ground sprite from foundation sprite offsets (#10256)
Change: Vertically centre sprite font relative to TrueType font (#10254)
Change: [macOS] Set minimum macOS version to 10.13 (#10253)
Change: Use lowered not disabled widget for current vehicle details tab (#10252)
Change: Various improvements to NewGRF sprite aligner (#10249)
Change: reset_engines console command now rerandomises introduction dates and reliability (#10220)
Change: Show error message on failed industry prospecting (#10202)
Fix: Local authority window rating list height ignored icon sizes (#10285)
Fix #10150: Town signs could be truncated when using custom fonts (#10283)
Fix #8971: Resize QueryStrings with interface scale change (#10281)
Fix #10274: Crash when rescanning scripts with GS selected (#10276)
Fix #10151: Use smaller padding for signs (#10272)
Fix #10263: [Script] Restore tile validation for commands (#10269)
Fix: Missing scrollbar for rail/roadtype dropdowns (#10264)
Fix #10260: Incorrect rect height drawing image in vehicle details (#10261)
Fix #10257: Incorrect catenary position on sloped bridge heads (#10258)
Fix: Vertically centre chat prompt (#10250)
Fix #10214: League and graph buttons in toolbar did not have a default action (#10246)
Fix #10242: Allow a space for text shadow when clipping text (#10243)
Fix #10206: Fully disable scripts in intro game (#10241)
Fix #10218: Don't try to create river tiles along incorrect slopes (#10235)
Fix #10208: [NewGRF] Allow using a specific underlay for road/tram tunnels (#10233)
Fix #10224: Don't change fast-forward mode while saving (#10230)
Fix #10147: Sound effect volume slider no longer set volume (#10228)
Fix #10223: Crash when vehicle cloning fails on order cloning (#10225)
Fix: Maximum space for engine preview image was never scaled (#10219)
Fix #10216: Crash when upgrading savegame with crashed vehicles (#10217)
Fix #10212: [Script] Nested ScriptAccounting scopes not restored properly (#10213)
Fix #10114: Incorrect drag-highlight position with non-power-of-2 scaling (#10211)
Fix #10198: Rearrange Intro GUI to make button rows narrower (#10203)
Fix: Missing extra padding when drawing tooltip text (#10201)
Fix: Bad alignment of button icons when using the original baseset (#10200)
Fix: Signal icons incorrectly positioned in UI (#10199)
Fix #10021: Object GUI resized when switching between different objects (#10196)
Fix #9720: Delay start of GS/AI to after loading of savegame (#9745)
13.0-beta2 (2022-11-27)
------------------------------------------------------------------------
Feature: Allow AI/GS to be fully modified in scenario editor (#10152)

@ -67,6 +67,7 @@ function(set_options)
option(OPTION_USE_NSIS "Use NSIS to create windows installer; enable only for stable releases" OFF)
option(OPTION_TOOLS_ONLY "Build only tools target" OFF)
option(OPTION_DOCS_ONLY "Build only docs target" OFF)
option(OPTION_ALLOW_INVALID_SIGNATURE "Allow loading of content with invalid signatures" OFF)
if (OPTION_DOCS_ONLY)
set(OPTION_TOOLS_ONLY ON PARENT_SCOPE)
@ -92,6 +93,11 @@ function(show_options)
else()
message(STATUS "Option Survey Key - NOT USED")
endif()
if(OPTION_ALLOW_INVALID_SIGNATURE)
message(STATUS "Option Allow Invalid Signature - USED")
message(WARNING "Ignoring invalid signatures is a security risk! Use with care!")
endif()
endfunction()
# Add the definitions for the options that are selected.
@ -116,4 +122,8 @@ function(add_definitions_based_on_options)
if(OPTION_SURVEY_KEY)
add_definitions(-DSURVEY_KEY="${OPTION_SURVEY_KEY}")
endif()
if(OPTION_ALLOW_INVALID_SIGNATURE)
add_definitions(-DALLOW_INVALID_SIGNATURE)
endif()
endfunction()

@ -13,7 +13,7 @@
.Op Fl c Ar config_file
.Op Fl d Op Ar level | Ar cat Ns = Ns Ar lvl Ns Op , Ns Ar ...
.Op Fl D Oo Ar host Oc Ns Op : Ns Ar port
.Op Fl g Op Ar savegame
.Op Fl g Op Ar file
.Op Fl G Ar seed
.Op Fl I Ar graphicsset
.Op Fl m Ar driver
@ -62,11 +62,11 @@ Start in world editor mode.
.It Fl f
Fork into background (dedicated server only, see
.Fl D ) .
.It Fl g Op Ar savegame
.It Fl g Op Ar file
Load
.Ar savegame
at start or start a new game if omitted.
.Ar savegame
.Ar file
(can be either a savegame, scenario, or heightmap) at start or start a new game if omitted.
.Ar file
must be either an absolute path or one relative to the current path or one of
the search paths.
.It Fl G Ar seed

@ -6,6 +6,7 @@ add_subdirectory(monocypher)
add_subdirectory(squirrel)
add_subdirectory(nlohmann)
add_subdirectory(opengl)
add_subdirectory(openttd_social_integration_api)
add_subdirectory(cpp-btree)
add_subdirectory(robin_hood)

@ -18,7 +18,7 @@
#include <ostream>
#include <type_traits>
#include "format.h"
#include "ostream.h" // formatbuf
FMT_BEGIN_NAMESPACE
@ -72,7 +72,8 @@ template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
-> To {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
@ -101,7 +102,8 @@ template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
-> To {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
@ -133,7 +135,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
-> To {
ec = 0;
return from;
} // function
@ -154,7 +157,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
@ -176,7 +179,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
@ -188,8 +191,8 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
@ -240,8 +243,8 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) {
auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
@ -321,12 +324,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
namespace detail {
template <typename T = void> struct null {};
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); }
inline const std::locale& get_classic_locale() {
inline auto get_classic_locale() -> const std::locale& {
static const auto& locale = std::locale::classic();
return locale;
}
@ -336,8 +339,6 @@ template <typename CodeUnit> struct codecvt_result {
CodeUnit buf[max_size];
CodeUnit* end;
};
template <typename CodeUnit>
constexpr const size_t codecvt_result<CodeUnit>::max_size;
template <typename CodeUnit>
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
@ -377,8 +378,8 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
unit_t unit;
write_codecvt(unit, in, loc);
// In UTF-8 is used one to four one-byte code units.
unicode_to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>
u;
auto u =
to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
FMT_THROW(format_error("failed to format time"));
return copy_str<char>(u.c_str(), u.c_str() + u.size(), out);
@ -408,8 +409,7 @@ inline void do_write(buffer<Char>& buf, const std::tm& time,
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& os = std::basic_ostream<Char>(&format_buf);
os.imbue(loc);
using iterator = std::ostreambuf_iterator<Char>;
const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
const auto& facet = std::use_facet<std::time_put<Char>>(loc);
auto end = facet.put(os, os, Char(' '), &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time"));
}
@ -432,6 +432,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
}
template <typename Rep1, typename Rep2>
struct is_same_arithmetic_type
: public std::integral_constant<bool,
(std::is_integral<Rep1>::value &&
std::is_integral<Rep2>::value) ||
(std::is_floating_point<Rep1>::value &&
std::is_floating_point<Rep2>::value)> {
};
template <
typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(is_same_arithmetic_type<FromRep, typename To::rep>::value)>
auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST
// Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts.
int ec;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
if (ec) FMT_THROW(format_error("cannot format duration"));
return to;
#else
// Standard duration cast, may overflow.
return std::chrono::duration_cast<To>(from);
#endif
}
template <
typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
return std::chrono::duration_cast<To>(from);
}
template <typename Duration>
auto to_time_t(
std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
-> std::time_t {
// Cannot use std::chrono::system_clock::to_time_t since this would first
// require a cast to std::chrono::system_clock::time_point, which could
// overflow.
return fmt_duration_cast<std::chrono::duration<std::time_t>>(
time_point.time_since_epoch())
.count();
}
} // namespace detail
FMT_BEGIN_EXPORT
@ -441,29 +486,29 @@ FMT_BEGIN_EXPORT
expressed in local time. Unlike ``std::localtime``, this function is
thread-safe on most platforms.
*/
inline std::tm localtime(std::time_t time) {
inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
bool handle(detail::null<>) {
auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
bool fallback(detail::null<>) {
auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
@ -480,8 +525,8 @@ inline std::tm localtime(std::time_t time) {
#if FMT_USE_LOCAL_TIME
template <typename Duration>
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
return localtime(std::chrono::system_clock::to_time_t(
std::chrono::current_zone()->to_sys(time)));
return localtime(
detail::to_time_t(std::chrono::current_zone()->to_sys(time)));
}
#endif
@ -490,90 +535,49 @@ inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
function is thread-safe on most platforms.
*/
inline std::tm gmtime(std::time_t time) {
inline auto gmtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
bool run() {
auto run() -> bool {
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
bool handle(detail::null<>) {
auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
bool fallback(detail::null<>) {
auto fallback(detail::null<>) -> bool {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher gt(time);
auto gt = dispatcher(time);
// Too big time values may be unsupported.
if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
}
inline std::tm gmtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return gmtime(std::chrono::system_clock::to_time_t(time_point));
template <typename Duration>
inline auto gmtime(
std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
-> std::tm {
return gmtime(detail::to_time_t(time_point));
}
FMT_BEGIN_DETAIL_NAMESPACE
// DEPRECATED!
template <typename Char>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
format_specs<Char>& specs) -> const Char* {
FMT_ASSERT(begin != end, "");
auto align = align::none;
auto p = begin + code_point_length(begin);
if (end - p <= 0) p = begin;
for (;;) {
switch (to_ascii(*p)) {
case '<':
align = align::left;
break;
case '>':
align = align::right;
break;
case '^':
align = align::center;
break;
}
if (align != align::none) {
if (p != begin) {
auto c = *begin;
if (c == '}') return begin;
if (c == '{') {
throw_format_error("invalid fill character '{'");
return begin;
}
specs.fill = {begin, to_unsigned(p - begin)};
begin = p + 1;
} else {
++begin;
}
break;
} else if (p == begin) {
break;
}
p = begin;
}
specs.align = align;
return begin;
}
namespace detail {
// Writes two-digit numbers a, b and c separated by sep to buf.
// The method by Pavel Novikov based on
@ -609,7 +613,8 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
}
}
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
template <typename Period>
FMT_CONSTEXPR inline auto get_units() -> const char* {
if (std::is_same<Period, std::atto>::value) return "as";
if (std::is_same<Period, std::femto>::value) return "fs";
if (std::is_same<Period, std::pico>::value) return "ps";
@ -627,8 +632,9 @@ template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
if (std::is_same<Period, std::tera>::value) return "Ts";
if (std::is_same<Period, std::peta>::value) return "Ps";
if (std::is_same<Period, std::exa>::value) return "Es";
if (std::is_same<Period, std::ratio<60>>::value) return "m";
if (std::is_same<Period, std::ratio<60>>::value) return "min";
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
if (std::is_same<Period, std::ratio<86400>>::value) return "d";
return nullptr;
}
@ -664,9 +670,8 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
// Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
const Char* end,
Handler&& handler) {
FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
Handler&& handler) -> const Char* {
if (begin == end || *begin == '}') return begin;
if (*begin != '%') FMT_THROW(format_error("invalid format"));
auto ptr = begin;
@ -997,25 +1002,25 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
FMT_CONSTEXPR void on_tz_name() {}
};
inline const char* tm_wday_full_name(int wday) {
inline auto tm_wday_full_name(int wday) -> const char* {
static constexpr const char* full_name_list[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
}
inline const char* tm_wday_short_name(int wday) {
inline auto tm_wday_short_name(int wday) -> const char* {
static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
}
inline const char* tm_mon_full_name(int mon) {
inline auto tm_mon_full_name(int mon) -> const char* {
static constexpr const char* full_name_list[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
}
inline const char* tm_mon_short_name(int mon) {
inline auto tm_mon_short_name(int mon) -> const char* {
static constexpr const char* short_name_list[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
@ -1047,21 +1052,21 @@ inline void tzset_once() {
// Converts value to Int and checks that it's in the range [0, upper).
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline Int to_nonnegative_int(T value, Int upper) {
FMT_ASSERT(std::is_unsigned<Int>::value ||
(value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
"invalid value");
(void)upper;
inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (!std::is_unsigned<Int>::value &&
(value < 0 || to_unsigned(value) > to_unsigned(upper))) {
FMT_THROW(fmt::format_error("chrono value is out of range"));
}
return static_cast<Int>(value);
}
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
inline Int to_nonnegative_int(T value, Int upper) {
inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (value < 0 || value > static_cast<T>(upper))
FMT_THROW(format_error("invalid value"));
return static_cast<Int>(value);
}
constexpr long long pow10(std::uint32_t n) {
constexpr auto pow10(std::uint32_t n) -> long long {
return n == 0 ? 1 : 10 * pow10(n - 1);
}
@ -1095,13 +1100,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
std::chrono::seconds::rep>::type,
std::ratio<1, detail::pow10(num_fractional_digits)>>;
const auto fractional =
d - std::chrono::duration_cast<std::chrono::seconds>(d);
const auto fractional = d - fmt_duration_cast<std::chrono::seconds>(d);
const auto subseconds =
std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value
? fractional.count()
: std::chrono::duration_cast<subsecond_precision>(fractional).count();
: fmt_duration_cast<subsecond_precision>(fractional).count();
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
const int num_digits = detail::count_digits(n);
@ -1152,11 +1156,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration,
num_fractional_digits = 6;
}
format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
std::fmod(val * static_cast<rep>(Duration::period::num) /
static_cast<rep>(Duration::period::den),
static_cast<rep>(60)),
num_fractional_digits);
fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
std::fmod(val * static_cast<rep>(Duration::period::num) /
static_cast<rep>(Duration::period::den),
static_cast<rep>(60)),
num_fractional_digits);
}
template <typename OutputIt, typename Char,
@ -1217,8 +1221,7 @@ class tm_writer {
return static_cast<int>(l);
}
// Algorithm:
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
auto iso_year_weeks(long long curr_year) const noexcept -> int {
const auto prev_year = curr_year - 1;
const auto curr_p =
@ -1358,7 +1361,7 @@ class tm_writer {
subsecs_(subsecs),
tm_(tm) {}
OutputIt out() const { return out_; }
auto out() const -> OutputIt { return out_; }
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
out_ = copy_str<Char>(begin, end, out_);
@ -1622,6 +1625,7 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_CONSTEXPR void on_day_of_year() {}
FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
@ -1640,16 +1644,16 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
template <typename T,
FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
inline bool isfinite(T) {
inline auto isfinite(T) -> bool {
return true;
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
inline auto mod(T x, int y) -> T {
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
inline T mod(T x, int y) {
inline auto mod(T x, int y) -> T {
return std::fmod(x, static_cast<T>(y));
}
@ -1664,49 +1668,38 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
#if FMT_SAFE_DURATION_CAST
// throwing version of safe_duration_cast
template <typename To, typename FromRep, typename FromPeriod>
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
int ec;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
if (ec) FMT_THROW(format_error("cannot format duration"));
return to;
}
#endif
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
std::chrono::duration<Rep, Period> d) {
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
-> std::chrono::duration<Rep, std::milli> {
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
using CommonSecondsType =
typename std::common_type<decltype(d), std::chrono::seconds>::type;
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
const auto d_as_common = fmt_duration_cast<CommonSecondsType>(d);
const auto d_as_whole_seconds =
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
fmt_duration_cast<std::chrono::seconds>(d_as_common);
// this conversion should be nonproblematic
const auto diff = d_as_common - d_as_whole_seconds;
const auto ms =
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
fmt_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms;
#else
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
auto s = fmt_duration_cast<std::chrono::seconds>(d);
return fmt_duration_cast<std::chrono::milliseconds>(d - s);
#endif
}
template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
OutputIt format_duration_value(OutputIt out, Rep val, int) {
auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt {
return write<Char>(out, val);
}
template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
auto specs = format_specs<Char>();
specs.precision = precision;
specs.type = precision >= 0 ? presentation_type::fixed_lower
@ -1715,12 +1708,12 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
@ -1728,7 +1721,7 @@ OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
}
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
auto format_duration_unit(OutputIt out) -> OutputIt {
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
*out++ = '[';
@ -1795,18 +1788,12 @@ struct chrono_formatter {
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
// might need checked conversion (rep!=Rep)
auto tmpval = std::chrono::duration<rep, Period>(val);
s = fmt_safe_duration_cast<seconds>(tmpval);
#else
s = std::chrono::duration_cast<seconds>(
std::chrono::duration<rep, Period>(val));
#endif
s = fmt_duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
}
// returns true if nan or inf, writes to out.
bool handle_nan_inf() {
auto handle_nan_inf() -> bool {
if (isfinite(val)) {
return false;
}
@ -1823,17 +1810,22 @@ struct chrono_formatter {
return true;
}
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
auto days() const -> Rep { return static_cast<Rep>(s.count() / 86400); }
auto hour() const -> Rep {
return static_cast<Rep>(mod((s.count() / 3600), 24));
}
Rep hour12() const {
auto hour12() const -> Rep {
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
return hour <= 0 ? 12 : hour;
}
Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
auto minute() const -> Rep {
return static_cast<Rep>(mod((s.count() / 60), 60));
}
auto second() const -> Rep { return static_cast<Rep>(mod(s.count(), 60)); }
std::tm time() const {
auto time() const -> std::tm {
auto time = std::tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
time.tm_min = to_nonnegative_int(minute(), 60);
@ -1901,10 +1893,14 @@ struct chrono_formatter {
void on_dec0_week_of_year(numeric_system) {}
void on_dec1_week_of_year(numeric_system) {}
void on_iso_week_of_year(numeric_system) {}
void on_day_of_year() {}
void on_day_of_month(numeric_system) {}
void on_day_of_month_space(numeric_system) {}
void on_day_of_year() {
if (handle_nan_inf()) return;
write(days(), 0);
}
void on_24_hour(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
@ -1997,7 +1993,7 @@ struct chrono_formatter {
}
};
FMT_END_DETAIL_NAMESPACE
} // namespace detail
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday;
@ -2011,7 +2007,7 @@ class weekday {
weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
constexpr unsigned c_encoding() const noexcept { return value; }
constexpr auto c_encoding() const noexcept -> unsigned { return value; }
};
class year_month_day {};
@ -2047,80 +2043,67 @@ template <typename Char> struct formatter<weekday, Char> {
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
format_specs<Char> specs;
int precision = -1;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
bool localized = false;
basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
detail::arg_ref<Char> precision_ref_;
bool localized_ = false;
basic_string_view<Char> format_str_;
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
begin = detail::parse_align(begin, end, specs);
if (begin == end) return {begin, begin};
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
if (begin == end) return {begin, begin};
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
auto checker = detail::chrono_format_checker();
if (*begin == '.') {
if (*it == '.') {
checker.has_precision_integral = !std::is_floating_point<Rep>::value;
begin =
detail::parse_precision(begin, end, precision, precision_ref, ctx);
it = detail::parse_precision(it, end, specs_.precision, precision_ref_,
ctx);
}
if (begin != end && *begin == 'L') {
++begin;
localized = true;
if (it != end && *it == 'L') {
localized_ = true;
++it;
}
end = detail::parse_chrono_format(begin, end, checker);
return {begin, end};
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
end = detail::parse_chrono_format(it, end, checker);
format_str_ = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename FormatContext>
auto format(const duration& d, FormatContext& ctx) const
auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs_copy = specs;
auto precision_copy = precision;
auto begin = format_str.begin(), end = format_str.end();
auto specs = specs_;
auto precision = specs.precision;
specs.precision = -1;
auto begin = format_str_.begin(), end = format_str_.end();
// As a possible future optimization, we could avoid extra copying if width
// is not specified.
basic_memory_buffer<Char> buf;
auto buf = basic_memory_buffer<Char>();
auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
precision_ref, ctx);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref_, ctx);
if (begin == end || *begin == '}') {
out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision_copy;
f.localized = localized;
using chrono_formatter =
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
auto f = chrono_formatter(ctx, out, d);
f.precision = precision;
f.localized = localized_;
detail::parse_chrono_format(begin, end, f);
}
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};
@ -2128,34 +2111,33 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
}
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
FormatContext& ctx) const -> decltype(ctx.out()) {
using period = typename Duration::period;
if (period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value) {
if (detail::const_check(
period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value)) {
const auto epoch = val.time_since_epoch();
auto subsecs = std::chrono::duration_cast<Duration>(
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
auto subsecs = detail::fmt_duration_cast<Duration>(
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) {
auto second = std::chrono::seconds(1);
auto second =
detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
if (epoch.count() < ((Duration::min)() + second).count())
FMT_THROW(format_error("duration is too small"));
subsecs += second;
val -= second;
}
return formatter<std::tm, Char>::do_format(
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
&subsecs);
return formatter<std::tm, Char>::do_format(gmtime(val), ctx, &subsecs);
}
return formatter<std::tm, Char>::format(
gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
return formatter<std::tm, Char>::format(gmtime(val), ctx);
}
};
@ -2164,7 +2146,7 @@ template <typename Char, typename Duration>
struct formatter<std::chrono::local_time<Duration>, Char>
: formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->format_str = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
this->format_str_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
}
template <typename FormatContext>
@ -2174,17 +2156,13 @@ struct formatter<std::chrono::local_time<Duration>, Char>
if (period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value) {
const auto epoch = val.time_since_epoch();
const auto subsecs = std::chrono::duration_cast<Duration>(
epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
const auto subsecs = detail::fmt_duration_cast<Duration>(
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
return formatter<std::tm, Char>::do_format(
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
ctx, &subsecs);
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
}
return formatter<std::tm, Char>::format(
localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
ctx);
return formatter<std::tm, Char>::format(localtime(val), ctx);
}
};
#endif
@ -2207,51 +2185,46 @@ struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
template <typename Char> struct formatter<std::tm, Char> {
private:
format_specs<Char> specs;
detail::arg_ref<Char> width_ref;
format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
protected:
basic_string_view<Char> format_str;
FMT_CONSTEXPR auto do_parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return begin;
begin = detail::parse_align(begin, end, specs);
if (begin == end) return end;
begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx);
if (begin == end) return end;
end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
// Replace default format_str only if the new spec is not empty.
if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)};
return end;
}
basic_string_view<Char> format_str_;
template <typename FormatContext, typename Duration>
auto do_format(const std::tm& tm, FormatContext& ctx,
const Duration* subsecs) const -> decltype(ctx.out()) {
auto specs_copy = specs;
basic_memory_buffer<Char> buf;
auto specs = specs_;
auto buf = basic_memory_buffer<Char>();
auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
width_ref, ctx);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
const auto loc_ref = ctx.locale();
auto loc_ref = ctx.locale();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w =
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
detail::parse_chrono_format(format_str.begin(), format_str.end(), w);
detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w);
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return this->do_parse(ctx);
auto it = ctx.begin(), end = ctx.end();
if (it == end || *it == '}') return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it == end) return it;
end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
// Replace the default format_str only if the new spec is not empty.
if (end != it) format_str_ = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename FormatContext>

File diff suppressed because it is too large Load Diff

@ -18,7 +18,7 @@
# include <locale>
#endif
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
# include <io.h> // _isatty
#endif
@ -58,8 +58,8 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out);
if (message.size() <= inline_buffer_size - error_code_size)
format_to(it, FMT_STRING("{}{}"), message, SEP);
format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
FMT_ASSERT(out.size() <= inline_buffer_size, "");
}
@ -73,9 +73,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
}
// A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
@ -86,7 +85,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
template <typename Locale> Locale locale_ref::get() const {
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
@ -98,7 +97,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep};
}
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
@ -144,24 +144,25 @@ FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
}
#endif
FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
format_args args) {
FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args)
-> std::system_error {
auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(fmt, args));
}
namespace detail {
template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
template <typename F>
inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
return x.f == y.f && x.e == y.e;
}
// Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@ -170,14 +171,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
namespace dragonbox {
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer.
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
}
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline uint128_fallback umul192_lower128(uint64_t x,
uint128_fallback y) noexcept {
inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
uint64_t high = x * y.high();
uint128_fallback high_low = umul128(x, y.low());
return {high + high_low.high(), high_low.low()};
@ -185,12 +186,12 @@ inline uint128_fallback umul192_lower128(uint64_t x,
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer.
inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return x * y;
}
// Various fast log computations.
inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
return (e * 631305 - 261663) >> 21;
}
@ -204,7 +205,7 @@ FMT_INLINE_VARIABLE constexpr struct {
// divisible by pow(10, N).
// Precondition: n <= pow(10, N + 1).
template <int N>
bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool {
// The numbers below are chosen such that:
// 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
// 2. nm mod 2^k < m if and only if n is divisible by d,
@ -229,7 +230,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
// Computes floor(n / pow(10, N)) for small n and N.
// Precondition: n <= pow(10, N + 1).
template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
template <int N> auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t {
constexpr auto info = div_small_pow10_infos[N - 1];
FMT_ASSERT(n <= info.divisor * 10, "n is too large");
constexpr uint32_t magic_number =
@ -238,12 +239,12 @@ template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
}
// Computes floor(n / 10^(kappa + 1)) (float)
inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t {
// 1374389535 = ceil(2^37/100)
return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
}
// Computes floor(n / 10^(kappa + 1)) (double)
inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t {
// 2361183241434822607 = ceil(2^(64+7)/1000)
return umul128_upper64(n, 2361183241434822607ull) >> 7;
}
@ -255,7 +256,7 @@ template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::carrier_uint;
using cache_entry_type = uint64_t;
static uint64_t get_cached_power(int k) noexcept {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
@ -297,20 +298,23 @@ template <> struct cache_accessor<float> {
bool is_integer;
};
static compute_mul_result compute_mul(
carrier_uint u, const cache_entry_type& cache) noexcept {
static auto compute_mul(carrier_uint u,
const cache_entry_type& cache) noexcept
-> compute_mul_result {
auto r = umul96_upper64(u, cache);
return {static_cast<carrier_uint>(r >> 32),
static_cast<carrier_uint>(r) == 0};
}
static uint32_t compute_delta(const cache_entry_type& cache,
int beta) noexcept {
static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
-> uint32_t {
return static_cast<uint32_t>(cache >> (64 - 1 - beta));
}
static compute_mul_parity_result compute_mul_parity(
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
static auto compute_mul_parity(carrier_uint two_f,
const cache_entry_type& cache,
int beta) noexcept
-> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, "");
@ -319,22 +323,22 @@ template <> struct cache_accessor<float> {
static_cast<uint32_t>(r >> (32 - beta)) == 0};
}
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>(
(cache - (cache >> (num_significand_bits<float>() + 2))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>(
(cache + (cache >> (num_significand_bits<float>() + 1))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
static carrier_uint compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (static_cast<carrier_uint>(
cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
1) /
@ -346,7 +350,7 @@ template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::carrier_uint;
using cache_entry_type = uint128_fallback;
static uint128_fallback get_cached_power(int k) noexcept {
static auto get_cached_power(int k) noexcept -> uint128_fallback {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
@ -985,8 +989,7 @@ template <> struct cache_accessor<double> {
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{ 0xdb68c2ca82ed2a05,
0xa67398db9f6820e2 }
{0xdb68c2ca82ed2a05, 0xa67398db9f6820e2},
#else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@ -1071,19 +1074,22 @@ template <> struct cache_accessor<double> {
bool is_integer;
};
static compute_mul_result compute_mul(
carrier_uint u, const cache_entry_type& cache) noexcept {
static auto compute_mul(carrier_uint u,
const cache_entry_type& cache) noexcept
-> compute_mul_result {
auto r = umul192_upper128(u, cache);
return {r.high(), r.low() == 0};
}
static uint32_t compute_delta(cache_entry_type const& cache,
int beta) noexcept {
static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
-> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
}
static compute_mul_parity_result compute_mul_parity(
carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
static auto compute_mul_parity(carrier_uint two_f,
const cache_entry_type& cache,
int beta) noexcept
-> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, "");
@ -1092,35 +1098,35 @@ template <> struct cache_accessor<double> {
((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
}
static carrier_uint compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_left_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() -
(cache.high() >> (num_significand_bits<double>() + 2))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
static carrier_uint compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_right_endpoint_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() +
(cache.high() >> (num_significand_bits<double>() + 1))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
static carrier_uint compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept {
static auto compute_round_up_for_shorter_interval_case(
const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
1) /
2;
}
};
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
return cache_accessor<double>::get_cached_power(k);
}
// Various integer checks
template <typename T>
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
const int case_shorter_interval_left_endpoint_lower_threshold = 2;
const int case_shorter_interval_left_endpoint_upper_threshold = 3;
return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
@ -1128,16 +1134,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
// See https://github.com/fmtlib/fmt/issues/3163 for more details.
const uint32_t mod_inv_5 = 0xcccccccd;
// Casts are needed to workaround a bug in MSVC 19.22 and older.
const uint32_t mod_inv_25 =
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
constexpr uint32_t mod_inv_5 = 0xcccccccd;
constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
int s = 0;
while (true) {
auto q = rotr(n * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
@ -1162,32 +1164,17 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
// If yes, work with the quotient.
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
const uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
int s = 8;
while (true) {
auto q = rotr(n32 * mod_inv_25, 2);
if (q > max_value<uint32_t>() / 100) break;
n32 = q;
s += 2;
}
auto q = rotr(n32 * mod_inv_5, 1);
if (q <= max_value<uint32_t>() / 10) {
n32 = q;
s |= 1;
}
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
n = n32;
return s;
}
// If n is not divisible by 10^8, work with n itself.
const uint64_t mod_inv_5 = 0xcccccccccccccccd;
const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5
int s = 0;
while (true) {
@ -1253,7 +1240,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
return ret_value;
}
template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
template <typename T> auto to_decimal(T x) noexcept -> decimal_fp<T> {
// Step 1: integer promotion & Schubfach multiplier calculation.
using carrier_uint = typename float_info<T>::carrier_uint;
@ -1392,15 +1379,15 @@ template <> struct formatter<detail::bigint> {
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1u];
if (first) {
out = format_to(out, FMT_STRING("{:x}"), value);
out = fmt::format_to(out, FMT_STRING("{:x}"), value);
first = false;
continue;
}
out = format_to(out, FMT_STRING("{:08x}"), value);
out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
}
if (n.exp_ > 0)
out = format_to(out, FMT_STRING("p{}"),
n.exp_ * detail::bigint::bigit_bits);
out = fmt::format_to(out, FMT_STRING("p{}"),
n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
@ -1436,7 +1423,7 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message);
}
FMT_FUNC std::string vformat(string_view fmt, format_args args) {
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
// Don't optimize the "{}" case to keep the binary size small and because it
// can be better optimized in fmt::format anyway.
auto buffer = memory_buffer();
@ -1445,33 +1432,38 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
}
namespace detail {
#ifndef _WIN32
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
void*, const void*, dword, dword*, void*);
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto fd = _fileno(f);
if (!_isatty(fd)) return false;
FMT_FUNC bool write_console(int fd, string_view text) {
auto u16 = utf8_to_utf16(text);
auto written = dword();
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<uint32_t>(u16.size()), &written, nullptr);
static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
}
#endif
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
detail::vformat_to(buffer, fmt, args);
fwrite_fully(buffer.data(), buffer.size(), f);
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
#ifdef _WIN32
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
if (write_console(fd, text)) return;
}
#endif
fwrite_fully(text.data(), text.size(), f);
}
} // namespace detail

File diff suppressed because it is too large Load Diff

@ -10,19 +10,50 @@
#include <fstream> // std::filebuf
#if defined(_WIN32) && defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
# include <__std_stream>
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
@ -37,36 +68,40 @@ class file_access {
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
template class file_access<file_access_tag, std::__stdoutbuf<char>,
&std::__stdoutbuf<char>::__file_>;
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
#endif
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
FILE* c_file;
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
c_file = sfbuf->file();
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
c_file = fbuf->file();
f = fbuf->file();
else
return false;
if (c_file) return write_console(c_file, data);
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
#else
ignore_unused(os, data);
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline bool write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) {
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
@ -87,18 +122,19 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view { const T& value; };
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
@ -111,7 +147,7 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value, ctx.locale());
detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
@ -140,7 +176,7 @@ struct formatter<detail::streamed_view<T>, Char>
\endrst
*/
template <typename T>
auto streamed(const T& value) -> detail::streamed_view<T> {
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
@ -155,7 +191,7 @@ inline void vprint_directly(std::ostream& os, string_view format_str,
} // namespace detail
FMT_MODULE_EXPORT template <typename Char>
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@ -174,7 +210,7 @@ void vprint(std::basic_ostream<Char>& os,
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_MODULE_EXPORT template <typename... T>
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8())
@ -183,7 +219,7 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
detail::vprint_directly(os, fmt, vargs);
}
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
@ -191,12 +227,12 @@ void print(std::wostream& os,
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
}
FMT_MODULE_EXPORT template <typename... T>
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,

@ -1,13 +1,9 @@
// Formatting library for C++ - experimental range support
// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
@ -187,7 +183,7 @@ template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
@ -211,15 +207,15 @@ class is_tuple_formattable_ {
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is>
static std::true_type check2(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>);
static std::false_type check2(...);
static auto check2(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>) -> std::true_type;
static auto check2(...) -> std::false_type;
template <std::size_t... Is>
static decltype(check2(
static auto check(index_sequence<Is...>) -> decltype(check2(
index_sequence<Is...>{},
integer_sequence<
bool, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{})) check(index_sequence<Is...>);
integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{}));
public:
static constexpr const bool value =
@ -421,6 +417,12 @@ struct is_formattable_delayed
#endif
} // namespace detail
template <typename...> struct conjunction : std::true_type {};
template <typename P> struct conjunction<P> : P {};
template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@ -486,7 +488,8 @@ struct range_formatter<
for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out);
ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx);
auto&& item = *it;
out = underlying_.format(mapper.map(item), ctx);
++i;
}
out = detail::copy_str<Char>(closing_bracket_, out);
@ -668,8 +671,11 @@ template <typename Container> struct all {
} // namespace detail
template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<detail::is_container_adaptor_like<T>::value>>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>

@ -8,6 +8,8 @@
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
@ -15,7 +17,9 @@
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "format.h"
#include "ostream.h"
#if FMT_HAS_INCLUDE(<version>)
@ -34,6 +38,10 @@
# endif
#endif
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
@ -44,67 +52,155 @@
# endif
#endif
#ifdef __cpp_lib_filesystem
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p) {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
# ifdef _WIN32
template <>
inline void write_escaped_path<char>(memory_buffer& quoted,
const std::filesystem::path& p) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
// Convert UTF-16 to UTF-8.
if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
FMT_THROW(std::runtime_error("invalid utf16"));
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
# endif
template <>
inline void write_escaped_path<std::filesystem::path::value_type>(
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
const std::filesystem::path& p) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), p.native());
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
struct formatter<std::filesystem::path, Char>
: formatter<basic_string_view<Char>> {
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto out = formatter<basic_string_view<Char>>::parse(ctx);
this->set_debug_format(false);
return out;
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = *it++;
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
typename FormatContext::iterator {
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
# ifdef _WIN32
auto path_string = !path_type_ ? p.native() : p.generic_wstring();
# else
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p);
return formatter<basic_string_view<Char>>::format(
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
FMT_END_NAMESPACE
#endif
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@ -132,7 +228,7 @@ struct formatter<std::optional<T>, Char,
}
template <typename FormatContext>
auto format(std::optional<T> const& opt, FormatContext& ctx) const
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
@ -146,24 +242,33 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#ifdef __cpp_lib_variant
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "monostate");
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
@ -197,6 +302,7 @@ auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
}
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
@ -206,7 +312,21 @@ template <typename T, typename C> struct is_variant_formattable {
detail::is_variant_formattable_<T, C>::value;
};
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
@ -223,13 +343,14 @@ struct formatter<
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
try {
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v);
},
value);
} catch (const std::bad_variant_access&) {
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
@ -237,10 +358,10 @@ struct formatter<
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_variant
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@ -258,10 +379,10 @@ template <typename Char> struct formatter<std::error_code, Char> {
}
};
FMT_MODULE_EXPORT
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char,
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
@ -274,7 +395,7 @@ struct formatter<
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = true;
with_typename_ = FMT_USE_TYPEID != 0;
}
return it;
}
@ -287,11 +408,12 @@ struct formatter<
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex);
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
@ -327,23 +449,89 @@ struct formatter<
demangled_name_view = string_view(ti.name());
}
out = detail::write_bytes(out, demangled_name_view, spec);
#elif FMT_MSC_VERSION
# elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec);
#else
# else
out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, Char(' '));
out = detail::write_bytes(out, string_view(ex.what()), spec);
}
};
return out;
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
FMT_END_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_END_NAMESPACE
#endif // FMT_STD_H_

File diff suppressed because it is too large Load Diff

@ -32,7 +32,7 @@ extern "C" {
#define GLAPI extern
#endif
#define GL_GLEXT_VERSION 20201119
#define GL_GLEXT_VERSION 20231129
#include "khrplatform.h"
@ -5397,12 +5397,12 @@ typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severi
typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf);
typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam);
typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufSize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message);
typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufSize, GLenum *categories, GLenum *severities, GLuint *ids, GLsizei *lengths, GLchar *message);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf);
GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam);
GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufSize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message);
GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufSize, GLenum *categories, GLenum *severities, GLuint *ids, GLsizei *lengths, GLchar *message);
#endif
#endif /* GL_AMD_debug_output */
@ -7370,6 +7370,16 @@ GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1,
#endif
#endif /* GL_EXT_framebuffer_blit */
#ifndef GL_EXT_framebuffer_blit_layers
#define GL_EXT_framebuffer_blit_layers 1
typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERLAYERSEXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERLAYEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint srcLayer, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstLayer, GLbitfield mask, GLenum filter);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlitFramebufferLayersEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
GLAPI void APIENTRY glBlitFramebufferLayerEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint srcLayer, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstLayer, GLbitfield mask, GLenum filter);
#endif
#endif /* GL_EXT_framebuffer_blit_layers */
#ifndef GL_EXT_framebuffer_multisample
#define GL_EXT_framebuffer_multisample 1
#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB
@ -7524,7 +7534,6 @@ GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index,
#ifndef GL_EXT_gpu_shader4
#define GL_EXT_gpu_shader4 1
#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD
#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0
#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1
#define GL_SAMPLER_BUFFER_EXT 0x8DC2
@ -7552,6 +7561,7 @@ GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index,
#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8
#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904
#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905
#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD
typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params);
typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name);
typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name);
@ -7563,6 +7573,29 @@ typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count,
typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value);
typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value);
typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params);
GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name);
@ -7575,6 +7608,29 @@ GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuin
GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value);
GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value);
GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value);
GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x);
GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y);
GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z);
GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w);
GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x);
GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y);
GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z);
GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v);
GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v);
GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v);
GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v);
GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params);
#endif
#endif /* GL_EXT_gpu_shader4 */
@ -8168,6 +8224,10 @@ GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers);
#define GL_EXT_shader_integer_mix 1
#endif /* GL_EXT_shader_integer_mix */
#ifndef GL_EXT_shader_samples_identical
#define GL_EXT_shader_samples_identical 1
#endif /* GL_EXT_shader_samples_identical */
#ifndef GL_EXT_shadow_funcs
#define GL_EXT_shadow_funcs 1
#endif /* GL_EXT_shadow_funcs */
@ -8536,6 +8596,11 @@ GLAPI void APIENTRY glTextureNormalEXT (GLenum mode);
#define GL_SR8_EXT 0x8FBD
#endif /* GL_EXT_texture_sRGB_R8 */
#ifndef GL_EXT_texture_sRGB_RG8
#define GL_EXT_texture_sRGB_RG8 1
#define GL_SRG8_EXT 0x8FBE
#endif /* GL_EXT_texture_sRGB_RG8 */
#ifndef GL_EXT_texture_sRGB_decode
#define GL_EXT_texture_sRGB_decode 1
#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48
@ -8574,6 +8639,36 @@ GLAPI void APIENTRY glTextureNormalEXT (GLenum mode);
#define GL_RGBA_SNORM 0x8F93
#endif /* GL_EXT_texture_snorm */
#ifndef GL_EXT_texture_storage
#define GL_EXT_texture_storage 1
#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F
#define GL_RGBA32F_EXT 0x8814
#define GL_RGB32F_EXT 0x8815
#define GL_ALPHA32F_EXT 0x8816
#define GL_LUMINANCE32F_EXT 0x8818
#define GL_LUMINANCE_ALPHA32F_EXT 0x8819
#define GL_RGBA16F_EXT 0x881A
#define GL_RGB16F_EXT 0x881B
#define GL_ALPHA16F_EXT 0x881C
#define GL_LUMINANCE16F_EXT 0x881E
#define GL_LUMINANCE_ALPHA16F_EXT 0x881F
#define GL_BGRA8_EXT 0x93A1
#define GL_R8_EXT 0x8229
#define GL_RG8_EXT 0x822B
#define GL_R32F_EXT 0x822E
#define GL_RG32F_EXT 0x8230
#define GL_R16F_EXT 0x822D
#define GL_RG16F_EXT 0x822F
typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width);
GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
#endif
#endif /* GL_EXT_texture_storage */
#ifndef GL_EXT_texture_swizzle
#define GL_EXT_texture_swizzle 1
#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42
@ -10163,12 +10258,6 @@ typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s,
typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q);
typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog);
typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog);
typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v);
typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight);
typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight);
typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x);
typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y);
@ -10181,6 +10270,12 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, c
typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v);
typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog);
typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog);
typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v);
typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight);
typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y);
GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v);
@ -10210,12 +10305,6 @@ GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t,
GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v);
GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q);
GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v);
GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog);
GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog);
GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v);
GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight);
GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight);
GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x);
GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v);
GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y);
@ -10228,6 +10317,12 @@ GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfN
GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v);
GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v);
GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v);
GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog);
GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog);
GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue);
GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v);
GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight);
GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight);
#endif
#endif /* GL_NV_half_float */
@ -10630,7 +10725,7 @@ typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenu
typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode);
typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]);
typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount);
typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);
@ -10695,7 +10790,7 @@ GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode,
GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode);
GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]);
GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount);
GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);
@ -11364,6 +11459,10 @@ GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id);
#endif
#endif /* GL_NV_transform_feedback2 */
#ifndef GL_NV_uniform_buffer_std430_layout
#define GL_NV_uniform_buffer_std430_layout 1
#endif /* GL_NV_uniform_buffer_std430_layout */
#ifndef GL_NV_uniform_buffer_unified_memory
#define GL_NV_uniform_buffer_unified_memory 1
#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E
@ -11765,54 +11864,6 @@ GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GL
#ifndef GL_NV_vertex_program4
#define GL_NV_vertex_program4 1
#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v);
typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x);
GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y);
GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z);
GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w);
GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x);
GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y);
GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z);
GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w);
GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v);
GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v);
GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v);
GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v);
GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v);
GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v);
GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params);
#endif
#endif /* GL_NV_vertex_program4 */
#ifndef GL_NV_video_capture

@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t;
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif

@ -20,7 +20,7 @@ extern "C" {
#include <windows.h>
#endif
#define WGL_WGLEXT_VERSION 20200813
#define WGL_WGLEXT_VERSION 20231018
/* Generated C header for:
* API: wgl

@ -0,0 +1,4 @@
add_files(
openttd_social_integration_api.h
openttd_social_integration_api_v1.h
)

@ -0,0 +1,20 @@
Copyright 2024 OpenTTD project
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,38 @@
/*
* Copyright 2024 OpenTTD project
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Although all the source-files created by OpenTTD are licensed under the
* GPL-v2, this file is an exception. This file is part of the API for
* social integration plugins, and licensed under the MIT license, to allow
* for non-free implementations.
*/
/** @file openttd_social_integration_api.h Interface definitions for plugins to report/respond to social integration. */
#ifndef OPENTTD_SOCIAL_INTEGRATION_API_H
#define OPENTTD_SOCIAL_INTEGRATION_API_H
#include "openttd_social_integration_api_v1.h"
#endif /* OPENTTD_SOCIAL_INTEGRATION_API_H */

@ -0,0 +1,157 @@
/*
* Copyright 2024 OpenTTD project
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Although all the source-files created by OpenTTD are licensed under the
* GPL-v2, this file is an exception. This file is part of the API for
* social integration plugins, and licensed under the MIT license, to allow
* for non-free implementations.
*/
/** @file v1.h Version 1 definition of the OpenTTD Social Integration Plugin API. */
#ifndef OPENTTD_SOCIAL_INTEGRATION_API_V1_H
#define OPENTTD_SOCIAL_INTEGRATION_API_V1_H
#ifdef __cplusplus
extern "C" {
#endif
/** Pointers supplied by the plugin for OpenTTD to use. */
struct OpenTTD_SocialIntegration_v1_PluginInfo {
/**
* The Social Platform this plugin is for.
*
* UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
*
* As there can only be one plugin active for each Social Platform, this
* value is used to determine which plugin to use.
*
* A complete list of names can be found here:
* https://wiki.openttd.org/en/Development/Social%20Integration
*
* Please use names from that list, including capitalization.
*
* If you create a plugin for a new Social Platform, please add it to the
* wiki page.
*/
const char *social_platform;
const char *name; ///< Full name of the plugin. UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
const char *version; ///< Version of the plugin. UTF-8, nul-terminated. The plugin is and remains the owner of the memory.
};
/** Pointers supplied by the plugin for OpenTTD to use. */
struct OpenTTD_SocialIntegration_v1_PluginApi {
/**
* OpenTTD tells the plugin to shut down.
*
* The plugin should free any resources it allocated, and must not call any of the callback functions after this call.
*/
void (*shutdown)();
/**
* OpenTTD calls this function at regular intervals, to handle any callbacks the plugin might have.
*
* It is also safe to call the OpenTTD_SocialIntegrationCallbacks functions here.
*
* @return True if the plugin wants to be called again, false if the plugin wants to be unloaded.
*/
bool (*run_callbacks)();
/**
* The player has entered the main menu.
*/
void (*event_enter_main_menu)();
/**
* The player has entered the Scenario Editor.
*
* @param map_width The width of the map in tiles.
* @param map_height The height of the map in tiles.
*/
void (*event_enter_scenario_editor)(unsigned int map_width, unsigned int map_height);
/**
* The player has entered a singleplayer game.
*
* @param map_width The width of the map in tiles.
* @param map_height The height of the map in tiles.
*/
void (*event_enter_singleplayer)(unsigned int map_width, unsigned int map_height);
/**
* The player has entered a multiplayer game.
*
* @param map_width The width of the map in tiles.
* @param map_height The height of the map in tiles.
*/
void (*event_enter_multiplayer)(unsigned int map_width, unsigned int map_height);
/**
* The player is joining a multiplayer game.
*
* This is followed by event_enter_multiplayer() if the join was successful.
*/
void (*event_joining_multiplayer)();
};
/** Pointers supplied by OpenTTD, for the plugin to use. */
struct OpenTTD_SocialIntegration_v1_OpenTTDInfo {
const char *openttd_version; ///< Version of OpenTTD. UTF-8, nul-terminated. OpenTTD is and remains the owner of the memory.
};
/** The result of the initialization. */
enum OpenTTD_SocialIntegration_v1_InitResult : int {
OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS = 1, ///< Plugin initialized successfully.
OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED = -1, ///< Plugin failed to initialize (generic error).
OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING = -2, ///< The Social Platform is not running.
};
/**
* Type of the Init function the plugin is expected to export from its dynamic library.
*
* The plugin has to export the implementation of this function as "SocialIntegration_vN_Init", where N is the API version this entry point is for.
* A single plugin can have multiple versions implemented.
*
* @param[out] plugin_api Structure the plugin must fill with pointers. Can contain nullptr if the plugin does not support a feature. The plugin is owner of the memory.
* @param[in] openttd_info Structure that OpenTTD filled with pointers. All pointers will remain valid until shutdown(). OpenTTD is owner of the memory.
* @return The status of the initialization.
*/
typedef OpenTTD_SocialIntegration_v1_InitResult (*OpenTTD_SocialIntegration_v1_Init)(OpenTTD_SocialIntegration_v1_PluginApi *plugin_api, const OpenTTD_SocialIntegration_v1_OpenTTDInfo *openttd_info);
/**
* Type of the GetInfo function the plugin is expected to export from its dynamic library.
*
* The plugin has to export the implementation of this function as "SocialIntegration_vN_GetInfo", where N is the API version this entry point is for.
* A single plugin can have multiple versions implemented.
*
* @param[out] plugin_info Structure the plugin must fill with pointers. The plugin is owner of the memory.
*/
typedef void (*OpenTTD_SocialIntegration_v1_GetInfo)(OpenTTD_SocialIntegration_v1_PluginInfo *plugin_info);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* OPENTTD_SOCIAL_INTEGRATION_API_V1_H */

@ -412,6 +412,8 @@ add_files(
signal.cpp
signal_func.h
signal_type.h
signature.cpp
signature.h
signs.cpp
signs_base.h
signs_cmd.cpp
@ -423,6 +425,8 @@ add_files(
smallmap_colours.h
smallmap_gui.cpp
smallmap_gui.h
social_integration.cpp
social_integration.h
sortlist_type.h
sound.cpp
sound_func.h

@ -77,9 +77,8 @@ static uint _script_current_depth; ///< Depth of scripts running (used to abort
/** File list storage for the console, for caching the last 'ls' command. */
class ConsoleFileList : public FileList {
public:
ConsoleFileList() : FileList()
ConsoleFileList(AbstractFileType abstract_filetype, bool show_dirs) : FileList(), abstract_filetype(abstract_filetype), show_dirs(show_dirs)
{
this->file_list_valid = false;
}
/** Declare the file storage cache as being invalid, also clears all stored files. */
@ -96,15 +95,19 @@ public:
void ValidateFileList(bool force_reload = false)
{
if (force_reload || !this->file_list_valid) {
this->BuildFileList(FT_SAVEGAME, SLO_LOAD);
this->BuildFileList(this->abstract_filetype, SLO_LOAD, this->show_dirs);
this->file_list_valid = true;
}
}
bool file_list_valid; ///< If set, the file list is valid.
AbstractFileType abstract_filetype; ///< The abstract file type to list.
bool show_dirs; ///< Whether to show directories in the file list.
bool file_list_valid = false; ///< If set, the file list is valid.
};
static ConsoleFileList _console_file_list; ///< File storage cache for the console.
static ConsoleFileList _console_file_list_savegame{FT_SAVEGAME, true}; ///< File storage cache for savegames.
static ConsoleFileList _console_file_list_scenario{FT_SCENARIO, false}; ///< File storage cache for scenarios.
static ConsoleFileList _console_file_list_heightmap{FT_HEIGHTMAP, false}; ///< File storage cache for heightmaps.
/* console command defines */
#define DEF_CONSOLE_CMD(function) static bool function([[maybe_unused]] byte argc, [[maybe_unused]] char *argv[])
@ -515,8 +518,8 @@ DEF_CONSOLE_CMD(ConLoad)
if (argc != 2) return false;
const char *file = argv[1];
_console_file_list.ValidateFileList();
const FiosItem *item = _console_file_list.FindItem(file);
_console_file_list_savegame.ValidateFileList();
const FiosItem *item = _console_file_list_savegame.FindItem(file);
if (item != nullptr) {
if (GetAbstractFileType(item->type) == FT_SAVEGAME) {
_switch_mode = SM_LOAD_GAME;
@ -531,6 +534,57 @@ DEF_CONSOLE_CMD(ConLoad)
return true;
}
DEF_CONSOLE_CMD(ConLoadScenario)
{
if (argc == 0) {
IConsoleHelp("Load a scenario by name or index. Usage: 'load_scenario <file | number>'.");
return true;
}
if (argc != 2) return false;
const char *file = argv[1];
_console_file_list_scenario.ValidateFileList();
const FiosItem *item = _console_file_list_scenario.FindItem(file);
if (item != nullptr) {
if (GetAbstractFileType(item->type) == FT_SCENARIO) {
_switch_mode = SM_LOAD_GAME;
_file_to_saveload.Set(*item);
} else {
IConsolePrintF(CC_ERROR, "'%s' is not a scenario.", file);
}
} else {
IConsolePrintF(CC_ERROR, "'%s' cannot be found.", file);
}
return true;
}
DEF_CONSOLE_CMD(ConLoadHeightmap)
{
if (argc == 0) {
IConsoleHelp("Load a heightmap by name or index. Usage: 'load_heightmap <file | number>'.");
return true;
}
if (argc != 2) return false;
const char *file = argv[1];
_console_file_list_heightmap.ValidateFileList();
const FiosItem *item = _console_file_list_heightmap.FindItem(file);
if (item != nullptr) {
if (GetAbstractFileType(item->type) == FT_HEIGHTMAP) {
_switch_mode = SM_START_HEIGHTMAP;
_file_to_saveload.Set(*item);
} else {
IConsolePrintF(CC_ERROR, "'%s' is not a heightmap.", file);
}
} else {
IConsolePrintF(CC_ERROR, "'%s' cannot be found.", file);
}
return true;
}
DEF_CONSOLE_CMD(ConRemove)
{
@ -542,8 +596,8 @@ DEF_CONSOLE_CMD(ConRemove)
if (argc != 2) return false;
const char *file = argv[1];
_console_file_list.ValidateFileList();
const FiosItem *item = _console_file_list.FindItem(file);
_console_file_list_savegame.ValidateFileList();
const FiosItem *item = _console_file_list_savegame.FindItem(file);
if (item != nullptr) {
if (unlink(item->name.c_str()) != 0) {
IConsolePrintF(CC_ERROR, "%s: Failed to delete file", file);
@ -552,7 +606,7 @@ DEF_CONSOLE_CMD(ConRemove)
IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
}
_console_file_list.InvalidateFileList();
_console_file_list_savegame.InvalidateFileList();
return true;
}
@ -565,9 +619,41 @@ DEF_CONSOLE_CMD(ConListFiles)
return true;
}
_console_file_list.ValidateFileList(true);
for (uint i = 0; i < _console_file_list.size(); i++) {
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title.c_str());
_console_file_list_savegame.ValidateFileList(true);
for (uint i = 0; i < _console_file_list_savegame.size(); i++) {
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list_savegame[i].title.c_str());
}
return true;
}
/* List all the scenarios */
DEF_CONSOLE_CMD(ConListScenarios)
{
if (argc == 0) {
IConsoleHelp("List all loadable scenarios. Usage: 'list_scenarios'.");
return true;
}
_console_file_list_scenario.ValidateFileList(true);
for (uint i = 0; i < _console_file_list_scenario.size(); i++) {
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list_scenario[i].title.c_str());
}
return true;
}
/* List all the heightmaps */
DEF_CONSOLE_CMD(ConListHeightmaps)
{
if (argc == 0) {
IConsoleHelp("List all loadable heightmaps. Usage: 'list_heightmaps'.");
return true;
}
_console_file_list_heightmap.ValidateFileList(true);
for (uint i = 0; i < _console_file_list_heightmap.size(); i++) {
IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list_heightmap[i].title.c_str());
}
return true;
@ -584,8 +670,8 @@ DEF_CONSOLE_CMD(ConChangeDirectory)
if (argc != 2) return false;
const char *file = argv[1];
_console_file_list.ValidateFileList(true);
const FiosItem *item = _console_file_list.FindItem(file);
_console_file_list_savegame.ValidateFileList(true);
const FiosItem *item = _console_file_list_savegame.FindItem(file);
if (item != nullptr) {
switch (item->type) {
case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT:
@ -597,7 +683,7 @@ DEF_CONSOLE_CMD(ConChangeDirectory)
IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
}
_console_file_list.InvalidateFileList();
_console_file_list_savegame.InvalidateFileList();
return true;
}
@ -609,8 +695,8 @@ DEF_CONSOLE_CMD(ConPrintWorkingDirectory)
}
/* XXX - Workaround for broken file handling */
_console_file_list.ValidateFileList(true);
_console_file_list.InvalidateFileList();
_console_file_list_savegame.ValidateFileList(true);
_console_file_list_savegame.InvalidateFileList();
IConsolePrint(CC_DEFAULT, FiosGetCurrentPath().c_str());
return true;
@ -2406,6 +2492,7 @@ DEF_CONSOLE_CMD(ConListDirs)
{ SAVE_DIR, "save", true },
{ AUTOSAVE_DIR, "autosave", true },
{ SCREENSHOT_DIR, "screenshot", true },
{ SOCIAL_INTEGRATION_DIR, "social_integration", true },
};
if (argc != 2) {
@ -3836,10 +3923,16 @@ void IConsoleStdLibRegister()
IConsole::AliasRegister("scrollto_highlight", "scrollto %+; highlight_tile %+");
IConsole::CmdRegister("alias", ConAlias);
IConsole::CmdRegister("load", ConLoad);
IConsole::CmdRegister("load_save", ConLoad);
IConsole::CmdRegister("load_scenario", ConLoadScenario);
IConsole::CmdRegister("load_heightmap", ConLoadHeightmap);
IConsole::CmdRegister("rm", ConRemove);
IConsole::CmdRegister("save", ConSave);
IConsole::CmdRegister("saveconfig", ConSaveConfig);
IConsole::CmdRegister("ls", ConListFiles);
IConsole::CmdRegister("list_saves", ConListFiles);
IConsole::CmdRegister("list_scenarios", ConListScenarios);
IConsole::CmdRegister("list_heightmaps", ConListHeightmaps);
IConsole::CmdRegister("cd", ConChangeDirectory);
IConsole::CmdRegister("pwd", ConPrintWorkingDirectory);
IConsole::CmdRegister("clear", ConClearBuffer);

@ -377,23 +377,6 @@ constexpr int RoundDivSU(int a, uint b)
}
}
/**
* Computes (a / b) rounded away from zero.
* @param a Numerator
* @param b Denominator
* @return Quotient, rounded away from zero
*/
constexpr int DivAwayFromZero(int a, uint b)
{
const int _b = static_cast<int>(b);
if (a > 0) {
return (a + _b - 1) / _b;
} else {
/* Note: Behaviour of negative numerator division is truncation toward zero. */
return (a - _b + 1) / _b;
}
}
/**
* Computes a / b rounded towards negative infinity for b > 0.
* @param a Numerator

@ -39,6 +39,7 @@
#include "progress.h"
#include "settings_type.h"
#include "settings_internal.h"
#include "social_integration.h"
#include "ai/ai_info.hpp"
#include "game/game.hpp"
@ -438,6 +439,20 @@ char *CrashLog::LogLibraries(char *buffer, const char *last) const
return buffer;
}
/**
* Writes information (versions) of the used plugins.
* @param buffer The begin where to write at.
* @param last The last position in the buffer to write to.
* @return the position of the \c '\0' character after the buffer.
*/
char *CrashLog::LogPlugins(char *buffer, const char *last) const
{
if (SocialIntegration::GetPluginCount() == 0) return buffer;
buffer += seprintf(buffer, last, "Plugins:\n");
return SocialIntegration::LogPluginSummary(buffer, last);
}
/**
* Helper function for printing the gamelog.
* @param s the string to print.
@ -633,6 +648,9 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last)
buffer = this->TryCrashLogFaultSection(buffer, last, "libraries", [](CrashLog *self, char *buffer, const char *last) -> char * {
return self->LogLibraries(buffer, last);
});
buffer = this->TryCrashLogFaultSection(buffer, last, "plugins", [](CrashLog *self, char *buffer, const char *last) -> char * {
return self->LogPlugins(buffer, last);
});
buffer = this->TryCrashLogFaultSection(buffer, last, "settings", [](CrashLog *self, char *buffer, const char *last) -> char * {
return self->LogSettings(buffer, last);
});
@ -784,6 +802,7 @@ char *CrashLog::FillVersionInfoLog(char *buffer, const char *last) const
buffer = this->LogCompiler(buffer, last);
buffer = this->LogOSVersionDetail(buffer, last);
buffer = this->LogLibraries(buffer, last);
buffer = this->LogPlugins(buffer, last);
buffer += seprintf(buffer, last, "*** End of OpenTTD Version Info Report ***\n");
return buffer;

@ -131,6 +131,7 @@ protected:
char *LogOpenTTDVersion(char *buffer, const char *last) const;
char *LogConfiguration(char *buffer, const char *last) const;
char *LogLibraries(char *buffer, const char *last) const;
char *LogPlugins(char *buffer, const char *last) const;
char *LogGamelog(char *buffer, const char *last) const;
char *LogRecentNews(char *buffer, const char *list) const;
char *LogCommandLog(char *buffer, const char *last) const;

@ -53,6 +53,7 @@ static const char * const _subdirs[] = {
"game" PATHSEP,
"game" PATHSEP "library" PATHSEP,
"screenshot" PATHSEP,
"social_integration" PATHSEP,
};
static_assert(lengthof(_subdirs) == NUM_SUBDIRS);
@ -1090,7 +1091,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
DEBUG(misc, 1, "%s found as personal directory", _personal_dir.c_str());
static const Subdirectory default_subdirs[] = {
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR, SOCIAL_INTEGRATION_DIR
};
for (uint i = 0; i < lengthof(default_subdirs); i++) {
@ -1104,7 +1105,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
FillValidSearchPaths(only_local_path);
/* Create the directory for each of the types of content */
const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR };
const Subdirectory dirs[] = { SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SOCIAL_INTEGRATION_DIR };
for (uint i = 0; i < lengthof(dirs); i++) {
FioCreateDirectory(FioGetDirectory(SP_AUTODOWNLOAD_DIR, dirs[i]));
}

@ -121,6 +121,7 @@ enum Subdirectory {
GAME_DIR, ///< Subdirectory for all game scripts
GAME_LIBRARY_DIR, ///< Subdirectory for all GS libraries
SCREENSHOT_DIR, ///< Subdirectory for all screenshots
SOCIAL_INTEGRATION_DIR, ///< Subdirectory for all social integration plugins
NUM_SUBDIRS, ///< Number of subdirectories
NO_DIRECTORY, ///< A path without any base directory
};

@ -68,8 +68,9 @@ bool FiosItem::operator< (const FiosItem &other) const
* Construct a file list with the given kind of files, for the stated purpose.
* @param abstract_filetype Kind of files to collect.
* @param fop Purpose of the collection, either #SLO_LOAD or #SLO_SAVE.
* @param show_dirs Whether to show directories.
*/
void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop)
void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs)
{
this->clear();
@ -79,15 +80,15 @@ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperati
break;
case FT_SAVEGAME:
FiosGetSavegameList(fop, *this);
FiosGetSavegameList(fop, show_dirs, *this);
break;
case FT_SCENARIO:
FiosGetScenarioList(fop, *this);
FiosGetScenarioList(fop, show_dirs, *this);
break;
case FT_HEIGHTMAP:
FiosGetHeightmapList(fop, *this);
FiosGetHeightmapList(fop, show_dirs, *this);
break;
default:
@ -345,11 +346,12 @@ bool FiosFileScanner::AddFile(const std::string &filename, size_t, const std::st
/**
* Fill the list of the files in a directory, according to some arbitrary rule.
* @param fop Purpose of collecting the list.
* @param show_dirs Whether to list directories.
* @param callback_proc The function that is called where you need to do the filtering.
* @param subdir The directory from where to start (global) searching.
* @param file_list Destination of the found files.
*/
static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *callback_proc, Subdirectory subdir, FileList &file_list)
static void FiosGetFileList(SaveLoadOperation fop, bool show_dirs, fios_getlist_callback_proc *callback_proc, Subdirectory subdir, FileList &file_list)
{
struct stat sb;
struct dirent *dirent;
@ -362,7 +364,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c
assert(_fios_path != nullptr);
/* A parent directory link exists if we are not in the root directory */
if (!FiosIsRoot(_fios_path->c_str())) {
if (show_dirs && !FiosIsRoot(_fios_path->c_str())) {
fios = &file_list.emplace_back();
fios->type = FIOS_TYPE_PARENT;
fios->mtime = 0;
@ -372,7 +374,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c
}
/* Show subdirectories */
if ((dir = ttd_opendir(_fios_path->c_str())) != nullptr) {
if (show_dirs && (dir = ttd_opendir(_fios_path->c_str())) != nullptr) {
while ((dirent = readdir(dir)) != nullptr) {
std::string d_name = FS2OTTD(dirent->d_name);
@ -392,7 +394,7 @@ static void FiosGetFileList(SaveLoadOperation fop, fios_getlist_callback_proc *c
}
/* Sort the subdirs always by name, ascending, remember user-sorting order */
{
if (show_dirs) {
SortingBits order = _savegame_sort_order;
_savegame_sort_order = SORT_BY_NAME | SORT_ASCENDING;
std::sort(file_list.begin(), file_list.end());
@ -461,7 +463,7 @@ FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &f
* .SV2 Transport Tycoon Deluxe (Patch) saved 2-player game */
/* Don't crash if we supply no extension */
if (ext == nullptr) return FIOS_TYPE_INVALID;
if (ext == nullptr) ext = "";
if (StrEqualsIgnoreCase(ext, ".sav")) {
GetFileTitle(file, title, last, SAVE_DIR);
@ -482,10 +484,11 @@ FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &f
/**
* Get a list of savegames.
* @param fop Purpose of collecting the list.
* @param show_dirs Whether to show directories.
* @param file_list Destination of the found files.
* @see FiosGetFileList
*/
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
{
static std::optional<std::string> fios_save_path;
@ -493,7 +496,7 @@ void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
_fios_path = &(*fios_save_path);
FiosGetFileList(fop, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list);
FiosGetFileList(fop, show_dirs, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list);
}
/**
@ -507,7 +510,7 @@ void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list)
* @see FiosGetFileList
* @see FiosGetScenarioList
*/
static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
{
/* Show scenario files
* .SCN OpenTTD style scenario file
@ -531,10 +534,11 @@ static FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::st
/**
* Get a list of scenarios.
* @param fop Purpose of collecting the list.
* @param show_dirs Whether to show directories.
* @param file_list Destination of the found files.
* @see FiosGetFileList
*/
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list)
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
{
static std::optional<std::string> fios_scn_path;
@ -545,10 +549,10 @@ void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list)
std::string base_path = FioFindDirectory(SCENARIO_DIR);
Subdirectory subdir = (fop == SLO_LOAD && base_path == *_fios_path) ? SCENARIO_DIR : NO_DIRECTORY;
FiosGetFileList(fop, &FiosGetScenarioListCallback, subdir, file_list);
FiosGetFileList(fop, show_dirs, &FiosGetScenarioListCallback, subdir, file_list);
}
static FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
{
/* Show heightmap files
* .PNG PNG Based heightmap files
@ -593,9 +597,10 @@ static FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const std::s
/**
* Get a list of heightmaps.
* @param fop Purpose of collecting the list.
* @param show_dirs Whether to show directories.
* @param file_list Destination of the found files.
*/
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list)
void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
{
static std::optional<std::string> fios_hmap_path;
@ -605,7 +610,7 @@ void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list)
std::string base_path = FioFindDirectory(HEIGHTMAP_DIR);
Subdirectory subdir = base_path == *_fios_path ? HEIGHTMAP_DIR : NO_DIRECTORY;
FiosGetFileList(fop, &FiosGetHeightmapListCallback, subdir, file_list);
FiosGetFileList(fop, show_dirs, &FiosGetHeightmapListCallback, subdir, file_list);
}
/**

@ -36,7 +36,7 @@ struct FiosItem {
/** List of file information. */
class FileList : public std::vector<FiosItem> {
public:
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop);
void BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperation fop, bool show_dirs);
const FiosItem *FindItem(const std::string_view file);
};
@ -53,9 +53,9 @@ extern SortingBits _savegame_sort_order;
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop);
void FiosGetSavegameList(SaveLoadOperation fop, FileList &file_list);
void FiosGetScenarioList(SaveLoadOperation fop, FileList &file_list);
void FiosGetHeightmapList(SaveLoadOperation fop, FileList &file_list);
void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
bool FiosBrowseTo(const FiosItem *item);
@ -66,6 +66,8 @@ std::string FiosMakeHeightmapName(const char *name);
std::string FiosMakeSavegameName(const char *name);
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
void ScanScenarios();
const char *FindScenario(const ContentInfo *ci, bool md5sum);

@ -879,7 +879,7 @@ public:
if (!gui_scope) break;
_fios_path_changed = true;
this->fios_items.BuildFileList(this->abstract_filetype, this->fop);
this->fios_items.BuildFileList(this->abstract_filetype, this->fop, true);
this->selected = nullptr;
_load_check_data.Clear();

@ -115,8 +115,14 @@ static void _GenerateWorld()
/* Must start economy early because of the costs. */
StartupEconomy();
bool landscape_generated = false;
/* Don't generate landscape items when in the scenario editor. */
if (_gw.mode == GWM_EMPTY) {
if (_gw.mode != GWM_EMPTY) {
landscape_generated = GenerateLandscape(_gw.mode);
}
if (!landscape_generated) {
SetGeneratingWorldProgress(GWP_OBJECT, 1);
/* Make sure the tiles at the north border are void tiles if needed. */
@ -135,7 +141,6 @@ static void _GenerateWorld()
UpdateCachedSnowLine();
UpdateCachedSnowLineBounds();
} else {
GenerateLandscape(_gw.mode);
GenerateClearTile();
/* Only generate towns, tree and industries in newgame mode. */

@ -525,14 +525,14 @@ bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x,
* @param dft Type of image file.
* @param filename of the heightmap file to be imported
*/
void LoadHeightmap(DetailedFileType dft, const char *filename)
bool LoadHeightmap(DetailedFileType dft, const char *filename)
{
uint x, y;
byte *map = nullptr;
if (!ReadHeightMap(dft, filename, &x, &y, &map)) {
free(map);
return;
return false;
}
GrayscaleToMapHeights(x, y, map);
@ -540,6 +540,8 @@ void LoadHeightmap(DetailedFileType dft, const char *filename)
FixSlopes();
MarkWholeScreenDirty();
return true;
}
/**

@ -22,7 +22,7 @@ enum HeightmapRotation {
};
bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x, uint *y);
void LoadHeightmap(DetailedFileType dft, const char *filename);
bool LoadHeightmap(DetailedFileType dft, const char *filename);
void FlatEmptyWorld(byte tile_height);
void FixSlopes();

@ -190,7 +190,7 @@ struct SelectGameWindow : public Window {
this->mouse_idle_pos = _cursor.pos;
}
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
void OnRealtimeTick(uint delta_ms) override
{
/* Move the main game viewport according to intro viewport commands. */
@ -252,7 +252,7 @@ struct SelectGameWindow : public Window {
/* Update the viewport position. */
mw->viewport->dest_scrollpos_x = mw->viewport->scrollpos_x = pos.x;
mw->viewport->dest_scrollpos_y = mw->viewport->scrollpos_y = pos.y;
UpdateNextViewportPosition(mw);
UpdateNextViewportPosition(mw, delta_ms);
ApplyNextViewportPosition(mw);
mw->SetDirty(); // Required during panning, otherwise logo graphics disappears

@ -1516,7 +1516,7 @@ static uint8_t CalculateDesertLine()
return CalculateCoverageLine(100 - _settings_game.game_creation.desert_coverage, 4);
}
void GenerateLandscape(byte mode)
bool GenerateLandscape(byte mode)
{
/** Number of steps of landscape generation */
enum GenLandscapeSteps {
@ -1530,7 +1530,9 @@ void GenerateLandscape(byte mode)
if (mode == GWM_HEIGHTMAP) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_HEIGHTMAP);
LoadHeightmap(_file_to_saveload.detail_ftype, _file_to_saveload.name.c_str());
if (!LoadHeightmap(_file_to_saveload.detail_ftype, _file_to_saveload.name.c_str())) {
return false;
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
} else if (_settings_game.game_creation.land_generator == LG_TERRAGENESIS) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_TERRAGENESIS);
@ -1616,6 +1618,7 @@ void GenerateLandscape(byte mode)
}
CreateRivers();
return true;
}
void OnTick_Town();

@ -172,6 +172,6 @@ void RunTileLoop(bool apply_day_length = false);
void RunAuxiliaryTileLoop();
void InitializeLandscape();
void GenerateLandscape(byte mode);
bool GenerateLandscape(byte mode);
#endif /* LANDSCAPE_H */

@ -29,6 +29,7 @@
#include "network_gamelist.h"
#include "../core/backup_type.hpp"
#include "../thread.h"
#include "../social_integration.h"
#include "../crashlog.h"
#include "../core/checksum_func.hpp"
#include "../core/alloc_func.hpp"
@ -1083,6 +1084,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
SetLocalCompany(_network_join.company);
}
SocialIntegration::EventEnterMultiplayer(MapSizeX(), MapSizeY());
return NETWORK_RECV_STATUS_OKAY;
}

@ -29,6 +29,8 @@
#include "../core/pool_func.hpp"
#include "../core/random_func.hpp"
#include "../rev.h"
#include "../timer/timer.h"
#include "../timer/timer_game_realtime.h"
#include "../crashlog.h"
#include "../3rdparty/monocypher/monocypher.h"
#include <mutex>
@ -1826,29 +1828,6 @@ void NetworkUpdateClientInfo(ClientID client_id)
NetworkAdminClientUpdate(ci);
}
/** Check if we want to restart the map */
static void NetworkCheckRestartMap()
{
if (_settings_client.network.restart_game_year != 0 && CalTime::CurYear() >= _settings_client.network.restart_game_year) {
DEBUG(net, 3, "Auto-restarting map: year %d reached", CalTime::CurYear().base());
_settings_newgame.game_creation.generation_seed = GENERATE_NEW_SEED;
switch(_file_to_saveload.abstract_ftype) {
case FT_SAVEGAME:
case FT_SCENARIO:
_switch_mode = SM_LOAD_GAME;
break;
case FT_HEIGHTMAP:
_switch_mode = SM_START_HEIGHTMAP;
break;
default:
_switch_mode = SM_NEWGAME;
}
}
}
/** Check if the server has autoclean_companies activated
* Two things happen:
* 1) If a company is not protected, it is closed after 1 year (for example)
@ -2148,10 +2127,64 @@ void NetworkServer_Tick(bool send_frame)
}
}
/** Helper function to restart the map. */
static void NetworkRestartMap()
{
_settings_newgame.game_creation.generation_seed = GENERATE_NEW_SEED;
switch (_file_to_saveload.abstract_ftype) {
case FT_SAVEGAME:
case FT_SCENARIO:
_switch_mode = SM_LOAD_GAME;
break;
case FT_HEIGHTMAP:
_switch_mode = SM_START_HEIGHTMAP;
break;
default:
_switch_mode = SM_NEWGAME;
}
}
/** Timer to restart a network server automatically based on real-time hours played. Initialized at zero to disable until settings are loaded. */
static IntervalTimer<TimerGameRealtime> _network_restart_map_timer({std::chrono::hours::zero(), TimerGameRealtime::UNPAUSED}, [](auto)
{
if (!_network_server) return;
/* If setting is 0, this feature is disabled. */
if (_settings_client.network.restart_hours == 0) return;
DEBUG(net, 3, "Auto-restarting map: %d hours played", _settings_client.network.restart_hours);
NetworkRestartMap();
});
/**
* Reset the automatic network restart time interval.
* @param reset Whether to reset the timer to zero.
*/
void ChangeNetworkRestartTime(bool reset)
{
if (!_network_server) return;
_network_restart_map_timer.SetInterval({ std::chrono::hours(_settings_client.network.restart_hours), TimerGameRealtime::UNPAUSED }, reset);
}
/** Check if we want to restart the map based on the year. */
static void NetworkCheckRestartMapYear()
{
/* If setting is 0, this feature is disabled. */
if (_settings_client.network.restart_game_year == 0) return;
if (CalTime::CurYear() >= _settings_client.network.restart_game_year) {
DEBUG(net, 3, "Auto-restarting map: year %d reached", CalTime::CurYear().base());
NetworkRestartMap();
}
}
/** Yearly "callback". Called whenever the year changes. */
void NetworkServerCalendarYearlyLoop()
{
NetworkCheckRestartMap();
NetworkCheckRestartMapYear();
}
/** Yearly "callback". Called whenever the year changes. */

@ -155,6 +155,7 @@ public:
};
void NetworkServer_Tick(bool send_frame);
void ChangeNetworkRestartTime(bool reset);
void NetworkServerSetCompanyPassword(CompanyID company_id, const std::string &password, bool already_hashed = true);
void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded);

@ -64,6 +64,7 @@ std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview)
SurveyFont(info["font"]);
SurveyCompiler(info["compiler"]);
SurveyLibraries(info["libraries"]);
SurveyPlugins(info["plugins"]);
}
{

@ -34,6 +34,7 @@
#include "console_func.h"
#include "screenshot.h"
#include "network/network.h"
#include "network/network_server.h"
#include "network/network_func.h"
#include "ai/ai.hpp"
#include "ai/ai_config.hpp"
@ -92,6 +93,7 @@
#include "timer/timer.h"
#include "timer/timer_game_realtime.h"
#include "timer/timer_game_tick.h"
#include "social_integration.h"
#include "network/network_sync.h"
#include "plans_func.h"
@ -286,7 +288,7 @@ static void ShowHelp()
" -t year = Set starting year\n"
" -d [[fac=]lvl[,...]]= Debug mode\n"
" -e = Start Editor\n"
" -g [savegame] = Start new/save game immediately\n"
" -g [savegame|scenario|heightmap] = Start new/savegame/scenario/heightmap immediately\n"
" -G seed = Set random seed\n"
" -n host[:port][#company]= Join network game\n"
" -p password = Password to join server\n"
@ -481,6 +483,7 @@ static void ShutdownGame()
if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
SocialIntegration::Shutdown();
DriverFactoryBase::ShutdownDrivers();
UnInitWindowSystem();
@ -829,21 +832,42 @@ int openttd_main(int argc, char *argv[])
if (mgo.opt != nullptr) SetDebugString(mgo.opt, ShowInfoI);
break;
}
case 'e': _switch_mode = (_switch_mode == SM_LOAD_GAME || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_EDITOR); break;
case 'e':
/* Allow for '-e' before or after '-g'. */
switch (_switch_mode) {
case SM_MENU: _switch_mode = SM_EDITOR; break;
case SM_LOAD_GAME: _switch_mode = SM_LOAD_SCENARIO; break;
case SM_START_HEIGHTMAP: _switch_mode = SM_LOAD_HEIGHTMAP; break;
default: break;
}
break;
case 'g':
if (mgo.opt != nullptr) {
_file_to_saveload.name = mgo.opt;
bool is_scenario = _switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO;
_switch_mode = is_scenario ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
_file_to_saveload.SetMode(SLO_LOAD, is_scenario ? FT_SCENARIO : FT_SAVEGAME, DFT_GAME_FILE);
/* if the file doesn't exist or it is not a valid savegame, let the saveload code show an error */
std::string extension;
auto t = _file_to_saveload.name.find_last_of('.');
if (t != std::string::npos) {
FiosType ft = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, _file_to_saveload.name.substr(t).c_str(), nullptr, nullptr);
if (ft != FIOS_TYPE_INVALID) _file_to_saveload.SetMode(ft);
extension = _file_to_saveload.name.substr(t);
}
FiosType ft = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, extension.c_str(), nullptr, nullptr);
if (ft == FIOS_TYPE_INVALID) {
ft = FiosGetScenarioListCallback(SLO_LOAD, _file_to_saveload.name, extension.c_str(), nullptr, nullptr);
}
if (ft == FIOS_TYPE_INVALID) {
ft = FiosGetHeightmapListCallback(SLO_LOAD, _file_to_saveload.name, extension.c_str(), nullptr, nullptr);
}
/* Allow for '-e' before or after '-g'. */
switch (GetAbstractFileType(ft)) {
case FT_SAVEGAME: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_SCENARIO: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_HEIGHTMAP: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP); break;
default: break;
}
_file_to_saveload.SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft));
break;
}
@ -1025,6 +1049,7 @@ int openttd_main(int argc, char *argv[])
/* The video driver is now selected, now initialise GUI zoom */
AdjustGUIZoom(AGZM_STARTUP);
SocialIntegration::Initialize();
NetworkStartUp(); // initialize network-core
if (!HandleBootstrap()) {
@ -1207,7 +1232,11 @@ static void MakeNewGameDone()
CheckIndustries();
MarkWholeScreenDirty();
if (_network_server && !_network_dedicated) ShowClientList();
if (_network_server) {
ChangeNetworkRestartTime(true);
if (!_network_dedicated) ShowClientList();
}
}
/*
@ -1333,6 +1362,28 @@ bool SafeLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileTy
return false;
}
static void UpdateSocialIntegration(GameMode game_mode)
{
switch (game_mode) {
case GM_BOOTSTRAP:
case GM_MENU:
SocialIntegration::EventEnterMainMenu();
break;
case GM_NORMAL:
if (_networking) {
SocialIntegration::EventEnterMultiplayer(MapSizeX(), MapSizeY());
} else {
SocialIntegration::EventEnterSingleplayer(MapSizeX(), MapSizeY());
}
break;
case GM_EDITOR:
SocialIntegration::EventEnterScenarioEditor(MapSizeX(), MapSizeY());
break;
}
}
void SwitchToMode(SwitchMode new_mode)
{
/* If we are saving something, the network stays in its current state */
@ -1383,6 +1434,8 @@ void SwitchToMode(SwitchMode new_mode)
case SM_EDITOR: // Switch to scenario editor
MakeNewEditorWorld();
GenerateSavegameId();
UpdateSocialIntegration(GM_EDITOR);
break;
case SM_RELOADGAME: // Reload with what-ever started the game
@ -1400,12 +1453,16 @@ void SwitchToMode(SwitchMode new_mode)
MakeNewGame(false, new_mode == SM_NEWGAME);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_RESTARTGAME: // Restart --> 'Random game' with current settings
case SM_NEWGAME: // New Game --> 'Random game'
MakeNewGame(false, new_mode == SM_NEWGAME);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_LOAD_GAME: { // Load game, Play Scenario
@ -1423,6 +1480,8 @@ void SwitchToMode(SwitchMode new_mode)
/* Decrease pause counter (was increased from opening load dialog) */
DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE);
}
UpdateSocialIntegration(GM_NORMAL);
break;
}
@ -1430,15 +1489,21 @@ void SwitchToMode(SwitchMode new_mode)
case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
MakeNewGame(true, new_mode == SM_START_HEIGHTMAP);
GenerateSavegameId();
UpdateSocialIntegration(GM_NORMAL);
break;
case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
SetLocalCompany(OWNER_NONE);
_game_mode = GM_EDITOR;
FixConfigMapSize();
GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
GenerateSavegameId();
MarkWholeScreenDirty();
UpdateSocialIntegration(GM_EDITOR);
break;
case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
@ -1452,12 +1517,16 @@ void SwitchToMode(SwitchMode new_mode)
SetDParamStr(0, GetSaveLoadErrorString());
ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_CRITICAL);
}
UpdateSocialIntegration(GM_EDITOR);
break;
}
case SM_JOIN_GAME: // Join a multiplayer game
LoadIntroGame();
NetworkClientJoinGame();
SocialIntegration::EventJoiningMultiplayer();
break;
case SM_MENU: // Switch to game intro menu
@ -1474,6 +1543,8 @@ void SwitchToMode(SwitchMode new_mode)
ShowNetworkAskSurvey();
}
}
UpdateSocialIntegration(GM_MENU);
break;
case SM_SAVE_GAME: { // Save game.
@ -2393,4 +2464,5 @@ void GameLoop()
SoundDriver::GetInstance()->MainLoop();
MusicLoop();
SocialIntegration::RunCallbacks();
}

@ -1929,9 +1929,6 @@ public:
this->GetWidget<NWidgetStacked>(WID_O_SEL_MGMT)->SetDisplayedPlane(this->current_mgmt_plane);
}
this->FinishInitNested(v->index);
if (v->owner == _local_company) {
this->DisableWidget(WID_O_EMPTY);
}
this->selected_order = -1;
this->order_over = INVALID_VEH_ORDER_ID;
@ -3792,8 +3789,7 @@ static constexpr NWidgetPart _nested_orders_train_widgets[] = {
SetDataTip(STR_NULL, STR_NULL), SetResize(1, 0),
EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_RIGHT),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_EMPTY), SetMinimalSize(93, 12), SetFill(1, 0),
SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(93, 12), SetFill(1, 0), SetResize(1, 0), EndContainer(),
NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(93, 12), SetFill(1, 0),
SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
EndContainer(),

@ -2249,8 +2249,18 @@ void DrawRoadBits(TileInfo *ti, RoadBits road, RoadBits tram, Roadside roadside,
/* If there are no road bits, return, as there is nothing left to do */
if (HasAtMostOneBit(road)) return;
/* Do not draw details when invisible. */
if (roadside == ROADSIDE_TREES && IsInvisibilitySet(TO_TREES)) return;
bool is_transparent = roadside == ROADSIDE_TREES && IsTransparencySet(TO_TREES);
if (roadside == ROADSIDE_STREET_LIGHTS && IsInvisibilitySet(TO_HOUSES)) return;
/* Check whether details should be transparent. */
bool is_transparent = false;
if (roadside == ROADSIDE_TREES && IsTransparencySet(TO_TREES)) {
is_transparent = true;
}
if (roadside == ROADSIDE_STREET_LIGHTS && IsTransparencySet(TO_HOUSES)) {
is_transparent = true;
}
/* Draw extra details. */
for (const DrawRoadTileStruct *drts = _road_display_table[roadside][road | tram]; drts->image != 0; drts++) {

@ -81,6 +81,10 @@
* \li GSGoal::SetDestination
* \li GSIndustry::GetProductionLevel
* \li GSIndustry::SetProductionLevel
* \li GSStoryPage::IsValidStoryPageElementType
* \li GSStoryPage::IsValidStoryPageButtonColour
* \li GSStoryPage::IsValidStoryPageButtonFlags
* \li GSStoryPage::IsValidStoryPageButtonCursor
*
* API removals:
* \li GSError::ERR_PRECONDITION_TOO_MANY_PARAMETERS, that error is never returned anymore.

@ -37,6 +37,11 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
return ::StoryPageElement::IsValidID(story_page_element_id);
}
/* static */ bool ScriptStoryPage::IsValidStoryPageElementType(StoryPageElementType type)
{
return type == SPET_TEXT || type == SPET_LOCATION || type == SPET_GOAL || type == SPET_BUTTON_PUSH || type == SPET_BUTTON_TILE || type == SPET_BUTTON_VEHICLE;
}
/* static */ ScriptStoryPage::StoryPageID ScriptStoryPage::New(ScriptCompany::CompanyID company, Text *title)
{
CCountedPtr<Text> counter(title);
@ -66,6 +71,7 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
EnforceDeityMode(STORY_PAGE_ELEMENT_INVALID);
EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, IsValidStoryPage(story_page_id));
EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, IsValidStoryPageElementType(type));
std::string encoded_text;
if (StoryPageElementTypeRequiresText(btype)) {
EnforcePrecondition(STORY_PAGE_ELEMENT_INVALID, text != nullptr);
@ -226,10 +232,32 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
return ScriptObject::DoCommand(0, story_page_element_id, 0, CMD_REMOVE_STORY_PAGE_ELEMENT);
}
/* static */ bool ScriptStoryPage::IsValidStoryPageButtonColour(StoryPageButtonColour colour)
{
return ::IsValidColours((::Colours)colour);
}
/* static */ bool ScriptStoryPage::IsValidStoryPageButtonFlags(StoryPageButtonFlags flags)
{
/* Don't allow float left and right together */
if ((flags & SPBF_FLOAT_LEFT) && (flags & SPBF_FLOAT_RIGHT)) return false;
/* Don't allow undefined flags */
if (flags & ~(SPBF_FLOAT_LEFT | SPBF_FLOAT_RIGHT)) return false;
return true;
}
/* static */ bool ScriptStoryPage::IsValidStoryPageButtonCursor(StoryPageButtonCursor cursor)
{
return ::IsValidStoryPageButtonCursor((::StoryPageButtonCursor)cursor);
}
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakePushButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags)
{
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonColour(colour));
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonFlags(flags));
StoryPageButtonData data;
data.SetColour((Colours)colour);
data.SetColour((::Colours)colour);
data.SetFlags((::StoryPageButtonFlags)flags);
if (!data.ValidateColour()) return UINT32_MAX;
if (!data.ValidateFlags()) return UINT32_MAX;
@ -238,8 +266,12 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakeTileButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor)
{
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonColour(colour));
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonFlags(flags));
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonCursor(cursor));
StoryPageButtonData data;
data.SetColour((Colours)colour);
data.SetColour((::Colours)colour);
data.SetFlags((::StoryPageButtonFlags)flags);
data.SetCursor((::StoryPageButtonCursor)cursor);
if (!data.ValidateColour()) return UINT32_MAX;
@ -250,8 +282,13 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
/* static */ ScriptStoryPage::StoryPageButtonFormatting ScriptStoryPage::MakeVehicleButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor, ScriptVehicle::VehicleType vehtype)
{
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonColour(colour));
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonFlags(flags));
EnforcePrecondition(UINT32_MAX, IsValidStoryPageButtonCursor(cursor));
EnforcePrecondition(UINT32_MAX, vehtype == ScriptVehicle::VT_INVALID || vehtype == ScriptVehicle::VT_RAIL || vehtype == ScriptVehicle::VT_ROAD || vehtype == ScriptVehicle::VT_WATER || vehtype == ScriptVehicle::VT_AIR);
StoryPageButtonData data;
data.SetColour((Colours)colour);
data.SetColour((::Colours)colour);
data.SetFlags((::StoryPageButtonFlags)flags);
data.SetCursor((::StoryPageButtonCursor)cursor);
data.SetVehicleType((::VehicleType)vehtype);
@ -261,4 +298,3 @@ static inline bool StoryPageElementTypeRequiresText(StoryPageElementType type)
if (!data.ValidateVehicleType()) return UINT32_MAX;
return data.referenced_id;
}

@ -146,7 +146,7 @@ public:
* Colour codes usable for story page button elements.
* Place a colour value in the lowest 8 bits of the \c reference parameter to the button.
*/
enum StoryPageButtonColour {
enum StoryPageButtonColour : byte {
SPBC_DARK_BLUE = ::COLOUR_DARK_BLUE,
SPBC_PALE_GREEN = ::COLOUR_PALE_GREEN,
SPBC_PINK = ::COLOUR_PINK,
@ -179,6 +179,13 @@ public:
*/
static bool IsValidStoryPageElement(StoryPageElementID story_page_element_id);
/**
* Check whether this is a valid story page element type.
* @param type The StoryPageElementType to check.
* @return True if and only if this story page element type is valid.
*/
static bool IsValidStoryPageElementType(StoryPageElementType type);
/**
* Create a new story page.
* @param company The company to create the story page for, or ScriptCompany::COMPANY_INVALID for all.
@ -202,6 +209,7 @@ public:
* @return The new StoryPageElementID, or STORY_PAGE_ELEMENT_INVALID if it failed.
* @pre ScriptCompanyMode::IsDeity().
* @pre IsValidStoryPage(story_page).
* @pre IsValidStoryPageElementType(type).
* @pre (type != SPET_TEXT && type != SPET_LOCATION) || (text != null && len(text) != 0).
* @pre type != SPET_LOCATION || ScriptMap::IsValidTile(reference).
* @pre type != SPET_GOAL || ScriptGoal::IsValidGoal(reference).
@ -312,11 +320,34 @@ public:
*/
static bool RemoveElement(StoryPageElementID story_page_element_id);
/**
* Check whether this is a valid story page button colour.
* @param colour The StoryPageButtonColour to check.
* @return True if and only if this story page button colour is valid.
*/
static bool IsValidStoryPageButtonColour(StoryPageButtonColour colour);
/**
* Check whether this is a valid story page button flag.
* @param colour The StoryPageButtonFlags to check.
* @return True if and only if this story page button flag is valid.
*/
static bool IsValidStoryPageButtonFlags(StoryPageButtonFlags flags);
/**
* Check whether this is a valid story page button cursor.
* @param colour The StoryPageButtonCursor to check.
* @return True if and only if this story page button cursor is valid.
*/
static bool IsValidStoryPageButtonCursor(StoryPageButtonCursor cursor);
/**
* Create a reference value for SPET_BUTTON_PUSH element parameters.
* @param colour The colour for the face of the button.
* @param flags The formatting and layout flags for the button.
* @return A reference value usable with the #NewElement and #UpdateElement functions.
* @pre IsValidStoryPageButtonColour(colour).
* @pre IsValidStoryPageButtonFlags(flags).
*/
static StoryPageButtonFormatting MakePushButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags);
@ -326,6 +357,9 @@ public:
* @param flags The formatting and layout flags for the button.
* @param cursor The mouse cursor to use when the player clicks the button and the game is ready for the player to select a tile.
* @return A reference value usable with the #NewElement and #UpdateElement functions.
* @pre IsValidStoryPageButtonColour(colour).
* @pre IsValidStoryPageButtonFlags(flags).
* @pre IsValidStoryPageButtonCursor(cursor).
*/
static StoryPageButtonFormatting MakeTileButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor);
@ -336,6 +370,10 @@ public:
* @param cursor The mouse cursor to use when the player clicks the button and the game is ready for the player to select a vehicle.
* @param vehtype The type of vehicle that will be selectable, or \c VT_INVALID to allow all types.
* @return A reference value usable with the #NewElement and #UpdateElement functions.
* @pre IsValidStoryPageButtonColour(colour).
* @pre IsValidStoryPageButtonFlags(flags).
* @pre IsValidStoryPageButtonCursor(cursor).
* @pre vehtype == ScriptVehicle::VT_INVALID || vehtype == ScriptVehicle::VT_RAIL || vehtype == ScriptVehicle::VT_ROAD || vehtype == ScriptVehicle::VT_WATER || vehtype == ScriptVehicle::VT_AIR.
*/
static StoryPageButtonFormatting MakeVehicleButtonReference(StoryPageButtonColour colour, StoryPageButtonFlags flags, StoryPageButtonCursor cursor, ScriptVehicle::VehicleType vehtype);
};

@ -185,7 +185,6 @@ typedef void SettingDescProc(IniFile &ini, const SettingTable &desc, const char
typedef void SettingDescProcList(IniFile &ini, const char *grpname, StringList &list);
static bool IsSignedVarMemType(VarType vt);
static bool DecodeHexText(const char *pos, uint8_t *dest, size_t dest_size);
/**
@ -526,6 +525,72 @@ static bool ValidateEnumSetting(const IntSettingDesc *sdb, int32_t &val)
return false;
}
/**
* Get the title of the setting.
* The string should include a {STRING2} to show the current value.
* @return The title string.
*/
StringID IntSettingDesc::GetTitle() const
{
return this->get_title_cb != nullptr ? this->get_title_cb(*this) : this->str;
}
/**
* Get the help text of the setting.
* @return The requested help text.
*/
StringID IntSettingDesc::GetHelp() const
{
StringID str = this->get_help_cb != nullptr ? this->get_help_cb(*this) : this->str_help;
if (this->guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_DESCRIPTION_TEXT;
data.text = str;
if (this->guiproc(data)) {
str = data.text;
}
}
return str;
}
/**
* Set the DParams for drawing the value of the setting.
* @param first_param First DParam to use
* @param value Setting value to set params for.
*/
void IntSettingDesc::SetValueDParams(uint first_param, int32_t value) const
{
const uint initial_first_param = first_param;
if (this->set_value_dparams_cb != nullptr) {
this->set_value_dparams_cb(*this, first_param, value);
} else if (this->IsBoolSetting()) {
SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} else {
if ((this->flags & SF_ENUM) != 0) {
StringID str = STR_UNDEFINED;
for (const SettingDescEnumEntry *enumlist = this->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) {
if (enumlist->val == value) {
str = enumlist->str;
break;
}
}
SetDParam(first_param++, str);
} else if ((this->flags & SF_GUI_DROPDOWN) != 0) {
SetDParam(first_param++, this->str_val - this->min + value);
} else {
SetDParam(first_param++, this->str_val + ((value == 0 && (this->flags & SF_GUI_0_IS_SPECIAL) != 0) ? 1 : 0));
}
SetDParam(first_param++, value);
}
if (this->guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_VALUE_DPARAMS;
data.offset = initial_first_param;
data.val = value;
this->guiproc(data);
}
}
/**
* Make the value valid and then write it to the setting.
* See #MakeValidValid and #Write for more details.
@ -1000,6 +1065,40 @@ const StringSettingDesc *SettingDesc::AsStringSetting() const
/* Begin - Callback Functions for the various settings. */
/** Switch setting title depending on wallclock setting */
static StringID SettingTitleWallclock(const IntSettingDesc &sd)
{
return EconTime::UsingWallclockUnits(_game_mode == GM_MENU) ? sd.str + 1 : sd.str;
}
/** Switch setting help depending on wallclock setting */
static StringID SettingHelpWallclock(const IntSettingDesc &sd)
{
return EconTime::UsingWallclockUnits(_game_mode == GM_MENU) ? sd.str_help + 1 : sd.str_help;
}
/** Setting values for velocity unit localisation */
static void SettingsValueVelocityUnit(const IntSettingDesc &, uint first_param, int32_t value)
{
StringID val;
switch (value) {
case 0: val = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL; break;
case 1: val = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC; break;
case 2: val = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI; break;
case 3: val = EconTime::UsingWallclockUnits(_game_mode == GM_MENU) ? STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS_SECS : STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS_DAYS; break;
case 4: val = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_KNOTS; break;
default: NOT_REACHED();
}
SetDParam(first_param, val);
}
/** A negative value has another string (the one after "strval"). */
static void SettingsValueAbsolute(const IntSettingDesc &sd, uint first_param, int32_t value)
{
SetDParam(first_param, sd.str_val + ((value >= 0) ? 1 : 0));
SetDParam(first_param + 1, abs(value));
}
/** Reposition the main toolbar as the setting changed. */
static void v_PositionMainToolbar(int32_t new_value)
{
@ -1993,14 +2092,14 @@ static void ParseCompanyPasswordStorageToken(const std::string &value)
{
extern std::array<uint8_t, 16> _network_company_password_storage_token;
if (value.size() != 32) return;
DecodeHexText(value.c_str(), _network_company_password_storage_token.data(), _network_company_password_storage_token.size());
ConvertHexToBytes(value, _network_company_password_storage_token);
}
static void ParseCompanyPasswordStorageSecret(const std::string &value)
{
extern std::array<uint8_t, 32> _network_company_password_storage_key;
if (value.size() != 64) return;
DecodeHexText(value.c_str(), _network_company_password_storage_key.data(), _network_company_password_storage_key.size());
ConvertHexToBytes(value, _network_company_password_storage_key);
}
/** Update the game info, and send it to the clients when we are running as a server. */
@ -2304,40 +2403,6 @@ static void GameLoadConfig(const IniFile &ini, const char *grpname)
if (item.value.has_value()) config->StringToSettings(*item.value);
}
/**
* Convert a character to a hex nibble value, or \c -1 otherwise.
* @param c Character to convert.
* @return Hex value of the character, or \c -1 if not a hex digit.
*/
static int DecodeHexNibble(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
return -1;
}
/**
* Parse a sequence of characters (supposedly hex digits) into a sequence of bytes.
* After the hex number should be a \c '|' character.
* @param pos First character to convert.
* @param[out] dest Output byte array to write the bytes.
* @param dest_size Number of bytes in \a dest.
* @return Whether reading was successful.
*/
static bool DecodeHexText(const char *pos, uint8_t *dest, size_t dest_size)
{
while (dest_size > 0) {
int hi = DecodeHexNibble(pos[0]);
int lo = (hi >= 0) ? DecodeHexNibble(pos[1]) : -1;
if (lo < 0) return false;
*dest++ = (hi << 4) | lo;
pos += 2;
dest_size--;
}
return *pos == '|';
}
/**
* Load BaseGraphics set selection and configuration.
*/
@ -2390,29 +2455,40 @@ static GRFConfig *GRFLoadConfig(const IniFile &ini, const char *grpname, bool is
for (const IniItem &item : group->items) {
GRFConfig *c = nullptr;
uint8_t grfid_buf[4];
std::array<uint8_t, 4> grfid_buf;
MD5Hash md5sum;
const char *filename = item.name.c_str();
bool has_grfid = false;
std::string_view item_name = item.name;
bool has_md5sum = false;
/* Try reading "<grfid>|" and on success, "<md5sum>|". */
has_grfid = DecodeHexText(filename, grfid_buf, lengthof(grfid_buf));
if (has_grfid) {
filename += 1 + 2 * lengthof(grfid_buf);
has_md5sum = DecodeHexText(filename, md5sum.data(), md5sum.size());
if (has_md5sum) filename += 1 + 2 * md5sum.size();
uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
if (has_md5sum) {
const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, &md5sum);
if (s != nullptr) c = new GRFConfig(*s);
}
if (c == nullptr && !FioCheckFileExists(filename, NEWGRF_DIR)) {
const GRFConfig *s = FindGRFConfig(grfid, FGCM_NEWEST_VALID);
if (s != nullptr) c = new GRFConfig(*s);
auto grfid_pos = item_name.find("|");
if (grfid_pos != std::string_view::npos) {
std::string_view grfid_str = item_name.substr(0, grfid_pos);
if (ConvertHexToBytes(grfid_str, grfid_buf)) {
item_name = item_name.substr(grfid_pos + 1);
auto md5sum_pos = item_name.find("|");
if (md5sum_pos != std::string_view::npos) {
std::string_view md5sum_str = item_name.substr(0, md5sum_pos);
has_md5sum = ConvertHexToBytes(md5sum_str, md5sum);
if (has_md5sum) item_name = item_name.substr(md5sum_pos + 1);
}
uint32_t grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
if (has_md5sum) {
const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, &md5sum);
if (s != nullptr) c = new GRFConfig(*s);
}
if (c == nullptr && !FioCheckFileExists(std::string(item_name), NEWGRF_DIR)) {
const GRFConfig *s = FindGRFConfig(grfid, FGCM_NEWEST_VALID);
if (s != nullptr) c = new GRFConfig(*s);
}
}
}
std::string filename = std::string(item_name);
if (c == nullptr) c = new GRFConfig(filename);
/* Parse parameters */
@ -2440,7 +2516,7 @@ static GRFConfig *GRFLoadConfig(const IniFile &ini, const char *grpname, bool is
SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN);
}
SetDParamStr(0, StrEmpty(filename) ? item.name.c_str() : filename);
SetDParamStr(0, filename.empty() ? item.name.c_str() : filename);
ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL);
delete c;
continue;

@ -47,12 +47,12 @@
#include "network/network_gui.h"
#include "network/network_survey.h"
#include "video/video_driver.hpp"
#include "social_integration.h"
#include <vector>
#include <functional>
#include <iterator>
#include <set>
#include <cmath>
#include "safeguards.h"
@ -181,6 +181,184 @@ static const std::map<int, StringID> _volume_labels = {
{ 127, STR_GAME_OPTIONS_VOLUME_100 },
};
static const NWidgetPart _nested_social_plugins_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_FRAME, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_TITLE), SetDataTip(STR_JUST_STRING2, STR_NULL),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_PLATFORM), SetMinimalSize(100, 12), SetDataTip(STR_JUST_RAW_STRING, STR_NULL), SetAlignment(SA_RIGHT),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_STATE), SetMinimalSize(100, 12), SetDataTip(STR_JUST_STRING1, STR_NULL), SetAlignment(SA_RIGHT),
EndContainer(),
EndContainer(),
EndContainer(),
};
static const NWidgetPart _nested_social_plugins_none_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE, STR_NULL),
EndContainer(),
};
class NWidgetSocialPlugins : public NWidgetVertical {
public:
NWidgetSocialPlugins()
{
this->plugins = SocialIntegration::GetPlugins();
if (this->plugins.empty()) {
auto widget = MakeNWidgets(std::begin(_nested_social_plugins_none_widgets), std::end(_nested_social_plugins_none_widgets), nullptr);
this->Add(std::move(widget));
} else {
for (size_t i = 0; i < this->plugins.size(); i++) {
auto widget = MakeNWidgets(std::begin(_nested_social_plugins_widgets), std::end(_nested_social_plugins_widgets), nullptr);
this->Add(std::move(widget));
}
}
this->SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0);
}
void FillWidgetLookup(WidgetLookup &widget_lookup) override
{
widget_lookup[WID_GO_SOCIAL_PLUGINS] = this;
NWidgetVertical::FillWidgetLookup(widget_lookup);
}
void SetupSmallestSize(Window *w) override
{
this->current_index = -1;
NWidgetVertical::SetupSmallestSize(w);
}
/**
* Find of all the plugins the one where the member is the widest (in pixels).
*
* @param member The member to check with.
* @return The plugin that has the widest value (in pixels) for the given member.
*/
template <typename T>
std::string &GetWidestPlugin(T SocialIntegrationPlugin::*member) const
{
std::string *longest = &(this->plugins[0]->*member);
int longest_length = 0;
for (auto *plugin : this->plugins) {
int length = GetStringBoundingBox(plugin->*member).width;
if (length > longest_length) {
longest_length = length;
longest = &(plugin->*member);
}
}
return *longest;
}
void SetStringParameters(int widget) const
{
switch (widget) {
case WID_GO_SOCIAL_PLUGIN_TITLE:
/* For SetupSmallestSize, use the longest string we have. */
if (this->current_index < 0) {
SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::name));
SetDParamStr(1, GetWidestPlugin(&SocialIntegrationPlugin::version));
break;
}
if (this->plugins[this->current_index]->name.empty()) {
SetDParam(0, STR_JUST_RAW_STRING);
SetDParamStr(1, this->plugins[this->current_index]->basepath);
} else {
SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE);
SetDParamStr(1, this->plugins[this->current_index]->name);
SetDParamStr(2, this->plugins[this->current_index]->version);
}
break;
case WID_GO_SOCIAL_PLUGIN_PLATFORM:
/* For SetupSmallestSize, use the longest string we have. */
if (this->current_index < 0) {
SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::social_platform));
break;
}
SetDParamStr(0, this->plugins[this->current_index]->social_platform);
break;
case WID_GO_SOCIAL_PLUGIN_STATE: {
static const std::pair<SocialIntegrationPlugin::State, StringID> state_to_string[] = {
{ SocialIntegrationPlugin::RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_RUNNING },
{ SocialIntegrationPlugin::FAILED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED },
{ SocialIntegrationPlugin::PLATFORM_NOT_RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_PLATFORM_NOT_RUNNING },
{ SocialIntegrationPlugin::UNLOADED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNLOADED },
{ SocialIntegrationPlugin::DUPLICATE, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_DUPLICATE },
{ SocialIntegrationPlugin::UNSUPPORTED_API, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNSUPPORTED_API },
{ SocialIntegrationPlugin::INVALID_SIGNATURE, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_INVALID_SIGNATURE },
};
/* For SetupSmallestSize, use the longest string we have. */
if (this->current_index < 0) {
auto longest_plugin = GetWidestPlugin(&SocialIntegrationPlugin::social_platform);
/* Set the longest plugin when looking for the longest status. */
SetDParamStr(0, longest_plugin);
StringID longest = STR_NULL;
int longest_length = 0;
for (auto state : state_to_string) {
int length = GetStringBoundingBox(state.second).width;
if (length > longest_length) {
longest_length = length;
longest = state.second;
}
}
SetDParam(0, longest);
SetDParamStr(1, longest_plugin);
break;
}
auto plugin = this->plugins[this->current_index];
/* Default string, in case no state matches. */
SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED);
SetDParamStr(1, plugin->social_platform);
/* Find the string for the state. */
for (auto state : state_to_string) {
if (plugin->state == state.first) {
SetDParam(0, state.second);
break;
}
}
}
break;
}
}
void Draw(const Window *w) override
{
this->current_index = 0;
for (auto &wid : this->children) {
wid->Draw(w);
this->current_index++;
}
}
private:
int current_index = -1;
std::vector<SocialIntegrationPlugin *> plugins;
};
/** Construct nested container widget for managing the list of social plugins. */
std::unique_ptr<NWidgetBase> MakeNWidgetSocialPlugins()
{
return std::make_unique<NWidgetSocialPlugins>();
}
struct GameOptionsWindow : Window {
GameSettings *opt;
bool reload;
@ -372,6 +550,16 @@ struct GameOptionsWindow : Window {
}
break;
}
case WID_GO_SOCIAL_PLUGIN_TITLE:
case WID_GO_SOCIAL_PLUGIN_PLATFORM:
case WID_GO_SOCIAL_PLUGIN_STATE: {
const NWidgetSocialPlugins *plugin = this->GetWidget<NWidgetSocialPlugins>(WID_GO_SOCIAL_PLUGINS);
assert(plugin != nullptr);
plugin->SetStringParameters(widget);
break;
}
}
}
@ -414,7 +602,7 @@ struct GameOptionsWindow : Window {
void SetTab(WidgetID widget)
{
this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND);
this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND, WID_GO_TAB_SOCIAL);
this->LowerWidget(widget);
GameOptionsWindow::active_tab = widget;
@ -423,6 +611,7 @@ struct GameOptionsWindow : Window {
case WID_GO_TAB_GENERAL: pane = 0; break;
case WID_GO_TAB_GRAPHICS: pane = 1; break;
case WID_GO_TAB_SOUND: pane = 2; break;
case WID_GO_TAB_SOCIAL: pane = 3; break;
default: NOT_REACHED();
}
@ -517,6 +706,7 @@ struct GameOptionsWindow : Window {
case WID_GO_TAB_GENERAL:
case WID_GO_TAB_GRAPHICS:
case WID_GO_TAB_SOUND:
case WID_GO_TAB_SOCIAL:
this->SetTab(widget);
break;
@ -879,6 +1069,7 @@ static constexpr NWidgetPart _nested_game_options_widgets[] = {
NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GENERAL), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GENERAL, STR_GAME_OPTIONS_TAB_GENERAL_TT), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GRAPHICS), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GRAPHICS, STR_GAME_OPTIONS_TAB_GRAPHICS_TT), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOUND), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOUND, STR_GAME_OPTIONS_TAB_SOUND_TT), SetFill(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOCIAL), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOCIAL, STR_GAME_OPTIONS_TAB_SOCIAL_TT), SetFill(1, 0),
EndContainer(),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
@ -1039,6 +1230,11 @@ static constexpr NWidgetPart _nested_game_options_widgets[] = {
EndContainer(),
EndContainer(),
EndContainer(),
/* Social tab */
NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.sparse), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0),
NWidgetFunction(MakeNWidgetSocialPlugins),
EndContainer(),
EndContainer(),
EndContainer(),
};
@ -1146,9 +1342,6 @@ struct SettingEntry : BaseSettingEntry {
bool UpdateFilterState(SettingFilter &filter, bool force_visible) override;
void SetButtons(byte new_val);
StringID GetHelpText() const;
void SetValueDParams(uint first_param, int32_t value) const;
protected:
SettingEntry(const IntSettingDesc *setting);
@ -1159,24 +1352,6 @@ private:
bool IsVisibleByRestrictionMode(RestrictionMode mode) const;
};
/**
* Get the help text of a single setting.
* @return The requested help text.
*/
StringID SettingEntry::GetHelpText() const
{
StringID str = this->setting->str_help;
if (this->setting->guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_DESCRIPTION_TEXT;
data.text = str;
if (this->setting->guiproc(data)) {
str = data.text;
}
}
return str;
}
/** Cargodist per-cargo setting */
struct CargoDestPerCargoSettingEntry : SettingEntry {
CargoID cargo;
@ -1414,7 +1589,7 @@ uint SettingEntry::Length() const
*/
uint SettingEntry::GetMaxHelpHeight(int maxw)
{
return GetStringHeight(this->GetHelpText(), maxw);
return GetStringHeight(this->setting->GetHelp(), maxw);
}
/**
@ -1480,8 +1655,8 @@ bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible)
filter.string.ResetState();
SetDParam(0, STR_EMPTY);
filter.string.AddLine(sd->str);
filter.string.AddLine(this->GetHelpText());
filter.string.AddLine(sd->GetTitle());
filter.string.AddLine(sd->GetHelp());
visible = filter.string.GetState();
}
@ -1513,52 +1688,6 @@ static const void *ResolveObject(const GameSettings *settings_ptr, const IntSett
return settings_ptr;
}
/**
* Set the DParams for drawing the value of a setting.
* @param first_param First DParam to use
* @param value Setting value to set params for.
*/
void SettingEntry::SetValueDParams(uint first_param, int32_t value) const
{
const uint initial_first_param = first_param;
if (this->setting->IsBoolSetting()) {
SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} else if (this->setting->flags & SF_DEC1SCALE) {
double scale = std::exp2(((double)value) / 10);
int log = -std::min(0, (int)std::floor(std::log10(scale)) - 2);
SetDParam(first_param++, STR_JUST_RAW_STRING);
auto tmp_params = MakeParameters(value, (int64_t)(scale * std::pow(10.f, (float)log)), log);
SetDParamStr(first_param++, GetStringWithArgs(this->setting->str_val, tmp_params));
} else {
if ((this->setting->flags & SF_ENUM) != 0) {
StringID str = STR_UNDEFINED;
for (const SettingDescEnumEntry *enumlist = this->setting->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) {
if (enumlist->val == value) {
str = enumlist->str;
break;
}
}
SetDParam(first_param++, str);
} else if ((this->setting->flags & SF_GUI_DROPDOWN) != 0) {
SetDParam(first_param++, this->setting->str_val - this->setting->min + value);
} else if ((this->setting->flags & SF_GUI_NEGATIVE_IS_SPECIAL) != 0) {
SetDParam(first_param++, this->setting->str_val + ((value >= 0) ? 1 : 0));
value = abs(value);
} else {
SetDParam(first_param++, this->setting->str_val + ((value == 0 && (this->setting->flags & SF_GUI_0_IS_SPECIAL) != 0) ? 1 : 0));
}
SetDParam(first_param++, value);
}
if (this->setting->guiproc != nullptr) {
SettingOnGuiCtrlData data;
data.type = SOGCT_VALUE_DPARAMS;
data.offset = initial_first_param;
data.val = value;
this->setting->guiproc(data);
}
}
/**
* Function to draw setting value (button + text + current value)
* @param settings_ptr Pointer to current values of all settings
@ -1599,8 +1728,9 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right,
void SettingEntry::DrawSettingString(uint left, uint right, int y, bool highlight, int32_t value) const
{
this->SetValueDParams(1, value);
int edge = DrawString(left, right, y, this->setting->str, highlight ? TC_WHITE : TC_LIGHT_BLUE);
const IntSettingDesc *sd = this->setting;
sd->SetValueDParams(1, value);
int edge = DrawString(left, right, y, sd->GetTitle(), highlight ? TC_WHITE : TC_LIGHT_BLUE);
if (this->setting->guiproc != nullptr && edge != 0) {
SettingOnGuiCtrlData data;
@ -1632,7 +1762,7 @@ void CargoDestPerCargoSettingEntry::DrawSettingString(uint left, uint right, int
assert(this->setting->str == STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO);
SetDParam(0, CargoSpec::Get(this->cargo)->name);
SetDParam(1, STR_CONFIG_SETTING_VALUE);
this->SetValueDParams(2, value);
this->setting->SetValueDParams(2, value);
DrawString(left, right, y, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_PARAM, highlight ? TC_WHITE : TC_LIGHT_BLUE);
}
@ -2824,7 +2954,7 @@ struct GameSettingsWindow : Window {
DrawString(tr, STR_CONFIG_SETTING_TYPE);
tr.top += GetCharacterHeight(FS_NORMAL);
this->last_clicked->SetValueDParams(0, sd->def);
sd->SetValueDParams(0, sd->def);
DrawString(tr, STR_CONFIG_SETTING_DEFAULT_VALUE);
tr.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal;
@ -2854,7 +2984,7 @@ struct GameSettingsWindow : Window {
}
}
DrawStringMultiLine(tr, this->last_clicked->GetHelpText(), TC_WHITE);
DrawStringMultiLine(tr, sd->GetHelp(), TC_WHITE);
}
break;
@ -2992,7 +3122,8 @@ struct GameSettingsWindow : Window {
}
}
assert_msg(val >= sd->min && val <= (int)sd->max, "min: %d, max: %d, val: %d", sd->min, sd->max, val);
list.push_back(std::make_unique<DropDownListStringItem>(sd->str_val + val - sd->min, val, false));
sd->SetValueDParams(0, val);
list.push_back(std::make_unique<DropDownListStringItem>(STR_JUST_STRING2, val, false));
}
} else if ((sd->flags & SF_ENUM)) {
for (const SettingDescEnumEntry *enumlist = sd->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) {
@ -3064,7 +3195,7 @@ struct GameSettingsWindow : Window {
if (sd->flags & SF_GUI_VELOCITY) value64 = ConvertKmhishSpeedToDisplaySpeed((uint)value64, VEH_TRAIN);
this->valuewindow_entry = pe;
if (sd->flags & SF_DECIMAL1 || (sd->flags & SF_GUI_VELOCITY && _settings_game.locale.units_velocity == 3)) {
if (sd->flags & SF_GUI_VELOCITY && _settings_game.locale.units_velocity == 3) {
CharSetFilter charset_filter = CS_NUMERAL_DECIMAL; //default, only numeric input and decimal point allowed
if (sd->min < 0) charset_filter = CS_NUMERAL_DECIMAL_SIGNED; // special case, also allow '-' sign for negative input
@ -3103,7 +3234,7 @@ struct GameSettingsWindow : Window {
int32_t value;
if (!StrEmpty(str)) {
long long llvalue;
if (sd->flags & SF_DECIMAL1 || (sd->flags & SF_GUI_VELOCITY && _settings_game.locale.units_velocity == 3)) {
if (sd->flags & SF_GUI_VELOCITY && _settings_game.locale.units_velocity == 3) {
llvalue = atof(str) * 10;
} else {
llvalue = atoll(str);

@ -21,7 +21,6 @@ enum SaveToConfigFlags : uint32_t;
enum SettingFlag : uint32_t {
SF_NONE = 0,
SF_GUI_0_IS_SPECIAL = 1 << 0, ///< A value of zero is possible and has a custom string (the one after "strval").
SF_GUI_NEGATIVE_IS_SPECIAL = 1 << 1, ///< A negative value has another string (the one after "strval").
SF_GUI_DROPDOWN = 1 << 2, ///< The value represents a limited number of string-options (internally integer) presented as dropdown.
SF_GUI_CURRENCY = 1 << 3, ///< The number represents money, so when reading value multiply by exchange rate.
SF_NETWORK_ONLY = 1 << 4, ///< This setting only applies to network games.
@ -33,10 +32,8 @@ enum SettingFlag : uint32_t {
SF_NOT_IN_SAVE = 1 << 10, ///< Do not save with savegame, basically client-based.
SF_NOT_IN_CONFIG = 1 << 11, ///< Do not save to config file.
SF_NO_NETWORK_SYNC = 1 << 12, ///< Do not synchronize over network (but it is saved if SF_NOT_IN_SAVE is not set).
SF_DECIMAL1 = 1 << 13, ///< display a decimal representation of the setting value divided by 10
SF_ENUM = 1 << 14, ///< the setting can take one of the values given by an array of struct SettingDescEnumEntry
SF_NO_NEWGAME = 1 << 15, ///< the setting does not apply and is not shown in a new game context
SF_DEC1SCALE = 1 << 16, ///< also display a float representation of the scale of a decimal1 scale parameter
SF_RUN_CALLBACKS_ON_PARSE = 1 << 17, ///< run callbacks when parsing from config file
SF_GUI_VELOCITY = 1 << 18, ///< setting value is a velocity
SF_ENUM_PRE_CB_VALIDATE = 1 << 20, ///< Call the pre_check callback for enum incoming value validation
@ -182,6 +179,10 @@ struct SettingDesc {
/** Base integer type, including boolean, settings. Only these are shown in the settings UI. */
struct IntSettingDesc : SettingDesc {
typedef StringID GetTitleCallback(const IntSettingDesc &sd);
typedef StringID GetHelpCallback(const IntSettingDesc &sd);
typedef void SetValueDParamsCallback(const IntSettingDesc &sd, uint first_param, int32_t value);
/**
* A check to be performed before the setting gets changed. The passed integer may be
* changed by the check if that is important, for example to remove some unwanted bit.
@ -199,10 +200,13 @@ struct IntSettingDesc : SettingDesc {
IntSettingDesc(const SaveLoad &save, const char *name, SettingFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, int32_t def,
int32_t min, uint32_t max, int32_t interval, StringID str, StringID str_help, StringID str_val,
SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback, const SettingDescEnumEntry *enumlist) :
SettingCategory cat, PreChangeCheck pre_check, PostChangeCallback post_callback,
GetTitleCallback get_title_cb, GetHelpCallback get_help_cb, SetValueDParamsCallback set_value_dparams_cb,
const SettingDescEnumEntry *enumlist) :
SettingDesc(save, name, flags, guiproc, startup, patx_name), def(def), min(min), max(max), interval(interval),
str(str), str_help(str_help), str_val(str_val), cat(cat), pre_check(pre_check),
post_callback(post_callback), enumlist(enumlist) {}
str(str), str_help(str_help), str_val(str_val), cat(cat), pre_check(pre_check), post_callback(post_callback),
get_title_cb(get_title_cb), get_help_cb(get_help_cb), set_value_dparams_cb(set_value_dparams_cb),
enumlist(enumlist) {}
int32_t def; ///< default value given when none is present
int32_t min; ///< minimum values
@ -214,9 +218,16 @@ struct IntSettingDesc : SettingDesc {
SettingCategory cat; ///< assigned categories of the setting
PreChangeCheck *pre_check; ///< Callback to check for the validity of the setting.
PostChangeCallback *post_callback; ///< Callback when the setting has been changed.
GetTitleCallback *get_title_cb;
GetHelpCallback *get_help_cb;
SetValueDParamsCallback *set_value_dparams_cb;
const SettingDescEnumEntry *enumlist; ///< For SF_ENUM. The last entry must use STR_NULL
StringID GetTitle() const;
StringID GetHelp() const;
void SetValueDParams(uint first_param, int32_t value) const;
/**
* Check whether this setting is a boolean type setting.
* @return True when the underlying type is an integer.
@ -243,9 +254,11 @@ private:
/** Boolean setting. */
struct BoolSettingDesc : IntSettingDesc {
BoolSettingDesc(const SaveLoad &save, const char *name, SettingFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name, bool def,
StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback) :
IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, 1, 0, str, str_help, str_val, cat, pre_check, post_callback, nullptr) {}
StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,
GetTitleCallback get_title_cb, GetHelpCallback get_help_cb, SetValueDParamsCallback set_value_dparams_cb) :
IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, 1, 0, str, str_help, str_val, cat,
pre_check, post_callback, get_title_cb, get_help_cb, set_value_dparams_cb, nullptr) {}
static std::optional<bool> ParseSingleValue(const char *str);
@ -259,10 +272,12 @@ struct OneOfManySettingDesc : IntSettingDesc {
typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error
OneOfManySettingDesc(const SaveLoad &save, const char *name, SettingFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name,
int32_t def, int32_t max, StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,
std::initializer_list<const char *> many, OnConvert *many_cnvt) :
IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, max, 0, str, str_help, str_val, cat, pre_check, post_callback, nullptr), many_cnvt(many_cnvt)
int32_t def, int32_t max, StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,
GetTitleCallback get_title_cb, GetHelpCallback get_help_cb, SetValueDParamsCallback set_value_dparams_cb,
std::initializer_list<const char *> many, OnConvert *many_cnvt) :
IntSettingDesc(save, name, flags, guiproc, startup, patx_name, def, 0, max, 0, str, str_help, str_val, cat,
pre_check, post_callback, get_title_cb, get_help_cb, set_value_dparams_cb, nullptr), many_cnvt(many_cnvt)
{
for (auto one : many) this->many.push_back(one);
}
@ -280,11 +295,12 @@ struct OneOfManySettingDesc : IntSettingDesc {
/** Many of many setting. */
struct ManyOfManySettingDesc : OneOfManySettingDesc {
ManyOfManySettingDesc(const SaveLoad &save, const char *name, SettingFlag flags, OnGuiCtrl *guiproc, bool startup, const char *patx_name,
int32_t def, StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,
std::initializer_list<const char *> many, OnConvert *many_cnvt) :
int32_t def, StringID str, StringID str_help, StringID str_val, SettingCategory cat,
PreChangeCheck pre_check, PostChangeCallback post_callback,
GetTitleCallback get_title_cb, GetHelpCallback get_help_cb, SetValueDParamsCallback set_value_dparams_cb,
std::initializer_list<const char *> many, OnConvert *many_cnvt) :
OneOfManySettingDesc(save, name, flags, guiproc, startup, patx_name, def, (1 << many.size()) - 1, str, str_help,
str_val, cat, pre_check, post_callback, many, many_cnvt) {}
str_val, cat, pre_check, post_callback, get_title_cb, get_help_cb, set_value_dparams_cb, many, many_cnvt) {}
size_t ParseValue(const char *str) const override;
char *FormatIntValue(char *buf, const char *last, uint32_t value) const override;

@ -448,6 +448,7 @@ struct NetworkSettings {
uint8_t max_companies; ///< maximum amount of companies
uint8_t max_clients; ///< maximum amount of clients
CalTime::Year restart_game_year; ///< year the server restarts
uint16_t restart_hours; ///< number of hours to run the server before automatic restart
uint8_t min_active_clients; ///< minimum amount of active clients to unpause the game
bool reload_cfg; ///< reload the config file before restarting
std::string last_joined; ///< Last joined server

@ -0,0 +1,280 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file signature.cpp Implementation of signature validation routines. */
#include "stdafx.h"
#include "signature.h"
#include "debug.h"
#include "debug_fmt.h"
#include "fileio_func.h"
#include "string_func.h"
#include "3rdparty/monocypher/monocypher.h"
#include "3rdparty/monocypher/monocypher-ed25519.h"
#include "3rdparty/nlohmann/json.hpp"
#include "safeguards.h"
/** The public keys used for signature validation. */
static const std::initializer_list<std::array<uint8_t, 32>> _public_keys_v1 = {
/* 2024-01-20 - Public key for Social Integration Plugins. */
{ 0xed, 0x5d, 0x57, 0x47, 0x21, 0x99, 0x8b, 0x02, 0xdf, 0x6e, 0x3d, 0x69, 0xe1, 0x87, 0xca, 0xd0, 0x0e, 0x88, 0xc3, 0xe2, 0xb2, 0xa6, 0x7b, 0xc0, 0x42, 0xc8, 0xd6, 0x4b, 0x65, 0xe6, 0x48, 0xf7 },
};
/**
* Calculate the 32-byte blake2b hash of a file.
*
* @param filename The filename to calculate the hash of.
* @return The 32-byte blake2b hash of the file, hex-encoded.
*/
static std::string CalculateHashV1(const std::string &filename)
{
FILE *f = FioFOpenFile(filename, "rb", NO_DIRECTORY);
if (f == nullptr) {
return "";
}
std::array<uint8_t, 32> digest;
crypto_blake2b_ctx ctx;
crypto_blake2b_init(&ctx, digest.size());
while (!feof(f)) {
std::array<uint8_t, 1024> buf;
size_t len = fread(buf.data(), 1, buf.size(), f);
crypto_blake2b_update(&ctx, buf.data(), len);
}
fclose(f);
crypto_blake2b_final(&ctx, digest.data());
return FormatArrayAsHex(digest);
}
/**
* Validate whether the checksum of a file is the same.
*
* @param filename The filename to validate the checksum of.
* @param checksum The expected checksum.
* @return True iff the checksum of the file is the same as the expected checksum.
*/
static bool ValidateChecksum(const std::string &filename, const std::string &checksum)
{
/* Checksums are "<version>$<hash>". Split out the version. */
auto pos = checksum.find('$');
assert(pos != std::string::npos); // Already validated by ValidateSchema().
const std::string version = checksum.substr(0, pos);
const std::string hash = checksum.substr(pos + 1);
/* Calculate the checksum over the file. */
std::string calculated_hash;
if (version == "1") {
calculated_hash = CalculateHashV1(filename);
} else {
Debug(misc, 0, "Failed to validate signature: unknown checksum version: {}", filename);
return false;
}
/* Validate the checksum is the same. */
if (calculated_hash.empty()) {
Debug(misc, 0, "Failed to validate signature: couldn't calculate checksum for: {}", filename);
return false;
}
if (calculated_hash != hash) {
Debug(misc, 0, "Failed to validate signature: checksum mismatch for: {}", filename);
return false;
}
return true;
}
/**
* Validate whether the signature is valid for this set of files.
*
* @param signature The signature to validate.
* @param files The files to validate the signature against.
* @param filename The filename of the signatures file (for error-reporting).
* @return True iff the signature is valid for this set of files.
*/
static bool ValidateSignature(const std::string &signature, const nlohmann::json &files, const std::string &filename)
{
/* Signatures are "<version>$<signature>". Split out the version. */
auto pos = signature.find('$');
assert(pos != std::string::npos); // Already validated by ValidateSchema().
const std::string version = signature.substr(0, pos);
const std::string sig_value = signature.substr(pos + 1);
/* Create the message we are going to validate. */
std::string message = files.dump(-1);
/* Validate the signature. */
if (version == "1") {
std::array<uint8_t, 64> sig;
if (sig_value.size() != 128 || !ConvertHexToBytes(sig_value, sig)) {
Debug(misc, 0, "Failed to validate signature: invalid signature: {}", filename);
return false;
}
for (auto &pk_value : _public_keys_v1) {
/* Check if the message is valid with this public key. */
auto res = crypto_ed25519_check(sig.data(), pk_value.data(), reinterpret_cast<uint8_t *>(message.data()), message.size());
if (res == 0) {
return true;
}
}
Debug(misc, 0, "Failed to validate signature: signature validation failed: {}", filename);
return false;
} else {
Debug(misc, 0, "Failed to validate signature: unknown signature version: {}", filename);
return false;
}
return true;
}
/**
* Validate the signatures file complies with the JSON schema.
*
* @param signatures The signatures JSON to validate.
* @param filename The filename of the signatures file (for error-reporting).
* @return True iff the signatures file complies with the JSON schema.
*/
static bool ValidateSchema(const nlohmann::json &signatures, const std::string &filename)
{
if (signatures["files"].is_null()) {
Debug(misc, 0, "Failed to validate signature: no files found: {}", filename);
return false;
}
if (signatures["signature"].is_null()) {
Debug(misc, 0, "Failed to validate signature: no signature found: {}", filename);
return false;
}
for (auto &signature : signatures["files"]) {
if (signature["filename"].is_null() || signature["checksum"].is_null()) {
Debug(misc, 0, "Failed to validate signature: invalid entry in files: {}", filename);
return false;
}
const std::string sig_filename = signature["filename"];
const std::string sig_checksum = signature["checksum"];
if (sig_filename.empty() || sig_checksum.empty()) {
Debug(misc, 0, "Failed to validate signature: invalid entry in files: {}", filename);
return false;
}
auto pos = sig_checksum.find('$');
if (pos == std::string::npos) {
Debug(misc, 0, "Failed to validate signature: invalid checksum format: {}", filename);
return false;
}
}
const std::string signature = signatures["signature"];
auto pos = signature.find('$');
if (pos == std::string::npos) {
Debug(misc, 0, "Failed to validate signature: invalid signature format: {}", filename);
return false;
}
return true;
}
/**
* Validate that the signatures mentioned in the signature file are matching
* the files in question.
*
* @return True iff the files in the signature file passed validation.
*/
static bool _ValidateSignatureFile(const std::string &filename)
{
size_t filesize;
FILE *f = FioFOpenFile(filename, "rb", NO_DIRECTORY, &filesize);
if (f == nullptr) {
Debug(misc, 0, "Failed to validate signature: file not found: {}", filename);
return false;
}
std::string text(filesize, '\0');
size_t len = fread(text.data(), filesize, 1, f);
if (len != 1) {
Debug(misc, 0, "Failed to validate signature: failed to read file: {}", filename);
return false;
}
nlohmann::json signatures;
try {
signatures = nlohmann::json::parse(text);
} catch (nlohmann::json::exception &) {
Debug(misc, 0, "Failed to validate signature: not a valid JSON file: {}", filename);
return false;
}
/*
* The JSON file should look like:
*
* {
* "files": [
* {
* "checksum": "version$hash"
* "filename": "filename",
* },
* ...
* ],
* "signature": "version$signature"
* }
*
* The signature is a signed message of the content of "files", dumped as
* JSON without spaces / newlines, keys in the order as indicated above.
*/
if (!ValidateSchema(signatures, filename)) {
return false;
}
if (!ValidateSignature(signatures["signature"], signatures["files"], filename)) {
return false;
}
std::string dirname = std::filesystem::path(filename).parent_path().string();
for (auto &signature : signatures["files"]) {
const std::string sig_filename = dirname + PATHSEPCHAR + signature["filename"].get<std::string>();
const std::string sig_checksum = signature["checksum"];
if (!ValidateChecksum(sig_filename, sig_checksum)) {
return false;
}
}
return true;
}
/**
* Validate that the signatures mentioned in the signature file are matching
* the files in question.
*
* @note if ALLOW_INVALID_SIGNATURE is defined, this function will always
* return true (but will still report any errors in the console).
*
* @return True iff the files in the signature file passed validation.
*/
bool ValidateSignatureFile(const std::string &filename)
{
auto res = _ValidateSignatureFile(filename);;
#if defined(ALLOW_INVALID_SIGNATURE)
(void)res; // Ignore the result.
return true;
#else
return res;
#endif
}

@ -0,0 +1,15 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file signature.h Routines to validate signature files. */
#ifndef SIGNATURE_H
#define SIGNATURE_H
bool ValidateSignatureFile(const std::string &filename);
#endif /* SIGNATURE_H */

@ -0,0 +1,269 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file social_integration.cpp Base implementation of social integration support. */
#include "stdafx.h"
#include "social_integration.h"
#include "3rdparty/openttd_social_integration_api/openttd_social_integration_api.h"
#include "debug.h"
#include "debug_fmt.h"
#include "fileio_func.h"
#include "library_loader.h"
#include "rev.h"
#include "string_func.h"
#include "signature.h"
#include <set>
#include "safeguards.h"
/**
* Container to track information per plugin.
*/
class InternalSocialIntegrationPlugin {
public:
InternalSocialIntegrationPlugin(const std::string &filename, const std::string &basepath) : library(nullptr), external(basepath)
{
openttd_info.openttd_version = _openttd_revision;
if (!ValidateSignatureFile(fmt::format("{}.sig", filename))) {
external.state = SocialIntegrationPlugin::INVALID_SIGNATURE;
return;
}
this->library = std::make_unique<LibraryLoader>(filename);
}
OpenTTD_SocialIntegration_v1_PluginInfo plugin_info = {}; ///< Information supplied by plugin.
OpenTTD_SocialIntegration_v1_PluginApi plugin_api = {}; ///< API supplied by plugin.
OpenTTD_SocialIntegration_v1_OpenTTDInfo openttd_info = {}; ///< Information supplied by OpenTTD.
std::unique_ptr<LibraryLoader> library = nullptr; ///< Library handle.
SocialIntegrationPlugin external; ///< Information of the plugin to be used by other parts of our codebase.
};
static std::vector<std::unique_ptr<InternalSocialIntegrationPlugin>> _plugins; ///< List of loaded plugins.
static std::set<std::string> _loaded_social_platform; ///< List of Social Platform plugins already loaded. Used to prevent loading a plugin for the same Social Platform twice.
/** Helper for scanning for files with SocialIntegration as extension */
class SocialIntegrationFileScanner : FileScanner {
public:
void Scan()
{
#ifdef _WIN32
std::string extension = "-social.dll";
#elif defined(__APPLE__)
std::string extension = "-social.dylib";
#else
std::string extension = "-social.so";
#endif
this->FileScanner::Scan(extension.c_str(), SOCIAL_INTEGRATION_DIR, false);
}
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &) override
{
std::string basepath = filename.substr(basepath_length);
Debug(misc, 1, "[Social Integration: {}] Loading ...", basepath);
auto &plugin = _plugins.emplace_back(std::make_unique<InternalSocialIntegrationPlugin>(filename, basepath));
/* Validation failed, so no library was loaded. */
if (plugin->library == nullptr) {
return false;
}
if (plugin->library->HasError()) {
plugin->external.state = SocialIntegrationPlugin::FAILED;
Debug(misc, 0, "[Social Integration: {}] Failed to load library: {}", basepath, plugin->library->GetLastError());
return false;
}
OpenTTD_SocialIntegration_v1_GetInfo getinfo_func = plugin->library->GetFunction("SocialIntegration_v1_GetInfo");
if (plugin->library->HasError()) {
plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_GetInfo: {}", basepath, plugin->library->GetLastError());
return false;
}
OpenTTD_SocialIntegration_v1_Init init_func = plugin->library->GetFunction("SocialIntegration_v1_Init");
if (plugin->library->HasError()) {
plugin->external.state = SocialIntegrationPlugin::UNSUPPORTED_API;
Debug(misc, 0, "[Social Integration: {}] Failed to find symbol SocialPlugin_v1_Init: {}", basepath, plugin->library->GetLastError());
return false;
}
getinfo_func(&plugin->plugin_info);
/* Setup the information for the outside world to see. */
plugin->external.social_platform = plugin->plugin_info.social_platform;
plugin->external.name = plugin->plugin_info.name;
plugin->external.version = plugin->plugin_info.version;
/* Lowercase the string for comparison. */
std::string lc_social_platform = plugin->plugin_info.social_platform;
strtolower(lc_social_platform);
/* Prevent more than one plugin for a certain Social Platform to be loaded, as that never ends well. */
if (_loaded_social_platform.find(lc_social_platform) != _loaded_social_platform.end()) {
plugin->external.state = SocialIntegrationPlugin::DUPLICATE;
Debug(misc, 0, "[Social Integration: {}] Another plugin for {} is already loaded", basepath, plugin->plugin_info.social_platform);
return false;
}
_loaded_social_platform.insert(lc_social_platform);
auto state = init_func(&plugin->plugin_api, &plugin->openttd_info);
switch (state) {
case OTTD_SOCIAL_INTEGRATION_V1_INIT_SUCCESS:
plugin->external.state = SocialIntegrationPlugin::RUNNING;
Debug(misc, 1, "[Social Integration: {}] Loaded for {}: {} ({})", basepath, plugin->plugin_info.social_platform, plugin->plugin_info.name, plugin->plugin_info.version);
return true;
case OTTD_SOCIAL_INTEGRATION_V1_INIT_FAILED:
plugin->external.state = SocialIntegrationPlugin::FAILED;
Debug(misc, 0, "[Social Integration: {}] Failed to initialize", basepath);
return false;
case OTTD_SOCIAL_INTEGRATION_V1_INIT_PLATFORM_NOT_RUNNING:
plugin->external.state = SocialIntegrationPlugin::PLATFORM_NOT_RUNNING;
Debug(misc, 1, "[Social Integration: {}] Failed to initialize: {} is not running", basepath, plugin->plugin_info.social_platform);
return false;
default:
NOT_REACHED();
}
}
};
std::vector<SocialIntegrationPlugin *> SocialIntegration::GetPlugins()
{
std::vector<SocialIntegrationPlugin *> plugins;
for (auto &plugin : _plugins) {
plugins.push_back(&plugin->external);
}
return plugins;
}
size_t SocialIntegration::GetPluginCount()
{
return _plugins.size();
}
char *SocialIntegration::LogPluginSummary(char *buffer, const char *last)
{
extern const char *PluginStateToString(SocialIntegrationPlugin::State state);
for (auto &plugin : _plugins) {
buffer += seprintf(buffer, last, " %s:\n", plugin->external.name.c_str());
buffer += seprintf(buffer, last, " Version: %s\n", plugin->external.version.c_str());
buffer += seprintf(buffer, last, " Basepath: %s\n", plugin->external.basepath.c_str());
buffer += seprintf(buffer, last, " State: %s\n", PluginStateToString(plugin->external.state));
}
buffer += seprintf(buffer, last, "\n");
return buffer;
}
void SocialIntegration::Initialize()
{
SocialIntegrationFileScanner fs;
fs.Scan();
}
/**
* Wrapper to call a function pointer of a plugin if it isn't a nullptr.
*
* @param plugin Plugin to call the function pointer on.
* @param func Function pointer to call.
*/
template <typename T, typename... Ts>
static void PluginCall(std::unique_ptr<InternalSocialIntegrationPlugin> &plugin, T func, Ts... args)
{
if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
return;
}
if (func != nullptr) {
func(args...);
}
}
void SocialIntegration::Shutdown()
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.shutdown);
}
_plugins.clear();
_loaded_social_platform.clear();
}
void SocialIntegration::RunCallbacks()
{
for (auto &plugin : _plugins) {
if (plugin->external.state != SocialIntegrationPlugin::RUNNING) {
continue;
}
if (plugin->plugin_api.run_callbacks != nullptr) {
if (!plugin->plugin_api.run_callbacks()) {
Debug(misc, 1, "[Social Plugin: {}] Requested to be unloaded", plugin->external.basepath);
_loaded_social_platform.erase(plugin->plugin_info.social_platform);
plugin->external.state = SocialIntegrationPlugin::UNLOADED;
PluginCall(plugin, plugin->plugin_api.shutdown);
}
}
}
}
void SocialIntegration::EventEnterMainMenu()
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.event_enter_main_menu);
}
}
void SocialIntegration::EventEnterScenarioEditor(uint map_width, uint map_height)
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.event_enter_scenario_editor, map_width, map_height);
}
}
void SocialIntegration::EventEnterSingleplayer(uint map_width, uint map_height)
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.event_enter_singleplayer, map_width, map_height);
}
}
void SocialIntegration::EventEnterMultiplayer(uint map_width, uint map_height)
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.event_enter_multiplayer, map_width, map_height);
}
}
void SocialIntegration::EventJoiningMultiplayer()
{
for (auto &plugin : _plugins) {
PluginCall(plugin, plugin->plugin_api.event_joining_multiplayer);
}
}

@ -0,0 +1,88 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file social_integration.h Interface definitions for game to report/respond to social integration. */
#ifndef SOCIAL_INTEGRATION_H
#define SOCIAL_INTEGRATION_H
class SocialIntegrationPlugin {
public:
enum State {
RUNNING, ///< The plugin is successfully loaded and running.
FAILED, ///< The plugin failed to initialize.
PLATFORM_NOT_RUNNING, ///< The plugin failed to initialize because the Social Platform is not running.
UNLOADED, ///< The plugin is unloaded upon request.
DUPLICATE, ///< Another plugin of the same Social Platform is already loaded.
UNSUPPORTED_API, ///< The plugin does not support the current API version.
INVALID_SIGNATURE, ///< The signature of the plugin is invalid.
};
std::string basepath; ///< Base path of the plugin.
std::string social_platform = "unknown"; ///< Social platform this plugin is for.
std::string name = ""; ///< Name of the plugin.
std::string version = ""; ///< Version of the plugin.
State state = FAILED; ///< Result of the plugin's init function.
SocialIntegrationPlugin(const std::string &basepath) : basepath(basepath) {}
};
class SocialIntegration {
public:
/**
* Get the list of loaded social integration plugins.
*/
static std::vector<SocialIntegrationPlugin *> GetPlugins();
static size_t GetPluginCount();
static char *LogPluginSummary(char *buffer, const char *last);
/**
* Initialize the social integration system, loading any social integration plugins that are available.
*/
static void Initialize();
/**
* Shutdown the social integration system, and all social integration plugins that are loaded.
*/
static void Shutdown();
/**
* Allow any social integration library to handle their own events.
*/
static void RunCallbacks();
/**
* Event: user entered the main menu.
*/
static void EventEnterMainMenu();
/**
* Event: user entered the Scenario Editor.
*/
static void EventEnterScenarioEditor(uint map_width, uint map_height);
/**
* Event: user entered a singleplayer game.
*/
static void EventEnterSingleplayer(uint map_width, uint map_height);
/**
* Event: user entered a multiplayer game.
*/
static void EventEnterMultiplayer(uint map_width, uint map_height);
/**
* Event: user is joining a multiplayer game.
*/
static void EventJoiningMultiplayer();
};
#endif /* SOCIAL_INTEGRATION_H */

@ -69,13 +69,16 @@ static bool VerifyElementContentParameters(StoryPageID page_id, StoryPageElement
break;
case SPET_BUTTON_PUSH:
if (!button_data.ValidateColour()) return false;
if (!button_data.ValidateFlags()) return false;
return true;
case SPET_BUTTON_TILE:
if (!button_data.ValidateColour()) return false;
if (!button_data.ValidateFlags()) return false;
if (!button_data.ValidateCursor()) return false;
return true;
case SPET_BUTTON_VEHICLE:
if (!button_data.ValidateColour()) return false;
if (!button_data.ValidateFlags()) return false;
if (!button_data.ValidateCursor()) return false;
if (!button_data.ValidateVehicleType()) return false;
return true;

@ -113,6 +113,17 @@ enum StoryPageButtonCursor : byte {
/** Define basic enum properties */
template <> struct EnumPropsT<StoryPageButtonCursor> : MakeEnumPropsT<StoryPageButtonCursor, byte, SPBC_MOUSE, SPBC_END, INVALID_SPBC, 8> {};
/**
* Checks if a StoryPageButtonCursor value is valid.
*
* @param wc The value to check
* @return true if the given value is a valid StoryPageButtonCursor.
*/
inline bool IsValidStoryPageButtonCursor(StoryPageButtonCursor cursor)
{
return cursor < SPBC_END;
}
/** Helper to construct packed "id" values for button-type StoryPageElement */
struct StoryPageButtonData {
uint32_t referenced_id;

@ -1069,6 +1069,55 @@ static int ICUStringContains(const std::string_view str, const std::string_view
return ci_str.find(ci_value) != CaseInsensitiveStringView::npos;
}
/**
* Convert a single hex-nibble to a byte.
*
* @param c The hex-nibble to convert.
* @return The byte the hex-nibble represents, or -1 if it is not a valid hex-nibble.
*/
static int ConvertHexNibbleToByte(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c + 10 - 'A';
if (c >= 'a' && c <= 'f') return c + 10 - 'a';
return -1;
}
/**
* Convert a hex-string to a byte-array, while validating it was actually hex.
*
* @param hex The hex-string to convert.
* @param bytes The byte-array to write the result to.
*
* @note The length of the hex-string has to be exactly twice that of the length
* of the byte-array, otherwise conversion will fail.
*
* @return True iff the hex-string was valid and the conversion succeeded.
*/
bool ConvertHexToBytes(std::string_view hex, std::span<uint8_t> bytes)
{
if (bytes.size() != hex.size() / 2) {
return false;
}
/* Hex-string lengths are always divisible by 2. */
if (hex.size() % 2 != 0) {
return false;
}
for (size_t i = 0; i < hex.size() / 2; i++) {
auto hi = ConvertHexNibbleToByte(hex[i * 2]);
auto lo = ConvertHexNibbleToByte(hex[i * 2 + 1]);
if (hi < 0 || lo < 0) {
return false;
}
bytes[i] = (hi << 4) | lo;
}
return true;
}
#ifdef WITH_UNISCRIBE

@ -81,6 +81,8 @@ inline const char *StrLastPathSegment(const std::string &path)
[[nodiscard]] bool StrNaturalContains(const std::string_view str, const std::string_view value);
[[nodiscard]] bool StrNaturalContainsIgnoreCase(const std::string_view str, const std::string_view value);
bool ConvertHexToBytes(std::string_view hex, std::span<uint8_t> bytes);
/** Case insensitive comparator for strings, for example for use in std::map. */
struct CaseInsensitiveComparator {
bool operator()(const std::string_view s1, const std::string_view s2) const { return StrCompareIgnoreCase(s1, s2) < 0; }

@ -34,6 +34,8 @@
#include "base_media_base.h"
#include "blitter/factory.hpp"
#include "social_integration.h"
#include "core/format.hpp"
#ifdef WITH_ALLEGRO
@ -86,6 +88,17 @@ NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, {
{GRFStatus::GCS_ACTIVATED, "activated"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(SocialIntegrationPlugin::State, {
{SocialIntegrationPlugin::State::RUNNING, "running"},
{SocialIntegrationPlugin::State::FAILED, "failed"},
{SocialIntegrationPlugin::State::PLATFORM_NOT_RUNNING, "platform_not_running"},
{SocialIntegrationPlugin::State::UNLOADED, "unloaded"},
{SocialIntegrationPlugin::State::DUPLICATE, "duplicate"},
{SocialIntegrationPlugin::State::UNSUPPORTED_API, "unsupported_api"},
{SocialIntegrationPlugin::State::INVALID_SIGNATURE, "invalid_signature"},
})
/** Lookup table to convert a VehicleType to a string. */
static const std::string _vehicle_type_to_string[] = {
"train",
@ -413,6 +426,33 @@ void SurveyLibraries(nlohmann::json &survey)
#endif
}
/**
* Convert plugin information to JSON.
*
* @param survey The JSON object.
*/
void SurveyPlugins(nlohmann::json &survey)
{
auto _plugins = SocialIntegration::GetPlugins();
for (auto &plugin : _plugins) {
auto &platform = survey[plugin->social_platform];
platform.push_back({
{"name", plugin->name},
{"version", plugin->version},
{"basepath", plugin->basepath},
{"state", plugin->state},
});
}
}
const char *PluginStateToString(SocialIntegrationPlugin::State state)
{
const char *output = "";
to_json<const char *>(output, state);
return output;
}
/**
* Change the bytes of memory into a textual version rounded up to the biggest unit.
*

@ -21,6 +21,7 @@ void SurveyFont(nlohmann::json &survey);
void SurveyGameScript(nlohmann::json &survey);
void SurveyGrfs(nlohmann::json &survey);
void SurveyLibraries(nlohmann::json &survey);
void SurveyPlugins(nlohmann::json &survey);
void SurveyOpenTTD(nlohmann::json &survey);
void SurveySettings(nlohmann::json &survey, bool skip_if_default);
void SurveyTimers(nlohmann::json &survey);

@ -10,13 +10,15 @@
/* Callback function used in _settings[] as well as _company_settings[] */
static size_t ConvertLandscape(const char *value);
static StringID SettingTitleWallclock(const IntSettingDesc &sd);
static StringID SettingHelpWallclock(const IntSettingDesc &sd);
/* Callback function used in _settings[] as well as _gui_settings[] */
static void UpdateTimeSettings(int32_t new_value);
/* Callback function used for various settings */
static bool CheckTTDPatchSettingFlag(uint flag);
/****************************
* OTTD specific INI stuff
****************************
@ -76,14 +78,14 @@ constexpr T::BaseType STUnwrap(T value)
/* Macros for various objects to go in the configuration file.
* This section is for global variables */
#define SDTG_VAR(name, type, flags, var, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, nullptr)
#define SDTG_VAR(name, type, flags, var, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, nullptr)
#define SDTG_ENUM(name, type, flags, var, def, str, strhelp, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
NSD(Int, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags | SF_ENUM, guiproc, startup, patxname, def, 0, 0, 0, str, strhelp, STR_NULL, cat, pre_check, post_callback, enumlist)
#define SDTG_ENUM(name, type, flags, var, def, str, strhelp, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
NSD(Int, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags | SF_ENUM, guiproc, startup, patxname, def, 0, 0, 0, str, strhelp, STR_NULL, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, enumlist)
#define SDTG_BOOL(name, flags, var, def, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Bool, SLEG_GENERAL_X(SL_VAR, var, SLE_BOOL, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback)
#define SDTG_BOOL(name, flags, var, def, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Bool, SLEG_GENERAL_X(SL_VAR, var, SLE_BOOL, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook)
#define SDTG_LIST(name, type, flags, var, def, length, from, to, extver, cat, guiproc, startup, patxname)\
NSD(List, SLEG_GENERAL_X(SL_ARR, var, type, length, from, to, extver), name, flags, guiproc, startup, patxname, def)
@ -91,28 +93,28 @@ constexpr T::BaseType STUnwrap(T value)
#define SDTG_SSTR(name, type, flags, var, def, max_length, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(String, SLEG_GENERAL_X(SL_STDSTR, var, type, sizeof(var), from, to, extver), name, flags, guiproc, startup, patxname, def, max_length, pre_check, post_callback)
#define SDTG_OMANY(name, type, flags, var, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(OneOfMany, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr)
#define SDTG_OMANY(name, type, flags, var, def, max, full, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(OneOfMany, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, full, nullptr)
#define SDTG_MMANY(name, type, flags, var, def, full, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(ManyOfMany, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr)
#define SDTG_MMANY(name, type, flags, var, def, full, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(ManyOfMany, SLEG_GENERAL_X(SL_VAR, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, full, nullptr)
#define SDTG_NULL(length, from, to, extver)\
NSD(Null, SLE_CONDNULL_X(length, from, to, extver))
/* Macros for various objects to go in the configuration file.
* This section is for structures where their various members are saved */
#define SDT_VAR(base, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, nullptr)
#define SDT_VAR(base, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, nullptr)
#define SDT_VAR2(base, name, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, nullptr)
#define SDT_VAR2(base, name, var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), name, flags, guiproc, startup, patxname, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, nullptr)
#define SDT_ENUM(base, var, type, flags, def, str, strhelp, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags | SF_ENUM, guiproc, startup, patxname, def, 0, 0, 0, str, strhelp, STR_NULL, cat, pre_check, post_callback, enumlist)
#define SDT_ENUM(base, var, type, flags, def, str, strhelp, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
NSD(Int, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags | SF_ENUM, guiproc, startup, patxname, def, 0, 0, 0, str, strhelp, STR_NULL, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, enumlist)
#define SDT_BOOL(base, var, flags, def, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Bool, SLE_GENERAL_X(SL_VAR, base, var, SLE_BOOL, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback)
#define SDT_BOOL(base, var, flags, def, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
NSD(Bool, SLE_GENERAL_X(SL_VAR, base, var, SLE_BOOL, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook)
#define SDT_LIST(base, var, type, flags, def, from, to, extver, cat, guiproc, startup, patxname)\
NSD(List, SLE_GENERAL_X(SL_ARR, base, var, type, lengthof(((base*)8)->var), from, to, extver), #var, flags, guiproc, startup, patxname, def)
@ -120,11 +122,11 @@ constexpr T::BaseType STUnwrap(T value)
#define SDT_SSTR(base, var, type, flags, def, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
NSD(String, SLE_GENERAL_X(SL_STDSTR, base, var, type, sizeof(((base*)8)->var), from, to, extver), #var, flags, guiproc, startup, patxname, def, 0, pre_check, post_callback)
#define SDT_OMANY(base, var, type, flags, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, extver, load, cat, guiproc, startup, patxname)\
NSD(OneOfMany, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, pre_check, post_callback, full, load)
#define SDT_OMANY(base, var, type, flags, def, max, full, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, load, cat, guiproc, startup, patxname)\
NSD(OneOfMany, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, max, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, full, load)
#define SDT_MMANY(base, var, type, flags, def, full, str, pre_check, post_callback, strhelp, strval, from, to, extver, cat, guiproc, startup, patxname)\
NSD(ManyOfMany, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, full, nullptr)
#define SDT_MMANY(base, var, type, flags, def, full, str, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, strhelp, strval, from, to, extver, cat, guiproc, startup, patxname)\
NSD(ManyOfMany, SLE_GENERAL_X(SL_VAR, base, var, type, 1, from, to, extver), #var, flags, guiproc, startup, patxname, def, str, strhelp, strval, cat, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, full, nullptr)
#define SDT_NULL(length, from, to, extver)\
NSD(Null, SLE_CONDNULL_X(length, from, to, extver))
@ -132,14 +134,14 @@ constexpr T::BaseType STUnwrap(T value)
#define SDT_NAMED_NULL(name, length, from, to, extver, patxname)\
NSD(Null, SLE_CONDNULL_X(length, from, to, extver), name, patxname)
#define SDTC_VAR(var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_VAR(#var, type, flags, _settings_client.var, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)
#define SDTC_VAR(var, type, flags, def, min, max, interval, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_VAR(#var, type, flags, _settings_client.var, STUnwrap(def), STUnwrap(min), STUnwrap(max), interval, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)
#define SDTC_ENUM(var, type, flags, def, str, strhelp, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
SDTG_ENUM(#var, type, flags, _settings_client.var, def, str, strhelp, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname, enumlist)
#define SDTC_ENUM(var, type, flags, def, str, strhelp, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname, enumlist)\
SDTG_ENUM(#var, type, flags, _settings_client.var, def, str, strhelp, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname, enumlist)
#define SDTC_BOOL(var, flags, def, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_BOOL(#var, flags, _settings_client.var, def, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)
#define SDTC_BOOL(var, flags, def, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_BOOL(#var, flags, _settings_client.var, def, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)
#define SDTC_LIST(var, type, flags, def, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_LIST(#var, type, flags, _settings_client.var, def, lengthof(_settings_client.var), from, to, extver, cat, guiproc, startup, patxname)
@ -147,5 +149,5 @@ constexpr T::BaseType STUnwrap(T value)
#define SDTC_SSTR(var, type, flags, def, max_length, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_SSTR(#var, type, flags, _settings_client.var, def, max_length, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
#define SDTC_OMANY(var, type, flags, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_OMANY(#var, type, flags, _settings_client.var, def, max, full, str, strhelp, strval, pre_check, post_callback, from, to, extver, cat, guiproc, startup, patxname)
#define SDTC_OMANY(var, type, flags, def, max, full, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)\
SDTG_OMANY(#var, type, flags, _settings_client.var, def, max, full, str, strhelp, strval, pre_check, post_callback, get_title_hook, get_help_hook, set_value_dparams_hook, from, to, extver, cat, guiproc, startup, patxname)

@ -11,13 +11,14 @@
static void UpdateAllServiceInterval(int32_t new_value);
static bool CanUpdateServiceInterval(VehicleType type, int32_t &new_value);
static void UpdateServiceInterval(VehicleType type, int32_t new_value);
static void SettingsValueAbsolute(const IntSettingDesc &sd, uint first_param, int32_t value);
static const SettingTable _company_settings{
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(CompanySettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(CompanySettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(CompanySettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(CompanySettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_NULL = SDT_NULL($length, $from, $to, $extver),
[validation]
@ -31,6 +32,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -52,13 +56,14 @@ cat = SC_BASIC
[SDT_VAR]
var = engine_renew_months
type = SLE_INT16
flags = SF_PER_COMPANY | SF_GUI_NEGATIVE_IS_SPECIAL
flags = SF_PER_COMPANY
def = 6
min = -12
max = 12
str = STR_CONFIG_SETTING_AUTORENEW_MONTHS
strhelp = STR_CONFIG_SETTING_AUTORENEW_MONTHS_HELPTEXT
strval = STR_CONFIG_SETTING_AUTORENEW_MONTHS_VALUE_BEFORE
val_cb = SettingsValueAbsolute
[SDT_VAR]
var = engine_renew_money

@ -11,8 +11,8 @@ static const SettingTable _currency_settings{
[post-amble]
};
[templates]
SDT_VAR = SDT_VAR (CurrencySpec, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_SSTR = SDT_SSTR(CurrencySpec, $var, $type, $flags, $def, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_VAR = SDT_VAR (CurrencySpec, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_SSTR = SDT_SSTR(CurrencySpec, $var, $type, $flags, $def, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for CurrencySpec.$var exceeds storage size");
@ -25,6 +25,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -40,10 +40,10 @@ static const SettingTable _difficulty_settings{
[post-amble]
};
[templates]
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -57,6 +57,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -249,6 +252,7 @@ max = 5000
interval = 1
str = STR_CONFIG_SETTING_SUBSIDY_DURATION
strhelp = STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT
help_cb = SettingHelpWallclock
strval = STR_CONFIG_SETTING_SUBSIDY_DURATION_VALUE
[SDT_VAR]

@ -30,9 +30,9 @@ static const SettingTable _economy_settings{
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR2 = SDT_VAR2(GameSettings, $name, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR2 = SDT_VAR2(GameSettings, $name, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
@ -45,6 +45,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -244,6 +247,7 @@ var = economy.bribe
def = true
str = STR_CONFIG_SETTING_BRIBE
strhelp = STR_CONFIG_SETTING_BRIBE_HELPTEXT
help_cb = SettingHelpWallclock
post_cb = [](auto) { SetWindowClassesDirty(WC_TOWN_AUTHORITY); }
cat = SC_BASIC
@ -253,6 +257,7 @@ from = SLV_79
def = true
str = STR_CONFIG_SETTING_ALLOW_EXCLUSIVE
strhelp = STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT
help_cb = SettingHelpWallclock
post_cb = [](auto) { SetWindowClassesDirty(WC_TOWN_AUTHORITY); }
cat = SC_BASIC
@ -857,7 +862,7 @@ patxname = ""town_cargo_adj.economy.town_cargo_factor""
name = ""economy.town_cargo_scale_factor""
var = old_economy.town_cargo_scale_factor
type = SLE_INT16
flags = SF_DECIMAL1 | SF_DEC1SCALE | SF_PATCH
flags = SF_PATCH
def = 0
min = -160
max = +80
@ -870,7 +875,7 @@ patxname = ""town_cargo_adj.economy.town_cargo_scale_factor""
name = ""economy.industry_cargo_scale_factor""
var = old_economy.industry_cargo_scale_factor
type = SLE_INT16
flags = SF_DECIMAL1 | SF_DEC1SCALE | SF_PATCH
flags = SF_PATCH
def = 0
min = -50
max = +50

@ -49,13 +49,13 @@ static const SettingTable _game_settings{
[post-amble]
};
[templates]
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -70,6 +70,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -72,12 +72,12 @@ const SettingTable _gui_settings{
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $def, $length, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_ENUM = SDTC_ENUM( $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags, $def, $length, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_ENUM = SDTC_ENUM( $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -91,6 +91,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -147,7 +150,9 @@ var = gui.show_finances
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = true
str = STR_CONFIG_SETTING_SHOWFINANCES
str_cb = SettingTitleWallclock
strhelp = STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT
help_cb = SettingHelpWallclock
cat = SC_BASIC
[SDTC_VAR]
@ -191,7 +196,7 @@ cat = SC_BASIC
[SDTC_BOOL]
var = gui.smooth_scroll
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = false
def = true
str = STR_CONFIG_SETTING_SMOOTH_SCROLLING
strhelp = STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT
@ -1106,6 +1111,7 @@ flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = true
str = STR_CONFIG_SETTING_WARN_INCOME_LESS
strhelp = STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT
help_cb = SettingHelpWallclock
cat = SC_BASIC
[SDTC_VAR]

@ -43,10 +43,10 @@ static const SettingTable _linkgraph_settings{
[post-amble]
};
[templates]
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_ENUM = SDT_ENUM(GameSettings, $var, $type, $flags, $def, $str, $strhelp, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname, $enumlist),
SDT_NAMED_NULL = SDT_NAMED_NULL($name, $length, $from, $to, $extver, $patxname),
SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SF_NOT_IN_CONFIG | SF_NO_NEWGAME | SF_PATCH, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $pre_cb, $post_cb, $from, $to, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), SC_EXPERT, LinkGraphDistributionSettingGUI, false, nullptr, _linkgraph_mode_per_cargo),
SDT_LINKGRAPH_PER_CARGO = SDT_ENUM(GameSettings, linkgraph.distribution_per_cargo[$linkgraph_cargo], SLE_UINT8, $flags | SF_NOT_IN_CONFIG | SF_NO_NEWGAME | SF_PATCH, DT_PER_CARGO_DEFAULT, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO, STR_CONFIG_SETTING_DISTRIBUTION_PER_CARGO_HELPTEXT, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_MODES), SC_EXPERT, LinkGraphDistributionSettingGUI, false, nullptr, _linkgraph_mode_per_cargo),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
@ -59,6 +59,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -8,6 +8,8 @@
; in the savegame PATS chunk. These settings are not sync'd over the network.
[pre-amble]
static void SettingsValueVelocityUnit(const IntSettingDesc &sd, uint first_param, int32_t value);
uint8_t _old_units; ///< Old units from old savegames
static constexpr std::initializer_list<const char*> _locale_currencies{"GBP", "USD", "EUR", "JPY", "ATS", "BEF", "CHF", "CZK", "DEM", "DKK", "ESP", "FIM", "FRF", "GRD", "HUF", "ISK", "ITL", "NLG", "NOK", "PLN", "RON", "RUR", "SIT", "SEK", "TRY", "SKK", "BRL", "EEK", "LTL", "KRW", "ZAR", "custom", "GEL", "IRR", "RUB", "MXN", "NTD", "CNY", "HKD", "INR", "IDR", "MYR", "LVL"};
@ -21,10 +23,10 @@ static const SettingTable _locale_settings{
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_SSTR = SDT_SSTR(GameSettings, $var, $type, $flags, $def, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_SSTR = SDT_SSTR(GameSettings, $var, $type, $flags, $def, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDTG_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -38,6 +40,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -85,7 +90,7 @@ post_cb = VelocityUnitsChanged
cat = SC_BASIC
str = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY
strhelp = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT
strval = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL
val_cb = SettingsValueVelocityUnit
[SDT_OMANY]
var = locale.units_velocity_nautical
@ -99,7 +104,7 @@ post_cb = VelocityUnitsChanged
cat = SC_BASIC
str = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_NAUTICAL
strhelp = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_HELPTEXT
strval = STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL
val_cb = SettingsValueVelocityUnit
extver = SlXvFeatureTest(XSLFTO_OR, XSLFI_VELOCITY_NAUTICAL)
patxname = ""locale.units_velocity_nautical""

@ -25,12 +25,12 @@ static const SettingTable _misc_settings{
[post-amble]
};
[templates]
SDTG_LIST = SDTG_LIST($name, $type, $flags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $var, $def, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $var, $def, 0, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_LIST = SDTG_LIST($name, $type, $flags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_MMANY = SDTG_MMANY($name, $type, $flags, $var, $def, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_SSTR = SDTG_SSTR($name, $type, $flags, $var, $def, 0, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -44,6 +44,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -12,9 +12,9 @@ const SettingTable _multimedia_settings = {
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -27,6 +27,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -56,7 +59,9 @@ var = sound.new_year
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = true
str = STR_CONFIG_SETTING_SOUND_NEW_YEAR
str_cb = SettingTitleWallclock
strhelp = STR_CONFIG_SETTING_SOUND_NEW_YEAR_HELPTEXT
help_cb = SettingHelpWallclock
[SDTC_BOOL]
var = sound.confirm

@ -14,9 +14,9 @@ static const SettingTable _network_private_settings = {
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags | SF_PRIVATE, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags | SF_PRIVATE, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags | SF_PRIVATE, $def, $length, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_BOOL = SDTC_BOOL( $var, $flags | SF_PRIVATE, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags | SF_PRIVATE, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_SSTR = SDTC_SSTR( $var, $type, $flags | SF_PRIVATE, $def, $length, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -29,6 +29,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -8,6 +8,7 @@
[pre-amble]
static void UpdateClientConfigValues();
void ChangeNetworkRestartTime(bool reset);
static constexpr std::initializer_list<const char*> _server_game_type{"local", "public", "invite-only"};
@ -15,9 +16,9 @@ static const SettingTable _network_settings = {
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -31,6 +32,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION
@ -243,6 +247,16 @@ min = CalTime::MIN_YEAR
max = CalTime::MAX_YEAR
interval = 1
[SDTC_VAR]
var = network.restart_hours
type = SLE_UINT16
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_GUI_0_IS_SPECIAL | SF_NETWORK_ONLY
def = 0
min = 0
max = UINT16_MAX
interval = 1
post_cb = [](auto) { ChangeNetworkRestartTime(false); }
[SDTC_VAR]
var = network.min_active_clients
type = SLE_UINT8

@ -13,7 +13,7 @@ const SettingTable _news_display_settings = {
[post-amble]
};
[templates]
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDTC_OMANY = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -26,6 +26,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -32,13 +32,13 @@ static const SettingTable _old_gameopt_settings{
[post-amble]
};
[templates]
SDTG_LIST = SDTG_LIST($name, $type, $flags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_NULL = SDT_NULL( $length, $from, $to, $extver),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, nullptr),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_LIST = SDTG_LIST($name, $type, $flags, $var, $def, $length, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_NULL = SDT_NULL( $length, $from, $to, $extver),
SDTC_OMANY = SDTC_OMANY( $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $var, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, nullptr),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -55,6 +55,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -17,8 +17,8 @@ static const SettingTable _pathfinding_settings{
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
@ -31,6 +31,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -12,9 +12,9 @@ const SettingTable _scenario_settings = {
[post-amble]
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_BOOL = SDTC_BOOL( $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_LIST = SDTC_LIST( $var, $type, $flags, $def, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDTC_VAR = SDTC_VAR( $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDTC_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -27,6 +27,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -18,9 +18,9 @@ const SettingTable _script_settings = {
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
@ -34,6 +34,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -17,8 +17,8 @@ static const SettingTable _win32_settings{
};
#endif /* _WIN32 */
[templates]
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_BOOL = SDTG_BOOL($name, $flags, $var, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDTG_VAR = SDTG_VAR($name, $type, $flags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDTG_VAR = static_assert($max <= MAX_$type, "Maximum value for $var exceeds storage size");
@ -31,6 +31,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -13,8 +13,8 @@ static const SettingTable _window_settings{
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(WindowDescPreferences, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_VAR = SDT_VAR(WindowDescPreferences, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_BOOL = SDT_BOOL(WindowDescPreferences, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
SDT_VAR = SDT_VAR(WindowDescPreferences, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, nullptr),
[validation]
SDT_VAR = static_assert($max <= MAX_$type, "Maximum value for WindowDescPreferences.$var exceeds storage size");
@ -27,6 +27,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -27,9 +27,9 @@ const SettingTable _world_settings = {
[post-amble]
};
[templates]
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_BOOL = SDT_BOOL(GameSettings, $var, $flags, $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
SDT_OMANY = SDT_OMANY(GameSettings, $var, $type, $flags, $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $load, $cat, $guiproc, $startup, $patxname),
SDT_VAR = SDT_VAR(GameSettings, $var, $type, $flags, $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $from, $to, $extver, $cat, $guiproc, $startup, $patxname),
[validation]
SDT_OMANY = static_assert($max <= MAX_$type, "Maximum value for GameSettings.$var exceeds storage size");
@ -43,6 +43,9 @@ strhelp = STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT
strval = STR_NULL
pre_cb = nullptr
post_cb = nullptr
str_cb = nullptr
help_cb = nullptr
val_cb = nullptr
guiproc = nullptr
load = nullptr
from = SL_MIN_VERSION

@ -343,3 +343,45 @@ TEST_CASE("FormatArrayAsHex")
CHECK(FormatArrayAsHex(std::array<byte, 1>{0x12}) == "12");
CHECK(FormatArrayAsHex(std::array<byte, 4>{0x13, 0x38, 0x42, 0xAF}) == "133842af");
}
TEST_CASE("ConvertHexToBytes")
{
CHECK(ConvertHexToBytes("", {}) == true);
CHECK(ConvertHexToBytes("1", {}) == false);
CHECK(ConvertHexToBytes("12", {}) == false);
std::array<uint8_t, 1> bytes1;
CHECK(ConvertHexToBytes("1", bytes1) == false);
CHECK(ConvertHexToBytes("12", bytes1) == true);
CHECK(bytes1[0] == 0x12);
CHECK(ConvertHexToBytes("123", bytes1) == false);
CHECK(ConvertHexToBytes("1g", bytes1) == false);
CHECK(ConvertHexToBytes("g1", bytes1) == false);
std::array<uint8_t, 2> bytes2;
CHECK(ConvertHexToBytes("12", bytes2) == false);
CHECK(ConvertHexToBytes("1234", bytes2) == true);
CHECK(bytes2[0] == 0x12);
CHECK(bytes2[1] == 0x34);
std::array<uint8_t, 8> bytes3;
CHECK(ConvertHexToBytes("123456789abcdef0", bytes3) == true);
CHECK(bytes3[0] == 0x12);
CHECK(bytes3[1] == 0x34);
CHECK(bytes3[2] == 0x56);
CHECK(bytes3[3] == 0x78);
CHECK(bytes3[4] == 0x9a);
CHECK(bytes3[5] == 0xbc);
CHECK(bytes3[6] == 0xde);
CHECK(bytes3[7] == 0xf0);
CHECK(ConvertHexToBytes("123456789ABCDEF0", bytes3) == true);
CHECK(bytes3[0] == 0x12);
CHECK(bytes3[1] == 0x34);
CHECK(bytes3[2] == 0x56);
CHECK(bytes3[3] == 0x78);
CHECK(bytes3[4] == 0x9a);
CHECK(bytes3[5] == 0xbc);
CHECK(bytes3[6] == 0xde);
CHECK(bytes3[7] == 0xf0);
}

@ -206,8 +206,8 @@ void VideoDriver_Dedicated::MainLoop()
_network_dedicated = true;
_current_company = _local_company = COMPANY_SPECTATOR;
/* If SwitchMode is SM_LOAD_GAME, it means that the user used the '-g' options */
if (_switch_mode != SM_LOAD_GAME) {
/* If SwitchMode is SM_LOAD_GAME / SM_START_HEIGHTMAP, it means that the user used the '-g' options */
if (_switch_mode != SM_LOAD_GAME && _switch_mode != SM_START_HEIGHTMAP) {
StartNewGameWithoutGUI(GENERATE_NEW_SEED);
}

@ -4286,11 +4286,47 @@ static inline void ClampViewportToMap(const Viewport *vp, int *scroll_x, int *sc
}
}
/**
* Clamp the smooth scroll to a maxmimum speed and distance based on time elapsed.
*
* Every 30ms, we move 1/4th of the distance, to give a smooth movement experience.
* But we never go over the max_scroll speed.
*
* @param delta_ms Time elapsed since last update.
* @param delta_hi The distance to move in highest dimension (can't be zero).
* @param delta_lo The distance to move in lowest dimension.
* @param[out] delta_hi_clamped The clamped distance to move in highest dimension.
* @param[out] delta_lo_clamped The clamped distance to move in lowest dimension.
*/
static void ClampSmoothScroll(uint32_t delta_ms, int64_t delta_hi, int64_t delta_lo, int &delta_hi_clamped, int &delta_lo_clamped)
{
/** A tile is 64 pixels in width at 1x zoom; viewport coordinates are in 4x zoom. */
constexpr int PIXELS_PER_TILE = TILE_PIXELS * 2 * ZOOM_LVL_BASE;
assert(delta_hi != 0);
/* Move at most 75% of the distance every 30ms, for a smooth experience */
int64_t delta_left = delta_hi * std::pow(0.75, delta_ms / 30.0);
/* Move never more than 16 tiles per 30ms. */
int max_scroll = ScaleByMapSize1D(16 * PIXELS_PER_TILE * delta_ms / 30);
/* We never go over the max_scroll speed. */
delta_hi_clamped = Clamp(delta_hi - delta_left, -max_scroll, max_scroll);
/* The lower delta is in ratio of the higher delta, so we keep going straight at the destination. */
delta_lo_clamped = delta_lo * delta_hi_clamped / delta_hi;
/* Ensure we always move (delta_hi can't be zero). */
if (delta_hi_clamped == 0) {
delta_hi_clamped = delta_hi > 0 ? 1 : -1;
}
}
/**
* Update the next viewport position being displayed.
* @param w %Window owning the viewport.
*/
void UpdateNextViewportPosition(Window *w)
void UpdateNextViewportPosition(Window *w, uint32_t delta_ms)
{
const Viewport *vp = w->viewport;
@ -4308,23 +4344,22 @@ void UpdateNextViewportPosition(Window *w)
int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
int current_x = w->viewport->scrollpos_x;
int current_y = w->viewport->scrollpos_y;
w->viewport->next_scrollpos_x = w->viewport->scrollpos_x;
w->viewport->next_scrollpos_y = w->viewport->scrollpos_y;
bool update_overlay = false;
if (delta_x != 0 || delta_y != 0) {
if (_settings_client.gui.smooth_scroll) {
int max_scroll = ScaleByMapSize1D(512 * ZOOM_LVL_BASE);
int delta_x_clamped;
int delta_y_clamped;
if (abs(delta_x) > abs(delta_y)) {
delta_x_clamped = Clamp(DivAwayFromZero(delta_x, 4), -max_scroll, max_scroll);
delta_y_clamped = delta_y * delta_x_clamped / delta_x;
ClampSmoothScroll(delta_ms, delta_x, delta_y, delta_x_clamped, delta_y_clamped);
} else {
delta_y_clamped = Clamp(DivAwayFromZero(delta_y, 4), -max_scroll, max_scroll);
delta_x_clamped = delta_x * delta_y_clamped / delta_y;
ClampSmoothScroll(delta_ms, delta_y, delta_x, delta_y_clamped, delta_x_clamped);
}
w->viewport->next_scrollpos_x += delta_x_clamped;
@ -4340,6 +4375,13 @@ void UpdateNextViewportPosition(Window *w)
ClampViewportToMap(vp, &w->viewport->next_scrollpos_x, &w->viewport->next_scrollpos_y);
/* When moving small amounts around the border we can get stuck, and
* not actually move. In those cases, teleport to the destination. */
if ((delta_x != 0 || delta_y != 0) && current_x == w->viewport->next_scrollpos_x && current_y == w->viewport->next_scrollpos_y) {
w->viewport->next_scrollpos_x = w->viewport->dest_scrollpos_x;
w->viewport->next_scrollpos_y = w->viewport->dest_scrollpos_y;
}
if (_scrolling_viewport == w) UpdateActiveScrollingViewport(w);
}
}

@ -33,7 +33,7 @@ void InitializeWindowViewport(Window *w, int x, int y, int width, int height, ui
Viewport *IsPtInWindowViewport(const Window *w, int x, int y);
Point TranslateXYToTileCoord(const Viewport *vp, int x, int y, bool clamp_to_map = true);
Point GetTileBelowCursor();
void UpdateNextViewportPosition(Window *w);
void UpdateNextViewportPosition(Window *w, uint32_t delta_ms);
void ApplyNextViewportPosition(Window *w);
void UpdateViewportSizeZoom(Viewport *vp);

@ -29,7 +29,6 @@ enum OrderWidgets : WidgetID {
WID_O_UNLOAD, ///< Select unload.
WID_O_REFIT, ///< Select refit.
WID_O_SERVICE, ///< Select service (at depot).
WID_O_EMPTY, ///< Placeholder for refit dropdown when not owner.
WID_O_REFIT_DROPDOWN, ///< Open refit options.
WID_O_REVERSE, ///< Select waypoint reverse type
WID_O_COND_VARIABLE, ///< Choose condition variable.

@ -15,6 +15,7 @@ enum GameOptionsWidgets : WidgetID {
WID_GO_TAB_GENERAL, ///< General tab.
WID_GO_TAB_GRAPHICS, ///< Graphics tab.
WID_GO_TAB_SOUND, ///< Sound tab.
WID_GO_TAB_SOCIAL, ///< Social tab.
WID_GO_TAB_SELECTION, ///< Background of the tab selection.
WID_GO_CURRENCY_DROPDOWN, ///< Currency dropdown.
WID_GO_DISTANCE_DROPDOWN, ///< Measuring unit dropdown.
@ -54,6 +55,10 @@ enum GameOptionsWidgets : WidgetID {
WID_GO_SURVEY_PARTICIPATE_BUTTON, ///< Toggle for participating in the automated survey.
WID_GO_SURVEY_LINK_BUTTON, ///< Button to open browser to go to the survey website.
WID_GO_SURVEY_PREVIEW_BUTTON, ///< Button to open a preview window with the survey results
WID_GO_SOCIAL_PLUGINS, ///< Main widget handling the social plugins.
WID_GO_SOCIAL_PLUGIN_TITLE, ///< Title of the frame of the social plugin.
WID_GO_SOCIAL_PLUGIN_PLATFORM, ///< Platform of the social plugin.
WID_GO_SOCIAL_PLUGIN_STATE, ///< State of the social plugin.
};
/** Widgets of the #GameSettingsWindow class. */

@ -1578,8 +1578,8 @@ void Window::FindWindowPlacementAndResize(int def_width, int def_height)
ResizeWindow(this, enlarge_x, enlarge_y);
/* ResizeWindow() calls this->OnResize(). */
} else {
/* Always call OnResize; that way the scrollbars and matrices get initialized. */
this->OnResize();
/* Schedule OnResize; that way the scrollbars and matrices get initialized. */
this->ScheduleResize();
}
int nx = this->left;
@ -2197,8 +2197,8 @@ void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
EnsureVisibleCaption(w, w->left, w->top);
/* Always call OnResize to make sure everything is initialised correctly if it needs to be. */
w->OnResize();
/* Schedule OnResize to make sure everything is initialised correctly if it needs to be. */
w->ScheduleResize();
extern bool _gfx_draw_active;
if (_gfx_draw_active) {
SetWindowDirtyPending(w);
@ -3272,6 +3272,7 @@ void UpdateWindows()
/* Process invalidations before anything else. */
for (Window *w : Window::Iterate()) {
w->ProcessScheduledResize();
w->ProcessScheduledInvalidations();
w->ProcessHighlightedInvalidations();
}
@ -3314,7 +3315,7 @@ void UpdateWindows()
for (Window *w : Window::Iterate()) {
/* Update viewport only if window is not shaded. */
if (w->viewport != nullptr && !w->IsShaded()) UpdateNextViewportPosition(w);
if (w->viewport != nullptr && !w->IsShaded()) UpdateNextViewportPosition(w, delta_ms);
}
DrawDirtyBlocks();
@ -3375,6 +3376,26 @@ void SetWindowClassesDirty(WindowClass cls)
}
}
/**
* Mark this window as resized and in need of OnResize() event.
*/
void Window::ScheduleResize()
{
this->scheduled_resize = true;
}
/**
* Process scheduled OnResize() event.
*/
void Window::ProcessScheduledResize()
{
/* Sometimes OnResize() resizes the window again, in which case we can reprocess immediately. */
while (this->scheduled_resize) {
this->scheduled_resize = false;
this->OnResize();
}
}
/**
* Mark this window's data as invalid (in need of re-computing)
* @param data The data to invalidate with

@ -324,6 +324,7 @@ protected:
virtual void FindWindowPlacementAndResize(int def_width, int def_height);
std::vector<int> scheduled_invalidation_data; ///< Data of scheduled OnInvalidateData() calls.
bool scheduled_resize; ///< Set if window has been resized.
virtual ~Window();
@ -600,6 +601,8 @@ public:
void SetShaded(bool make_shaded);
void ScheduleResize();
void ProcessScheduledResize();
void InvalidateData(int data = 0, bool gui_scope = true);
void ProcessScheduledInvalidations();
void ProcessHighlightedInvalidations();

Loading…
Cancel
Save