diff --git a/.github/changelog.py b/.github/changelog.py new file mode 100644 index 0000000000..007774e8ec --- /dev/null +++ b/.github/changelog.py @@ -0,0 +1,36 @@ +import sys + +""" +This script assumes changelogs use the following format: + ## .x (eg. "## 15.x") to indicate a major version series + ### .[-] (eg. "## 15.0 (2025-04-01)", "### 15.1-beta1 (2024-12-25)") to indicate an individual version +""" + +def main(): + current_version = sys.argv[1] + stable_version = current_version.split("-")[0] + major_version = current_version.split(".")[0] + # set when current version is found + current_found = False + + with open("changelog.md", "r") as file: + for line in file: + if line.startswith("### "): + if not line.startswith(f"### {current_version} ") and not current_found: + # First version in changelog should be the current one + sys.stderr.write(f"Changelog doesn't start with current version ({current_version})\n") + sys.exit(1) + if not line.startswith(f"### {stable_version}"): + # Reached a previous stable version + break + if line.startswith(f"### {current_version} "): + current_found = True + elif line.startswith("## "): + if not line.startswith(f"## {major_version}.x"): + # Reached a previous major version + break + + print(line.rstrip()) + +if __name__ == '__main__': + main() diff --git a/.github/changelog.sh b/.github/changelog.sh deleted file mode 100755 index 26a66d6bdd..0000000000 --- a/.github/changelog.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -tag=$(git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed 's@\^0$@@') - -# If we are a tag, show the part of the changelog till (but excluding) the last stable -if [ -n "$tag" ]; then - grep='^[0-9]\+\.[0-9]\+[^-]' - next=$(cat changelog.md | grep '^[0-9]' | awk 'BEGIN { show="false" } // { if (show=="true") print $0; if ($1=="'$tag'") show="true"} ' | grep "$grep" | head -n1 | sed 's/ .*//') - cat changelog.md | awk 'BEGIN { show="false" } /^[0-9]+.[0-9]+/ { if ($1=="'$next'") show="false"; if ($1=="'$tag'") show="true";} // { if (show=="true") print $0 }' - exit 0 -fi - -# In all other cases, show the git log of the last 7 days -revdate=$(git log -1 --pretty=format:"%ci") -last_week=$(date -d "$revdate -7days" +"%Y-%m-%d %H:%M") -git log --after="${last_week}" --pretty=fuller diff --git a/.github/workflows/ci-emscripten.yml b/.github/workflows/ci-emscripten.yml index 13cce318c7..d115ea9e56 100644 --- a/.github/workflows/ci-emscripten.yml +++ b/.github/workflows/ci-emscripten.yml @@ -12,7 +12,8 @@ jobs: runs-on: ubuntu-latest container: - # If you change this version, change the number in the cache step too. + # If you change this version, change the numbers in the cache step, + # .github/workflows/preview-build.yml (2x) and os/emscripten/Dockerfile too. image: emscripten/emsdk:3.1.57 steps: @@ -23,9 +24,18 @@ jobs: run: | git config --global --add safe.directory ${GITHUB_WORKSPACE} + - name: Update to modern GCC + run: | + apt-get update + apt-get install -y gcc-12 g++-12 + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100 + - name: Setup cache uses: actions/cache@v4 with: + # If you change this version, change the numbers in the image configuration step, + # .github/workflows/preview-build.yml (2x) and os/emscripten/Dockerfile too. path: /emsdk/upstream/emscripten/cache key: 3.1.57-${{ runner.os }} diff --git a/.github/workflows/ci-nightly.yml b/.github/workflows/ci-nightly.yml index 15bc55a97c..a4cf09eb48 100644 --- a/.github/workflows/ci-nightly.yml +++ b/.github/workflows/ci-nightly.yml @@ -9,27 +9,6 @@ env: CTEST_OUTPUT_ON_FAILURE: 1 jobs: - linux: - strategy: - fail-fast: false - matrix: - include: - - name: GCC - SDL1.2 - compiler: gcc - cxxcompiler: g++ - libraries: libsdl1.2-dev - - name: Linux (${{ matrix.name }}) - - uses: ./.github/workflows/ci-linux.yml - secrets: inherit - - with: - compiler: ${{ matrix.compiler }} - cxxcompiler: ${{ matrix.cxxcompiler }} - libraries: ${{ matrix.libraries }} - extra-cmake-parameters: - macos: strategy: fail-fast: false @@ -69,7 +48,6 @@ jobs: check_annotations: name: Check Annotations needs: - - linux - macos - mingw diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 447cd5bcdd..bf08f47ed0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -88,6 +88,7 @@ jobs: with: languages: cpp config-file: ./.github/codeql/codeql-config.yml + trap-caching: false - name: Build run: | diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 9cf6e9573c..cf9e8394af 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -20,7 +20,8 @@ jobs: runs-on: ubuntu-latest container: - # If you change this version, change the number in the cache step too. + # If you change this version, change the numbers in the cache step, + # .github/workflows/ci-emscripten.yml (2x) and os/emscripten/Dockerfile too. image: emscripten/emsdk:3.1.57 steps: @@ -34,10 +35,19 @@ jobs: git config --global --add safe.directory ${GITHUB_WORKSPACE} git checkout -b pr${{ github.event.pull_request.number }} + - name: Update to modern GCC + run: | + apt-get update + apt-get install -y gcc-12 g++-12 + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100 + - name: Setup cache uses: actions/cache@v4 with: path: /emsdk/upstream/emscripten/cache + # If you change this version, change the numbers in the image configuration step, + # .github/workflows/ci-emscripten.yml (2x) and os/emscripten/Dockerfile too. key: 3.1.57-${{ runner.os }} - name: Add liblzma support diff --git a/.github/workflows/rebase-checker.yml b/.github/workflows/rebase-checker.yml new file mode 100644 index 0000000000..29ac833a39 --- /dev/null +++ b/.github/workflows/rebase-checker.yml @@ -0,0 +1,19 @@ +name: "Update 'work: needs rebase' label status" +on: + # So that PRs touching the same files as the push are updated + push: + # So that the `dirtyLabel` is removed if conflicts are resolve + # We recommend `pull_request_target` so that github secrets are available. + # In `pull_request` we wouldn't be able to change labels of fork PRs + pull_request_target: + types: [synchronize] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: check if prs are in need of a rebase + uses: eps1lon/actions-label-merge-conflict@v3 + with: + dirtyLabel: "work: needs rebase" + repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index 1fdfce193e..93006d56a2 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -72,10 +72,10 @@ jobs: # dependencies as possible. We do it before anything else is installed, # to make sure it doesn't pick up on any of the drivers. echo "::group::Install fluidsynth" - wget https://github.com/FluidSynth/fluidsynth/archive/v2.3.3.tar.gz - tar xf v2.3.3.tar.gz + wget https://github.com/FluidSynth/fluidsynth/archive/v2.4.4.tar.gz + tar xf v2.4.4.tar.gz ( - cd fluidsynth-2.3.3 + cd fluidsynth-2.4.4 mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr diff --git a/.github/workflows/release-source.yml b/.github/workflows/release-source.yml index bccb7042b8..5cd7f184c5 100644 --- a/.github/workflows/release-source.yml +++ b/.github/workflows/release-source.yml @@ -90,10 +90,10 @@ jobs: - name: Generate metadata id: metadata + shell: bash run: | echo "::group::Prepare metadata files" cmake -DGENERATE_OTTDREV=1 -P cmake/scripts/FindVersion.cmake - ./.github/changelog.sh > .changelog TZ='UTC' date +"%Y-%m-%d %H:%M UTC" > .release_date cat .ottdrev | cut -f 1 -d$'\t' > .version @@ -103,6 +103,8 @@ jobs: FOLDER="${{ env.FOLDER_RELEASES }}" TRIGGER_TYPE="new-tag" + + python3 ./.github/changelog.py "$(cat .version)" > .changelog else IS_TAG="false" @@ -124,6 +126,13 @@ jobs: FOLDER="${{ env.FOLDER_BRANCHES }}/${BRANCH}" TRIGGER_TYPE="new-branch" fi + + # For nightlies / branches, use the git log of the last 7 days as changelog. + revdate=$(git log -1 --pretty=format:"%ci") + last_week=$(date -d "$revdate -7days" +"%Y-%m-%d %H:%M") + echo "## Version $(cat .version) - changes since ${last_week}" > .changelog + echo "" >> .changelog + git log --oneline --after="${last_week}" >> .changelog fi mkdir -p build/bundles diff --git a/.github/workflows/upload-gog.yml b/.github/workflows/upload-gog.yml index cbe81c6a84..66d3520404 100644 --- a/.github/workflows/upload-gog.yml +++ b/.github/workflows/upload-gog.yml @@ -62,9 +62,10 @@ jobs: echo "::group::Unpack OpenGFX" unzip opengfx-all.zip + tar xf opengfx-*.tar echo "::endgroup::" - rm -f opengfx-all.zip + rm -f opengfx-all.zip opengfx-*.tar - name: Install OpenMSX shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index 90b5b5cc00..d88455b65f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) if(NOT BINARY_NAME) set(BINARY_NAME openttd) @@ -51,9 +51,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS YES) # An empty target for the tools add_custom_target(tools) -include(Endian) -add_endian_definition() - include(CompileFlags) compile_flags() @@ -136,18 +133,17 @@ endif() # Breakpad doesn't support emscripten. if(NOT EMSCRIPTEN) - find_package(unofficial-breakpad) + find_package(unofficial-breakpad NO_MODULE) endif() if(NOT OPTION_DEDICATED) - if(NOT WIN32) + if(WIN32) + find_package(Xaudio2) + else() find_package(Allegro) if(NOT APPLE) find_package(Freetype) find_package(SDL2) - if(NOT SDL2_FOUND) - find_package(SDL) - endif() find_package(Fluidsynth) if(Freetype_FOUND) find_package(Fontconfig) @@ -178,7 +174,6 @@ if(MSVC) endif() find_package(SSE) -find_package(Xaudio2) find_package(Grfcodec) @@ -188,8 +183,8 @@ check_ipo_supported(RESULT IPO_FOUND) show_options() if(UNIX AND NOT APPLE AND NOT OPTION_DEDICATED) - if(NOT SDL_FOUND AND NOT SDL2_FOUND AND NOT ALLEGRO_FOUND) - message(FATAL_ERROR "SDL, SDL2 or Allegro is required for this platform") + if(NOT SDL2_FOUND AND NOT ALLEGRO_FOUND) + message(FATAL_ERROR "SDL2 or Allegro is required for this platform") endif() if(HARFBUZZ_FOUND AND NOT ICU_i18n_FOUND) message(WARNING "HarfBuzz depends on ICU i18n to function; HarfBuzz will be disabled") @@ -237,8 +232,6 @@ endif() include(CTest) include(SourceList) -# Needed by rev.cpp -include_directories(${CMAKE_SOURCE_DIR}/src) # Needed by everything that uses Squirrel include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/squirrel/include) @@ -268,6 +261,7 @@ target_precompile_headers(openttd_lib src/stdafx.h src/core/format.hpp ) +set_source_files_properties(src/3rdparty/fmt/format.cc PROPERTIES SKIP_PRECOMPILE_HEADERS ON) add_subdirectory(${CMAKE_SOURCE_DIR}/bin) add_subdirectory(${CMAKE_SOURCE_DIR}/src) @@ -307,7 +301,6 @@ if(IPO_FOUND) set_target_properties(openttd PROPERTIES INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO True) endif() set_target_properties(openttd PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") -process_compile_flags() include(LinkPackage) link_package(PNG TARGET PNG::PNG ENCOURAGED) @@ -321,12 +314,11 @@ if(NOT WIN32 AND NOT EMSCRIPTEN) endif() if(NOT EMSCRIPTEN) - link_package(unofficial-breakpad TARGET unofficial::breakpad::libbreakpad_client ENCOURAGED) + link_package(unofficial-breakpad TARGET unofficial::breakpad::libbreakpad_client) endif() if(NOT OPTION_DEDICATED) link_package(Fluidsynth) - link_package(SDL) link_package(SDL2 TARGET SDL2::SDL2) link_package(Allegro) link_package(FREETYPE TARGET Freetype::Freetype) @@ -399,6 +391,7 @@ if(EMSCRIPTEN) target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/desync.md@/docs/desync.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/directory_structure.md@/docs/directory_structure.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/eints.md@/docs/eints.md") + target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/fonts.md@/docs/fonts.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/linkgraph.md@/docs/linkgraph.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/logging_and_performance_metrics.md@/docs/logging_and_performance_metrics.md") target_link_libraries(WASM::WASM INTERFACE "--preload-file ${CMAKE_SOURCE_DIR}/docs/multiplayer.md@/docs/multiplayer.md") diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md index a4230c03a0..4607271b0e 100644 --- a/CODINGSTYLE.md +++ b/CODINGSTYLE.md @@ -10,6 +10,8 @@ What is simple to some might appear very complicated to others. Documentation he * Function names use [CamelCase](http://www.wikipedia.org/wiki/Camelcase) without underscores. * Opening curly bracket **{** for a function starts on the next line. * Use Foo() instead of Foo(void). +* Prefer using "const" for reference and compound parameters when appropriate. +* If a member function can be a const function, make it so. ```c++ void ThisIsAFunction() { @@ -292,7 +294,6 @@ OpenTTD used to vertically-align inline Doxygen comments as shown above. OpenTTD * Put a space before and after binary operators: "a + b", "a == b", "a & b", "a <<= b", etc.. Exceptions are ".", "->" and "[]" (no spaces) and "," (just space after it). * Put parenthesis where it improves readability: "*(b++)" instead of "*b++", and "if ((a & b) && c == 2)" instead of "if (a & b && c == 2)". * Do not put external declarations in implementation (i.e. cpp) files. -* Use const where possible. * Do not typedef enums and structs. * Don't treat non-flags as flags: use "if (char_pointer != nullptr && *char_pointer != '\0')", not "if (char_pointer && *char_pointer)". * Use "free(p)" instead of "if (p != nullptr) free(p)". "free(nullptr)" doesn't hurt anyone. diff --git a/CREDITS.md b/CREDITS.md index e8001f7e54..e761c94b8f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -12,6 +12,7 @@ - Remko Bijker (Rubidium) - Coder and way more (since 0.4.5) - Patric Stout (TrueBrain) - NoProgrammer (since 0.3), sys op - Tyler Trahan (2TallTyler) - General / Time Lord (since 13) +- Richard Wheeler (zephyris) - Precision pixel production (since 15) ### Inactive Developers: diff --git a/Doxyfile.in b/Doxyfile.in index a4cf1897ce..e2c183aebb 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -311,7 +311,7 @@ PREDEFINED = WITH_ZLIB \ WITH_LZO \ WITH_LIBLZMA \ WITH_ZSTD \ - WITH_SDL \ + WITH_SDL2 \ WITH_PNG \ WITH_FONTCONFIG \ WITH_FREETYPE \ diff --git a/bin/ai/CMakeLists.txt b/bin/ai/CMakeLists.txt index 8b54f74973..7fb23cada3 100644 --- a/bin/ai/CMakeLists.txt +++ b/bin/ai/CMakeLists.txt @@ -15,7 +15,6 @@ set(AI_COMPAT_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut - ${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut ) foreach(AI_COMPAT_SOURCE_FILE IN LISTS AI_COMPAT_SOURCE_FILES) diff --git a/bin/ai/compat_0.7.nut b/bin/ai/compat_0.7.nut index 341b543bd3..974a1d684b 100644 --- a/bin/ai/compat_0.7.nut +++ b/bin/ai/compat_0.7.nut @@ -5,7 +5,8 @@ * 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 . */ -AILog.Info("0.7 API compatibility in effect:"); +/* This file contains code to downgrade the API from 1.0 to 0.7. */ + AILog.Info(" - AITown::GetLastMonthProduction's behaviour has slightly changed."); AILog.Info(" - AISubsidy::GetDestination returns STATION_INVALID for awarded subsidies."); AILog.Info(" - AISubsidy::GetSource returns STATION_INVALID for awarded subsidies."); @@ -95,158 +96,158 @@ AIEngine.IsValidEngine <- function(engine_id) return AIEngine.IsBuildable(engine_id); } -AIEngine._GetName <- AIEngine.GetName; +AIEngine.GetNameCompat0_7 <- AIEngine.GetName; AIEngine.GetName <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return null; - return AIEngine._GetName(engine_id); + return AIEngine.GetNameCompat0_7(engine_id); } -AIEngine._GetCargoType <- AIEngine.GetCargoType; +AIEngine.GetCargoTypeCompat0_7 <- AIEngine.GetCargoType; AIEngine.GetCargoType <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return 255; - return AIEngine._GetCargoType(engine_id); + return AIEngine.GetCargoTypeCompat0_7(engine_id); } -AIEngine._CanRefitCargo <- AIEngine.CanRefitCargo; +AIEngine.CanRefitCargoCompat0_7 <- AIEngine.CanRefitCargo; AIEngine.CanRefitCargo <- function(engine_id, cargo_id) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._CanRefitCargo(engine_id, cargo_id); + return AIEngine.CanRefitCargoCompat0_7(engine_id, cargo_id); } -AIEngine._CanPullCargo <- AIEngine.CanPullCargo; +AIEngine.CanPullCargoCompat0_7 <- AIEngine.CanPullCargo; AIEngine.CanPullCargo <- function(engine_id, cargo_id) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._CanPullCargo(engine_id, cargo_id); + return AIEngine.CanPullCargoCompat0_7(engine_id, cargo_id); } -AIEngine._GetCapacity <- AIEngine.GetCapacity; +AIEngine.GetCapacityCompat0_7 <- AIEngine.GetCapacity; AIEngine.GetCapacity <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetCapacity(engine_id); + return AIEngine.GetCapacityCompat0_7(engine_id); } -AIEngine._GetReliability <- AIEngine.GetReliability; +AIEngine.GetReliabilityCompat0_7 <- AIEngine.GetReliability; AIEngine.GetReliability <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetReliability(engine_id); + return AIEngine.GetReliabilityCompat0_7(engine_id); } -AIEngine._GetMaxSpeed <- AIEngine.GetMaxSpeed; +AIEngine.GetMaxSpeedCompat0_7 <- AIEngine.GetMaxSpeed; AIEngine.GetMaxSpeed <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetMaxSpeed(engine_id); + return AIEngine.GetMaxSpeedCompat0_7(engine_id); } -AIEngine._GetPrice <- AIEngine.GetPrice; +AIEngine.GetPriceCompat0_7 <- AIEngine.GetPrice; AIEngine.GetPrice <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetPrice(engine_id); + return AIEngine.GetPriceCompat0_7(engine_id); } -AIEngine._GetMaxAge <- AIEngine.GetMaxAge; +AIEngine.GetMaxAgeCompat0_7 <- AIEngine.GetMaxAge; AIEngine.GetMaxAge <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetMaxAge(engine_id); + return AIEngine.GetMaxAgeCompat0_7(engine_id); } -AIEngine._GetRunningCost <- AIEngine.GetRunningCost; +AIEngine.GetRunningCostCompat0_7 <- AIEngine.GetRunningCost; AIEngine.GetRunningCost <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetRunningCost(engine_id); + return AIEngine.GetRunningCostCompat0_7(engine_id); } -AIEngine._GetPower <- AIEngine.GetPower; +AIEngine.GetPowerCompat0_7 <- AIEngine.GetPower; AIEngine.GetPower <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetPower(engine_id); + return AIEngine.GetPowerCompat0_7(engine_id); } -AIEngine._GetWeight <- AIEngine.GetWeight; +AIEngine.GetWeightCompat0_7 <- AIEngine.GetWeight; AIEngine.GetWeight <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetWeight(engine_id); + return AIEngine.GetWeightCompat0_7(engine_id); } -AIEngine._GetMaxTractiveEffort <- AIEngine.GetMaxTractiveEffort; +AIEngine.GetMaxTractiveEffortCompat0_7 <- AIEngine.GetMaxTractiveEffort; AIEngine.GetMaxTractiveEffort <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetMaxTractiveEffort(engine_id); + return AIEngine.GetMaxTractiveEffortCompat0_7(engine_id); } -AIEngine._GetDesignDate <- AIEngine.GetDesignDate; +AIEngine.GetDesignDateCompat0_7 <- AIEngine.GetDesignDate; AIEngine.GetDesignDate <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetDesignDate(engine_id); + return AIEngine.GetDesignDateCompat0_7(engine_id); } -AIEngine._GetVehicleType <- AIEngine.GetVehicleType; +AIEngine.GetVehicleTypeCompat0_7 <- AIEngine.GetVehicleType; AIEngine.GetVehicleType <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return AIVehicle.VT_INVALID; - return AIEngine._GetVehicleType(engine_id); + return AIEngine.GetVehicleTypeCompat0_7(engine_id); } -AIEngine._IsWagon <- AIEngine.IsWagon; +AIEngine.IsWagonCompat0_7 <- AIEngine.IsWagon; AIEngine.IsWagon <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._IsWagon(engine_id); + return AIEngine.IsWagonCompat0_7(engine_id); } -AIEngine._CanRunOnRail <- AIEngine.CanRunOnRail; +AIEngine.CanRunOnRailCompat0_7 <- AIEngine.CanRunOnRail; AIEngine.CanRunOnRail <- function(engine_id, track_rail_type) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._CanRunOnRail(engine_id, track_rail_type); + return AIEngine.CanRunOnRailCompat0_7(engine_id, track_rail_type); } -AIEngine._HasPowerOnRail <- AIEngine.HasPowerOnRail; +AIEngine.HasPowerOnRailCompat0_7 <- AIEngine.HasPowerOnRail; AIEngine.HasPowerOnRail <- function(engine_id, track_rail_type) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._HasPowerOnRail(engine_id, track_rail_type); + return AIEngine.HasPowerOnRailCompat0_7(engine_id, track_rail_type); } -AIEngine._GetRoadType <- AIEngine.GetRoadType; +AIEngine.GetRoadTypeCompat0_7 <- AIEngine.GetRoadType; AIEngine.GetRoadType <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return AIRoad.ROADTYPE_INVALID; - return AIEngine._GetRoadType(engine_id); + return AIEngine.GetRoadTypeCompat0_7(engine_id); } -AIEngine._GetRailType <- AIEngine.GetRailType; +AIEngine.GetRailTypeCompat0_7 <- AIEngine.GetRailType; AIEngine.GetRailType <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return AIRail.RAILTYPE_INVALID; - return AIEngine._GetRailType(engine_id); + return AIEngine.GetRailTypeCompat0_7(engine_id); } -AIEngine._IsArticulated <- AIEngine.IsArticulated; +AIEngine.IsArticulatedCompat0_7 <- AIEngine.IsArticulated; AIEngine.IsArticulated <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return false; - return AIEngine._IsArticulated(engine_id); + return AIEngine.IsArticulatedCompat0_7(engine_id); } -AIEngine._GetPlaneType <- AIEngine.GetPlaneType; +AIEngine.GetPlaneTypeCompat0_7 <- AIEngine.GetPlaneType; AIEngine.GetPlaneType <- function(engine_id) { if (!AIEngine.IsBuildable(engine_id)) return -1; - return AIEngine._GetPlaneType(engine_id); + return AIEngine.GetPlaneTypeCompat0_7(engine_id); } _AIWaypointList <- AIWaypointList; @@ -256,139 +257,3 @@ class AIWaypointList extends _AIWaypointList { ::_AIWaypointList.constructor(AIWaypoint.WAYPOINT_RAIL); } } - -AIRoad._BuildRoadStation <- AIRoad.BuildRoadStation; -AIRoad.BuildRoadStation <- function(tile, front, road_veh_type, station_id) -{ - if (AIRoad.IsRoadStationTile(tile) && AICompany.IsMine(AITile.GetOwner(tile))) return false; - - return AIRoad._BuildRoadStation(tile, front, road_veh_type, station_id); -} - -AIRoad._BuildDriveThroughRoadStation <- AIRoad.BuildDriveThroughRoadStation; -AIRoad.BuildDriveThroughRoadStation <- function(tile, front, road_veh_type, station_id) -{ - if (AIRoad.IsRoadStationTile(tile) && AICompany.IsMine(AITile.GetOwner(tile))) return false; - - return AIRoad._BuildDriveThroughRoadStation(tile, front, road_veh_type, station_id); -} - -AIBridgeList.HasNext <- -AIBridgeList_Length.HasNext <- -AICargoList.HasNext <- -AICargoList_IndustryAccepting.HasNext <- -AICargoList_IndustryProducing.HasNext <- -AIDepotList.HasNext <- -AIEngineList.HasNext <- -AIGroupList.HasNext <- -AIIndustryList.HasNext <- -AIIndustryList_CargoAccepting.HasNext <- -AIIndustryList_CargoProducing.HasNext <- -AIIndustryTypeList.HasNext <- -AIList.HasNext <- -AIRailTypeList.HasNext <- -AISignList.HasNext <- -AIStationList.HasNext <- -AIStationList_Vehicle.HasNext <- -AISubsidyList.HasNext <- -AITileList.HasNext <- -AITileList_IndustryAccepting.HasNext <- -AITileList_IndustryProducing.HasNext <- -AITileList_StationType.HasNext <- -AITownList.HasNext <- -AIVehicleList.HasNext <- -AIVehicleList_DefaultGroup.HasNext <- -AIVehicleList_Group.HasNext <- -AIVehicleList_SharedOrders.HasNext <- -AIVehicleList_Station.HasNext <- -AIWaypointList.HasNext <- -AIWaypointList_Vehicle.HasNext <- -function() -{ - return !this.IsEnd(); -} - -AIIndustry._IsCargoAccepted <- AIIndustry.IsCargoAccepted; -AIIndustry.IsCargoAccepted <- function(industry_id, cargo_id) -{ - return AIIndustry._IsCargoAccepted(industry_id, cargo_id) != AIIndustry.CAS_NOT_ACCEPTED; -} - -AIAbstractList <- AIList; - -AIList.ChangeItem <- AIList.SetValue; - -AIRail.ERR_NONUNIFORM_STATIONS_DISABLED <- 0xFFFF; - -AICompany.GetCompanyValue <- function(company) -{ - return AICompany.GetQuarterlyCompanyValue(company, AICompany.CURRENT_QUARTER); -} - -AITown.GetLastMonthTransported <- AITown.GetLastMonthSupplied; - -AIEvent.AI_ET_INVALID <- AIEvent.ET_INVALID; -AIEvent.AI_ET_TEST <- AIEvent.ET_TEST; -AIEvent.AI_ET_SUBSIDY_OFFER <- AIEvent.ET_SUBSIDY_OFFER; -AIEvent.AI_ET_SUBSIDY_OFFER_EXPIRED <- AIEvent.ET_SUBSIDY_OFFER_EXPIRED; -AIEvent.AI_ET_SUBSIDY_AWARDED <- AIEvent.ET_SUBSIDY_AWARDED; -AIEvent.AI_ET_SUBSIDY_EXPIRED <- AIEvent.ET_SUBSIDY_EXPIRED; -AIEvent.AI_ET_ENGINE_PREVIEW <- AIEvent.ET_ENGINE_PREVIEW; -AIEvent.AI_ET_COMPANY_NEW <- AIEvent.ET_COMPANY_NEW; -AIEvent.AI_ET_COMPANY_IN_TROUBLE <- AIEvent.ET_COMPANY_IN_TROUBLE; -AIEvent.AI_ET_COMPANY_MERGER <- AIEvent.ET_COMPANY_MERGER; -AIEvent.AI_ET_COMPANY_BANKRUPT <- AIEvent.ET_COMPANY_BANKRUPT; -AIEvent.AI_ET_VEHICLE_CRASHED <- AIEvent.ET_VEHICLE_CRASHED; -AIEvent.AI_ET_VEHICLE_LOST <- AIEvent.ET_VEHICLE_LOST; -AIEvent.AI_ET_VEHICLE_WAITING_IN_DEPOT <- AIEvent.ET_VEHICLE_WAITING_IN_DEPOT; -AIEvent.AI_ET_VEHICLE_UNPROFITABLE <- AIEvent.ET_VEHICLE_UNPROFITABLE; -AIEvent.AI_ET_INDUSTRY_OPEN <- AIEvent.ET_INDUSTRY_OPEN; -AIEvent.AI_ET_INDUSTRY_CLOSE <- AIEvent.ET_INDUSTRY_CLOSE; -AIEvent.AI_ET_ENGINE_AVAILABLE <- AIEvent.ET_ENGINE_AVAILABLE; -AIEvent.AI_ET_STATION_FIRST_VEHICLE <- AIEvent.ET_STATION_FIRST_VEHICLE; -AIEvent.AI_ET_DISASTER_ZEPPELINER_CRASHED <- AIEvent.ET_DISASTER_ZEPPELINER_CRASHED; -AIEvent.AI_ET_DISASTER_ZEPPELINER_CLEARED <- AIEvent.ET_DISASTER_ZEPPELINER_CLEARED; -AIOrder.AIOF_NONE <- AIOrder.OF_NONE -AIOrder.AIOF_NON_STOP_INTERMEDIATE <- AIOrder.OF_NON_STOP_INTERMEDIATE -AIOrder.AIOF_NON_STOP_DESTINATION <- AIOrder.OF_NON_STOP_DESTINATION -AIOrder.AIOF_UNLOAD <- AIOrder.OF_UNLOAD -AIOrder.AIOF_TRANSFER <- AIOrder.OF_TRANSFER -AIOrder.AIOF_NO_UNLOAD <- AIOrder.OF_NO_UNLOAD -AIOrder.AIOF_FULL_LOAD <- AIOrder.OF_FULL_LOAD -AIOrder.AIOF_FULL_LOAD_ANY <- AIOrder.OF_FULL_LOAD_ANY -AIOrder.AIOF_NO_LOAD <- AIOrder.OF_NO_LOAD -AIOrder.AIOF_SERVICE_IF_NEEDED <- AIOrder.OF_SERVICE_IF_NEEDED -AIOrder.AIOF_STOP_IN_DEPOT <- AIOrder.OF_STOP_IN_DEPOT -AIOrder.AIOF_GOTO_NEAREST_DEPOT <- AIOrder.OF_GOTO_NEAREST_DEPOT -AIOrder.AIOF_NON_STOP_FLAGS <- AIOrder.OF_NON_STOP_FLAGS -AIOrder.AIOF_UNLOAD_FLAGS <- AIOrder.OF_UNLOAD_FLAGS -AIOrder.AIOF_LOAD_FLAGS <- AIOrder.OF_LOAD_FLAGS -AIOrder.AIOF_DEPOT_FLAGS <- AIOrder.OF_DEPOT_FLAGS -AIOrder.AIOF_INVALID <- AIOrder.OF_INVALID - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} diff --git a/bin/ai/compat_1.0.nut b/bin/ai/compat_1.0.nut index 6b76f11534..e6c616a5c8 100644 --- a/bin/ai/compat_1.0.nut +++ b/bin/ai/compat_1.0.nut @@ -5,22 +5,22 @@ * 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 . */ -AILog.Info("1.0 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.1 to 1.0. */ -AIRoad._BuildRoadStation <- AIRoad.BuildRoadStation; +AIRoad.BuildRoadStationCompat1_0 <- AIRoad.BuildRoadStation; AIRoad.BuildRoadStation <- function(tile, front, road_veh_type, station_id) { if (AIRoad.IsRoadStationTile(tile) && AICompany.IsMine(AITile.GetOwner(tile))) return false; - return AIRoad._BuildRoadStation(tile, front, road_veh_type, station_id); + return AIRoad.BuildRoadStationCompat1_0(tile, front, road_veh_type, station_id); } -AIRoad._BuildDriveThroughRoadStation <- AIRoad.BuildDriveThroughRoadStation; +AIRoad.BuildDriveThroughRoadStationCompat1_0 <- AIRoad.BuildDriveThroughRoadStation; AIRoad.BuildDriveThroughRoadStation <- function(tile, front, road_veh_type, station_id) { if (AIRoad.IsRoadStationTile(tile) && AICompany.IsMine(AITile.GetOwner(tile))) return false; - return AIRoad._BuildDriveThroughRoadStation(tile, front, road_veh_type, station_id); + return AIRoad.BuildDriveThroughRoadStationCompat1_0(tile, front, road_veh_type, station_id); } AIBridgeList.HasNext <- @@ -59,10 +59,10 @@ function() return !this.IsEnd(); } -AIIndustry._IsCargoAccepted <- AIIndustry.IsCargoAccepted; +AIIndustry.IsCargoAcceptedCompat1_0 <- AIIndustry.IsCargoAccepted; AIIndustry.IsCargoAccepted <- function(industry_id, cargo_id) { - return AIIndustry._IsCargoAccepted(industry_id, cargo_id) != AIIndustry.CAS_NOT_ACCEPTED; + return AIIndustry.IsCargoAcceptedCompat1_0(industry_id, cargo_id) != AIIndustry.CAS_NOT_ACCEPTED; } AIAbstractList <- AIList; @@ -70,77 +70,3 @@ AIAbstractList <- AIList; AIList.ChangeItem <- AIList.SetValue; AIRail.ERR_NONUNIFORM_STATIONS_DISABLED <- 0xFFFF; - -AICompany.GetCompanyValue <- function(company) -{ - return AICompany.GetQuarterlyCompanyValue(company, AICompany.CURRENT_QUARTER); -} - -AITown.GetLastMonthTransported <- AITown.GetLastMonthSupplied; - -AIEvent.AI_ET_INVALID <- AIEvent.ET_INVALID; -AIEvent.AI_ET_TEST <- AIEvent.ET_TEST; -AIEvent.AI_ET_SUBSIDY_OFFER <- AIEvent.ET_SUBSIDY_OFFER; -AIEvent.AI_ET_SUBSIDY_OFFER_EXPIRED <- AIEvent.ET_SUBSIDY_OFFER_EXPIRED; -AIEvent.AI_ET_SUBSIDY_AWARDED <- AIEvent.ET_SUBSIDY_AWARDED; -AIEvent.AI_ET_SUBSIDY_EXPIRED <- AIEvent.ET_SUBSIDY_EXPIRED; -AIEvent.AI_ET_ENGINE_PREVIEW <- AIEvent.ET_ENGINE_PREVIEW; -AIEvent.AI_ET_COMPANY_NEW <- AIEvent.ET_COMPANY_NEW; -AIEvent.AI_ET_COMPANY_IN_TROUBLE <- AIEvent.ET_COMPANY_IN_TROUBLE; -AIEvent.AI_ET_COMPANY_ASK_MERGER <- AIEvent.ET_COMPANY_ASK_MERGER; -AIEvent.AI_ET_COMPANY_MERGER <- AIEvent.ET_COMPANY_MERGER; -AIEvent.AI_ET_COMPANY_BANKRUPT <- AIEvent.ET_COMPANY_BANKRUPT; -AIEvent.AI_ET_VEHICLE_CRASHED <- AIEvent.ET_VEHICLE_CRASHED; -AIEvent.AI_ET_VEHICLE_LOST <- AIEvent.ET_VEHICLE_LOST; -AIEvent.AI_ET_VEHICLE_WAITING_IN_DEPOT <- AIEvent.ET_VEHICLE_WAITING_IN_DEPOT; -AIEvent.AI_ET_VEHICLE_UNPROFITABLE <- AIEvent.ET_VEHICLE_UNPROFITABLE; -AIEvent.AI_ET_INDUSTRY_OPEN <- AIEvent.ET_INDUSTRY_OPEN; -AIEvent.AI_ET_INDUSTRY_CLOSE <- AIEvent.ET_INDUSTRY_CLOSE; -AIEvent.AI_ET_ENGINE_AVAILABLE <- AIEvent.ET_ENGINE_AVAILABLE; -AIEvent.AI_ET_STATION_FIRST_VEHICLE <- AIEvent.ET_STATION_FIRST_VEHICLE; -AIEvent.AI_ET_DISASTER_ZEPPELINER_CRASHED <- AIEvent.ET_DISASTER_ZEPPELINER_CRASHED; -AIEvent.AI_ET_DISASTER_ZEPPELINER_CLEARED <- AIEvent.ET_DISASTER_ZEPPELINER_CLEARED; -AIOrder.AIOF_NONE <- AIOrder.OF_NONE -AIOrder.AIOF_NON_STOP_INTERMEDIATE <- AIOrder.OF_NON_STOP_INTERMEDIATE -AIOrder.AIOF_NON_STOP_DESTINATION <- AIOrder.OF_NON_STOP_DESTINATION -AIOrder.AIOF_UNLOAD <- AIOrder.OF_UNLOAD -AIOrder.AIOF_TRANSFER <- AIOrder.OF_TRANSFER -AIOrder.AIOF_NO_UNLOAD <- AIOrder.OF_NO_UNLOAD -AIOrder.AIOF_FULL_LOAD <- AIOrder.OF_FULL_LOAD -AIOrder.AIOF_FULL_LOAD_ANY <- AIOrder.OF_FULL_LOAD_ANY -AIOrder.AIOF_NO_LOAD <- AIOrder.OF_NO_LOAD -AIOrder.AIOF_SERVICE_IF_NEEDED <- AIOrder.OF_SERVICE_IF_NEEDED -AIOrder.AIOF_STOP_IN_DEPOT <- AIOrder.OF_STOP_IN_DEPOT -AIOrder.AIOF_GOTO_NEAREST_DEPOT <- AIOrder.OF_GOTO_NEAREST_DEPOT -AIOrder.AIOF_NON_STOP_FLAGS <- AIOrder.OF_NON_STOP_FLAGS -AIOrder.AIOF_UNLOAD_FLAGS <- AIOrder.OF_UNLOAD_FLAGS -AIOrder.AIOF_LOAD_FLAGS <- AIOrder.OF_LOAD_FLAGS -AIOrder.AIOF_DEPOT_FLAGS <- AIOrder.OF_DEPOT_FLAGS -AIOrder.AIOF_INVALID <- AIOrder.OF_INVALID - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} diff --git a/bin/ai/compat_1.1.nut b/bin/ai/compat_1.1.nut index 9c568a7006..eed756e1e3 100644 --- a/bin/ai/compat_1.1.nut +++ b/bin/ai/compat_1.1.nut @@ -5,7 +5,7 @@ * 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 . */ -AILog.Info("1.1 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.2 to 1.1. */ AICompany.GetCompanyValue <- function(company) { @@ -54,30 +54,3 @@ AIOrder.AIOF_UNLOAD_FLAGS <- AIOrder.OF_UNLOAD_FLAGS AIOrder.AIOF_LOAD_FLAGS <- AIOrder.OF_LOAD_FLAGS AIOrder.AIOF_DEPOT_FLAGS <- AIOrder.OF_DEPOT_FLAGS AIOrder.AIOF_INVALID <- AIOrder.OF_INVALID - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} diff --git a/bin/ai/compat_1.10.nut b/bin/ai/compat_1.10.nut index 44bc2542ce..0a7c37c638 100644 --- a/bin/ai/compat_1.10.nut +++ b/bin/ai/compat_1.10.nut @@ -5,17 +5,4 @@ * 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 . */ -AILog.Info("1.10 API compatibility in effect."); - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.11 to 1.10. */ diff --git a/bin/ai/compat_1.11.nut b/bin/ai/compat_1.11.nut index 3d8370ffc6..ca44c2d9fe 100644 --- a/bin/ai/compat_1.11.nut +++ b/bin/ai/compat_1.11.nut @@ -5,17 +5,4 @@ * 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 . */ -AILog.Info("1.11 API compatibility in effect."); - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 12 to 1.11. */ diff --git a/bin/ai/compat_1.2.nut b/bin/ai/compat_1.2.nut index fc52b04935..23bf995eff 100644 --- a/bin/ai/compat_1.2.nut +++ b/bin/ai/compat_1.2.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.2 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.3 to 1.2. */ diff --git a/bin/ai/compat_1.3.nut b/bin/ai/compat_1.3.nut index a06e98d2dd..6ebdb7350c 100644 --- a/bin/ai/compat_1.3.nut +++ b/bin/ai/compat_1.3.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.3 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.4 to 1.3. */ diff --git a/bin/ai/compat_1.4.nut b/bin/ai/compat_1.4.nut index 6de24bf80c..ab752521a6 100644 --- a/bin/ai/compat_1.4.nut +++ b/bin/ai/compat_1.4.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.4 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.5 to 1.4. */ diff --git a/bin/ai/compat_1.5.nut b/bin/ai/compat_1.5.nut index 8ff5a39d15..5113c41b87 100644 --- a/bin/ai/compat_1.5.nut +++ b/bin/ai/compat_1.5.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.5 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.6 to 1.5. */ diff --git a/bin/ai/compat_1.6.nut b/bin/ai/compat_1.6.nut index 91744512a7..c320c9db66 100644 --- a/bin/ai/compat_1.6.nut +++ b/bin/ai/compat_1.6.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.6 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.7 to 1.6. */ diff --git a/bin/ai/compat_1.7.nut b/bin/ai/compat_1.7.nut index 584a970f60..1e8814c5e2 100644 --- a/bin/ai/compat_1.7.nut +++ b/bin/ai/compat_1.7.nut @@ -5,31 +5,4 @@ * 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 . */ -AILog.Info("1.7 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; -AIBridge.GetName <- function(bridge_id) -{ - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); -} - -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; -AIGroup.CreateGroup <- function(vehicle_type) -{ - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.8 to 1.7. */ diff --git a/bin/ai/compat_1.8.nut b/bin/ai/compat_1.8.nut index f57a0eab27..5d7539f7d9 100644 --- a/bin/ai/compat_1.8.nut +++ b/bin/ai/compat_1.8.nut @@ -5,31 +5,16 @@ * 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 . */ -AILog.Info("1.8 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.9 to 1.8. */ -/* 1.9 adds a vehicle type parameter. */ -AIBridge._GetName <- AIBridge.GetName; +AIBridge.GetNameCompat1_8 <- AIBridge.GetName; AIBridge.GetName <- function(bridge_id) { - return AIBridge._GetName(bridge_id, AIVehicle.VT_RAIL); + return AIBridge.GetNameCompat1_8(bridge_id, AIVehicle.VT_RAIL); } -/* 1.9 adds parent_group_id to CreateGroup function */ -AIGroup._CreateGroup <- AIGroup.CreateGroup; +AIGroup.CreateGroupCompat1_8 <- AIGroup.CreateGroup; AIGroup.CreateGroup <- function(vehicle_type) { - return AIGroup._CreateGroup(vehicle_type, AIGroup.GROUP_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; + return AIGroup.CreateGroupCompat1_8(vehicle_type, AIGroup.GROUP_INVALID); } diff --git a/bin/ai/compat_1.9.nut b/bin/ai/compat_1.9.nut index 0dde6dc6da..76da9d2c39 100644 --- a/bin/ai/compat_1.9.nut +++ b/bin/ai/compat_1.9.nut @@ -5,17 +5,4 @@ * 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 . */ -AILog.Info("1.9 API compatibility in effect."); - -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; -AIRoad.HasRoadType <- function(tile, road_type) -{ - local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.10 to 1.9. */ diff --git a/bin/ai/compat_12.nut b/bin/ai/compat_12.nut index d54895632f..298d765228 100644 --- a/bin/ai/compat_12.nut +++ b/bin/ai/compat_12.nut @@ -5,15 +5,14 @@ * 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 . */ -AILog.Info("12 API compatibility in effect."); +/* This file contains code to downgrade the API from 13 to 12. */ -/* 13 really checks RoadType against RoadType */ -AIRoad._HasRoadType <- AIRoad.HasRoadType; +AIRoad.HasRoadTypeCompat12 <- AIRoad.HasRoadType; AIRoad.HasRoadType <- function(tile, road_type) { local list = AIRoadTypeList(AIRoad.GetRoadTramType(road_type)); foreach (rt, _ in list) { - if (AIRoad._HasRoadType(tile, rt)) { + if (AIRoad.HasRoadTypeCompat12(tile, rt)) { return true; } } diff --git a/bin/ai/compat_13.nut b/bin/ai/compat_13.nut index 5f808e38a0..a5e69f706a 100644 --- a/bin/ai/compat_13.nut +++ b/bin/ai/compat_13.nut @@ -5,4 +5,4 @@ * 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 . */ -AILog.Info("13 API compatibility in effect."); +/* This file contains code to downgrade the API from 14 to 13. */ diff --git a/bin/ai/compat_14.nut b/bin/ai/compat_14.nut index 0588a2b7fa..012a8d070c 100644 --- a/bin/ai/compat_14.nut +++ b/bin/ai/compat_14.nut @@ -5,4 +5,34 @@ * 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 . */ -AILog.Info("14 API compatibility in effect."); +/* This file contains code to downgrade the API from 15 to 14. */ + +AIBridge.GetBridgeID <- AIBridge.GetBridgeType; + +class AICompat14 { + function Text(text) + { + if (typeof text == "string") return text; + return null; + } +} + +AIBaseStation.SetNameCompat14 <- AIBaseStation.SetName; +AIBaseStation.SetName <- function(id, name) { return AIBaseStation.SetNameCompat14(id, AICompat14.Text(name)); } + +AICompany.SetNameCompat14 <- AICompany.SetName; +AICompany.SetName <- function(name) { return AICompany.SetNameCompat14(AICompat14.Text(name)); } +AICompany.SetPresidentNameCompat14 <- AICompany.SetPresidentName; +AICompany.SetPresidentName <- function(name) { return AICompany.SetPresidentNameCompat14(AICompat14.Text(name)); } + +AIGroup.SetNameCompat14 <- AIGroup.SetName; +AIGroup.SetName <- function(id, name) { return AIGroup.SetNameCompat14(id, AICompat14.Text(name)); } + +AISign.BuildSignCompat14 <- AISign.BuildSign; +AISign.BuildSign <- function(id, name) { return AISign.BuildSignCompat14(id, AICompat14.Text(name)); } + +AITown.FoundTownCompat14 <- AITown.FoundTown; +AITown.FoundTown <- function(tile, size, city, layout, name) { return AITown.FoundTownCompat14(tile, size, city, layout, AICompat14.Text(name)); } + +AIVehicle.SetNameCompat14 <- AIVehicle.SetName; +AIVehicle.SetName <- function(id, name) { return AIVehicle.SetNameCompat14(id, AICompat14.Text(name)); } diff --git a/bin/game/CMakeLists.txt b/bin/game/CMakeLists.txt index d0b5448997..0b48e97857 100644 --- a/bin/game/CMakeLists.txt +++ b/bin/game/CMakeLists.txt @@ -12,7 +12,6 @@ set(GS_COMPAT_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/compat_12.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_13.nut ${CMAKE_CURRENT_SOURCE_DIR}/compat_14.nut - ${CMAKE_CURRENT_SOURCE_DIR}/compat_15.nut ) foreach(GS_COMPAT_SOURCE_FILE IN LISTS GS_COMPAT_SOURCE_FILES) diff --git a/bin/game/compat_1.10.nut b/bin/game/compat_1.10.nut index 2559ff0a9c..d3dad84eb3 100644 --- a/bin/game/compat_1.10.nut +++ b/bin/game/compat_1.10.nut @@ -5,24 +5,10 @@ * 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 . */ -GSLog.Info("1.10 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.11 to 1.10. */ -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; +GSCompany.ChangeBankBalanceCompat1_10 <- GSCompany.ChangeBankBalance; GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) { - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; + return GSCompany.ChangeBankBalanceCompat1_10(company, delta, expenses_type, GSMap.TILE_INVALID); } diff --git a/bin/game/compat_1.11.nut b/bin/game/compat_1.11.nut index eac9a05d36..471a2a402f 100644 --- a/bin/game/compat_1.11.nut +++ b/bin/game/compat_1.11.nut @@ -5,17 +5,5 @@ * 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 . */ -GSLog.Info("1.11 API compatibility in effect."); +/* This file contains code to downgrade the API from 12 to 1.11. */ -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} diff --git a/bin/game/compat_1.2.nut b/bin/game/compat_1.2.nut index 66e2ca5d62..23bf995eff 100644 --- a/bin/game/compat_1.2.nut +++ b/bin/game/compat_1.2.nut @@ -5,46 +5,4 @@ * 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 . */ -GSLog.Info("1.2 API compatibility in effect."); - -GSTown._SetGrowthRate <- GSTown.SetGrowthRate; -GSTown.SetGrowthRate <- function(town_id, days_between_town_growth) -{ - /* Growth rate 0 caused resetting the custom growth rate. While this was undocumented, it was used nevertheless (ofc). */ - if (days_between_town_growth == 0) days_between_town_growth = GSTown.TOWN_GROWTH_NORMAL; - return GSTown._SetGrowthRate(town_id, days_between_town_growth); -} - -/* 1.5 adds a game element reference to the news. */ -GSNews._Create <- GSNews.Create; -GSNews.Create <- function(type, text, company) -{ - return GSNews._Create(type, text, company, GSNews.NR_NONE, 0); -} - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.3 to 1.2. */ diff --git a/bin/game/compat_1.3.nut b/bin/game/compat_1.3.nut index 9986ea03e5..184a5f8a12 100644 --- a/bin/game/compat_1.3.nut +++ b/bin/game/compat_1.3.nut @@ -5,46 +5,12 @@ * 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 . */ -GSLog.Info("1.3 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.4 to 1.3. */ -GSTown._SetGrowthRate <- GSTown.SetGrowthRate; +GSTown.SetGrowthRateCompat1_3 <- GSTown.SetGrowthRate; GSTown.SetGrowthRate <- function(town_id, days_between_town_growth) { /* Growth rate 0 caused resetting the custom growth rate. While this was undocumented, it was used nevertheless (ofc). */ if (days_between_town_growth == 0) days_between_town_growth = GSTown.TOWN_GROWTH_NORMAL; - return GSTown._SetGrowthRate(town_id, days_between_town_growth); -} - -/* 1.5 adds a game element reference to the news. */ -GSNews._Create <- GSNews.Create; -GSNews.Create <- function(type, text, company) -{ - return GSNews._Create(type, text, company, GSNews.NR_NONE, 0); -} - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; + return GSTown.SetGrowthRateCompat1_3(town_id, days_between_town_growth); } diff --git a/bin/game/compat_1.4.nut b/bin/game/compat_1.4.nut index b3ec5c45cb..befc109e24 100644 --- a/bin/game/compat_1.4.nut +++ b/bin/game/compat_1.4.nut @@ -5,38 +5,10 @@ * 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 . */ -GSLog.Info("1.4 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.5 to 1.4 */ -/* 1.5 adds a game element reference to the news. */ -GSNews._Create <- GSNews.Create; +GSNews.CreateCompat1_4 <- GSNews.Create; GSNews.Create <- function(type, text, company) { - return GSNews._Create(type, text, company, GSNews.NR_NONE, 0); -} - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; + return GSNews.CreateCompat1_4(type, text, company, GSNews.NR_NONE, 0); } diff --git a/bin/game/compat_1.5.nut b/bin/game/compat_1.5.nut index 2d25d7acf0..5113c41b87 100644 --- a/bin/game/compat_1.5.nut +++ b/bin/game/compat_1.5.nut @@ -5,31 +5,4 @@ * 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 . */ -GSLog.Info("1.5 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.6 to 1.5. */ diff --git a/bin/game/compat_1.6.nut b/bin/game/compat_1.6.nut index d205832209..c320c9db66 100644 --- a/bin/game/compat_1.6.nut +++ b/bin/game/compat_1.6.nut @@ -5,31 +5,4 @@ * 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 . */ -GSLog.Info("1.6 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.7 to 1.6. */ diff --git a/bin/game/compat_1.7.nut b/bin/game/compat_1.7.nut index 1108f41a7c..1e8814c5e2 100644 --- a/bin/game/compat_1.7.nut +++ b/bin/game/compat_1.7.nut @@ -5,31 +5,4 @@ * 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 . */ -GSLog.Info("1.7 API compatibility in effect."); - -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; -GSBridge.GetName <- function(bridge_id) -{ - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.8 to 1.7. */ diff --git a/bin/game/compat_1.8.nut b/bin/game/compat_1.8.nut index 139f91214b..fc6021073a 100644 --- a/bin/game/compat_1.8.nut +++ b/bin/game/compat_1.8.nut @@ -5,31 +5,10 @@ * 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 . */ -GSLog.Info("1.8 API compatibility in effect."); +/* This file contains code to downgrade the API from 1.9 to 1.8. */ -/* 1.9 adds a vehicle type parameter. */ -GSBridge._GetName <- GSBridge.GetName; +GSBridge.GetNameCompat1_8 <- GSBridge.GetName; GSBridge.GetName <- function(bridge_id) { - return GSBridge._GetName(bridge_id, GSVehicle.VT_RAIL); -} - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; + return GSBridge.GetNameCompat1_8(bridge_id, GSVehicle.VT_RAIL); } diff --git a/bin/game/compat_1.9.nut b/bin/game/compat_1.9.nut index 053e377d4c..76da9d2c39 100644 --- a/bin/game/compat_1.9.nut +++ b/bin/game/compat_1.9.nut @@ -5,24 +5,4 @@ * 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 . */ -GSLog.Info("1.9 API compatibility in effect."); - -/* 1.11 adds a tile parameter. */ -GSCompany._ChangeBankBalance <- GSCompany.ChangeBankBalance; -GSCompany.ChangeBankBalance <- function(company, delta, expenses_type) -{ - return GSCompany._ChangeBankBalance(company, delta, expenses_type, GSMap.TILE_INVALID); -} - -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; -GSRoad.HasRoadType <- function(tile, road_type) -{ - local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); - foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { - return true; - } - } - return false; -} +/* This file contains code to downgrade the API from 1.10 to 1.9. */ diff --git a/bin/game/compat_12.nut b/bin/game/compat_12.nut index a11cd04a23..46cb452443 100644 --- a/bin/game/compat_12.nut +++ b/bin/game/compat_12.nut @@ -5,15 +5,14 @@ * 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 . */ -GSLog.Info("12 API compatibility in effect."); +/* This file contains code to downgrade the API from 13 to 12. */ -/* 13 really checks RoadType against RoadType */ -GSRoad._HasRoadType <- GSRoad.HasRoadType; +GSRoad.HasRoadTypeCompat12 <- GSRoad.HasRoadType; GSRoad.HasRoadType <- function(tile, road_type) { local list = GSRoadTypeList(GSRoad.GetRoadTramType(road_type)); foreach (rt, _ in list) { - if (GSRoad._HasRoadType(tile, rt)) { + if (GSRoad.HasRoadTypeCompat12(tile, rt)) { return true; } } diff --git a/bin/game/compat_13.nut b/bin/game/compat_13.nut index 0ee9faf2bd..a5e69f706a 100644 --- a/bin/game/compat_13.nut +++ b/bin/game/compat_13.nut @@ -5,4 +5,4 @@ * 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 . */ -GSLog.Info("13 API compatibility in effect."); +/* This file contains code to downgrade the API from 14 to 13. */ diff --git a/bin/game/compat_14.nut b/bin/game/compat_14.nut index 9a5f872147..00efae0b65 100644 --- a/bin/game/compat_14.nut +++ b/bin/game/compat_14.nut @@ -5,4 +5,76 @@ * 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 . */ -GSLog.Info("14 API compatibility in effect."); +/* This file contains code to downgrade the API from 15 to 14. */ + +GSBridge.GetBridgeID <- GSBridge.GetBridgeType; + +class GSCompat14 { + function Text(text) + { + if (typeof text == "string") return text; + if (typeof text == "instance" && text instanceof GSText) return text; + return null; + } +} + +GSBaseStation.SetNameCompat14 <- GSBaseStation.SetName; +GSBaseStation.SetName <- function(id, name) { return GSBaseStation.SetNameCompat14(id, GSCompat14.Text(name)); } + +GSCompany.SetNameCompat14 <- GSCompany.SetName; +GSCompany.SetName <- function(name) { return GSCompany.SetNameCompat14(GSCompat14.Text(name)); } +GSCompany.SetPresidentNameCompat14 <- GSCompany.SetPresidentName; +GSCompany.SetPresidentName <- function(name) { return GSCompany.SetPresidentNameCompat14(GSCompat14.Text(name)); } + +GSGoal.NewCompat14 <- GSGoal.New; +GSGoal.New <- function(company, goal, type, dest) { return GSGoal.NewCompat14(company, GSCompat14.Text(goal), type, dest); } +GSGoal.SetTextCompat14 <- GSGoal.SetText; +GSGoal.SetText <- function(id, goal) { return GSGoal.SetTextCompat14(id, GSCompat14.Text(goal)); } +GSGoal.SetProgressCompat14 <- GSGoal.SetProgress; +GSGoal.SetProgress <- function(id, progress) { return GSGoal.SetProgressCompat14(id, GSCompat14.Text(progress)); } +GSGoal.QuestionCompat14 <- GSGoal.Question; +GSGoal.Question <- function(id, company, question, type, buttons) { return GSGoal.QuestionCompat14(id, company, GSCompat14.Text(question), type, buttons); } +GSGoal.QuestionClientCompat14 <- GSGoal.QuestionClient; +GSGoal.QuestionClient <- function(id, target, is_client, question, type, buttons) { return GSGoal.QuestionClientCompat14(id, target, is_client, GSCompat14.Text(question), type, buttons); } + +GSGroup.SetNameCompat14 <- GSGroup.SetName; +GSGroup.SetName <- function(id, name) { return GSGroup.SetNameCompat14(id, GSCompat14.Text(name)); } + +GSIndustry.SetTextCompat14 <- GSIndustry.SetText; +GSIndustry.SetText <- function(id, text) { return GSIndustry.SetTextCompat14(id, GSCompat14.Text(text)); } +GSIndustry.SetProductionLevelCompat14 <- GSIndustry.SetProductionLevel; +GSIndustry.SetProductionLevel <- function(id, level, news, text) { return GSIndustry.SetProductionLevelCompat14(id, level, news, GSCompat14.Text(text)); } + +GSLeagueTable.NewCompat14 <- GSLeagueTable.New; +GSLeagueTable.New <- function(title, header, footer) { return GSLeagueTable.NewCompat14(GSCompat14.Text(title), GSCompat14.Text(header), GSCompat14.Text(footer)); } +GSLeagueTable.NewElementCompat14 <- GSLeagueTable.NewElement; +GSLeagueTable.NewElement <- function(table, rating, company, text, score, type, target) { return GSLeagueTable.NewElementCompat14(table, rating, company, GSCompat14.Text(text), GSCompat14.Text(score), type, target); } +GSLeagueTable.UpdateElementDataCompat14 <- GSLeagueTable.UpdateElementData; +GSLeagueTable.UpdateElementData <- function(element, company, text, type, target) { return GSLeagueTable.UpdateElementDataCompat14(element, company, GSCompat14.Text(text), type, target); } +GSLeagueTable.UpdateElementScoreCompat14 <- GSLeagueTable.UpdateElementScore; +GSLeagueTable.UpdateElementScore <- function(element, rating, score) { return GSLeagueTable.UpdateElementScoreCompat14(element, rating, GSCompat14.Text(score)); } + +GSNews.CreateCompat14 <- GSNews.Create; +GSNews.Create <- function(type, text, company, ref_type, ref) { return GSNews.CreateCompat14(type, GSCompat14.Text(text), company, ref_type, ref); } + +GSSign.BuildSignCompat14 <- GSSign.BuildSign; +GSSign.BuildSign <- function(id, name) { return GSSign.BuildSignCompat14(id, GSCompat14.Text(name)); } + +GSStoryPage.NewCompat14 <- GSStoryPage.New; +GSStoryPage.New <- function(company, title) { return GSStoryPage.NewCompat14(company, GSCompat14.Text(title)); } +GSStoryPage.NewElementCompat14 <- GSStoryPage.NewElement; +GSStoryPage.NewElement <- function(page, type, ref, text) { return GSStoryPage.NewElementCompat14(page, type, ref, GSCompat14.Text(text)); } +GSStoryPage.UpdateElementCompat14 <- GSStoryPage.UpdateElement; +GSStoryPage.UpdateElement <- function(id, ref, text) { return GSStoryPage.UpdateElementCompat14(id, ref, GSCompat14.Text(text)); } +GSStoryPage.SetTitleCompat14 <- GSStoryPage.SetTitle; +GSStoryPage.SetTitle <- function(page, tile) { return GSStoryPage.SetTitleCompat14(page, GSCompat14.Text(title)); } + +GSTown.SetNameCompat14 <- GSTown.SetName; +GSTown.SetName <- function(id, name) { return GSTown.SetNameCompat14(id, GSCompat14.Text(name)); } +GSTown.SetTextCompat14 <- GSTown.SetText; +GSTown.SetText <- function(id, text) { return GSTown.SetTextCompat14(id, GSCompat14.Text(text)); } +GSTown.FoundTownCompat14 <- GSTown.FoundTown; +GSTown.FoundTown <- function(tile, size, city, layout, name) { return GSTown.FoundTownCompat14(tile, size, city, layout, GSCompat14.Text(name)); } + +GSVehicle.SetNameCompat14 <- GSVehicle.SetName; +GSVehicle.SetName <- function(id, name) { return GSVehicle.SetNameCompat14(id, GSCompat14.Text(name)); } diff --git a/bin/game/compat_15.nut b/bin/game/compat_15.nut deleted file mode 100644 index 3081fb58e8..0000000000 --- a/bin/game/compat_15.nut +++ /dev/null @@ -1,6 +0,0 @@ -/* - * 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 . - */ diff --git a/changelog.md b/changelog.md index 8ed27438a8..e70a8b5069 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,107 @@ ## 15.x +### 15.0-beta2 (2025-04-13) + +- Feature: Snow-covered rocks are now visible (#13627) +- Feature: Generate more rocks on steep slopes during map generation or heightmap import (#13462) +- Feature: Prevent towns from upgrading individually-placed houses (#13270) +- Feature: [Win32] Touchpad two-finger map scrolling (#13172) +- Feature: NewGRF Badges (#13073) +- Add: [NewGRF] Add road-/tram-/rail-type variable 0x45 to get mutual road-/tram-/rail-type on same tile (#13934) +- Add: [Script] Newer Cargo Classes (#13779) +- Add: Show hyperlink destination tooltips in text window (#13742) +- Add: [Script] Saving/loading ScriptList (#13556) +- Add: Press Ctrl to build diagonal canals in game mode (#13432) +- Add: Sandbox settings to Sandbox Options window (#13268) +- Add: Setting to allow placing houses manually in-game (#13266) +- Add: [Script] Event for when a company's president name changes (#13208) +- Add: Ability to toggle visibility of station signs by facility (#13207) +- Add: [Script] ScriptEventCompanyRename (#12878) +- Add: Ukrainian Hryvnia currency (#12877) +- Add: Convert 32bpp-only sprites to 8bpp when needed (#11602) +- Change: [Script] Start GS (but don't run it) when generating world in scenario editor (#13961) +- Change: Make tree placement at world generation look more organic (#13515) +- Change: [MacOS] Put the icon in a rounded rectangle (#13446) +- Change: [Script] GetWaypointID to return the StationID of any waypoint (#13407) +- Change: Draw company manager face jacket after collar (#13390) +- Change: Don't distinguish between bus and truck stops when removing them (#13384) +- Change: [Script] Rename BridgeID to BridgeType in the script API (#13352) +- Change: Add fonts document to help window (#13305) +- Change: Log changes to sandbox settings (#13267) +- Change: When player joins network company, use its name instead of number in chat (#13263) +- Change: [Win32] Draw window title bar according to current Windows light/dark theme (#13196) +- Change: Restore wider spacers in main toolbars (#12039) +- Fix: NewGRF Global variables 0D, 0E and 1E refer to wrong GRFFile (#13986) +- Fix #13980: Allow diagonal selection for road convert (#13983) +- Fix: Validate raw strings from game-scripts, and strip invalid and control characters (#13976) +- Fix: Capitalise "Disabled" for the "maximum non-sticky open windows" setting (#13975) +- Fix: Frame widget with label had incorrect spacing (#13967) +- Fix: StringFilter included quotes in the search and failed (#13965) +- Fix #13955: Make graphs respect RTL (#13957) +- Fix: Numbers were left-aligned for RTL languages in several windows (#13959) +- Fix: MayHaveRoad claimed rail station tiles had road, so the custom stationspec index would be read as roadtype (#13949) +- Fix: [Script] Prevent cloning of API instances (#13947) +- Fix: Reference to the correct section of the README, if a graphics or a sound set is incomplete (#13946) +- Fix: Draw the bevel around the music track name as inset (#13935) +- Fix #13923: Padding in music GUI was asymmetric, so it looked different for LTR and RTL languages (#13933) +- Fix #13928: BuildOilRig did not properly set airport rotation (#13929) +- Fix: SDL2 application name hint was not effective (#13926) +- Fix #13921: [Win32] Don't try close an already closed event handle during destruction (#13924) +- Fix #13921: Don't reject MIDI files with a valid file magic value (#13924) +- Fix #13912: Multitile buildings break apart in house picker (#13914) +- Fix #13908: Require double click on order to change stop location (#13913) +- Fix #13910: Invalidate content of house picker window if language is changed (#13911) +- Fix: [Script] Reset instance when changing running scripts in scenario editor (#13906) +- Fix: [Script] Only run the gamescript GameLoop() in-game (#13896) +- Fix #13893: Reversed all x-axis labels for company related and industry production graphs in wallclock mode (#13894) +- Fix #13842: Close industry production graph if industry is removed (#13890) +- Fix #11528: Starting autorail dragging from existing track tiles resulted in adding non-continuous tracks (#13885) +- Fix: Autoreplace rail/road list only listed buildable types (#13887) +- Fix: [NewGRF] Display an error, if NewGRF reference out-of-bounds string parameters in gender/plural choices (#13881) +- Fix #13849: Settings in old saves could be overridden by defaults (#13874) +- Fix #13562: Removed cost estimation message from money cheat (#13857) +- Fix: [NewGRF] Plurals and genders did not work in strings with cases or substrings (#13853, #13852) +- Fix: [NewGRF] String parameter stack and case selection were not processed for control code 0x81 (#13851) +- Fix #13839: Incorrect colour of first company legend in smallmap window (#13841) +- Fix: i circumflex width in TrueType small font (#13836) +- Fix: Don't show owner of non-existent road (#13824) +- Fix: Error message window timeout doesn't match setting (#13812) +- Fix #13795: Crash in vehicle list of 32-bit platforms (#13796) +- Fix: [Script] Company rename event sometimes had the wrong name (#13794) +- Fix: Improve manager face randomisation (#13776) +- Fix #13740: [Script] Handle implicit orders for jump orders (#13753) +- Fix #13749: Default service intervals were not updated when changing timekeeping unit (#13751) +- Fix #13725: Use proper query strings for changing timetable values (#13737) +- Fix #11226: Don't draw story page elements that won't be visible (#13736) +- Fix: More AI than max_no_competitors could start with competitors_interval=0 (#13670) +- Fix: League table window ignored the minimal size in its widget description (#13629) +- Fix: Incorrect snow density when making rocks snowy (#13626) +- Fix: NewGRF vehicles display loading sprites when not actually loading or unloading (#13554) +- Fix #12925: Prevent cost estimates for settings changes (#13550) +- Fix: [Script] Report errors happening during 'Load()' (#13537) +- Fix: [Script] Improve type checking of parameters (#13522) +- Fix: [Script] Don't set CommandCallback for asynchronous commands (#13501) +- Fix: Missing error messages with sell- and autoreplace-all commands (#13469) +- Fix: Too many trees when generating trees at same height (#13460) +- Fix #12912: Company inaugurated year in wallclock mode was not saved (#13448) +- Fix: [Script] Wrong return value for failed preconditions Vehicle::CloneVehicle (#13445) +- Fix #13140: Scale initial industry production estimate by cargo scale (#13427) +- Fix #13384: Crash when remove bus/truck stop tool used on road waypoints (#13391) +- Fix #12987: Historical houses now always spawn completed (#13332) +- Fix: [Win32] Font detection didn't work for locales not supporting code pages (#13306) +- Fix: Restore ability to disable service interval (#13281) +- Fix: Hide company settings from console commands (#13269) +- Fix: Disable service interval widgets for non-owned vehicles (#13260) +- Fix #13225: Cargo payment graph key toggled wrong data sets (#13226) +- Fix: Rail station tile flags were not set early enough (#13203) +- Fix #13199: -f command line parameter does not need a value (#13200) +- Fix: Missing water region invalidation after flooding a half tile with rail in the highest corner (#13047) +- Fix: Strip control codes before sorting NewGRF names (#13034) +- Fix #12968: Added back ability to create unremovable houses (#12989) +- Remove: Drop support for UCS2/UTF-16 encoded scripts (#13992) +- Remove: Support for SDL1.2 (#13298) + + ### 15.0-beta1 (2024-12-24) - Feature: Town, industry and vehicle window zoom with mouse wheel (#12810, #12809, #12797) diff --git a/cmake/CompileFlags.cmake b/cmake/CompileFlags.cmake index dc45544969..c539d765b8 100644 --- a/cmake/CompileFlags.cmake +++ b/cmake/CompileFlags.cmake @@ -81,6 +81,10 @@ macro(compile_flags) # We use 'ABCD' multichar for SaveLoad chunks identifiers -Wno-multichar + + # Prevent optimisation supposing enums are in a range specified by the standard + # For details, see http://gcc.gnu.org/PR43680 and PR#5246. + -fno-strict-enums ) # Ninja processes the output so the output from the compiler @@ -107,10 +111,6 @@ macro(compile_flags) # about its own optimized code in some places. "-fno-strict-overflow" - # Prevent optimisation supposing enums are in a range specified by the standard - # For details, see http://gcc.gnu.org/PR43680 - "-fno-tree-vrp" - # -flifetime-dse=2 (default since GCC 6) doesn't play # well with our custom pool item allocator "$<$:-flifetime-dse=1>" diff --git a/cmake/Endian.cmake b/cmake/Endian.cmake deleted file mode 100644 index 3bfba653ca..0000000000 --- a/cmake/Endian.cmake +++ /dev/null @@ -1,14 +0,0 @@ -# Add the definitions to indicate which endian we are building for. -# -# add_endian_definition() -# -function(add_endian_definition) - include(TestBigEndian) - TEST_BIG_ENDIAN(IS_BIG_ENDIAN) - - if(IS_BIG_ENDIAN) - add_definitions(-DTTD_ENDIAN=TTD_BIG_ENDIAN) - else() - add_definitions(-DTTD_ENDIAN=TTD_LITTLE_ENDIAN) - endif() -endfunction() diff --git a/cmake/FindLZO.cmake b/cmake/FindLZO.cmake index 20ea4c1b57..920b58b71f 100644 --- a/cmake/FindLZO.cmake +++ b/cmake/FindLZO.cmake @@ -43,29 +43,8 @@ find_library(LZO_LIBRARY PATHS ${PC_LZO_LIBRARY_DIRS} ) -# With vcpkg, the library path should contain both 'debug' and 'optimized' -# entries (see target_link_libraries() documentation for more information) -# -# NOTE: we only patch up when using vcpkg; the same issue might happen -# when not using vcpkg, but this is non-trivial to fix, as we have no idea -# what the paths are. With vcpkg we do. And we only official support vcpkg -# with Windows. -# -# NOTE: this is based on the assumption that the debug file has the same -# name as the optimized file. This is not always the case, but so far -# experiences has shown that in those case vcpkg CMake files do the right -# thing. -if(VCPKG_TOOLCHAIN AND LZO_LIBRARY AND LZO_LIBRARY MATCHES "${VCPKG_INSTALLED_DIR}") - if(LZO_LIBRARY MATCHES "/debug/") - set(LZO_LIBRARY_DEBUG ${LZO_LIBRARY}) - string(REPLACE "/debug/lib/" "/lib/" LZO_LIBRARY_RELEASE ${LZO_LIBRARY}) - else() - set(LZO_LIBRARY_RELEASE ${LZO_LIBRARY}) - string(REPLACE "/lib/" "/debug/lib/" LZO_LIBRARY_DEBUG ${LZO_LIBRARY}) - endif() - include(SelectLibraryConfigurations) - select_library_configurations(LZO) -endif() +include(FixVcpkgLibrary) +FixVcpkgLibrary(LZO) set(LZO_VERSION ${PC_LZO_VERSION}) diff --git a/cmake/FindOgg.cmake b/cmake/FindOgg.cmake index a6487fe083..e0106d25b8 100644 --- a/cmake/FindOgg.cmake +++ b/cmake/FindOgg.cmake @@ -4,6 +4,9 @@ find_library(Ogg_LIBRARY NAMES ogg ) +include(FixVcpkgLibrary) +FixVcpkgLibrary(Ogg) + set(Ogg_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of ogg") set(Ogg_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of ogg") @@ -33,5 +36,6 @@ if (Ogg_FOUND) INTERFACE_LINK_LIBRARIES "${Ogg_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${Ogg_LINK_FLAGS}" ) + FixVcpkgTarget(Ogg Ogg::ogg) endif() endif() diff --git a/cmake/FindOpus.cmake b/cmake/FindOpus.cmake index c8ad1b48a5..742a17b818 100644 --- a/cmake/FindOpus.cmake +++ b/cmake/FindOpus.cmake @@ -4,6 +4,9 @@ find_library(Opus_LIBRARY NAMES opus ) +include(FixVcpkgLibrary) +FixVcpkgLibrary(Opus) + set(Opus_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opus") set(Opus_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opus") @@ -33,5 +36,6 @@ if (Opus_FOUND) INTERFACE_LINK_LIBRARIES "${Opus_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${Opus_LINK_FLAGS}" ) + FixVcpkgTarget(Opus Opus::opus) endif() endif() diff --git a/cmake/FindOpusFile.cmake b/cmake/FindOpusFile.cmake index 8cc4a3b263..809a948618 100644 --- a/cmake/FindOpusFile.cmake +++ b/cmake/FindOpusFile.cmake @@ -4,6 +4,9 @@ find_library(OpusFile_LIBRARY NAMES opusfile ) +include(FixVcpkgLibrary) +FixVcpkgLibrary(OpusFile) + set(OpusFile_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of opusfile") set(OpusFile_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of opusfile") @@ -36,5 +39,6 @@ if (OpusFile_FOUND) INTERFACE_LINK_LIBRARIES "Ogg::ogg;Opus::opus;${OpusFile_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${OpusFile_LINK_FLAGS}" ) + FixVcpkgTarget(OpusFile OpusFile::opusfile) endif() endif() diff --git a/cmake/FindSSE.cmake b/cmake/FindSSE.cmake index e8dc243d9b..0df9884a3d 100644 --- a/cmake/FindSSE.cmake +++ b/cmake/FindSSE.cmake @@ -2,6 +2,7 @@ # SSE version (SSE 2.0, SSSE 3.0). include(CheckCXXSourceCompiles) +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") @@ -15,3 +16,5 @@ check_cxx_source_compiles(" int main() { return 0; }" SSE_FOUND ) + +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) diff --git a/cmake/FindXaudio2.cmake b/cmake/FindXaudio2.cmake index 477dfea728..777dd7ca2a 100644 --- a/cmake/FindXaudio2.cmake +++ b/cmake/FindXaudio2.cmake @@ -1,6 +1,7 @@ # Autodetect if xaudio2 can be used. include(CheckCXXSourceCompiles) +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "") check_cxx_source_compiles(" @@ -17,3 +18,5 @@ check_cxx_source_compiles(" int main() { printf(\"%s\\\\n\", XAUDIO2_DLL_A); return 0; }" XAUDIO2_FOUND ) + +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) diff --git a/cmake/FixVcpkgLibrary.cmake b/cmake/FixVcpkgLibrary.cmake new file mode 100644 index 0000000000..db75d96ff6 --- /dev/null +++ b/cmake/FixVcpkgLibrary.cmake @@ -0,0 +1,40 @@ +macro(FixVcpkgLibrary NAME) + # With vcpkg, the library path should contain both 'debug' and 'optimized' + # entries (see target_link_libraries() documentation for more information) + # + # NOTE: we only patch up when using vcpkg; the same issue might happen + # when not using vcpkg, but this is non-trivial to fix, as we have no idea + # what the paths are. With vcpkg we do. And we only official support vcpkg + # with Windows. + # + # NOTE: this is based on the assumption that the debug file has the same + # name as the optimized file. This is not always the case, but so far + # experiences has shown that in those case vcpkg CMake files do the right + # thing. + if(VCPKG_TOOLCHAIN AND ${NAME}_LIBRARY AND ${NAME}_LIBRARY MATCHES "${VCPKG_INSTALLED_DIR}") + if(${NAME}_LIBRARY MATCHES "/debug/") + set(${NAME}_LIBRARY_DEBUG ${${NAME}_LIBRARY}) + string(REPLACE "/debug/lib/" "/lib/" ${NAME}_LIBRARY_RELEASE ${${NAME}_LIBRARY}) + else() + set(${NAME}_LIBRARY_RELEASE ${${NAME}_LIBRARY}) + string(REPLACE "/lib/" "/debug/lib/" ${NAME}_LIBRARY_DEBUG ${${NAME}_LIBRARY}) + endif() + include(SelectLibraryConfigurations) + select_library_configurations(${NAME}) + endif() +endmacro() + +function(FixVcpkgTarget NAME TARGET) + if(EXISTS "${${NAME}_LIBRARY_RELEASE}") + set_property(TARGET ${TARGET} APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(${TARGET} PROPERTIES + IMPORTED_LOCATION_RELEASE "${${NAME}_LIBRARY_RELEASE}") + endif() + if(EXISTS "${${NAME}_LIBRARY_DEBUG}") + set_property(TARGET ${TARGET} APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(${TARGET} PROPERTIES + IMPORTED_LOCATION_DEBUG "${${NAME}_LIBRARY_DEBUG}") + endif() +endfunction() diff --git a/cmake/InstallAndPackage.cmake b/cmake/InstallAndPackage.cmake index 94bca6eb7d..c6eb70d685 100644 --- a/cmake/InstallAndPackage.cmake +++ b/cmake/InstallAndPackage.cmake @@ -63,6 +63,7 @@ install(FILES ${CMAKE_SOURCE_DIR}/docs/desync.md ${CMAKE_SOURCE_DIR}/docs/directory_structure.md ${CMAKE_SOURCE_DIR}/docs/eints.md + ${CMAKE_SOURCE_DIR}/docs/fonts.md ${CMAKE_SOURCE_DIR}/docs/game_coordinator.md ${CMAKE_SOURCE_DIR}/docs/linkgraph.md ${CMAKE_SOURCE_DIR}/docs/logging_and_performance_metrics.md diff --git a/cmake/SourceList.cmake b/cmake/SourceList.cmake index f01f5db86b..6c9ebe933f 100644 --- a/cmake/SourceList.cmake +++ b/cmake/SourceList.cmake @@ -46,44 +46,3 @@ endfunction() function(add_test_files) _add_files_tgt(openttd_test ${ARGV}) endfunction() - -# This function works around an 'issue' with CMake, where -# set_source_files_properties() only works in the scope of the file. We want -# to set properties for the source file on a more global level. To solve this, -# this function records the flags you want, and a macro adds them in the root -# CMakeLists.txt. -# See this URL for more information on the issue: -# http://cmake.3232098.n2.nabble.com/scope-of-set-source-files-properties-td4766111.html -# -# set_compile_flags([file1 ...] COMPILE_FLAGS cflag [cflag ...]) -# -function(set_compile_flags) - cmake_parse_arguments(PARAM "" "" "COMPILE_FLAGS" ${ARGN}) - set(PARAM_FILES "${PARAM_UNPARSED_ARGUMENTS}") - - get_property(SOURCE_PROPERTIES GLOBAL PROPERTY source_properties) - - foreach(FILE IN LISTS PARAM_FILES) - list(APPEND SOURCE_PROPERTIES "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}::${PARAM_COMPILE_FLAGS}") - endforeach() - - set_property(GLOBAL PROPERTY source_properties "${SOURCE_PROPERTIES}") -endfunction() - -# Call this macro in the same CMakeLists.txt and after add_executable(). -# This makes sure all the COMPILE_FLAGS of set_compile_flags() are set -# correctly. -# -# process_compile_flags() -# -function(process_compile_flags) - get_property(SOURCE_PROPERTIES GLOBAL PROPERTY source_properties) - - foreach(ENTRY ${SOURCE_PROPERTIES}) - string(REPLACE "::" ";" ENTRY "${ENTRY}") - list(GET ENTRY 0 FILE) - list(GET ENTRY 1 PROPERTIES) - - set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS ${PROPERTIES}) - endforeach() -endfunction() diff --git a/cmake/scripts/Baseset.cmake b/cmake/scripts/Baseset.cmake index d822a5720c..88e6f14b3c 100644 --- a/cmake/scripts/Baseset.cmake +++ b/cmake/scripts/Baseset.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) # # Create a single baseset meta file with the correct translations. diff --git a/cmake/scripts/CreateGRF.cmake b/cmake/scripts/CreateGRF.cmake index 832c475799..f8288d8a5c 100644 --- a/cmake/scripts/CreateGRF.cmake +++ b/cmake/scripts/CreateGRF.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) # # Create a single GRF file based on sprites/.nfo and sprites/*.png diff --git a/cmake/scripts/Desktop.cmake b/cmake/scripts/Desktop.cmake index 7f64a60c49..eba56cf63c 100644 --- a/cmake/scripts/Desktop.cmake +++ b/cmake/scripts/Desktop.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) # # Create a desktop file with the correct translations. diff --git a/cmake/scripts/FindVersion.cmake b/cmake/scripts/FindVersion.cmake index f931926592..995d1d124d 100644 --- a/cmake/scripts/FindVersion.cmake +++ b/cmake/scripts/FindVersion.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) if(NOT REV_MAJOR) set(REV_MAJOR 0) diff --git a/cmake/scripts/GenerateWidget.cmake b/cmake/scripts/GenerateWidget.cmake index 4387202037..ae5626a817 100644 --- a/cmake/scripts/GenerateWidget.cmake +++ b/cmake/scripts/GenerateWidget.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) # # CMake script to automatically generate the enums in script_window.hpp diff --git a/cmake/scripts/Regression.cmake b/cmake/scripts/Regression.cmake index bfc5ee4ff4..a6833e2dd1 100644 --- a/cmake/scripts/Regression.cmake +++ b/cmake/scripts/Regression.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) # # Runs a single regressoion test @@ -87,12 +87,7 @@ set(ERROR NO) list(LENGTH REGRESSION_EXPECTED REGRESSION_EXPECTED_LENGTH) # Compare the output -foreach(RESULT IN LISTS REGRESSION_RESULT) - unset(EXPECTED) - if(ARGC LESS REGRESSION_EXPECTED_LENGTH) - list(GET REGRESSION_EXPECTED ${ARGC} EXPECTED) - endif() - +foreach(RESULT EXPECTED IN ZIP_LISTS REGRESSION_RESULT REGRESSION_EXPECTED) math(EXPR ARGC "${ARGC} + 1") if(NOT RESULT STREQUAL EXPECTED) diff --git a/cmake/scripts/SquirrelExport.cmake b/cmake/scripts/SquirrelExport.cmake index 2ed2130a1e..6549078844 100644 --- a/cmake/scripts/SquirrelExport.cmake +++ b/cmake/scripts/SquirrelExport.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) if(NOT SCRIPT_API_SOURCE_FILE) message(FATAL_ERROR "Script needs SCRIPT_API_SOURCE_FILE defined") @@ -28,10 +28,10 @@ endmacro() macro(dump_class_templates NAME) string(REGEX REPLACE "^Script" "" REALNAME ${NAME}) - string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };") - string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };") - string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return (${NAME} *)instance; } };") - string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, nullptr); return *(${NAME} *)instance; } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") + string(APPEND SQUIRREL_EXPORT "\n template <> struct Param { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") if("${NAME}" STREQUAL "ScriptEvent") string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };") elseif("${NAME}" STREQUAL "ScriptText") @@ -44,7 +44,10 @@ macro(dump_class_templates NAME) string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_STRING) {") string(APPEND SQUIRREL_EXPORT "\n return new RawText(Param::Get(vm, index));") string(APPEND SQUIRREL_EXPORT "\n }") - string(APPEND SQUIRREL_EXPORT "\n return nullptr;") + string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_NULL) {") + string(APPEND SQUIRREL_EXPORT "\n return nullptr;") + string(APPEND SQUIRREL_EXPORT "\n }") + string(APPEND SQUIRREL_EXPORT "\n throw sq_throwerror(vm, fmt::format(\"parameter {} has an invalid type ; expected: 'Text'\", index - 1));") string(APPEND SQUIRREL_EXPORT "\n }") string(APPEND SQUIRREL_EXPORT "\n };") else() @@ -307,11 +310,6 @@ foreach(LINE IN LISTS SOURCE_LINES) string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {") set(NAMESPACE_OPENED TRUE) endif() - string(APPEND SQUIRREL_EXPORT "\n /* Allow enums to be used as Squirrel parameters */") - foreach(ENUM IN LISTS ENUMS) - string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${ENUM}> { static inline ${ENUM} Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (${ENUM})tmp; } };") - string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${ENUM}> { static inline int Set(HSQUIRRELVM vm, ${ENUM} res) { sq_pushinteger(vm, res); return 1; } };") - endforeach() endif() # Then check whether we have structs/classes to print @@ -342,14 +340,14 @@ foreach(LINE IN LISTS SOURCE_LINES) endif() string(APPEND SQUIRREL_EXPORT "\n") - string(APPEND SQUIRREL_EXPORT "\ntemplate <> const char *GetClassName<${CLS}, ScriptType::${APIUC}>() { return \"${API_CLS}\"; }") + string(APPEND SQUIRREL_EXPORT "\ntemplate <> SQInteger PushClassName<${CLS}, ScriptType::${APIUC}>(HSQUIRRELVM vm) { sq_pushstring(vm, \"${API_CLS}\", -1); return 1; }") string(APPEND SQUIRREL_EXPORT "\n") # Then do the registration functions of the class. string(APPEND SQUIRREL_EXPORT "\nvoid SQ${API_CLS}_Register(Squirrel *engine)") string(APPEND SQUIRREL_EXPORT "\n{") string(APPEND SQUIRREL_EXPORT "\n DefSQClass<${CLS}, ScriptType::${APIUC}> SQ${API_CLS}(\"${API_CLS}\");") - if("${SUPER_CLS}" STREQUAL "Text" OR "${SUPER_CLS}" STREQUAL "ScriptObject" OR "${SUPER_CLS}" STREQUAL "AIAbstractiveList::Valuator") + if("${SUPER_CLS}" STREQUAL "Text") string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine);") else() string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine, \"${API_SUPER_CLS}\");") @@ -574,6 +572,10 @@ foreach(LINE IN LISTS SOURCE_LINES) list(APPEND CONST_VALUES "${CMAKE_MATCH_1}") continue() endif() + if("${LINE}" MATCHES "^[ ]*static constexpr [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;") + list(APPEND CONST_VALUES "${CMAKE_MATCH_1}") + continue() + endif() # Add a method to the list if("${LINE}" MATCHES "^.*\\(.*\\).*$") diff --git a/cmake/scripts/SquirrelIncludes.cmake b/cmake/scripts/SquirrelIncludes.cmake index 80a77775fb..d6022f73dd 100644 --- a/cmake/scripts/SquirrelIncludes.cmake +++ b/cmake/scripts/SquirrelIncludes.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.17) if(NOT INCLUDES_SOURCE_FILE) message(FATAL_ERROR "Script needs INCLUDES_SOURCE_FILE defined") diff --git a/docs/admin_network.md b/docs/admin_network.md index 7dae8a0de8..9a1171f392 100644 --- a/docs/admin_network.md +++ b/docs/admin_network.md @@ -223,7 +223,7 @@ Last updated: 2024-03-26 ## 5.1) Receiving chat - Register `ADMIN_UPDATE_CHAT` at `ADMIN_FREQUENCY_AUTOMATIC` to receive chat. + Register `ADMIN_UPDATE_CHAT` at `AdminUpdateFrequency::Automatic` to receive chat. The application will be able to receive all chat the server can see. The configuration option `network.server_admin_chat` specifies whether diff --git a/docs/fonts.md b/docs/fonts.md new file mode 100644 index 0000000000..712ba6f696 --- /dev/null +++ b/docs/fonts.md @@ -0,0 +1,62 @@ +# Fonts for OpenTTD + +OpenTTD uses four different fonts: +- a medium font (used for most texts in the game) +- a small font (used for the smallmap legend etc) +- a large font (used for news headlines etc) +- a monospace font (used for text files such as NewGRF readmes). + +You can use the following types of fonts with OpenTTD: + +## OpenTTD's default fonts + +These fonts are OpenTTD Sans (small and medium), OpenTTD Serif (large) and OpenTTD Mono (monospace). They are distributed as part of OpenTTD since version 14. The font files are included in the baseset directory of OpenTTD. + +These fonts are active by default and support the Latin, Greek and Cyrillic scripts at present. + +## Traditional sprite fonts + +These are the classic bitmap fonts included as part of the base graphics. They support only the Latin script. + +These fonts can be activated in the Graphics section of the Game options window, by enabling the option "Use traditional sprite fonts". + +## System fonts + +These are fonts installed on your computer. OpenTTD tries to automatically detect and activate a suitable system font in case you have selected a language not supported by the default fonts. However, if this fails, you may have to set a font manually. + +There are two ways to manually set system fonts, using the `font` console command or editing the openttd.cfg file. + +### Using the console + +Open the console. On most keyboards, this is done by pressing the key to the left of 1 (\` on most English keyboards). + +The command to change a font is `font [medium|small|large|mono] [] []`. +For example, `font large "Times New Roman" 16`. + +The font name should be enclosed in double quotes if it contains spaces. Note that the size provided is multiplied by the interface scaling factor. + +You can reset the font and size to the defaults by providing the font name "" (a blank font name). This will result in the OpenTTD default font or sprite font (depending on the setting) if you are using a supported language, or a default font determined by your OS otherwise. + +You can view the current font configuration by running the command `font` without any arguments. + +For more information, run the command `help font`. + +### Using openttd.cfg + +In openttd.cfg, the following settings under the `[misc]` section determine the font (this is just an example): +``` +small_font = +medium_font = Arial Bold +large_font = Times New Roman +mono_font = +small_size = 6 +medium_size = 10 +large_size = 18 +mono_size = 10 +``` + +If these settings are not present, you can add them under the `[misc]` section. + +If any font names are left blank, the default font and size are used. + +If you cannot find the openttd.cfg file, see [the directory structure guide](./directory_structure.md). diff --git a/docs/landscape.html b/docs/landscape.html index ab918a77ff..3f33126dd3 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -64,7 +64,7 @@ Only meaningful in tropic climate. It contains the definition of the available zones 00normal 01desert - 02rain forest + 02rainforest In any other climate these 2 bits are theoretically free of use, however using them does not seem useful. @@ -721,7 +721,8 @@ -
  • m3 bits 6..5 : free
  • +
  • m3 bit 6 : free
  • +
  • m3 bit 5 : The house is protected from the town upgrading it
  • m3 bits 4..0 : triggers activated (newhouses)
  • m4 : free
  • m5 : see m3 bit 7
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index b2ee8293e2..495d6de112 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -156,7 +156,7 @@ the array so you can quickly see what is used and what is not. finished house XXXX XXXX XXXX XXXX XXXX XXXX - 1OOX XXXX + 1OXX XXXX OOOO OOOO XXXX XXXX XXXX XXXX @@ -165,7 +165,7 @@ the array so you can quickly see what is used and what is not. house under construction - OXOX XXXX + OXXX XXXX OOOX XXXX diff --git a/docs/releasing_openttd.md b/docs/releasing_openttd.md index d0cc591b65..b229f2c333 100644 --- a/docs/releasing_openttd.md +++ b/docs/releasing_openttd.md @@ -8,10 +8,10 @@ This guide is for OpenTTD developers/maintainers, to release a new version of Op * If this is an RC1 (first Release Candidate) build, create a new branch `release/nn` where `nn` is the major version number, then apply changes similar to [PR#9573](https://github.com/OpenTTD/OpenTTD/pull/9573). You also need to forwardport the changelog, as in [PR#10113](https://github.com/OpenTTD/OpenTTD/pull/10113). * Update the version in `CMakeLists.txt` in the master branch, heading for the next major release, e.g. from 14.0 to 15.0. - * Add a new (empty) AI compatibility script in `bin/ai/` - * Add the new version to CheckAPIVersion in `src/ai/ai_info.cpp` and `src/game/game_info.cpp` - * Add the new version to `src/script/api/ai_changelog.hpp` and `src/script/api/game_changelog.hpp` - * Update the version of regression in `bin/ai/regression/regression_info.nut` + * Add the new version to `ApiVersions` in `src/ai/ai_info.hpp` and `src/game/game_info.hpp`. + * Add the new version to `src/script/api/ai_changelog.hpp` and `src/script/api/game_changelog.hpp`. + * Update the version of regression in `bin/ai/regression/regression_info.nut`. + * Add a new (empty) AI compatibility script in `bin/ai/` and `bin/game/` for the version of the branch. * Add a note to `src/saveload/saveload.h` about which savegame version is used in the branch. * If this is a later RC or release build and the release branch already exists, you'll need to backport fixes and language from master to this branch, which were merged after the branch diverged from master. You can use these two helper scripts: https://github.com/OpenTTD/scripts/tree/main/backport diff --git a/media/baseset/OpenTTD-Sans.ttf b/media/baseset/OpenTTD-Sans.ttf index 406825f513..202cef8dd2 100644 Binary files a/media/baseset/OpenTTD-Sans.ttf and b/media/baseset/OpenTTD-Sans.ttf differ diff --git a/media/baseset/OpenTTD-font.md b/media/baseset/OpenTTD-font.md index 39de7ca1b4..87e040308f 100644 --- a/media/baseset/OpenTTD-font.md +++ b/media/baseset/OpenTTD-font.md @@ -1,6 +1,6 @@ # OpenTTD TrueType font -The OpenTTD TrueType font was created by Zephyris and is maintained on [Github](https://github.com/zephyris/openttd-ttf). +The OpenTTD TrueType font was created by Zephyris and is maintained on [Github](https://github.com/OpenTTD/OpenTTD-TTF). It is licensed under GPL-2.0. -The currently included files correspond to release v0.6. +The currently included files correspond to release v0.7. diff --git a/media/baseset/openttd.grf b/media/baseset/openttd.grf index 6cee79959e..540ddf3fe9 100644 Binary files a/media/baseset/openttd.grf and b/media/baseset/openttd.grf differ diff --git a/media/baseset/openttd.grf.hash b/media/baseset/openttd.grf.hash index f3b216de53..b0a772e8d0 100644 --- a/media/baseset/openttd.grf.hash +++ b/media/baseset/openttd.grf.hash @@ -1 +1 @@ -8bc3926cb50e19747de498357417d973 +019dba4830a64ee4345d3d647633e1da diff --git a/media/baseset/openttd/CMakeLists.txt b/media/baseset/openttd/CMakeLists.txt index ee93ba80b1..f2df774d1b 100644 --- a/media/baseset/openttd/CMakeLists.txt +++ b/media/baseset/openttd/CMakeLists.txt @@ -18,6 +18,7 @@ if(GRFCODEC_FOUND) ${CMAKE_CURRENT_SOURCE_DIR}/oneway.nfo ${CMAKE_CURRENT_SOURCE_DIR}/openttd.nfo ${CMAKE_CURRENT_SOURCE_DIR}/openttdgui.nfo + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_rocks.nfo ${CMAKE_CURRENT_SOURCE_DIR}/palette.nfo ${CMAKE_CURRENT_SOURCE_DIR}/roadstops.nfo ${CMAKE_CURRENT_SOURCE_DIR}/road_waypoints.nfo @@ -42,6 +43,11 @@ if(GRFCODEC_FOUND) ${CMAKE_CURRENT_SOURCE_DIR}/openttdgui_convert_road.png ${CMAKE_CURRENT_SOURCE_DIR}/openttdgui_convert_tram.png ${CMAKE_CURRENT_SOURCE_DIR}/openttdgui_group_livery.png + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_rocks.png + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_snowy_rocks_1.png + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_snowy_rocks_2.png + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_snowy_rocks_3.png + ${CMAKE_CURRENT_SOURCE_DIR}/overlay_snowy_rocks_4.png ${CMAKE_CURRENT_SOURCE_DIR}/roadstops.png ${CMAKE_CURRENT_SOURCE_DIR}/road_waypoints.png ${CMAKE_CURRENT_SOURCE_DIR}/signals.png diff --git a/media/baseset/openttd/openttd.nfo b/media/baseset/openttd/openttd.nfo index 3d59f08543..9cfce9ffdd 100644 --- a/media/baseset/openttd/openttd.nfo +++ b/media/baseset/openttd/openttd.nfo @@ -99,3 +99,4 @@ #include "tunnel_portals.nfo" #include "palette.nfo" #include "road_waypoints.nfo" +#include "overlay_rocks.nfo" diff --git a/media/baseset/openttd/overlay_rocks.nfo b/media/baseset/openttd/overlay_rocks.nfo new file mode 100644 index 0000000000..b5256b2bfe --- /dev/null +++ b/media/baseset/openttd/overlay_rocks.nfo @@ -0,0 +1,112 @@ +// 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 . +// + -1 * 0 0C "Overlay rocks" + -1 * 3 05 1A 5F + +// Plain overlay rocks (unused...) + -1 sprites/overlay_rocks.png 8bpp 1 1 64 31 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 81 1 64 31 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 161 1 64 23 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 241 1 64 23 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 321 1 64 31 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 401 1 64 31 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 481 1 64 23 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 561 1 64 23 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 641 1 64 39 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 721 1 64 39 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 801 1 64 31 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 881 1 64 31 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 961 1 64 39 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 1041 1 64 39 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 1121 1 64 31 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 1201 1 64 47 -31 -16 normal + -1 sprites/overlay_rocks.png 8bpp 1281 1 64 15 -31 0 normal + -1 sprites/overlay_rocks.png 8bpp 1361 1 64 31 -31 -8 normal + -1 sprites/overlay_rocks.png 8bpp 1441 1 64 31 -31 -8 normal + +// Snowy rocks level 0 + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 81 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 161 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 241 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 321 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 401 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 481 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 561 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 641 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 721 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 801 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 881 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 961 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1041 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1121 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1201 1 64 47 -31 -16 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1281 1 64 15 -31 0 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1361 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_1.png 8bpp 1441 1 64 31 -31 -8 normal + +// Snowy rocks level 1 + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 81 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 161 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 241 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 321 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 401 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 481 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 561 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 641 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 721 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 801 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 881 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 961 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1041 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1121 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1201 1 64 47 -31 -16 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1281 1 64 15 -31 0 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1361 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_2.png 8bpp 1441 1 64 31 -31 -8 normal + +// Snowy rocks level 2 + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 81 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 161 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 241 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 321 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 401 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 481 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 561 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 641 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 721 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 801 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 881 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 961 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1041 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1121 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1201 1 64 47 -31 -16 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1281 1 64 15 -31 0 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1361 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_3.png 8bpp 1441 1 64 31 -31 -8 normal + +// Snowy rocks level 4 + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 81 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 161 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 241 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 321 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 401 1 64 31 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 481 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 561 1 64 23 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 641 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 721 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 801 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 881 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 961 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1041 1 64 39 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1121 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1201 1 64 47 -31 -16 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1281 1 64 15 -31 0 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1361 1 64 31 -31 -8 normal + -1 sprites/overlay_snowy_rocks_4.png 8bpp 1441 1 64 31 -31 -8 normal diff --git a/media/baseset/openttd/overlay_rocks.png b/media/baseset/openttd/overlay_rocks.png new file mode 100644 index 0000000000..2f34e61394 Binary files /dev/null and b/media/baseset/openttd/overlay_rocks.png differ diff --git a/media/baseset/openttd/overlay_snowy_rocks_1.png b/media/baseset/openttd/overlay_snowy_rocks_1.png new file mode 100644 index 0000000000..fab20aede3 Binary files /dev/null and b/media/baseset/openttd/overlay_snowy_rocks_1.png differ diff --git a/media/baseset/openttd/overlay_snowy_rocks_2.png b/media/baseset/openttd/overlay_snowy_rocks_2.png new file mode 100644 index 0000000000..5cee3d85d6 Binary files /dev/null and b/media/baseset/openttd/overlay_snowy_rocks_2.png differ diff --git a/media/baseset/openttd/overlay_snowy_rocks_3.png b/media/baseset/openttd/overlay_snowy_rocks_3.png new file mode 100644 index 0000000000..51e6f988d3 Binary files /dev/null and b/media/baseset/openttd/overlay_snowy_rocks_3.png differ diff --git a/media/baseset/openttd/overlay_snowy_rocks_4.png b/media/baseset/openttd/overlay_snowy_rocks_4.png new file mode 100644 index 0000000000..5ccc1bfdf4 Binary files /dev/null and b/media/baseset/openttd/overlay_snowy_rocks_4.png differ diff --git a/os/emscripten/Dockerfile b/os/emscripten/Dockerfile index 543bf6b2de..e429867f1d 100644 --- a/os/emscripten/Dockerfile +++ b/os/emscripten/Dockerfile @@ -1,3 +1,12 @@ +# If you change this version, change the numbers in .github/workflows/ci-emscripten.yml (2x) +# and .github/workflows/preview-build.yml (2x) too. FROM emscripten/emsdk:3.1.57 +RUN apt-get update \ + && apt-get install -y gcc-12 g++-12 \ + && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 100 \ + && update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 100 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + COPY ports/liblzma.py /emsdk/upstream/emscripten/tools/ports/contrib/liblzma.py diff --git a/os/emscripten/cmake/FindLibLZMA.cmake b/os/emscripten/cmake/FindLibLZMA.cmake index 0880d62f70..47fab8c9d0 100644 --- a/os/emscripten/cmake/FindLibLZMA.cmake +++ b/os/emscripten/cmake/FindLibLZMA.cmake @@ -1,6 +1,7 @@ # LibLZMA is a custom addition to the emscripten SDK, so it is possible # someone patched their SDK. Test out if the SDK supports LibLZMA. include(CheckCXXSourceCompiles) +set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "--use-port=contrib.liblzma") check_cxx_source_compiles(" @@ -18,3 +19,5 @@ if (LIBLZMA_FOUND) else() message(WARNING "You are using an emscripten SDK without LibLZMA support. Many savegames won't be able to load in OpenTTD. Please copy liblzma.py to your ports/contrib folder in your local emsdk installation.") endif() + +set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) diff --git a/os/macosx/openttd.icns b/os/macosx/openttd.icns index 5b8db48f0f..1dc5c0d87a 100644 Binary files a/os/macosx/openttd.icns and b/os/macosx/openttd.icns differ diff --git a/regression/regression/main.nut b/regression/regression/main.nut index fce258a739..c0fe80ea4b 100644 --- a/regression/regression/main.nut +++ b/regression/regression/main.nut @@ -265,15 +265,15 @@ function Regression::Bridge() print(" Valid Bridges: " + j); print(" IsBridgeTile(): " + AIBridge.IsBridgeTile(33160)); - print(" GetBridgeID(): " + AIBridge.GetBridgeID(33160)); + print(" GetBridgeType(): " + AIBridge.GetBridgeType(33160)); print(" RemoveBridge(): " + AIBridge.RemoveBridge(33155)); print(" GetLastErrorString(): " + AIError.GetLastErrorString()); print(" GetOtherBridgeEnd(): " + AIBridge.GetOtherBridgeEnd(33160)); print(" BuildBridge(): " + AIBridge.BuildBridge(AIVehicle.VT_ROAD, 5, 33160, 33155)); print(" IsBridgeTile(): " + AIBridge.IsBridgeTile(33160)); - print(" GetBridgeID(): " + AIBridge.GetBridgeID(33160)); + print(" GetBridgeType(): " + AIBridge.GetBridgeType(33160)); print(" IsBridgeTile(): " + AIBridge.IsBridgeTile(33155)); - print(" GetBridgeID(): " + AIBridge.GetBridgeID(33155)); + print(" GetBridgeType(): " + AIBridge.GetBridgeType(33155)); print(" GetOtherBridgeEnd(): " + AIBridge.GetOtherBridgeEnd(33160)); print(" BuildBridge(): " + AIBridge.BuildBridge(AIVehicle.VT_ROAD, 5, 33160, 33155)); print(" GetLastErrorString(): " + AIError.GetLastErrorString()); @@ -2035,6 +2035,20 @@ function Regression::Start() print(" VehicleID: " + c.GetVehicleID()); } break; + case AIEvent.ET_COMPANY_RENAMED: { + local c = AIEventCompanyRenamed.Convert(e); + print(" EventName: CompanyRenamed"); + print(" CompanyID: " + c.GetCompanyID()); + print(" CompanyName: " + c.GetNewName()); + } break; + + case AIEvent.ET_PRESIDENT_RENAMED: { + local c = AIEventPresidentRenamed.Convert(e); + print(" EventName: PresidentRenamed"); + print(" CompanyID: " + c.GetCompanyID()); + print(" PresidentName: " + c.GetNewName()); + } break; + default: print(" Unknown Event"); break; diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 27a81675a9..f00c9e3813 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -1051,15 +1051,15 @@ ERROR: IsEnd() is invalid as Begin() is never called GetMinLength(): -1 Valid Bridges: 10 IsBridgeTile(): false - GetBridgeID(): -1 + GetBridgeType(): -1 RemoveBridge(): false GetLastErrorString(): ERR_PRECONDITION_FAILED GetOtherBridgeEnd(): -1 BuildBridge(): true IsBridgeTile(): true - GetBridgeID(): 5 + GetBridgeType(): 5 IsBridgeTile(): true - GetBridgeID(): 5 + GetBridgeType(): 5 GetOtherBridgeEnd(): 33155 BuildBridge(): false GetLastErrorString(): ERR_ALREADY_BUILT @@ -9711,6 +9711,21 @@ ERROR: IsEnd() is invalid as Begin() is never called GetDestinationType(): 1 GetDestinationIndex(): 7 GetCargoType(): 0 + GetNextEvent: instance + GetEventType: 33 + EventName: CompanyRenamed + CompanyID: 1 + CompanyName: Regression + GetNextEvent: instance + GetEventType: 34 + EventName: PresidentRenamed + CompanyID: 1 + PresidentName: Regression AI + GetNextEvent: instance + GetEventType: 33 + EventName: CompanyRenamed + CompanyID: 1 + CompanyName: Little Frutford Transport IsEventWaiting: false --Math-- @@ -9748,9 +9763,9 @@ ERROR: IsEnd() is invalid as Begin() is never called --Valuate() with excessive CPU usage-- Your script made an error: excessive CPU usage in valuator function -*FUNCTION [unknown()] regression/main.nut line [2051] +*FUNCTION [unknown()] regression/main.nut line [2065] *FUNCTION [Valuate()] NATIVE line [-1] -*FUNCTION [Start()] regression/main.nut line [2052] +*FUNCTION [Start()] regression/main.nut line [2066] [id] 0 [this] TABLE @@ -9759,7 +9774,7 @@ Your script made an error: excessive CPU usage in valuator function [this] INSTANCE Your script made an error: excessive CPU usage in valuator function -*FUNCTION [Start()] regression/main.nut line [2052] +*FUNCTION [Start()] regression/main.nut line [2066] [Infinite] CLOSURE [list] INSTANCE diff --git a/src/3rdparty/fmt/CMakeLists.txt b/src/3rdparty/fmt/CMakeLists.txt index e687b2b3d8..f3e5dbfb6a 100644 --- a/src/3rdparty/fmt/CMakeLists.txt +++ b/src/3rdparty/fmt/CMakeLists.txt @@ -1,9 +1,18 @@ add_files( + args.h + base.h chrono.h + color.h + compile.h core.h + format.cc format.h format-inl.h + os.cc + os.h ostream.h + printf.h ranges.h std.h + xchar.h ) diff --git a/src/3rdparty/fmt/args.h b/src/3rdparty/fmt/args.h new file mode 100644 index 0000000000..3ff4788074 --- /dev/null +++ b/src/3rdparty/fmt/args.h @@ -0,0 +1,220 @@ +// Formatting library for C++ - dynamic argument lists +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_ARGS_H_ +#define FMT_ARGS_H_ + +#ifndef FMT_MODULE +# include // std::reference_wrapper +# include // std::unique_ptr +# include +#endif + +#include "format.h" // std_string_view + +FMT_BEGIN_NAMESPACE +namespace detail { + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template auto unwrap(const T& v) -> const T& { return v; } +template +auto unwrap(const std::reference_wrapper& v) -> const T& { + return static_cast(v); +} + +// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC +// 2022 (v17.10.0). +// +// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for +// templates it doesn't complain about inability to deduce single translation +// unit for placing vtable. So node is made a fake template. +template struct node { + virtual ~node() = default; + std::unique_ptr> next; +}; + +class dynamic_arg_list { + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template auto push(const Arg& arg) -> const T& { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +/** + * A dynamic list of formatting arguments with storage. + * + * It can be implicitly converted into `fmt::basic_format_args` for passing + * into type-erased formatting functions such as `fmt::vformat`. + */ +template class dynamic_format_arg_store { + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_t = conditional_t< + std::is_convertible>::value && + !detail::is_reference_wrapper::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + auto data() const -> const basic_format_arg* { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(arg); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) + data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); + data_.emplace_back(detail::unwrap(arg.value)); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0] = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + constexpr dynamic_format_arg_store() = default; + + operator basic_format_args() const { + return basic_format_args(data(), static_cast(data_.size()), + !named_info_.empty()); + } + + /** + * Adds an argument into the dynamic store for later passing to a formatting + * function. + * + * Note that custom types and string types (but not string views) are copied + * into the store dynamically allocating memory if necessary. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * store.push_back(42); + * store.push_back("abc"); + * store.push_back(1.5f); + * std::string result = fmt::vformat("{} and {} and {}", store); + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + * Adds a reference to the argument into the dynamic store for later passing + * to a formatting function. + * + * **Example**: + * + * fmt::dynamic_format_arg_store store; + * char band[] = "Rolling Stones"; + * store.push_back(std::cref(band)); + * band[9] = 'c'; // Changing str affects the output. + * std::string result = fmt::vformat("{}", store); + * // result == "Rolling Scones" + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + * Adds named argument into the dynamic store for later passing to a + * formatting function. `std::reference_wrapper` is supported to avoid + * copying of the argument. The name is always copied into the store. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /// Erase all elements from the store. + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = {}; + } + + /// Reserves space to store at least `new_cap` arguments including + /// `new_cap_named` named arguments. + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } + + /// Returns the number of elements in the store. + size_t size() const noexcept { return data_.size(); } +}; + +FMT_END_NAMESPACE + +#endif // FMT_ARGS_H_ diff --git a/src/3rdparty/fmt/base.h b/src/3rdparty/fmt/base.h new file mode 100644 index 0000000000..0e867954bc --- /dev/null +++ b/src/3rdparty/fmt/base.h @@ -0,0 +1,2962 @@ +// Formatting library for C++ - the base API for char/UTF-8 +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_BASE_H_ +#define FMT_BASE_H_ + +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // memcmp + +# include // std::enable_if +#endif + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 110104 + +// Detect compiler versions. +#if defined(__clang__) && !defined(__ibmxl__) +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif +#if defined(__ICL) +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif +#if defined(_MSC_VER) +# define FMT_MSC_VERSION _MSC_VER +#else +# define FMT_MSC_VERSION 0 +#endif + +// Detect standard library versions. +#ifdef _GLIBCXX_RELEASE +# define FMT_GLIBCXX_RELEASE _GLIBCXX_RELEASE +#else +# define FMT_GLIBCXX_RELEASE 0 +#endif +#ifdef _LIBCPP_VERSION +# define FMT_LIBCPP_VERSION _LIBCPP_VERSION +#else +# define FMT_LIBCPP_VERSION 0 +#endif + +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + +// Detect __has_*. +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif +#ifdef __has_include +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Detect C++14 relaxed constexpr. +#ifdef FMT_USE_CONSTEXPR +// Use the provided definition. +#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L +// GCC only allows constexpr member functions in non-literal types since 7.2: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297. +# define FMT_USE_CONSTEXPR 1 +#elif FMT_ICC_VERSION +# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628 +#elif FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 +# define FMT_USE_CONSTEXPR 1 +#else +# define FMT_USE_CONSTEXPR 0 +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +#else +# define FMT_CONSTEXPR +#endif + +// Detect consteval, C++20 constexpr extensions and std::is_constant_evaluated. +#if !defined(__cpp_lib_is_constant_evaluated) +# define FMT_USE_CONSTEVAL 0 +#elif FMT_CPLUSPLUS < 201709L +# define FMT_USE_CONSTEVAL 0 +#elif FMT_GLIBCXX_RELEASE && FMT_GLIBCXX_RELEASE < 10 +# define FMT_USE_CONSTEVAL 0 +#elif FMT_LIBCPP_VERSION && FMT_LIBCPP_VERSION < 10000 +# define FMT_USE_CONSTEVAL 0 +#elif defined(__apple_build_version__) && __apple_build_version__ < 14000029L +# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang < 14. +#elif FMT_MSC_VERSION && FMT_MSC_VERSION < 1929 +# define FMT_USE_CONSTEVAL 0 // consteval is broken in MSVC VS2019 < 16.10. +#elif defined(__cpp_consteval) +# define FMT_USE_CONSTEVAL 1 +#elif FMT_GCC_VERSION >= 1002 || FMT_CLANG_VERSION >= 1101 +# define FMT_USE_CONSTEVAL 1 +#else +# define FMT_USE_CONSTEVAL 0 +#endif +#if FMT_USE_CONSTEVAL +# define FMT_CONSTEVAL consteval +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEVAL +# define FMT_CONSTEXPR20 +#endif + +// Check if exceptions are disabled. +#ifdef FMT_USE_EXCEPTIONS +// Use the provided definition. +#elif defined(__GNUC__) && !defined(__EXCEPTIONS) +# define FMT_USE_EXCEPTIONS 0 +#elif defined(__clang__) && !defined(__cpp_exceptions) +# define FMT_USE_EXCEPTIONS 0 +#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS +# define FMT_USE_EXCEPTIONS 0 +#else +# define FMT_USE_EXCEPTIONS 1 +#endif +#if FMT_USE_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifdef FMT_NO_UNIQUE_ADDRESS +// Use the provided definition. +#elif FMT_CPLUSPLUS < 202002L +// Not supported. +#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address) +# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] +// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485). +#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION +# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#endif +#ifndef FMT_NO_UNIQUE_ADDRESS +# define FMT_NO_UNIQUE_ADDRESS +#endif + +#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) +# define FMT_FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +#elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +#else +# define FMT_FALLTHROUGH +#endif + +// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. +#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__) +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifdef FMT_NODISCARD +// Use the provided definition. +#elif FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +#else +# define FMT_NODISCARD +#endif + +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +#ifdef FMT_ALWAYS_INLINE +// Use the provided definition. +#elif FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE inline +#endif +// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. +#ifdef NDEBUG +# define FMT_INLINE FMT_ALWAYS_INLINE +#else +# define FMT_INLINE inline +#endif + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_VISIBILITY(value) __attribute__((visibility(value))) +#else +# define FMT_VISIBILITY(value) +#endif + +// Detect pragmas. +#define FMT_PRAGMA_IMPL(x) _Pragma(#x) +#if FMT_GCC_VERSION >= 504 && !defined(__NVCOMPILER) +// Workaround a _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884 +// and an nvhpc warning: https://github.com/fmtlib/fmt/pull/2582. +# define FMT_PRAGMA_GCC(x) FMT_PRAGMA_IMPL(GCC x) +#else +# define FMT_PRAGMA_GCC(x) +#endif +#if FMT_CLANG_VERSION +# define FMT_PRAGMA_CLANG(x) FMT_PRAGMA_IMPL(clang x) +#else +# define FMT_PRAGMA_CLANG(x) +#endif +#if FMT_MSC_VERSION +# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) +#else +# define FMT_MSC_WARNING(...) +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + inline namespace v11 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_EXPORT +# define FMT_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT +#endif + +#ifdef _WIN32 +# define FMT_WIN32 1 +#else +# define FMT_WIN32 0 +#endif + +#if !defined(FMT_HEADER_ONLY) && FMT_WIN32 +# if defined(FMT_LIB_EXPORT) +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) +# define FMT_API FMT_VISIBILITY("default") +#endif +#ifndef FMT_API +# define FMT_API +#endif + +#ifndef FMT_OPTIMIZE_SIZE +# define FMT_OPTIMIZE_SIZE 0 +#endif + +// FMT_BUILTIN_TYPE=0 may result in smaller library size at the cost of higher +// per-call binary size by passing built-in types through the extension API. +#ifndef FMT_BUILTIN_TYPES +# define FMT_BUILTIN_TYPES 1 +#endif + +#define FMT_APPLY_VARIADIC(expr) \ + using unused = int[]; \ + (void)unused { 0, (expr, 0)... } + +// Enable minimal optimizations for more compact code in debug mode. +FMT_PRAGMA_GCC(push_options) +//#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +//FMT_PRAGMA_GCC(optimize("Og")) +//#endif +FMT_PRAGMA_CLANG(diagnostic push) + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template +using make_unsigned_t = typename std::make_unsigned::type; +template +using underlying_t = typename std::underlying_type::type; +template using decay_t = typename std::decay::type; +using nullptr_t = decltype(nullptr); + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +// A workaround for gcc 4.9 to make void_t work in a SFINAE context. +template struct void_t_impl { + using type = void; +}; +template using void_t = typename void_t_impl::type; +#else +template using void_t = void; +#endif + +struct monostate { + constexpr monostate() {} +}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#ifdef FMT_DOC +# define FMT_ENABLE_IF(...) +#else +# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +template constexpr auto min_of(T a, T b) -> T { + return a < b ? a : b; +} +template constexpr auto max_of(T a, T b) -> T { + return a > b ? a : b; +} + +namespace detail { +// Suppresses "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr auto is_constant_evaluated(bool default_value = false) noexcept + -> bool { +// Workaround for incompatibility between clang 14 and libstdc++ consteval-based +// std::is_constant_evaluated: https://github.com/fmtlib/fmt/issues/3247. +#if FMT_CPLUSPLUS >= 202002L && FMT_GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__cpp_lib_is_constant_evaluated) + ignore_unused(default_value); + return std::is_constant_evaluated(); +#else + return default_value; +#endif +} + +// Suppresses "conditional expression is constant" warnings. +template FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T { + return val; +} + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#if defined(FMT_ASSERT) +// Use the provided definition. +#elif defined(NDEBUG) +// FMT_ASSERT is not empty to avoid -Wempty-body. +# define FMT_ASSERT(condition, message) \ + fmt::detail::ignore_unused((condition), (message)) +#else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +#endif + +#ifdef FMT_USE_INT128 +// Use the provided definition. +#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ + !(FMT_CLANG_VERSION && FMT_MSC_VERSION) +# define FMT_USE_INT128 1 +using int128_opt = __int128_t; // An optional native 128-bit integer. +using uint128_opt = __uint128_t; +inline auto map(int128_opt x) -> int128_opt { return x; } +inline auto map(uint128_opt x) -> uint128_opt { return x; } +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +enum class int128_opt {}; +enum class uint128_opt {}; +// Reduce template instantiations. +inline auto map(int128_opt) -> monostate { return {}; } +inline auto map(uint128_opt) -> monostate { return {}; } +#endif + +#ifndef FMT_USE_BITINT +# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500) +#endif + +#if FMT_USE_BITINT +FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension") +template using bitint = _BitInt(N); +template using ubitint = unsigned _BitInt(N); +#else +template struct bitint {}; +template struct ubitint {}; +#endif // FMT_USE_BITINT + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR auto to_unsigned(Int value) -> make_unsigned_t { + FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); + return static_cast>(value); +} + +template +using unsigned_char = conditional_t; + +// A heuristic to detect std::string and std::[experimental::]string_view. +// It is mainly used to avoid dependency on <[experimental/]string_view>. +template +struct is_std_string_like : std::false_type {}; +template +struct is_std_string_like().find_first_of( + typename T::value_type(), 0))>> + : std::is_convertible().data()), + const typename T::value_type*> {}; + +// Check if the literal encoding is UTF-8. +enum { is_utf8_enabled = "\u00A7"[1] == '\xA7' }; +enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; + +#ifndef FMT_UNICODE +# define FMT_UNICODE 1 +#endif + +static_assert(!FMT_UNICODE || use_utf8, + "Unicode support requires compiling with /utf-8"); + +template constexpr const char* narrow(const T*) { return nullptr; } +constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } + +template +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) + -> int { + if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); + for (; n != 0; ++s1, ++s2, --n) { + if (*s1 < *s2) return -1; + if (*s1 > *s2) return 1; + } + return 0; +} + +namespace adl { +using namespace std; + +template +auto invoke_back_inserter() + -> decltype(back_inserter(std::declval())); +} // namespace adl + +template +struct is_back_insert_iterator : std::false_type {}; + +template +struct is_back_insert_iterator< + It, bool_constant()), + It>::value>> : std::true_type {}; + +// Extracts a reference to the container from *insert_iterator. +template +inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> + typename OutputIt::container_type& { + struct accessor : OutputIt { + FMT_CONSTEXPR20 accessor(OutputIt base) : OutputIt(base) {} + using OutputIt::container; + }; + return *accessor(it).container; +} +} // namespace detail + +// Parsing-related public API and forward declarations. +FMT_BEGIN_EXPORT + +/** + * An implementation of `std::basic_string_view` for pre-C++17. It provides a + * subset of the API. `fmt::basic_string_view` is used for format strings even + * if `std::basic_string_view` is available to prevent issues when a library is + * compiled with a different `-std` option than the client code (which is not + * recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} + + /// Constructs a string reference object from a C string and a size. + constexpr basic_string_view(const Char* s, size_t count) noexcept + : data_(s), size_(count) {} + + constexpr basic_string_view(nullptr_t) = delete; + + /// Constructs a string reference object from a C string. +#if FMT_GCC_VERSION + FMT_ALWAYS_INLINE +#endif + FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { +#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION + if (std::is_same::value) { + size_ = __builtin_strlen(detail::narrow(s)); + return; + } +#endif + size_t len = 0; + while (*s++) ++len; + size_ = len; + } + + /// Constructs a string reference from a `std::basic_string` or a + /// `std::basic_string_view` object. + template ::value&& std::is_same< + typename S::value_type, Char>::value)> + FMT_CONSTEXPR basic_string_view(const S& s) noexcept + : data_(s.data()), size_(s.size()) {} + + /// Returns a pointer to the string data. + constexpr auto data() const noexcept -> const Char* { return data_; } + + /// Returns the string size. + constexpr auto size() const noexcept -> size_t { return size_; } + + constexpr auto begin() const noexcept -> iterator { return data_; } + constexpr auto end() const noexcept -> iterator { return data_ + size_; } + + constexpr auto operator[](size_t pos) const noexcept -> const Char& { + return data_[pos]; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { + data_ += n; + size_ -= n; + } + + FMT_CONSTEXPR auto starts_with(basic_string_view sv) const noexcept + -> bool { + return size_ >= sv.size_ && detail::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR auto starts_with(Char c) const noexcept -> bool { + return size_ >= 1 && *data_ == c; + } + FMT_CONSTEXPR auto starts_with(const Char* s) const -> bool { + return starts_with(basic_string_view(s)); + } + + // Lexicographically compare this string reference to other. + FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { + int result = + detail::compare(data_, other.data_, min_of(size_, other.size_)); + if (result != 0) return result; + return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + } + + FMT_CONSTEXPR friend auto operator==(basic_string_view lhs, + basic_string_view rhs) -> bool { + return lhs.compare(rhs) == 0; + } + friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) != 0; + } + friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) < 0; + } + friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) <= 0; + } + friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) > 0; + } + friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; + +/// Specifies if `T` is an extended character type. Can be specialized by users. +template struct is_xchar : std::false_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +template <> struct is_xchar : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_xchar : std::true_type {}; +#endif + +// DEPRECATED! Will be replaced with an alias to prevent specializations. +template struct is_char : is_xchar {}; +template <> struct is_char : std::true_type {}; + +template class basic_appender; +using appender = basic_appender; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; + +class context; +template class generic_context; +template class parse_context; + +// Longer aliases for C++20 compatibility. +template using basic_format_parse_context = parse_context; +using format_parse_context = parse_context; +template +using basic_format_context = + conditional_t::value, context, + generic_context>; +using format_context = context; + +template +using buffered_context = + conditional_t::value, context, + generic_context, Char>>; + +template class basic_format_arg; +template class basic_format_args; + +// A separate type would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +/// Reports a format error at compile time or, via a `format_error` exception, +/// at runtime. +// This function is intentionally not constexpr to give a compile-time error. +FMT_NORETURN FMT_API void report_error(const char* message); + +enum class presentation_type : unsigned char { + // Common specifiers: + none = 0, + debug = 1, // '?' + string = 2, // 's' (string, bool) + + // Integral, bool and character specifiers: + dec = 3, // 'd' + hex, // 'x' or 'X' + oct, // 'o' + bin, // 'b' or 'B' + chr, // 'c' + + // String and pointer specifiers: + pointer = 3, // 'p' + + // Floating-point specifiers: + exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation) + fixed, // 'f' or 'F' + general, // 'g' or 'G' + hexfloat // 'a' or 'A' +}; + +enum class align { none, left, right, center, numeric }; +enum class sign { none, minus, plus, space }; +enum class arg_id_kind { none, index, name }; + +// Basic format specifiers for built-in and string types. +class basic_specs { + private: + // Data is arranged as follows: + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |type |align| w | p | s |u|#|L| f | unused | + // +-----+-----+---+---+---+-+-+-+-----+---------------------------+ + // + // w - dynamic width info + // p - dynamic precision info + // s - sign + // u - uppercase (e.g. 'X' for 'x') + // # - alternate form ('#') + // L - localized + // f - fill size + // + // Bitfields are not used because of compiler bugs such as gcc bug 61414. + enum : unsigned { + type_mask = 0x00007, + align_mask = 0x00038, + width_mask = 0x000C0, + precision_mask = 0x00300, + sign_mask = 0x00C00, + uppercase_mask = 0x01000, + alternate_mask = 0x02000, + localized_mask = 0x04000, + fill_size_mask = 0x38000, + + align_shift = 3, + width_shift = 6, + precision_shift = 8, + sign_shift = 10, + fill_size_shift = 15, + + max_fill_size = 4 + }; + + unsigned data_ = 1 << fill_size_shift; + static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, ""); + + // Character (code unit) type is erased to prevent template bloat. + char fill_data_[max_fill_size] = {' '}; + + FMT_CONSTEXPR void set_fill_size(size_t size) { + data_ = (data_ & ~fill_size_mask) | + (static_cast(size) << fill_size_shift); + } + + public: + constexpr auto type() const -> presentation_type { + return static_cast(data_ & type_mask); + } + FMT_CONSTEXPR void set_type(presentation_type t) { + data_ = (data_ & ~type_mask) | static_cast(t); + } + + constexpr auto align() const -> align { + return static_cast((data_ & align_mask) >> align_shift); + } + FMT_CONSTEXPR void set_align(fmt::align a) { + data_ = (data_ & ~align_mask) | (static_cast(a) << align_shift); + } + + constexpr auto dynamic_width() const -> arg_id_kind { + return static_cast((data_ & width_mask) >> width_shift); + } + FMT_CONSTEXPR void set_dynamic_width(arg_id_kind w) { + data_ = (data_ & ~width_mask) | (static_cast(w) << width_shift); + } + + FMT_CONSTEXPR auto dynamic_precision() const -> arg_id_kind { + return static_cast((data_ & precision_mask) >> + precision_shift); + } + FMT_CONSTEXPR void set_dynamic_precision(arg_id_kind p) { + data_ = (data_ & ~precision_mask) | + (static_cast(p) << precision_shift); + } + + constexpr bool dynamic() const { + return (data_ & (width_mask | precision_mask)) != 0; + } + + constexpr auto sign() const -> sign { + return static_cast((data_ & sign_mask) >> sign_shift); + } + FMT_CONSTEXPR void set_sign(fmt::sign s) { + data_ = (data_ & ~sign_mask) | (static_cast(s) << sign_shift); + } + + constexpr auto upper() const -> bool { return (data_ & uppercase_mask) != 0; } + FMT_CONSTEXPR void set_upper() { data_ |= uppercase_mask; } + + constexpr auto alt() const -> bool { return (data_ & alternate_mask) != 0; } + FMT_CONSTEXPR void set_alt() { data_ |= alternate_mask; } + FMT_CONSTEXPR void clear_alt() { data_ &= ~alternate_mask; } + + constexpr auto localized() const -> bool { + return (data_ & localized_mask) != 0; + } + FMT_CONSTEXPR void set_localized() { data_ |= localized_mask; } + + constexpr auto fill_size() const -> size_t { + return (data_ & fill_size_mask) >> fill_size_shift; + } + + template ::value)> + constexpr auto fill() const -> const Char* { + return fill_data_; + } + template ::value)> + constexpr auto fill() const -> const Char* { + return nullptr; + } + + template constexpr auto fill_unit() const -> Char { + using uchar = unsigned char; + return static_cast(static_cast(fill_data_[0]) | + (static_cast(fill_data_[1]) << 8) | + (static_cast(fill_data_[2]) << 16)); + } + + FMT_CONSTEXPR void set_fill(char c) { + fill_data_[0] = c; + set_fill_size(1); + } + + template + FMT_CONSTEXPR void set_fill(basic_string_view s) { + auto size = s.size(); + set_fill_size(size); + if (size == 1) { + unsigned uchar = static_cast>(s[0]); + fill_data_[0] = static_cast(uchar); + fill_data_[1] = static_cast(uchar >> 8); + fill_data_[2] = static_cast(uchar >> 16); + return; + } + FMT_ASSERT(size <= max_fill_size, "invalid fill"); + for (size_t i = 0; i < size; ++i) + fill_data_[i & 3] = static_cast(s[i]); + } + + FMT_CONSTEXPR void copy_fill_from(const basic_specs& specs) { + set_fill_size(specs.fill_size()); + for (size_t i = 0; i < max_fill_size; ++i) + fill_data_[i] = specs.fill_data_[i]; + } +}; + +// Format specifiers for built-in and string types. +struct format_specs : basic_specs { + int width; + int precision; + + constexpr format_specs() : width(0), precision(-1) {} +}; + +/** + * Parsing context consisting of a format string range being parsed and an + * argument counter for automatic indexing. + */ +template class parse_context { + private: + basic_string_view fmt_; + int next_arg_id_; + + enum { use_constexpr_cast = !FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200 }; + + FMT_CONSTEXPR void do_check_arg_id(int arg_id); + + public: + using char_type = Char; + using iterator = const Char*; + + constexpr explicit parse_context(basic_string_view fmt, + int next_arg_id = 0) + : fmt_(fmt), next_arg_id_(next_arg_id) {} + + /// Returns an iterator to the beginning of the format string range being + /// parsed. + constexpr auto begin() const noexcept -> iterator { return fmt_.begin(); } + + /// Returns an iterator past the end of the format string range being parsed. + constexpr auto end() const noexcept -> iterator { return fmt_.end(); } + + /// Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator it) { + fmt_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /// Reports an error if using the manual argument indexing; otherwise returns + /// the next argument index and switches to the automatic indexing. + FMT_CONSTEXPR auto next_arg_id() -> int { + if (next_arg_id_ < 0) { + report_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + int id = next_arg_id_++; + do_check_arg_id(id); + return id; + } + + /// Reports an error if using the automatic argument indexing; otherwise + /// switches to the manual indexing. + FMT_CONSTEXPR void check_arg_id(int id) { + if (next_arg_id_ > 0) { + report_error("cannot switch from automatic to manual argument indexing"); + return; + } + next_arg_id_ = -1; + do_check_arg_id(id); + } + FMT_CONSTEXPR void check_arg_id(basic_string_view) { + next_arg_id_ = -1; + } + FMT_CONSTEXPR void check_dynamic_spec(int arg_id); +}; + +FMT_END_EXPORT + +namespace detail { + +// Constructs fmt::basic_string_view from types implicitly convertible +// to it, deducing Char. Explicitly convertible types such as the ones returned +// from FMT_STRING are intentionally excluded. +template ::value)> +constexpr auto to_string_view(const Char* s) -> basic_string_view { + return s; +} +template ::value)> +constexpr auto to_string_view(const T& s) + -> basic_string_view { + return s; +} +template +constexpr auto to_string_view(basic_string_view s) + -> basic_string_view { + return s; +} + +template +struct has_to_string_view : std::false_type {}; +// detail:: is intentional since to_string_view is not an extension point. +template +struct has_to_string_view< + T, void_t()))>> + : std::true_type {}; + +/// String's character (code unit) type. detail:: is intentional to prevent ADL. +template ()))> +using char_t = typename V::value_type; + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_opt, int128_type); +FMT_TYPE_CONSTANT(uint128_opt, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr auto is_integral_type(type t) -> bool { + return t > type::none_type && t <= type::last_integer_type; +} +constexpr auto is_arithmetic_type(type t) -> bool { + return t > type::none_type && t <= type::last_numeric_type; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +struct view {}; + +template struct named_arg; +template struct is_named_arg : std::false_type {}; +template struct is_static_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template struct named_arg : view { + const Char* name; + const T& value; + + named_arg(const Char* n, const T& v) : name(n), value(v) {} + static_assert(!is_named_arg::value, "nested named arguments"); +}; + +template constexpr auto count() -> int { return B ? 1 : 0; } +template constexpr auto count() -> int { + return (B1 ? 1 : 0) + count(); +} + +template constexpr auto count_named_args() -> int { + return count::value...>(); +} +template constexpr auto count_static_named_args() -> int { + return count::value...>(); +} + +template struct named_arg_info { + const Char* name; + int id; +}; + +template ::value)> +void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { + ++arg_index; +} +template ::value)> +void init_named_arg(named_arg_info* named_args, int& arg_index, + int& named_arg_index, const T& arg) { + named_args[named_arg_index++] = {arg.name, arg_index++}; +} + +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info*, int& arg_index, + int&) { + ++arg_index; +} +template ::value)> +FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, + int& arg_index, int& named_arg_index) { + named_args[named_arg_index++] = {T::name, arg_index++}; +} + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +template +using format_as_result = + remove_cvref_t()))>; +template +using format_as_member_result = + remove_cvref_t::format_as(std::declval()))>; + +template +struct use_format_as : std::false_type {}; +// format_as member is only used to avoid injection into the std namespace. +template +struct use_format_as_member : std::false_type {}; + +// Only map owning types because mapping views can be unsafe. +template +struct use_format_as< + T, bool_constant>::value>> + : std::true_type {}; +template +struct use_format_as_member< + T, bool_constant>::value>> + : std::true_type {}; + +template > +using use_formatter = + bool_constant<(std::is_class::value || std::is_enum::value || + std::is_union::value || std::is_array::value) && + !has_to_string_view::value && !is_named_arg::value && + !use_format_as::value && !use_format_as_member::value>; + +template > +auto has_formatter_impl(T* p, buffered_context* ctx = nullptr) + -> decltype(formatter().format(*p, *ctx), std::true_type()); +template auto has_formatter_impl(...) -> std::false_type; + +// T can be const-qualified to check if it is const-formattable. +template constexpr auto has_formatter() -> bool { + return decltype(has_formatter_impl(static_cast(nullptr)))::value; +} + +// Maps formatting argument types to natively supported types or user-defined +// types with formatters. Returns void on errors to be SFINAE-friendly. +template struct type_mapper { + static auto map(signed char) -> int; + static auto map(unsigned char) -> unsigned; + static auto map(short) -> int; + static auto map(unsigned short) -> unsigned; + static auto map(int) -> int; + static auto map(unsigned) -> unsigned; + static auto map(long) -> long_type; + static auto map(unsigned long) -> ulong_type; + static auto map(long long) -> long long; + static auto map(unsigned long long) -> unsigned long long; + static auto map(int128_opt) -> int128_opt; + static auto map(uint128_opt) -> uint128_opt; + static auto map(bool) -> bool; + + template + static auto map(bitint) -> conditional_t; + template + static auto map(ubitint) + -> conditional_t; + + template ::value)> + static auto map(T) -> conditional_t< + std::is_same::value || std::is_same::value, Char, void>; + + static auto map(float) -> float; + static auto map(double) -> double; + static auto map(long double) -> long double; + + static auto map(Char*) -> const Char*; + static auto map(const Char*) -> const Char*; + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + static auto map(const T&) -> conditional_t::value, + basic_string_view, void>; + + static auto map(void*) -> const void*; + static auto map(const void*) -> const void*; + static auto map(volatile void*) -> const void*; + static auto map(const volatile void*) -> const void*; + static auto map(nullptr_t) -> const void*; + template ::value || + std::is_member_pointer::value)> + static auto map(const T&) -> void; + + template ::value)> + static auto map(const T& x) -> decltype(map(format_as(x))); + template ::value)> + static auto map(const T& x) -> decltype(map(formatter::format_as(x))); + + template ::value)> + static auto map(T&) -> conditional_t(), T&, void>; + + template ::value)> + static auto map(const T& named_arg) -> decltype(map(named_arg.value)); +}; + +// detail:: is used to workaround a bug in MSVC 2017. +template +using mapped_t = decltype(detail::type_mapper::map(std::declval())); + +// A type constant after applying type_mapper. +template +using mapped_type_constant = type_constant, Char>; + +template ::value> +using stored_type_constant = std::integral_constant< + type, Context::builtin_types || TYPE == type::int_type ? TYPE + : type::custom_type>; +// A parse context with extra data used only in compile-time checks. +template +class compile_parse_context : public parse_context { + private: + int num_args_; + const type* types_; + using base = parse_context; + + public: + FMT_CONSTEXPR explicit compile_parse_context(basic_string_view fmt, + int num_args, const type* types, + int next_arg_id = 0) + : base(fmt, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) report_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) report_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + ignore_unused(arg_id); + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + report_error("width/precision is not integer"); + } +}; + +// An argument reference. +template union arg_ref { + FMT_CONSTEXPR arg_ref(int idx = 0) : index(idx) {} + FMT_CONSTEXPR arg_ref(basic_string_view n) : name(n) {} + + int index; + basic_string_view name; +}; + +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 3) + 1; +} + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, + int error_value) noexcept -> int { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0, prev = 0; + auto p = begin; + do { + prev = value; + value = value * 10 + unsigned(*p - '0'); + ++p; + } while (p != end && '0' <= *p && *p <= '9'); + auto num_digits = p - begin; + begin = p; + int digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) return static_cast(value); + // Check for overflow. + unsigned max = INT_MAX; + return num_digits == digits10 + 1 && + prev * 10ull + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align { + switch (c) { + case '<': return align::left; + case '>': return align::right; + case '^': return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + if (c != '0') + index = parse_nonnegative_int(begin, end, INT_MAX); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + report_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (FMT_OPTIMIZE_SIZE > 1 || !is_name_start(c)) { + report_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template struct dynamic_spec_handler { + parse_context& ctx; + arg_ref& ref; + arg_id_kind& kind; + + FMT_CONSTEXPR void on_index(int id) { + ref = id; + kind = arg_id_kind::index; + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = id; + kind = arg_id_kind::name; + ctx.check_arg_id(id); + } +}; + +template struct parse_dynamic_spec_result { + const Char* end; + arg_id_kind kind; +}; + +// Parses integer | "{" [arg_id] "}". +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + parse_context& ctx) + -> parse_dynamic_spec_result { + FMT_ASSERT(begin != end, ""); + auto kind = arg_id_kind::none; + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val == -1) report_error("number is too big"); + value = val; + } else { + if (*begin == '{') { + ++begin; + if (begin != end) { + Char c = *begin; + if (c == '}' || c == ':') { + int id = ctx.next_arg_id(); + ref = id; + kind = arg_id_kind::index; + ctx.check_dynamic_spec(id); + } else { + begin = parse_arg_id(begin, end, + dynamic_spec_handler{ctx, ref, kind}); + } + } + if (begin != end && *begin == '}') return {++begin, kind}; + } + report_error("invalid format string"); + } + return {begin, kind}; +} + +template +FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end, + format_specs& specs, arg_ref& width_ref, + parse_context& ctx) -> const Char* { + auto result = parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + specs.set_dynamic_width(result.kind); + return result.end; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + format_specs& specs, + arg_ref& precision_ref, + parse_context& ctx) -> const Char* { + ++begin; + if (begin == end) { + report_error("invalid precision"); + return begin; + } + auto result = + parse_dynamic_spec(begin, end, specs.precision, precision_ref, ctx); + specs.set_dynamic_precision(result.kind); + return result.end; +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, + dynamic_format_specs& specs, + parse_context& ctx, type arg_type) + -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + report_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { + if (!in(arg_type, set)) report_error("invalid format specifier"); + specs.set_type(pres_type); + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.set_align(parse_align(c)); + ++begin; + break; + case '+': + case ' ': + specs.set_sign(c == ' ' ? sign::space : sign::plus); + FMT_FALLTHROUGH; + case '-': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.set_alt(); + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + report_error("format specifier requires numeric argument"); + if (specs.align() == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.set_align(align::numeric); + specs.set_fill('0'); + } + ++begin; + break; + // clang-format off + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '{': + // clang-format on + enter_state(state::width); + begin = parse_width(begin, end, specs, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs, specs.precision_ref, ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.set_localized(); + ++begin; + break; + case 'd': return parse_presentation_type(pres::dec, integral_set); + case 'X': specs.set_upper(); FMT_FALLTHROUGH; + case 'x': return parse_presentation_type(pres::hex, integral_set); + case 'o': return parse_presentation_type(pres::oct, integral_set); + case 'B': specs.set_upper(); FMT_FALLTHROUGH; + case 'b': return parse_presentation_type(pres::bin, integral_set); + case 'E': specs.set_upper(); FMT_FALLTHROUGH; + case 'e': return parse_presentation_type(pres::exp, float_set); + case 'F': specs.set_upper(); FMT_FALLTHROUGH; + case 'f': return parse_presentation_type(pres::fixed, float_set); + case 'G': specs.set_upper(); FMT_FALLTHROUGH; + case 'g': return parse_presentation_type(pres::general, float_set); + case 'A': specs.set_upper(); FMT_FALLTHROUGH; + case 'a': return parse_presentation_type(pres::hexfloat, float_set); + case 'c': + if (arg_type == type::bool_type) report_error("invalid format specifier"); + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + report_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + report_error("invalid fill character '{'"); + return begin; + } + auto alignment = parse_align(to_ascii(*fill_end)); + enter_state(state::align, alignment != align::none); + specs.set_fill( + basic_string_view(begin, to_unsigned(fill_end - begin))); + specs.set_align(alignment); + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*begin); + } +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, + const Char* end, + Handler&& handler) + -> const Char* { + ++begin; + if (begin == end) { + handler.on_error("invalid format string"); + return end; + } + int arg_id = 0; + switch (*begin) { + case '}': + handler.on_replacement_field(handler.on_arg_id(), begin); + return begin + 1; + case '{': handler.on_text(begin, begin + 1); return begin + 1; + case ':': arg_id = handler.on_arg_id(); break; + default: { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + } adapter = {handler, 0}; + begin = parse_arg_id(begin, end, adapter); + arg_id = adapter.arg_id; + Char c = begin != end ? *begin : Char(); + if (c == '}') { + handler.on_replacement_field(arg_id, begin); + return begin + 1; + } + if (c != ':') { + handler.on_error("missing '}' in format string"); + return end; + } + break; + } + } + begin = handler.on_format_specs(arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; + return begin + 1; +} + +template +FMT_CONSTEXPR void parse_format_string(basic_string_view fmt, + Handler&& handler) { + auto begin = fmt.data(), end = begin + fmt.size(); + auto p = begin; + while (p != end) { + auto c = *p++; + if (c == '{') { + handler.on_text(begin, p - 1); + begin = p = parse_replacement_field(p - 1, end, handler); + } else if (c == '}') { + if (p == end || *p != '}') + return handler.on_error("unmatched '}' in format string"); + handler.on_text(begin, p); + begin = ++p; + } + } + handler.on_text(begin, end); +} + +// Checks char specs and returns true iff the presentation type is char-like. +FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool { + auto type = specs.type(); + if (type != presentation_type::none && type != presentation_type::chr && + type != presentation_type::debug) { + return false; + } + if (specs.align() == align::numeric || specs.sign() != sign::none || + specs.alt()) { + report_error("invalid format specifier for char"); + } + return true; +} + +// A base class for compile-time strings. +struct compile_string {}; + +template +FMT_VISIBILITY("hidden") // Suppress an ld warning on macOS (#3769). +FMT_CONSTEXPR auto invoke_parse(parse_context& ctx) -> const Char* { + using mapped_type = remove_cvref_t>; + constexpr bool formattable = + std::is_constructible>::value; + if (!formattable) return ctx.begin(); // Error is reported in the value ctor. + using formatted_type = conditional_t; + return formatter().parse(ctx); +} + +template struct arg_pack {}; + +template +class format_string_checker { + private: + type types_[max_of(1, NUM_ARGS)]; + named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; + compile_parse_context context_; + + using parse_func = auto (*)(parse_context&) -> const Char*; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + + public: + template + FMT_CONSTEXPR explicit format_string_checker(basic_string_view fmt, + arg_pack) + : types_{mapped_type_constant::value...}, + named_args_{}, + context_(fmt, NUM_ARGS, types_), + parse_funcs_{&invoke_parse...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_static_named_arg(named_args_, arg_index, named_arg_index)); + ignore_unused(arg_index, named_arg_index); + } + + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + + FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + context_.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + for (int i = 0; i < NUM_NAMED_ARGS; ++i) { + if (named_args_[i].name == id) return named_args_[i].id; + } + if (!DYNAMIC_NAMES) on_error("argument not found"); + return -1; + } + + FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { + on_format_specs(id, begin, begin); // Call parse() on empty specs. + } + + FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + context_.advance_to(begin); + if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); + while (begin != end && *begin != '}') ++begin; + return begin; + } + + FMT_NORETURN FMT_CONSTEXPR void on_error(const char* message) { + report_error(message); + } +}; + +/// A contiguous memory buffer with an optional growing ability. It is an +/// internal class and shouldn't be used directly, only via `memory_buffer`. +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + using grow_fun = void (*)(buffer& buf, size_t capacity); + grow_fun grow_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_MSC_WARNING(suppress : 26495) + FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept + : size_(sz), capacity_(sz), grow_(grow) {} + + constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0, + size_t cap = 0) noexcept + : ptr_(p), size_(sz), capacity_(cap), grow_(grow) {} + + FMT_CONSTEXPR20 ~buffer() = default; + buffer(buffer&&) = default; + + /// Sets the buffer data and capacity. + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + auto begin() noexcept -> T* { return ptr_; } + auto end() noexcept -> T* { return ptr_ + size_; } + + auto begin() const noexcept -> const T* { return ptr_; } + auto end() const noexcept -> const T* { return ptr_ + size_; } + + /// Returns the size of this buffer. + constexpr auto size() const noexcept -> size_t { return size_; } + + /// Returns the capacity of this buffer. + constexpr auto capacity() const noexcept -> size_t { return capacity_; } + + /// Returns a pointer to the buffer data (not null-terminated). + FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } + FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } + + /// Clears this buffer. + FMT_CONSTEXPR void clear() { size_ = 0; } + + // Tries resizing the buffer to contain `count` elements. If T is a POD type + // the new elements may not be initialized. + FMT_CONSTEXPR void try_resize(size_t count) { + try_reserve(count); + size_ = min_of(count, capacity_); + } + + // Tries increasing the buffer capacity to `new_capacity`. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + FMT_CONSTEXPR void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow_(*this, new_capacity); + } + + FMT_CONSTEXPR void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /// Appends data to the end of the buffer. + template +// Workaround for MSVC2019 to fix error C2893: Failed to specialize function +// template 'void fmt::v11::detail::buffer::append(const U *,const U *)'. +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940 + FMT_CONSTEXPR20 +#endif + void + append(const U* begin, const U* end) { + while (begin != end) { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + // A loop is faster than memcpy on small sizes. + T* out = ptr_ + size_; + for (size_t i = 0; i < count; ++i) out[i] = begin[i]; + size_ += count; + begin += count; + } + } + + template FMT_CONSTEXPR auto operator[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { + return ptr_[index]; + } +}; + +struct buffer_traits { + constexpr explicit buffer_traits(size_t) {} + constexpr auto count() const -> size_t { return 0; } + constexpr auto limit(size_t size) const -> size_t { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + constexpr auto count() const -> size_t { return count_; } + FMT_CONSTEXPR auto limit(size_t size) -> size_t { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return min_of(size, n); + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buffer_size) static_cast(buf).flush(); + } + + void flush() { + auto size = this->size(); + this->clear(); + const T* begin = data_; + const T* end = begin + this->limit(size); + while (begin != end) *out_++ = *begin++; + } + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), buffer(grow, data_, 0, buffer_size), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : Traits(other), + buffer(grow, data_, 0, buffer_size), + out_(other.out_) {} + ~iterator_buffer() { + // Don't crash if flush fails during unwinding. + FMT_TRY { flush(); } + FMT_CATCH(...) {} + } + + auto out() -> OutputIt { + flush(); + return out_; + } + auto count() const -> size_t { return Traits::count() + this->size(); } +}; + +template +class iterator_buffer : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) + static_cast(buf).flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(grow, out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) noexcept + : fixed_buffer_traits(other), + buffer(static_cast(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + +template class iterator_buffer : public buffer { + public: + explicit iterator_buffer(T* out, size_t = 0) + : buffer([](buffer&, size_t) {}, out, 0, ~size_t()) {} + + auto out() -> T* { return &*this->end(); } +}; + +template +class container_buffer : public buffer { + private: + using value_type = typename Container::value_type; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t capacity) { + auto& self = static_cast(buf); + self.container.resize(capacity); + self.set(&self.container[0], capacity); + } + + public: + Container& container; + + explicit container_buffer(Container& c) + : buffer(grow, c.size()), container(c) {} +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer< + OutputIt, + enable_if_t::value && + is_contiguous::value, + typename OutputIt::container_type::value_type>> + : public container_buffer { + private: + using base = container_buffer; + + public: + explicit iterator_buffer(typename OutputIt::container_type& c) : base(c) {} + explicit iterator_buffer(OutputIt out, size_t = 0) + : base(get_container(out)) {} + + auto out() -> OutputIt { return OutputIt(this->container); } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + static FMT_CONSTEXPR void grow(buffer& buf, size_t) { + if (buf.size() != buffer_size) return; + static_cast(buf).count_ += buf.size(); + buf.clear(); + } + + public: + FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} + + constexpr auto count() const noexcept -> size_t { + return count_ + this->size(); + } +}; + +template +struct is_back_insert_iterator> : std::true_type {}; + +template +struct has_back_insert_iterator_container_append : std::false_type {}; +template +struct has_back_insert_iterator_container_append< + OutputIt, InputIt, + void_t()) + .append(std::declval(), + std::declval()))>> : std::true_type {}; + +// An optimized version of std::copy with the output value type (T). +template ::value&& + has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + get_container(out).append(begin, end); + return out; +} + +template ::value && + !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value)> +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { + auto& c = get_container(out); + c.insert(c.end(), begin, end); + return out; +} + +template ::value)> +FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { + while (begin != end) *out++ = static_cast(*begin++); + return out; +} + +template +FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { + return copy(s.begin(), s.end(), out); +} + +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + +// Maps an output iterator to a buffer. +template ::value)> +auto get_buffer(OutputIt out) -> iterator_buffer { + return iterator_buffer(out); +} +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { + return get_container(out); +} + +template +auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { + return buf.out(); +} +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; +} + +// This type is intentionally undefined, only used for errors. +template struct type_is_unformattable_for; + +template struct string_value { + const Char* data; + size_t size; + auto str() const -> basic_string_view { return {data, size}; } +}; + +template struct custom_value { + using char_type = typename Context::char_type; + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +struct custom_tag {}; + +#if !FMT_BUILTIN_TYPES +# define FMT_BUILTIN , monostate +#else +# define FMT_BUILTIN +#endif + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + monostate no_value; + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_opt int128_value; + uint128_opt uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value() : no_value() {} + constexpr FMT_INLINE value(signed char x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned char x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(signed short x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned short x FMT_BUILTIN) : uint_value(x) {} + constexpr FMT_INLINE value(int x) : int_value(x) {} + constexpr FMT_INLINE value(unsigned x FMT_BUILTIN) : uint_value(x) {} + FMT_CONSTEXPR FMT_INLINE value(long x FMT_BUILTIN) : value(long_type(x)) {} + FMT_CONSTEXPR FMT_INLINE value(unsigned long x FMT_BUILTIN) + : value(ulong_type(x)) {} + constexpr FMT_INLINE value(long long x FMT_BUILTIN) : long_long_value(x) {} + constexpr FMT_INLINE value(unsigned long long x FMT_BUILTIN) + : ulong_long_value(x) {} + FMT_INLINE value(int128_opt x FMT_BUILTIN) : int128_value(x) {} + FMT_INLINE value(uint128_opt x FMT_BUILTIN) : uint128_value(x) {} + constexpr FMT_INLINE value(bool x FMT_BUILTIN) : bool_value(x) {} + + template + constexpr FMT_INLINE value(bitint x FMT_BUILTIN) : long_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + template + constexpr FMT_INLINE value(ubitint x FMT_BUILTIN) : ulong_long_value(x) { + static_assert(N <= 64, "unsupported _BitInt"); + } + + template ::value)> + constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + } + + constexpr FMT_INLINE value(float x FMT_BUILTIN) : float_value(x) {} + constexpr FMT_INLINE value(double x FMT_BUILTIN) : double_value(x) {} + FMT_INLINE value(long double x FMT_BUILTIN) : long_double_value(x) {} + + FMT_CONSTEXPR FMT_INLINE value(char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + FMT_CONSTEXPR FMT_INLINE value(const char_type* x FMT_BUILTIN) { + string.data = x; + if (is_constant_evaluated()) string.size = 0; + } + template , + FMT_ENABLE_IF(!std::is_pointer::value)> + FMT_CONSTEXPR value(const T& x FMT_BUILTIN) { + static_assert(std::is_same::value, + "mixing character types is disallowed"); + auto sv = to_string_view(x); + string.data = sv.data(); + string.size = sv.size(); + } + FMT_INLINE value(void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(const void* x FMT_BUILTIN) : pointer(x) {} + FMT_INLINE value(volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(const volatile void* x FMT_BUILTIN) + : pointer(const_cast(x)) {} + FMT_INLINE value(nullptr_t) : pointer(nullptr) {} + + template ::value || + std::is_member_pointer::value)> + value(const T&) { + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` printed as bool by iostreams. + static_assert(sizeof(T) == 0, + "formatting of non-void pointers is disallowed"); + } + + template ::value)> + value(const T& x) : value(format_as(x)) {} + template ::value)> + value(const T& x) : value(formatter::format_as(x)) {} + + template ::value)> + value(const T& named_arg) : value(named_arg.value) {} + + template ::value || !FMT_BUILTIN_TYPES)> + FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {} + + FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + private: + template ())> + FMT_CONSTEXPR value(T& x, custom_tag) { + using value_type = remove_const_t; + // T may overload operator& e.g. std::vector::reference in libc++. + if (!is_constant_evaluated()) { + custom.value = + const_cast(&reinterpret_cast(x)); + } else { + custom.value = nullptr; +#if defined(__cpp_if_constexpr) + if constexpr (std::is_same*>::value) + custom.value = const_cast(&x); +#endif + } + custom.format = format_custom>; + } + + template ())> + FMT_CONSTEXPR value(const T&, custom_tag) { + // Cannot format an argument; to make type T formattable provide a + // formatter specialization: https://fmt.dev/latest/api.html#udt. + type_is_unformattable_for _; + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom(void* arg, parse_context& parse_ctx, + Context& ctx) { + auto f = Formatter(); + parse_ctx.advance_to(f.parse(parse_ctx)); + using qualified_type = + conditional_t(), const T, T>; + // format must be const for compatibility with std::format and compilation. + const auto& cf = f; + ctx.advance_to(cf.format(*static_cast(arg), ctx)); + } +}; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; + +template +struct is_output_iterator : std::false_type {}; + +template <> struct is_output_iterator : std::true_type {}; + +template +struct is_output_iterator< + It, T, + enable_if_t&>()++), + T>::value>> : std::true_type {}; + +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to an std::locale to avoid a heavy include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + template locale_ref(const Locale& loc); + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + +template constexpr auto encode_types() -> unsigned long long { + return 0; +} + +template +constexpr auto encode_types() -> unsigned long long { + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +constexpr auto make_descriptor() -> unsigned long long { + return NUM_ARGS <= max_packed_args ? encode_types() + : is_unpacked_bit | NUM_ARGS; +} + +template +using arg_t = conditional_t, + basic_format_arg>; + +template +struct named_arg_store { + // args_[0].named_args points to named_args to avoid bloating format_args. + arg_t args[1 + NUM_ARGS]; + named_arg_info named_args[NUM_NAMED_ARGS]; + + template + FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) + : args{{named_args, NUM_NAMED_ARGS}, values...} { + int arg_index = 0, named_arg_index = 0; + FMT_APPLY_VARIADIC( + init_named_arg(named_args, arg_index, named_arg_index, values)); + } + + named_arg_store(named_arg_store&& rhs) { + args[0] = {named_args, NUM_NAMED_ARGS}; + for (size_t i = 1; i < sizeof(args) / sizeof(*args); ++i) + args[i] = rhs.args[i]; + for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) + named_args[i] = rhs.named_args[i]; + } + + named_arg_store(const named_arg_store& rhs) = delete; + named_arg_store& operator=(const named_arg_store& rhs) = delete; + named_arg_store& operator=(named_arg_store&& rhs) = delete; + operator const arg_t*() const { return args + 1; } +}; + +// An array of references to arguments. It can be implicitly converted to +// `basic_format_args` for passing into type-erased formatting functions +// such as `vformat`. It is a plain struct to reduce binary size in debug mode. +template +struct format_arg_store { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + using type = + conditional_t[max_of(1, NUM_ARGS)], + named_arg_store>; + type args; +}; + +// TYPE can be different from type_constant, e.g. for __float128. +template struct native_formatter { + private: + dynamic_format_specs specs_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); + if (const_check(TYPE == type::char_type)) check_char_specs(specs_); + return end; + } + + template + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.set_type(set ? presentation_type::debug : presentation_type::none); + } + + FMT_PRAGMA_CLANG(diagnostic ignored "-Wundefined-inline") + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template +struct locking + : bool_constant::value == type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + +FMT_API void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc = {}); + +#if FMT_WIN32 +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +#else // format_args is passed by reference since it is defined later. +inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} +#endif +} // namespace detail + +// The main public API. + +template +FMT_CONSTEXPR void parse_context::do_check_arg_id(int arg_id) { + // Argument id is only checked at compile time during parsing because + // formatting has its own validation. + if (detail::is_constant_evaluated() && use_constexpr_cast) { + auto ctx = static_cast*>(this); + if (arg_id >= ctx->num_args()) report_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void parse_context::check_dynamic_spec(int arg_id) { + using detail::compile_parse_context; + if (detail::is_constant_evaluated() && use_constexpr_cast) + static_cast*>(this)->check_dynamic_spec(arg_id); +} + +FMT_BEGIN_EXPORT + +// An output iterator that appends to a buffer. It is used instead of +// back_insert_iterator to reduce symbol sizes and avoid dependency. +template class basic_appender { + protected: + detail::buffer* container; + + public: + using container_type = detail::buffer; + + FMT_CONSTEXPR basic_appender(detail::buffer& buf) : container(&buf) {} + + FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { + container->push_back(c); + return *this; + } + FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } +}; + +// A formatting argument. Context is a template parameter for the compiled API +// where output can be unbuffered. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + friend class basic_format_args; + + using char_type = typename Context::char_type; + + public: + class handle { + private: + detail::custom_value custom_; + + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(parse_context& parse_ctx, Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + template + basic_format_arg(T&& val) + : value_(val), type_(detail::stored_type_constant::value) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + auto type() const -> detail::type { return type_; } + + /** + * Visits an argument dispatching to the appropriate visit method based on + * the argument type. For example, if the argument type is `double` then + * `vis(value)` will be called with the value of type `double`. + */ + template + FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { + using detail::map; + switch (type_) { + case detail::type::none_type: break; + case detail::type::int_type: return vis(value_.int_value); + case detail::type::uint_type: return vis(value_.uint_value); + case detail::type::long_long_type: return vis(value_.long_long_value); + case detail::type::ulong_long_type: return vis(value_.ulong_long_value); + case detail::type::int128_type: return vis(map(value_.int128_value)); + case detail::type::uint128_type: return vis(map(value_.uint128_value)); + case detail::type::bool_type: return vis(value_.bool_value); + case detail::type::char_type: return vis(value_.char_value); + case detail::type::float_type: return vis(value_.float_value); + case detail::type::double_type: return vis(value_.double_value); + case detail::type::long_double_type: return vis(value_.long_double_value); + case detail::type::cstring_type: return vis(value_.string.data); + case detail::type::string_type: return vis(value_.string.str()); + case detail::type::pointer_type: return vis(value_.pointer); + case detail::type::custom_type: return vis(handle(value_.custom)); + } + return vis(monostate()); + } + + auto format_custom(const char_type* parse_begin, + parse_context& parse_ctx, Context& ctx) + -> bool { + if (type_ != detail::type::custom_type) return false; + parse_ctx.advance_to(parse_begin); + value_.custom.format(value_.custom.value, parse_ctx, ctx); + return true; + } +}; + +/** + * A view of a collection of formatting arguments. To avoid lifetime issues it + * should only be used as a parameter type in type-erased functions such as + * `vformat`: + * + * void vlog(fmt::string_view fmt, fmt::format_args args); // OK + * fmt::format_args args = fmt::make_format_args(); // Dangling reference + */ +template class basic_format_args { + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const basic_format_arg* args_; + }; + + constexpr auto is_packed() const -> bool { + return (desc_ & detail::is_unpacked_bit) == 0; + } + constexpr auto has_named_args() const -> bool { + return (desc_ & detail::has_named_args_bit) != 0; + } + + FMT_CONSTEXPR auto type(int index) const -> detail::type { + int shift = index * detail::packed_arg_bits; + unsigned mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + template + using store = + detail::format_arg_store; + + public: + using format_arg = basic_format_arg; + + constexpr basic_format_args() : desc_(0), args_(nullptr) {} + + /// Constructs a `basic_format_args` object from `format_arg_store`. + template + constexpr FMT_ALWAYS_INLINE basic_format_args( + const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + values_(s.args) {} + + template detail::max_packed_args)> + constexpr basic_format_args(const store& s) + : desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)), + args_(s.args) {} + + /// Constructs a `basic_format_args` object from a dynamic list of arguments. + constexpr basic_format_args(const format_arg* args, int count, + bool has_named = false) + : desc_(detail::is_unpacked_bit | detail::to_unsigned(count) | + (has_named ? +detail::has_named_args_bit : 0)), + args_(args) {} + + /// Returns the argument with the specified id. + FMT_CONSTEXPR auto get(int id) const -> format_arg { + auto arg = format_arg(); + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (static_cast(id) >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; + return arg; + } + + template + auto get(basic_string_view name) const -> format_arg { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template + FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + auto max_size() const -> int { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +// A formatting context. +class context { + private: + appender out_; + format_args args_; + FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + + public: + /// The character type for the output. + using char_type = char; + + using iterator = appender; + using format_arg = basic_format_arg; + using parse_context_type FMT_DEPRECATED = parse_context<>; + template using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; + + /// Constructs a `context` object. References to the arguments are stored + /// in the object so make sure they have appropriate lifetimes. + FMT_CONSTEXPR context(iterator out, format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + context(context&&) = default; + context(const context&) = delete; + void operator=(const context&) = delete; + + FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } + inline auto arg(string_view name) const -> format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(string_view name) const -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() const -> iterator { return out_; } + + // Advances the begin iterator to `it`. + FMT_CONSTEXPR void advance_to(iterator) {} + + FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } +}; + +template struct runtime_format_string { + basic_string_view str; +}; + +/** + * Creates a runtime format string. + * + * **Example**: + * + * // Check format string at runtime instead of compile-time. + * fmt::print(fmt::runtime("{:d}"), "I am not a number"); + */ +inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } + +/// A compile-time format string. Use `format_string` in the public API to +/// prevent type deduction. +template struct fstring { + private: + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + string_view str; + using t = fstring; + + // Reports a compile-time error if S is not a valid format string for T. + template + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { + using namespace detail; + static_assert(count<(std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) { + auto sv = string_view(str); + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(sv, checker(sv, arg_pack())); +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert( + FMT_USE_CONSTEVAL && sizeof(s) != 0, + "FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING"); +#endif + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE fstring(const S&) : str(S()) { + FMT_CONSTEXPR auto sv = string_view(S()); + FMT_CONSTEXPR int unused = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(unused); + } + fstring(runtime_format_string<> fmt) : str(fmt.str) {} + + // Returning by reference generates better code in debug mode. + FMT_ALWAYS_INLINE operator const string_view&() const { return str; } + auto get() const -> string_view { return str; } +}; + +template using format_string = typename fstring::t; + +template +using is_formattable = bool_constant::value, int*, T>, Char>, + void>::value>; +#ifdef __cpp_concepts +template +concept formattable = is_formattable, Char>::value; +#endif + +template +using has_formatter FMT_DEPRECATED = std::is_constructible>; + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> + : detail::native_formatter::value> { +}; + +/** + * Constructs an object that stores references to arguments and can be + * implicitly converted to `format_args`. `Context` can be omitted in which case + * it defaults to `context`. See `arg` for lifetime considerations. + */ +// Take arguments by lvalue references to avoid some lifetime issues, e.g. +// auto args = make_format_args(std::string()); +template (), + unsigned long long DESC = detail::make_descriptor()> +constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) + -> detail::format_arg_store { + // Suppress warnings for pathological types convertible to detail::value. + FMT_PRAGMA_GCC(diagnostic ignored "-Wconversion") + return {{args...}}; +} + +template +using vargs = + detail::format_arg_store(), + detail::make_descriptor()>; + +/** + * Returns a named argument to be used in a formatting function. + * It should only be used in a call to a formatting function. + * + * **Example**: + * + * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + return {name, arg}; +} + +/// Formats a string and writes the output to `out`. +template , + char>::value)> +auto vformat_to(OutputIt&& out, string_view fmt, format_args args) + -> remove_cvref_t { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); +} + +/** + * Formats `args` according to specifications in `fmt`, writes the result to + * the output iterator `out` and returns the iterator past the end of the output + * range. `format_to` does not append a terminating null character. + * + * **Example**: + * + * auto out = std::vector(); + * fmt::format_to(std::back_inserter(out), "{}", 42); + */ +template , + char>::value)> +FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) + -> remove_cvref_t { + return vformat_to(out, fmt.str, vargs{{args...}}); +} + +template struct format_to_n_result { + /// Iterator past the end of the output range. + OutputIt out; + /// Total (not truncated) output size. + size_t size; +}; + +template ::value)> +auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args, {}); + return {buf.out(), buf.count()}; +} + +/** + * Formats `args` according to specifications in `fmt`, writes up to `n` + * characters of the result to the output iterator `out` and returns the total + * (not truncated) output size and the iterator past the end of the output + * range. `format_to_n` does not append a terminating null character. + */ +template ::value)> +FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, + T&&... args) -> format_to_n_result { + return vformat_to_n(out, n, fmt.str, vargs{{args...}}); +} + +struct format_to_result { + /// Pointer to just after the last successful write in the array. + char* out; + /// Specifies if the output was truncated. + bool truncated; + + FMT_CONSTEXPR operator char*() const { + // Report truncation to prevent silent data loss. + if (truncated) report_error("output is truncated"); + return out; + } +}; + +template +auto vformat_to(char (&out)[N], string_view fmt, format_args args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt, args); + return {result.out, result.size > N}; +} + +template +FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) + -> format_to_result { + auto result = vformat_to_n(out, N, fmt.str, vargs{{args...}}); + return {result.out, result.size > N}; +} + +/// Returns the number of chars in the output of `format(fmt, args...)`. +template +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { + auto buf = detail::counting_buffer<>(); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, {}); + return buf.count(); +} + +FMT_API void vprint(string_view fmt, format_args args); +FMT_API void vprint(FILE* f, string_view fmt, format_args args); +FMT_API void vprintln(FILE* f, string_view fmt, format_args args); +FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::print("The answer is {}.", 42); + */ +template +FMT_INLINE void print(format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(stdout, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(stdout, fmt.str, va) + : vprint(fmt.str, va); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the + * output to the file `f`. + * + * **Example**: + * + * fmt::print(stderr, "Don't {}!", "panic"); + */ +template +FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + if (detail::const_check(!detail::use_utf8)) + return detail::vprint_mojibake(f, fmt.str, va, false); + return detail::is_locking() ? vprint_buffered(f, fmt.str, va) + : vprint(f, fmt.str, va); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to the file `f` followed by a newline. +template +FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { + vargs va = {{args...}}; + return detail::const_check(detail::use_utf8) + ? vprintln(f, fmt.str, va) + : detail::vprint_mojibake(f, fmt.str, va, true); +} + +/// Formats `args` according to specifications in `fmt` and writes the output +/// to `stdout` followed by a newline. +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, static_cast(args)...); +} + +FMT_END_EXPORT +FMT_PRAGMA_CLANG(diagnostic pop) +FMT_PRAGMA_GCC(pop_options) +FMT_END_NAMESPACE + +#ifdef FMT_HEADER_ONLY +# include "format.h" +#endif +#endif // FMT_BASE_H_ diff --git a/src/3rdparty/fmt/chrono.h b/src/3rdparty/fmt/chrono.h index 9d54574e16..50c777c841 100644 --- a/src/3rdparty/fmt/chrono.h +++ b/src/3rdparty/fmt/chrono.h @@ -8,52 +8,37 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#include -#include -#include // std::isfinite -#include // std::memcpy -#include -#include -#include -#include -#include +#ifndef FMT_MODULE +# include +# include +# include // std::isfinite +# include // std::memcpy +# include +# include +# include +# include +# include +#endif -#include "ostream.h" // formatbuf +#include "format.h" + +namespace fmt_detail { +struct time_zone { + template + auto to_sys(T) + -> std::chrono::time_point { + return {}; + } +}; +template inline auto current_zone(T...) -> time_zone* { + return nullptr; +} + +template inline void _tzset(T...) {} +} // namespace fmt_detail FMT_BEGIN_NAMESPACE -// Check if std::chrono::local_t is available. -#ifndef FMT_USE_LOCAL_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_LOCAL_TIME 0 -# endif -#endif - -// Check if std::chrono::utc_timestamp is available. -#ifndef FMT_USE_UTC_TIME -# ifdef __cpp_lib_chrono -# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) -# else -# define FMT_USE_UTC_TIME 0 -# endif -#endif - -// Enable tzset. -#ifndef FMT_USE_TZSET -// UWP doesn't provide _tzset. -# if FMT_HAS_INCLUDE("winapifamily.h") -# include -# endif -# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ - (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# define FMT_USE_TZSET 1 -# else -# define FMT_USE_TZSET 0 -# endif -#endif - // Enable safe chrono durations, unless explicitly disabled. #ifndef FMT_SAFE_DURATION_CAST # define FMT_SAFE_DURATION_CAST 1 @@ -94,10 +79,8 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) return static_cast(from); } -/** - * converts From to To, without loss. If the dynamic value of from - * can't be converted to To without loss, ec is set. - */ +/// Converts From to To, without loss. If the dynamic value of from +/// can't be converted to To without loss, ec is set. template ::value && std::numeric_limits::is_signed != @@ -185,61 +168,7 @@ FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { return from; } -/** - * safe duration cast between integral durations - */ -template ::value), - FMT_ENABLE_IF(std::is_integral::value)> -auto safe_duration_cast(std::chrono::duration from, - int& ec) -> To { - using From = std::chrono::duration; - ec = 0; - // the basic idea is that we need to convert from count() in the from type - // to count() in the To type, by multiplying it with this: - struct Factor - : std::ratio_divide {}; - - static_assert(Factor::num > 0, "num must be positive"); - static_assert(Factor::den > 0, "den must be positive"); - - // the conversion is like this: multiply from.count() with Factor::num - // /Factor::den and convert it to To::rep, all this without - // overflow/underflow. let's start by finding a suitable type that can hold - // both To, From and Factor::num - using IntermediateRep = - typename std::common_type::type; - - // safe conversion to IntermediateRep - IntermediateRep count = - lossless_integral_conversion(from.count(), ec); - if (ec) return {}; - // multiply with Factor::num without overflow or underflow - if (detail::const_check(Factor::num != 1)) { - const auto max1 = detail::max_value() / Factor::num; - if (count > max1) { - ec = 1; - return {}; - } - const auto min1 = - (std::numeric_limits::min)() / Factor::num; - if (detail::const_check(!std::is_unsigned::value) && - count < min1) { - ec = 1; - return {}; - } - count *= Factor::num; - } - - if (detail::const_check(Factor::den != 1)) count /= Factor::den; - auto tocount = lossless_integral_conversion(count, ec); - return ec ? To() : To(tocount); -} - -/** - * safe duration_cast between floating point durations - */ +/// Safe duration_cast between floating point durations template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> @@ -318,17 +247,94 @@ auto safe_duration_cast(std::chrono::duration from, } // namespace safe_duration_cast #endif +namespace detail { + +// Check if std::chrono::utc_time is available. +#ifdef FMT_USE_UTC_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_UTC_TIME 0 +#endif +#if FMT_USE_UTC_TIME +using utc_clock = std::chrono::utc_clock; +#else +struct utc_clock { + template void to_sys(T); +}; +#endif + +// Check if std::chrono::local_time is available. +#ifdef FMT_USE_LOCAL_TIME +// Use the provided definition. +#elif defined(__cpp_lib_chrono) +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +#else +# define FMT_USE_LOCAL_TIME 0 +#endif +#if FMT_USE_LOCAL_TIME +using local_t = std::chrono::local_t; +#else +struct local_t {}; +#endif + +} // namespace detail + +template +using sys_time = std::chrono::time_point; + +template +using utc_time = std::chrono::time_point; + +template +using local_time = std::chrono::time_point; + +namespace detail { + // Prevents expansion of a preceding token as a function-style macro. // Usage: f FMT_NOMACRO() #define FMT_NOMACRO -namespace detail { template struct 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<>(); } +// It is defined here and not in ostream.h because the latter has expensive +// includes. +template class formatbuf : public StreamBuf { + private: + using char_type = typename StreamBuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename StreamBuf::int_type; + using traits_type = typename StreamBuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& 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(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; @@ -341,20 +347,16 @@ template struct codecvt_result { }; template -void write_codecvt(codecvt_result& out, string_view in_buf, +void write_codecvt(codecvt_result& out, string_view in, const std::locale& loc) { -#if FMT_CLANG_VERSION -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated" + FMT_PRAGMA_CLANG(diagnostic push) + FMT_PRAGMA_CLANG(diagnostic ignored "-Wdeprecated") auto& f = std::use_facet>(loc); -# pragma clang diagnostic pop -#else - auto& f = std::use_facet>(loc); -#endif + FMT_PRAGMA_CLANG(diagnostic pop) auto mb = std::mbstate_t(); const char* from_next = nullptr; - auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, - std::begin(out.buf), std::end(out.buf), out.end); + auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf), + std::end(out.buf), out.end); if (result != std::codecvt_base::ok) FMT_THROW(format_error("failed to format time")); } @@ -362,11 +364,12 @@ void write_codecvt(codecvt_result& out, string_view in_buf, template auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -> OutputIt { - if (detail::is_utf8() && loc != get_classic_locale()) { + if (const_check(detail::use_utf8) && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. -#if FMT_MSC_VERSION != 0 || \ - (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) +#if FMT_MSC_VERSION != 0 || \ + (defined(__GLIBCXX__) && \ + (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // and newer. using code_unit = wchar_t; @@ -382,9 +385,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) to_utf8>(); if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) FMT_THROW(format_error("failed to format time")); - return copy_str(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } - return copy_str(in.data(), in.data() + in.size(), out); + return copy(in.data(), in.data() + in.size(), out); } template OutputIt { codecvt_result unit; write_codecvt(unit, sv, loc); - return copy_str(unit.buf, unit.end, out); + return copy(unit.buf, unit.end, out); } template ::value)> { }; -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { +FMT_NORETURN inline void throw_duration_error() { + FMT_THROW(format_error("cannot format duration")); +} + +// Cast one integral duration to another with an overflow check. +template ::value&& + std::is_integral::value)> +auto duration_cast(std::chrono::duration from) -> To { +#if !FMT_SAFE_DURATION_CAST + return std::chrono::duration_cast(from); +#else + // The conversion factor: to.count() == factor * from.count(). + using factor = std::ratio_divide; + + using common_rep = typename std::common_type::type; + + int ec = 0; + auto count = safe_duration_cast::lossless_integral_conversion( + from.count(), ec); + if (ec) throw_duration_error(); + + // Multiply from.count() by factor and check for overflow. + if (const_check(factor::num != 1)) { + if (count > max_value() / factor::num) throw_duration_error(); + const auto min = (std::numeric_limits::min)() / factor::num; + if (const_check(!std::is_unsigned::value) && count < min) + throw_duration_error(); + count *= factor::num; + } + if (const_check(factor::den != 1)) count /= factor::den; + auto to = + To(safe_duration_cast::lossless_integral_conversion( + count, ec)); + if (ec) throw_duration_error(); + return to; +#endif +} + +template ::value&& + std::is_floating_point::value)> +auto duration_cast(std::chrono::duration 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(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); + if (ec) throw_duration_error(); return to; #else // Standard duration cast, may overflow. @@ -461,54 +504,60 @@ auto fmt_duration_cast(std::chrono::duration from) -> To { template < typename To, typename FromRep, typename FromPeriod, FMT_ENABLE_IF(!is_same_arithmetic_type::value)> -auto fmt_duration_cast(std::chrono::duration from) -> To { +auto duration_cast(std::chrono::duration from) -> To { // Mixed integer <-> float cast is not supported by safe_duration_cast. return std::chrono::duration_cast(from); } template -auto to_time_t( - std::chrono::time_point time_point) - -> std::time_t { +auto to_time_t(sys_time 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>( + return detail::duration_cast>( time_point.time_since_epoch()) .count(); } + +// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without +// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160. +template FMT_CONSTEXPR auto has_current_zone() -> bool { + using namespace std::chrono; + using namespace fmt_detail; + return !std::is_same::value; +} } // namespace detail FMT_BEGIN_EXPORT /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in local time. Unlike ``std::localtime``, this function is - thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in local time. Unlike `std::localtime`, this function is + * thread-safe on most platforms. */ 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) {} + inline dispatcher(std::time_t t) : time_(t) {} - auto run() -> bool { + inline auto run() -> bool { using namespace fmt::detail; return handle(localtime_r(&time_, &tm_)); } - auto handle(std::tm* tm) -> bool { return tm != nullptr; } + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - auto handle(detail::null<>) -> bool { + inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(localtime_s(&tm_, &time_)); } - auto fallback(int res) -> bool { return res == 0; } + inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { + inline auto fallback(detail::null<>) -> bool { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); if (tm) tm_ = *tm; @@ -523,41 +572,43 @@ inline auto localtime(std::time_t time) -> std::tm { } #if FMT_USE_LOCAL_TIME -template +template ())> inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime( - detail::to_time_t(std::chrono::current_zone()->to_sys(time))); + using namespace std::chrono; + using namespace fmt_detail; + return localtime(detail::to_time_t(current_zone()->to_sys(time))); } #endif /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this - function is thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this + * function is thread-safe on most platforms. */ 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) {} + inline dispatcher(std::time_t t) : time_(t) {} - auto run() -> bool { + inline auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } - auto handle(std::tm* tm) -> bool { return tm != nullptr; } + inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - auto handle(detail::null<>) -> bool { + inline auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } - auto fallback(int res) -> bool { return res == 0; } + inline auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - auto fallback(detail::null<>) -> bool { + inline auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; @@ -571,9 +622,7 @@ inline auto gmtime(std::time_t time) -> std::tm { } template -inline auto gmtime( - std::chrono::time_point time_point) - -> std::tm { +inline auto gmtime(sys_time time_point) -> std::tm { return gmtime(detail::to_time_t(time_point)); } @@ -619,7 +668,8 @@ FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; + if (std::is_same::value) + return detail::use_utf8 ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; @@ -646,12 +696,10 @@ enum class numeric_system { // Glibc extensions for formatting numeric values. enum class pad_type { - unspecified, + // Pad a numeric result string with zeros (the default). + zero, // Do not pad a numeric result string. none, - // Pad a numeric result string with zeros even if the conversion specifier - // character uses space-padding by default. - zero, // Pad a numeric result string with spaces. space, }; @@ -659,7 +707,7 @@ enum class pad_type { template auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { if (pad == pad_type::none) return out; - return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); + return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); } template @@ -675,8 +723,8 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; - pad_type pad = pad_type::unspecified; while (ptr != end) { + pad_type pad = pad_type::zero; auto c = *ptr; if (c == '}') break; if (c != '%') { @@ -696,17 +744,11 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, pad = pad_type::none; ++ptr; break; - case '0': - pad = pad_type::zero; - ++ptr; - break; } if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case '%': - handler.on_text(ptr - 1, ptr); - break; + case '%': handler.on_text(ptr - 1, ptr); break; case 'n': { const Char newline[] = {'\n'}; handler.on_text(newline, newline + 1); @@ -718,145 +760,66 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, break; } // Year: - case 'Y': - handler.on_year(numeric_system::standard); - break; - case 'y': - handler.on_short_year(numeric_system::standard); - break; - case 'C': - handler.on_century(numeric_system::standard); - break; - case 'G': - handler.on_iso_week_based_year(); - break; - case 'g': - handler.on_iso_week_based_short_year(); - break; + case 'Y': handler.on_year(numeric_system::standard, pad); break; + case 'y': handler.on_short_year(numeric_system::standard); break; + case 'C': handler.on_century(numeric_system::standard); break; + case 'G': handler.on_iso_week_based_year(); break; + case 'g': handler.on_iso_week_based_short_year(); break; // Day of the week: - case 'a': - handler.on_abbr_weekday(); - break; - case 'A': - handler.on_full_weekday(); - break; - case 'w': - handler.on_dec0_weekday(numeric_system::standard); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::standard); - break; + case 'a': handler.on_abbr_weekday(); break; + case 'A': handler.on_full_weekday(); break; + case 'w': handler.on_dec0_weekday(numeric_system::standard); break; + case 'u': handler.on_dec1_weekday(numeric_system::standard); break; // Month: case 'b': - case 'h': - handler.on_abbr_month(); - break; - case 'B': - handler.on_full_month(); - break; - case 'm': - handler.on_dec_month(numeric_system::standard); - break; + case 'h': handler.on_abbr_month(); break; + case 'B': handler.on_full_month(); break; + case 'm': handler.on_dec_month(numeric_system::standard, pad); break; // Day of the year/month: case 'U': - handler.on_dec0_week_of_year(numeric_system::standard); + handler.on_dec0_week_of_year(numeric_system::standard, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::standard); - break; - case 'V': - handler.on_iso_week_of_year(numeric_system::standard); - break; - case 'j': - handler.on_day_of_year(); - break; - case 'd': - handler.on_day_of_month(numeric_system::standard); + handler.on_dec1_week_of_year(numeric_system::standard, pad); break; + case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad); break; + case 'j': handler.on_day_of_year(pad); break; + case 'd': handler.on_day_of_month(numeric_system::standard, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::standard); + handler.on_day_of_month(numeric_system::standard, pad_type::space); break; // Hour, minute, second: - case 'H': - handler.on_24_hour(numeric_system::standard, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::standard, pad); - break; - case 'M': - handler.on_minute(numeric_system::standard, pad); - break; - case 'S': - handler.on_second(numeric_system::standard, pad); - break; + case 'H': handler.on_24_hour(numeric_system::standard, pad); break; + case 'I': handler.on_12_hour(numeric_system::standard, pad); break; + case 'M': handler.on_minute(numeric_system::standard, pad); break; + case 'S': handler.on_second(numeric_system::standard, pad); break; // Other: - case 'c': - handler.on_datetime(numeric_system::standard); - break; - case 'x': - handler.on_loc_date(numeric_system::standard); - break; - case 'X': - handler.on_loc_time(numeric_system::standard); - break; - case 'D': - handler.on_us_date(); - break; - case 'F': - handler.on_iso_date(); - break; - case 'r': - handler.on_12_hour_time(); - break; - case 'R': - handler.on_24_hour_time(); - break; - case 'T': - handler.on_iso_time(); - break; - case 'p': - handler.on_am_pm(); - break; - case 'Q': - handler.on_duration_value(); - break; - case 'q': - handler.on_duration_unit(); - break; - case 'z': - handler.on_utc_offset(numeric_system::standard); - break; - case 'Z': - handler.on_tz_name(); - break; + case 'c': handler.on_datetime(numeric_system::standard); break; + case 'x': handler.on_loc_date(numeric_system::standard); break; + case 'X': handler.on_loc_time(numeric_system::standard); break; + case 'D': handler.on_us_date(); break; + case 'F': handler.on_iso_date(); break; + case 'r': handler.on_12_hour_time(); break; + case 'R': handler.on_24_hour_time(); break; + case 'T': handler.on_iso_time(); break; + case 'p': handler.on_am_pm(); break; + case 'Q': handler.on_duration_value(); break; + case 'q': handler.on_duration_unit(); break; + case 'z': handler.on_utc_offset(numeric_system::standard); break; + case 'Z': handler.on_tz_name(); break; // Alternative representation: case 'E': { if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case 'Y': - handler.on_year(numeric_system::alternative); - break; - case 'y': - handler.on_offset_year(); - break; - case 'C': - handler.on_century(numeric_system::alternative); - break; - case 'c': - handler.on_datetime(numeric_system::alternative); - break; - case 'x': - handler.on_loc_date(numeric_system::alternative); - break; - case 'X': - handler.on_loc_time(numeric_system::alternative); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + case 'Y': handler.on_year(numeric_system::alternative, pad); break; + case 'y': handler.on_offset_year(); break; + case 'C': handler.on_century(numeric_system::alternative); break; + case 'c': handler.on_datetime(numeric_system::alternative); break; + case 'x': handler.on_loc_date(numeric_system::alternative); break; + case 'X': handler.on_loc_time(numeric_system::alternative); break; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; } @@ -864,54 +827,34 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { - case 'y': - handler.on_short_year(numeric_system::alternative); - break; - case 'm': - handler.on_dec_month(numeric_system::alternative); - break; + case 'y': handler.on_short_year(numeric_system::alternative); break; + case 'm': handler.on_dec_month(numeric_system::alternative, pad); break; case 'U': - handler.on_dec0_week_of_year(numeric_system::alternative); + handler.on_dec0_week_of_year(numeric_system::alternative, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::alternative); + handler.on_dec1_week_of_year(numeric_system::alternative, pad); break; case 'V': - handler.on_iso_week_of_year(numeric_system::alternative); + handler.on_iso_week_of_year(numeric_system::alternative, pad); break; case 'd': - handler.on_day_of_month(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad_type::space); break; - case 'w': - handler.on_dec0_weekday(numeric_system::alternative); - break; - case 'u': - handler.on_dec1_weekday(numeric_system::alternative); - break; - case 'H': - handler.on_24_hour(numeric_system::alternative, pad); - break; - case 'I': - handler.on_12_hour(numeric_system::alternative, pad); - break; - case 'M': - handler.on_minute(numeric_system::alternative, pad); - break; - case 'S': - handler.on_second(numeric_system::alternative, pad); - break; - case 'z': - handler.on_utc_offset(numeric_system::alternative); - break; - default: - FMT_THROW(format_error("invalid format")); + case 'w': handler.on_dec0_weekday(numeric_system::alternative); break; + case 'u': handler.on_dec1_weekday(numeric_system::alternative); break; + case 'H': handler.on_24_hour(numeric_system::alternative, pad); break; + case 'I': handler.on_12_hour(numeric_system::alternative, pad); break; + case 'M': handler.on_minute(numeric_system::alternative, pad); break; + case 'S': handler.on_second(numeric_system::alternative, pad); break; + case 'z': handler.on_utc_offset(numeric_system::alternative); break; + default: FMT_THROW(format_error("invalid format")); } break; - default: - FMT_THROW(format_error("invalid format")); + default: FMT_THROW(format_error("invalid format")); } begin = ptr; } @@ -923,7 +866,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void unsupported() { static_cast(this)->unsupported(); } - FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_year(numeric_system, pad_type) { unsupported(); } FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_offset_year() { unsupported(); } FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } @@ -935,13 +878,20 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } - FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_year() { unsupported(); } - FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_day_of_year(pad_type) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { + unsupported(); + } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } @@ -962,11 +912,13 @@ template struct null_chrono_spec_handler { }; struct tm_format_checker : null_chrono_spec_handler { - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + FMT_NORETURN inline void unsupported() { + FMT_THROW(format_error("no format")); + } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_short_year(numeric_system) {} FMT_CONSTEXPR void on_offset_year() {} FMT_CONSTEXPR void on_century(numeric_system) {} @@ -978,13 +930,12 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_full_month() {} - FMT_CONSTEXPR void on_dec_month(numeric_system) {} - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_day_of_month(numeric_system) {} - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_dec_month(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} 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) {} @@ -1040,15 +991,14 @@ template struct has_member_data_tm_zone> : std::true_type {}; -#if FMT_USE_TZSET inline void tzset_once() { - static bool init = []() -> bool { + static bool init = []() { + using namespace fmt_detail; _tzset(); - return true; + return false; }(); ignore_unused(init); } -#endif // Converts value to Int and checks that it's in the range [0, upper). template ::value)> @@ -1061,9 +1011,10 @@ inline auto to_nonnegative_int(T value, Int upper) -> Int { } template ::value)> inline auto to_nonnegative_int(T value, Int upper) -> Int { - if (value < 0 || value > static_cast(upper)) + auto int_value = static_cast(value); + if (int_value < 0 || value > static_cast(upper)) FMT_THROW(format_error("invalid value")); - return static_cast(value); + return int_value; } constexpr auto pow10(std::uint32_t n) -> long long { @@ -1098,16 +1049,16 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { using subsecond_precision = std::chrono::duration< typename std::common_type::type, - std::ratio<1, detail::pow10(num_fractional_digits)>>; + std::ratio<1, pow10(num_fractional_digits)>>; - const auto fractional = d - fmt_duration_cast(d); + const auto fractional = d - detail::duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() - : fmt_duration_cast(fractional).count(); + : detail::duration_cast(fractional).count(); auto n = static_cast>(subseconds); - const int num_digits = detail::count_digits(n); + const int num_digits = count_digits(n); int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); if (precision < 0) { @@ -1115,22 +1066,25 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { if (std::ratio_less::value) { *out++ = '.'; - out = std::fill_n(out, leading_zeroes, '0'); - out = format_decimal(out, n, num_digits).end; + out = detail::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits); } - } else { + } else if (precision > 0) { *out++ = '.'; - leading_zeroes = (std::min)(leading_zeroes, precision); - out = std::fill_n(out, leading_zeroes, '0'); + leading_zeroes = min_of(leading_zeroes, precision); int remaining = precision - leading_zeroes; - if (remaining != 0 && remaining < num_digits) { - n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); - out = format_decimal(out, n, remaining).end; + out = detail::fill_n(out, leading_zeroes, '0'); + if (remaining < num_digits) { + int num_truncated_digits = num_digits - remaining; + n /= to_unsigned(pow10(to_unsigned(num_truncated_digits))); + if (n != 0) out = format_decimal(out, n, remaining); return; } - out = format_decimal(out, n, num_digits).end; - remaining -= num_digits; - out = std::fill_n(out, remaining, '0'); + if (n != 0) { + out = format_decimal(out, n, num_digits); + remaining -= num_digits; + } + out = detail::fill_n(out, remaining, '0'); } } @@ -1271,29 +1225,28 @@ class tm_writer { } } - void write_year_extended(long long year) { + void write_year_extended(long long year, pad_type pad) { // At least 4 characters. int width = 4; - if (year < 0) { - *out_++ = '-'; + bool negative = year < 0; + if (negative) { year = 0 - year; --width; } uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); - if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); - out_ = format_decimal(out_, n, num_digits).end; - } - void write_year(long long year) { - if (year >= 0 && year < 10000) { - write2(static_cast(year / 100)); - write2(static_cast(year % 100)); - } else { - write_year_extended(year); + if (negative && pad == pad_type::zero) *out_++ = '-'; + if (width > num_digits) { + out_ = detail::write_padding(out_, pad, width - num_digits); } + if (negative && pad != pad_type::zero) *out_++ = '-'; + out_ = format_decimal(out_, n, num_digits); + } + void write_year(long long year, pad_type pad) { + write_year_extended(year, pad); } - void write_utc_offset(long offset, numeric_system ns) { + void write_utc_offset(long long offset, numeric_system ns) { if (offset < 0) { *out_++ = '-'; offset = -offset; @@ -1305,6 +1258,7 @@ class tm_writer { if (ns != numeric_system::standard) *out_++ = ':'; write2(static_cast(offset % 60)); } + template ::value)> void format_utc_offset_impl(const T& tm, numeric_system ns) { write_utc_offset(tm.tm_gmtoff, ns); @@ -1312,9 +1266,7 @@ class tm_writer { template ::value)> void format_utc_offset_impl(const T& tm, numeric_system ns) { #if defined(_WIN32) && defined(_UCRT) -# if FMT_USE_TZSET tzset_once(); -# endif long offset = 0; _get_timezone(&offset); if (tm.tm_isdst) { @@ -1331,7 +1283,7 @@ class tm_writer { std::time_t gt = std::mktime(>m); std::tm ltm = gmtime(gt); std::time_t lt = std::mktime(<m); - long offset = gt - lt; + long long offset = gt - lt; write_utc_offset(offset, ns); #endif } @@ -1364,7 +1316,7 @@ class tm_writer { auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { - out_ = copy_str(begin, end, out_); + out_ = copy(begin, end, out_); } void on_abbr_weekday() { @@ -1411,11 +1363,11 @@ class tm_writer { *out_++ = ' '; on_abbr_month(); *out_++ = ' '; - on_day_of_month_space(numeric_system::standard); + on_day_of_month(numeric_system::standard, pad_type::space); *out_++ = ' '; on_iso_time(); *out_++ = ' '; - on_year(numeric_system::standard); + on_year(numeric_system::standard, pad_type::space); } else { format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); } @@ -1437,31 +1389,31 @@ class tm_writer { write_digit2_separated(buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); } void on_iso_date() { auto year = tm_year(); char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); + write2digits(buf, static_cast(year / 100)); } else { offset = 4; - write_year_extended(year); + write_year_extended(year, pad_type::zero); year = 0; } write_digit2_separated(buf + 2, static_cast(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-'); - out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + out_ = copy(std::begin(buf) + offset, std::end(buf), out_); } void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } void on_tz_name() { format_tz_name_impl(tm_); } - void on_year(numeric_system ns) { + void on_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write_year(tm_year()); + return write_year(tm_year(), pad); format_localized('Y', 'E'); } void on_short_year(numeric_system ns) { @@ -1492,56 +1444,57 @@ class tm_writer { } } - void on_dec_month(numeric_system ns) { + void on_dec_month(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_mon() + 1); + return write2(tm_mon() + 1, pad); format_localized('m', 'O'); } - void on_dec0_week_of_year(numeric_system ns) { + void on_dec0_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, + pad); format_localized('U', 'O'); } - void on_dec1_week_of_year(numeric_system ns) { + void on_dec1_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week); + days_per_week, + pad); } else { format_localized('W', 'O'); } } - void on_iso_week_of_year(numeric_system ns) { + void on_iso_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_iso_week_of_year()); + return write2(tm_iso_week_of_year(), pad); format_localized('V', 'O'); } - void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_year() { + write_year(tm_iso_week_year(), pad_type::zero); + } void on_iso_week_based_short_year() { write2(split_year_lower(tm_iso_week_year())); } - void on_day_of_year() { + void on_day_of_year(pad_type pad) { auto yday = tm_yday() + 1; - write1(yday / 100); - write2(yday % 100); - } - void on_day_of_month(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); - format_localized('d', 'O'); - } - void on_day_of_month_space(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto mday = to_unsigned(tm_mday()) % 100; - const char* d2 = digits2(mday); - *out_++ = mday < 10 ? ' ' : d2[0]; - *out_++ = d2[1]; + auto digit1 = yday / 100; + if (digit1 != 0) { + write1(digit1); } else { - format_localized('e', 'O'); + out_ = detail::write_padding(out_, pad); } + write2(yday % 100, pad); + } + + void on_day_of_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mday(), pad); + format_localized('d', 'O'); } void on_24_hour(numeric_system ns, pad_type pad) { @@ -1569,7 +1522,7 @@ class tm_writer { write_floating_seconds(buf, *subsecs_); if (buf.size() > 1) { // Remove the leading "0", write something like ".123". - out_ = std::copy(buf.begin() + 1, buf.end(), out_); + out_ = copy(buf.begin() + 1, buf.end(), out_); } } else { write_fractional_seconds(out_, *subsecs_); @@ -1586,7 +1539,7 @@ class tm_writer { char buf[8]; write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); - out_ = copy_str(std::begin(buf), std::end(buf), out_); + out_ = copy(std::begin(buf), std::end(buf), out_); *out_++ = ' '; on_am_pm(); } else { @@ -1601,7 +1554,7 @@ class tm_writer { void on_iso_time() { on_24_hour_time(); *out_++ = ':'; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1621,11 +1574,11 @@ class tm_writer { struct chrono_format_checker : null_chrono_spec_handler { bool has_precision_integral = false; - FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } + FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); } template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_year(pad_type) {} 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) {} @@ -1635,9 +1588,8 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} FMT_CONSTEXPR void on_duration_value() const { - if (has_precision_integral) { + if (has_precision_integral) FMT_THROW(format_error("precision not allowed for this argument type")); - } } FMT_CONSTEXPR void on_duration_unit() {} }; @@ -1677,17 +1629,17 @@ inline auto get_milliseconds(std::chrono::duration d) #if FMT_SAFE_DURATION_CAST using CommonSecondsType = typename std::common_type::type; - const auto d_as_common = fmt_duration_cast(d); + const auto d_as_common = detail::duration_cast(d); const auto d_as_whole_seconds = - fmt_duration_cast(d_as_common); + detail::duration_cast(d_as_common); // this conversion should be nonproblematic const auto diff = d_as_common - d_as_whole_seconds; const auto ms = - fmt_duration_cast>(diff); + detail::duration_cast>(diff); return ms; #else - auto s = fmt_duration_cast(d); - return fmt_duration_cast(d - s); + auto s = detail::duration_cast(d); + return detail::duration_cast(d - s); #endif } @@ -1700,16 +1652,16 @@ auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { template ::value)> auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { - auto specs = format_specs(); + auto specs = format_specs(); specs.precision = precision; - specs.type = precision >= 0 ? presentation_type::fixed_lower - : presentation_type::general_lower; + specs.set_type(precision >= 0 ? presentation_type::fixed + : presentation_type::general); return write(out, val, specs); } template auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { - return std::copy(unit.begin(), unit.end(), out); + return copy(unit.begin(), unit.end(), out); } template @@ -1717,7 +1669,7 @@ 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); - return std::copy(u.c_str(), u.c_str() + u.size(), out); + return copy(u.c_str(), u.c_str() + u.size(), out); } template @@ -1743,14 +1695,14 @@ class get_locale { bool has_locale_ = false; public: - get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) { if (localized) ::new (&locale_) std::locale(loc.template get()); } - ~get_locale() { + inline ~get_locale() { if (has_locale_) locale_.~locale(); } - operator const std::locale&() const { + inline operator const std::locale&() const { return has_locale_ ? locale_ : get_classic_locale(); } }; @@ -1789,7 +1741,7 @@ struct chrono_formatter { // this may overflow and/or the result may not fit in the // target type. // might need checked conversion (rep!=Rep) - s = fmt_duration_cast(std::chrono::duration(val)); + s = detail::duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. @@ -1840,7 +1792,7 @@ struct chrono_formatter { } } - void write(Rep value, int width, pad_type pad = pad_type::unspecified) { + void write(Rep value, int width, pad_type pad = pad_type::zero) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = @@ -1849,7 +1801,7 @@ struct chrono_formatter { if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, n, num_digits); } void write_nan() { std::copy_n("nan", 3, out); } @@ -1866,7 +1818,7 @@ struct chrono_formatter { } void on_text(const char_type* begin, const char_type* end) { - std::copy(begin, end, out); + copy(begin, end, out); } // These are not implemented because durations don't have date information. @@ -1883,20 +1835,19 @@ struct chrono_formatter { void on_iso_date() {} void on_utc_offset(numeric_system) {} void on_tz_name() {} - void on_year(numeric_system) {} + void on_year(numeric_system, pad_type) {} void on_short_year(numeric_system) {} void on_offset_year() {} void on_century(numeric_system) {} void on_iso_week_based_year() {} void on_iso_week_based_short_year() {} - void on_dec_month(numeric_system) {} - 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_month(numeric_system) {} - void on_day_of_month_space(numeric_system) {} + void on_dec_month(numeric_system, pad_type) {} + void on_dec0_week_of_year(numeric_system, pad_type) {} + void on_dec1_week_of_year(numeric_system, pad_type) {} + void on_iso_week_of_year(numeric_system, pad_type) {} + void on_day_of_month(numeric_system, pad_type) {} - void on_day_of_year() { + void on_day_of_year(pad_type) { if (handle_nan_inf()) return; write(days(), 0); } @@ -1940,7 +1891,7 @@ struct chrono_formatter { if (buf.size() < 2 || buf[1] == '.') { out = detail::write_padding(out, pad); } - out = std::copy(buf.begin(), buf.end(), out); + out = copy(buf.begin(), buf.end(), out); } else { write(second(), 2, pad); write_fractional_seconds( @@ -1974,7 +1925,7 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1997,82 +1948,240 @@ struct chrono_formatter { #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 using weekday = std::chrono::weekday; +using day = std::chrono::day; +using month = std::chrono::month; +using year = std::chrono::year; +using year_month_day = std::chrono::year_month_day; #else // A fallback version of weekday. class weekday { private: - unsigned char value; + unsigned char value_; public: weekday() = default; - explicit constexpr weekday(unsigned wd) noexcept - : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr auto c_encoding() const noexcept -> unsigned { return value; } + constexpr explicit weekday(unsigned wd) noexcept + : value_(static_cast(wd != 7 ? wd : 0)) {} + constexpr auto c_encoding() const noexcept -> unsigned { return value_; } }; -class year_month_day {}; -#endif - -// A rudimentary weekday formatter. -template struct formatter { +class day { private: - bool localized = false; + unsigned char value_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - auto begin = ctx.begin(), end = ctx.end(); - if (begin != end && *begin == 'L') { - ++begin; - localized = true; + day() = default; + constexpr explicit day(unsigned d) noexcept + : value_(static_cast(d)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class month { + private: + unsigned char value_; + + public: + month() = default; + constexpr explicit month(unsigned m) noexcept + : value_(static_cast(m)) {} + constexpr explicit operator unsigned() const noexcept { return value_; } +}; + +class year { + private: + int value_; + + public: + year() = default; + constexpr explicit year(int y) noexcept : value_(y) {} + constexpr explicit operator int() const noexcept { return value_; } +}; + +class year_month_day { + private: + fmt::year year_; + fmt::month month_; + fmt::day day_; + + public: + year_month_day() = default; + constexpr year_month_day(const year& y, const month& m, const day& d) noexcept + : year_(y), month_(m), day_(d) {} + constexpr auto year() const noexcept -> fmt::year { return year_; } + constexpr auto month() const noexcept -> fmt::month { return month_; } + constexpr auto day() const noexcept -> fmt::day { return day_; } +}; +#endif + +template +struct formatter : private formatter { + private: + bool localized_ = false; + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + localized_ = true; + return it; } - return begin; + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; } template auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); - detail::get_locale loc(localized, ctx.locale()); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(localized_, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); } }; +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mday = static_cast(static_cast(d)); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool localized_ = false; + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'L') { + ++it; + localized_ = true; + return it; + } + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_mon = static_cast(static_cast(m)) - 1; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(localized_, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_month(); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(y) - 1900; + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(false, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_year(detail::numeric_system::standard, detail::pad_type::zero); + return w.out(); + } +}; + +template +struct formatter : private formatter { + private: + bool use_tm_formatter_ = false; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + use_tm_formatter_ = it != end && *it != '}'; + return use_tm_formatter_ ? formatter::parse(ctx) : it; + } + + template + auto format(year_month_day val, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto time = std::tm(); + time.tm_year = static_cast(val.year()) - 1900; + time.tm_mon = static_cast(static_cast(val.month())) - 1; + time.tm_mday = static_cast(static_cast(val.day())); + if (use_tm_formatter_) return formatter::format(time, ctx); + detail::get_locale loc(true, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_iso_date(); + return w.out(); + } +}; + template struct formatter, Char> { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; detail::arg_ref precision_ref_; bool localized_ = false; - basic_string_view format_str_; + basic_string_view fmt_; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { 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; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } auto checker = detail::chrono_format_checker(); if (*it == '.') { checker.has_precision_integral = !std::is_floating_point::value; - it = detail::parse_precision(it, end, specs_.precision, precision_ref_, - ctx); + it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); } if (it != end && *it == 'L') { localized_ = true; ++it; } end = detail::parse_chrono_format(it, end, checker); - format_str_ = {it, detail::to_unsigned(end - it)}; + fmt_ = {it, detail::to_unsigned(end - it)}; return end; } @@ -2082,15 +2191,15 @@ struct formatter, Char> { auto specs = specs_; auto precision = specs.precision; specs.precision = -1; - auto begin = format_str_.begin(), end = format_str_.end(); + auto begin = fmt_.begin(), end = fmt_.end(); // As a possible future optimization, we could avoid extra copying if width // is not specified. auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - detail::handle_dynamic_spec(precision, - precision_ref_, ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), precision, + precision_ref_, ctx); if (begin == end || *begin == '}') { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); @@ -2107,130 +2216,119 @@ struct formatter, Char> { } }; -template -struct formatter, - Char> : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - using period = typename Duration::period; - if (detail::const_check( - period::num != 1 || period::den != 1 || - std::is_floating_point::value)) { - const auto epoch = val.time_since_epoch(); - auto subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); - - if (subsecs.count() < 0) { - auto second = - detail::fmt_duration_cast(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::do_format(gmtime(val), ctx, &subsecs); - } - - return formatter::format(gmtime(val), ctx); - } -}; - -#if FMT_USE_LOCAL_TIME -template -struct formatter, Char> - : formatter { - FMT_CONSTEXPR formatter() { - this->format_str_ = detail::string_literal{}; - } - - template - auto format(std::chrono::local_time val, FormatContext& ctx) const - -> decltype(ctx.out()) { - using period = typename Duration::period; - if (period::num != 1 || period::den != 1 || - std::is_floating_point::value) { - const auto epoch = val.time_since_epoch(); - const auto subsecs = detail::fmt_duration_cast( - epoch - detail::fmt_duration_cast(epoch)); - - return formatter::do_format(localtime(val), ctx, &subsecs); - } - - return formatter::format(localtime(val), ctx); - } -}; -#endif - -#if FMT_USE_UTC_TIME -template -struct formatter, - Char> - : formatter, - Char> { - template - auto format(std::chrono::time_point val, - FormatContext& ctx) const -> decltype(ctx.out()) { - return formatter< - std::chrono::time_point, - Char>::format(std::chrono::utc_clock::to_sys(val), ctx); - } -}; -#endif - template struct formatter { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; protected: - basic_string_view format_str_; + basic_string_view fmt_; - template + template auto do_format(const std::tm& tm, FormatContext& ctx, const Duration* subsecs) const -> decltype(ctx.out()) { auto specs = specs_; auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); auto loc_ref = ctx.locale(); detail::get_locale loc(static_cast(loc_ref), loc_ref); auto w = detail::tm_writer(loc, out, tm, subsecs); - detail::parse_chrono_format(format_str_.begin(), format_str_.end(), w); + detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { 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; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, 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)}; + // Replace the default format string only if the new spec is not empty. + if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; return end; } template auto format(const std::tm& tm, FormatContext& ctx) const -> decltype(ctx.out()) { - return do_format(tm, ctx, nullptr); + return do_format(tm, ctx, nullptr); + } +}; + +template +struct formatter, Char> : formatter { + FMT_CONSTEXPR formatter() { + this->fmt_ = detail::string_literal(); + } + + template + auto format(sys_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + std::tm tm = gmtime(val); + using period = typename Duration::period; + if (detail::const_check( + period::num == 1 && period::den == 1 && + !std::is_floating_point::value)) { + return formatter::format(tm, ctx); + } + Duration epoch = val.time_since_epoch(); + Duration subsecs = detail::duration_cast( + epoch - detail::duration_cast(epoch)); + if (subsecs.count() < 0) { + auto second = detail::duration_cast(std::chrono::seconds(1)); + if (tm.tm_sec != 0) + --tm.tm_sec; + else + tm = gmtime(val - second); + subsecs += detail::duration_cast(std::chrono::seconds(1)); + } + return formatter::do_format(tm, ctx, &subsecs); + } +}; + +template +struct formatter, Char> + : formatter, Char> { + template + auto format(utc_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format( + detail::utc_clock::to_sys(val), ctx); + } +}; + +template +struct formatter, Char> : formatter { + FMT_CONSTEXPR formatter() { + this->fmt_ = detail::string_literal(); + } + + template + auto format(local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num == 1 && period::den == 1 && + !std::is_floating_point::value) { + return formatter::format(localtime(val), ctx); + } + auto epoch = val.time_since_epoch(); + auto subsecs = detail::duration_cast( + epoch - detail::duration_cast(epoch)); + return formatter::do_format(localtime(val), ctx, &subsecs); } }; diff --git a/src/3rdparty/fmt/color.h b/src/3rdparty/fmt/color.h new file mode 100644 index 0000000000..2faaf3a067 --- /dev/null +++ b/src/3rdparty/fmt/color.h @@ -0,0 +1,610 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +namespace detail { + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) noexcept + : is_rgb(), value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; +} // namespace detail + +/// A text style consisting of foreground and background colors and emphasis. +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept + : set_foreground_color(), set_background_color(), ems(em) {} + + FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + report_error("can't OR a terminal color"); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + report_error("can't OR a terminal color"); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs) + -> text_style { + return lhs |= rhs; + } + + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { + return set_foreground_color; + } + FMT_CONSTEXPR auto has_background() const noexcept -> bool { + return set_background_color; + } + FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) noexcept + : set_foreground_color(), set_background_color(), ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept + -> text_style; + + friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept + -> text_style; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +/// Creates a text style from the foreground (text) color. +FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept + -> text_style { + return text_style(true, foreground); +} + +/// Creates a text style from the background color. +FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept + -> text_style { + return text_style(false, background); +} + +FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept + -> text_style { + return text_style(lhs) | rhs; +} + +namespace detail { + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(color_type text_color, + const char* esc) noexcept { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == string_view("\x1b[48;2;"); + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; + + size_t index = 0; + for (size_t i = 0; i < num_emphases; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } + + FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } + FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { + return buffer + basic_string_view(buffer).size(); + } + + private: + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) noexcept { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } + static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept + -> bool { + return static_cast(em) & static_cast(mask); + } +}; + +template +FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept + -> ansi_color_escape { + return ansi_color_escape(foreground, "\x1b[38;2;"); +} + +template +FMT_CONSTEXPR auto make_background_color(color_type background) noexcept + -> ansi_color_escape { + return ansi_color_escape(background, "\x1b[48;2;"); +} + +template +FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept + -> ansi_color_escape { + return ansi_color_escape(em); +} + +template inline void reset_color(buffer& buffer) { + auto reset_color = string_view("\x1b[0m"); + buffer.append(reset_color.begin(), reset_color.end()); +} + +template struct styled_arg : view { + const T& value; + text_style style; + styled_arg(const T& v, text_style s) : value(v), style(s) {} +}; + +template +void vformat_to(buffer& buf, const text_style& ts, + basic_string_view fmt, + basic_format_args> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + vformat_to(buf, fmt, args); + if (has_style) reset_color(buf); +} +} // namespace detail + +inline void vprint(FILE* f, const text_style& ts, string_view fmt, + format_args args) { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); +} + +/** + * Formats a string and prints it to the specified file stream using ANSI + * escape sequences to specify text formatting. + * + * **Example**: + * + * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + * "Elapsed time: {0:.2f} seconds", 1.23); + */ +template +void print(FILE* f, const text_style& ts, format_string fmt, + T&&... args) { + vprint(f, ts, fmt.str, vargs{{args...}}); +} + +/** + * Formats a string and prints it to stdout using ANSI escape sequences to + * specify text formatting. + * + * **Example**: + * + * fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + * "Elapsed time: {0:.2f} seconds", 1.23); + */ +template +void print(const text_style& ts, format_string fmt, T&&... args) { + return print(stdout, ts, fmt, std::forward(args)...); +} + +inline auto vformat(const text_style& ts, string_view fmt, format_args args) + -> std::string { + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + return fmt::to_string(buf); +} + +/** + * Formats arguments and returns the result as a string using ANSI escape + * sequences to specify text formatting. + * + * **Example**: + * + * ``` + * #include + * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + * "The answer is {}", 42); + * ``` + */ +template +inline auto format(const text_style& ts, format_string fmt, T&&... args) + -> std::string { + return fmt::vformat(ts, fmt.str, vargs{{args...}}); +} + +/// Formats a string with the given text_style and writes the output to `out`. +template ::value)> +auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, + format_args args) -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, ts, fmt, args); + return detail::get_iterator(buf, out); +} + +/** + * Formats arguments with the given text style, writes the result to the output + * iterator `out` and returns the iterator past the end of the output range. + * + * **Example**: + * + * std::vector out; + * fmt::format_to(std::back_inserter(out), + * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + */ +template ::value)> +inline auto format_to(OutputIt out, const text_style& ts, + format_string fmt, T&&... args) -> OutputIt { + return vformat_to(out, ts, fmt.str, vargs{{args...}}); +} + +template +struct formatter, Char> : formatter { + template + auto format(const detail::styled_arg& arg, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto& ts = arg.style; + auto out = ctx.out(); + + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + out = detail::copy(emphasis.begin(), emphasis.end(), out); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = + detail::make_foreground_color(ts.get_foreground()); + out = detail::copy(foreground.begin(), foreground.end(), out); + } + if (ts.has_background()) { + has_style = true; + auto background = + detail::make_background_color(ts.get_background()); + out = detail::copy(background.begin(), background.end(), out); + } + out = formatter::format(arg.value, ctx); + if (has_style) { + auto reset_color = string_view("\x1b[0m"); + out = detail::copy(reset_color.begin(), reset_color.end(), out); + } + return out; + } +}; + +/** + * Returns an argument that will be formatted using ANSI escape sequences, + * to be used in a formatting function. + * + * **Example**: + * + * fmt::print("Elapsed time: {0:.2f} seconds", + * fmt::styled(1.23, fmt::fg(fmt::color::green) | + * fmt::bg(fmt::color::blue))); + */ +template +FMT_CONSTEXPR auto styled(const T& value, text_style ts) + -> detail::styled_arg> { + return detail::styled_arg>{value, ts}; +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/src/3rdparty/fmt/compile.h b/src/3rdparty/fmt/compile.h new file mode 100644 index 0000000000..08d9427ff2 --- /dev/null +++ b/src/3rdparty/fmt/compile.h @@ -0,0 +1,539 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#ifndef FMT_MODULE +# include // std::back_inserter +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// A compile-time string which is compiled into fast formatting code. +FMT_EXPORT class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +namespace detail { + +/** + * Converts a string literal `s` into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires C++17 + * `constexpr if` compiler support. + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * std::string s = fmt::format(FMT_COMPILE("{}"), 42); + */ +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) +#else +# define FMT_COMPILE(s) FMT_STRING(s) +#endif + +template +auto first(const T& value, const Tail&...) -> const T& { + return value; +} + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return detail::get(rest...); +} + +# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +constexpr auto get_arg_index_by_name(basic_string_view name) -> int { + if constexpr (is_static_named_arg()) { + if (name == T::name) return N; + } + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name(name); + (void)name; // Workaround an MSVC bug about "unused" parameter. + return -1; +} +# endif + +template +FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { +# if FMT_USE_NONTYPE_TEMPLATE_ARGS + if constexpr (sizeof...(Args) > 0) + return get_arg_index_by_name<0, Args...>(name); +# endif + (void)name; + return -1; +} + +template +constexpr int get_arg_index_by_name(basic_string_view name, + type_list) { + return get_arg_index_by_name(name); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = + remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&...) const { + *out++ = value; + return out; + } +}; + +// This ensures that the argument type is convertible to `const T&`. +template +constexpr const T& get_arg_checked(const Args&... args) { + const auto& arg = detail::get(args...); + if constexpr (detail::is_named_arg>()) { + return arg.value; + } else { + return arg; + } +} + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + const T& arg = get_arg_checked(args...); + if constexpr (std::is_convertible>::value) { + auto s = basic_string_view(arg); + return copy(s.begin(), s.end(), out); + } else { + return write(out, arg); + } + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument with name. +template struct runtime_named_field { + using char_type = Char; + basic_string_view name; + + template + constexpr static bool try_format_argument( + OutputIt& out, + // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 + [[maybe_unused]] basic_string_view arg_name, const T& arg) { + if constexpr (is_named_arg::type>::value) { + if (arg_name == arg.name) { + out = write(out, arg.value); + return true; + } + } + return false; + } + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + bool found = (try_format_argument(out, name, args) || ...); + if (!found) { + FMT_THROW(format_error("argument with specified name is not found")); + } + return out; + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + formatter fmt; + + template + constexpr FMT_INLINE OutputIt format(OutputIt out, + const Args&... args) const { + const auto& vargs = + fmt::make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(get_arg_checked(args...), ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + constexpr OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S fmt); + +template +constexpr auto parse_tail(T head, S fmt) { + if constexpr (POS != basic_string_view(fmt).size()) { + constexpr auto tail = compile_format_string(fmt); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +enum { manual_indexing_id = -1 }; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int next_arg_id) { + str.remove_prefix(pos); + auto ctx = + compile_parse_context(str, max_value(), nullptr, next_arg_id); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + fmt::detail::to_unsigned(end - str.data()), + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + arg_id_kind kind; + arg_ref arg_id; + + constexpr int on_auto() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int on_index(int id) { + kind = arg_id_kind::index; + arg_id = arg_ref(id); + return 0; + } + constexpr int on_name(basic_string_view id) { + kind = arg_id_kind::name; + arg_id = arg_ref(id); + return 0; + } +}; + +template struct parse_arg_id_result { + arg_id_kind kind; + arg_ref arg_id; + const Char* arg_id_end; +}; + +template +constexpr auto parse_arg_id(const Char* begin, const Char* end) { + auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; + auto arg_id_end = parse_arg_id(begin, end, handler); + return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; +} + +template struct field_type { + using type = remove_cvref_t; +}; + +template +struct field_type::value>> { + using type = remove_cvref_t; +}; + +template +constexpr auto parse_replacement_field_then_tail(S fmt) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(fmt); + constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); + if constexpr (c == '}') { + return parse_tail( + field::type, ARG_INDEX>(), fmt); + } else if constexpr (c != ':') { + FMT_THROW(format_error("expected ':'")); + } else { + constexpr auto result = parse_specs::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + return parse_tail( + spec_field::type, ARG_INDEX>{ + result.fmt}, + fmt); + } + } +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S fmt) { + using char_type = typename S::char_type; + constexpr auto str = basic_string_view(fmt); + if constexpr (str[POS] == '{') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '{' in format string")); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), fmt); + } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { + static_assert(ID != manual_indexing_id, + "cannot switch from manual to automatic argument indexing"); + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail, Args, + POS + 1, ID, next_id>(fmt); + } else { + constexpr auto arg_id_result = + parse_arg_id(str.data() + POS + 1, str.data() + str.size()); + constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); + constexpr char_type c = + arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); + static_assert(c == '}' || c == ':', "missing '}' in format string"); + if constexpr (arg_id_result.kind == arg_id_kind::index) { + static_assert( + ID == manual_indexing_id || ID == 0, + "cannot switch from automatic to manual argument indexing"); + constexpr auto arg_index = arg_id_result.arg_id.index; + return parse_replacement_field_then_tail, + Args, arg_id_end_pos, + arg_index, manual_indexing_id>( + fmt); + } else if constexpr (arg_id_result.kind == arg_id_kind::name) { + constexpr auto arg_index = + get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); + if constexpr (arg_index >= 0) { + constexpr auto next_id = + ID != manual_indexing_id ? ID + 1 : manual_indexing_id; + return parse_replacement_field_then_tail< + decltype(get_type::value), Args, arg_id_end_pos, + arg_index, next_id>(fmt); + } else if constexpr (c == '}') { + return parse_tail( + runtime_named_field{arg_id_result.arg_id.name}, fmt); + } else if constexpr (c == ':') { + return unknown_format(); // no type info for specs parsing + } + } + } + } else if constexpr (str[POS] == '}') { + if constexpr (POS + 1 == str.size()) + FMT_THROW(format_error("unmatched '}' in format string")); + return parse_tail(make_text(str, POS, 1), fmt); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), fmt); + } else { + return parse_tail(code_unit{str[POS]}, fmt); + } + } +} + +template ::value)> +constexpr auto compile(S fmt) { + constexpr auto str = basic_string_view(fmt); + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>(fmt); + return result; + } +} +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) +} // namespace detail + +FMT_BEGIN_EXPORT + +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + auto s = std::basic_string(); + cf.format(std::back_inserter(s), args...); + return s; +} + +template ::value)> +constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { + if constexpr (std::is_same::value) { + constexpr auto str = basic_string_view(S()); + if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { + const auto& first = detail::first(args...); + if constexpr (detail::is_named_arg< + remove_cvref_t>::value) { + return fmt::to_string(first.value); + } else { + return fmt::to_string(first); + } + } + } + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format( + static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format(compiled, std::forward(args)...); + } +} + +template ::value)> +FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { + constexpr auto compiled = detail::compile(S()); + if constexpr (std::is_same, + detail::unknown_format>()) { + return fmt::format_to( + out, static_cast>(S()), + std::forward(args)...); + } else { + return fmt::format_to(out, compiled, std::forward(args)...); + } +} +#endif + +template ::value)> +auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); + return {buf.out(), buf.count()}; +} + +template ::value)> +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) + -> size_t { + auto buf = detail::counting_buffer<>(); + fmt::format_to(appender(buf), fmt, args...); + return buf.count(); +} + +template ::value)> +void print(std::FILE* f, const S& fmt, const Args&... args) { + auto buf = memory_buffer(); + fmt::format_to(appender(buf), fmt, args...); + detail::print(f, {buf.data(), buf.size()}); +} + +template ::value)> +void print(const S& fmt, const Args&... args) { + print(stdout, fmt, args...); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals +#endif + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/src/3rdparty/fmt/core.h b/src/3rdparty/fmt/core.h index 7fe8550413..8ca735f0c0 100644 --- a/src/3rdparty/fmt/core.h +++ b/src/3rdparty/fmt/core.h @@ -1,2964 +1,5 @@ -// Formatting library for C++ - the core API for char/UTF-8 -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. +// This file is only provided for compatibility and may be removed in future +// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h +// otherwise. -#ifndef FMT_CORE_H_ -#define FMT_CORE_H_ - -#include // std::byte -#include // std::FILE -#include // std::strlen -#include -#include -#include // std::addressof -#include -#include - -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100200 - -#if defined(__clang__) && !defined(__ibmxl__) -# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#else -# define FMT_CLANG_VERSION 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ - !defined(__NVCOMPILER) -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#else -# define FMT_GCC_VERSION 0 -#endif - -#ifndef FMT_GCC_PRAGMA -// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. -# if FMT_GCC_VERSION >= 504 -# define FMT_GCC_PRAGMA(arg) _Pragma(arg) -# else -# define FMT_GCC_PRAGMA(arg) -# endif -#endif - -#ifdef __ICL -# define FMT_ICC_VERSION __ICL -#elif defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#else -# define FMT_ICC_VERSION 0 -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VERSION _MSC_VER -# define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__)) -#else -# define FMT_MSC_VERSION 0 -# define FMT_MSC_WARNING(...) -#endif - -#ifdef _MSVC_LANG -# define FMT_CPLUSPLUS _MSVC_LANG -#else -# define FMT_CPLUSPLUS __cplusplus -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#if defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900 -# define FMT_HAS_INCLUDE(x) __has_include(x) -#else -# define FMT_HAS_INCLUDE(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ - (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) - -// Check if relaxed C++14 constexpr is supported. -// GCC doesn't allow throw in constexpr until version 6 (bug 67371). -#ifndef FMT_USE_CONSTEXPR -# if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \ - (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) && \ - !FMT_ICC_VERSION && (!defined(__NVCC__) || FMT_CPLUSPLUS >= 202002L) -# define FMT_USE_CONSTEXPR 1 -# else -# define FMT_USE_CONSTEXPR 0 -# endif -#endif -#if FMT_USE_CONSTEXPR -# define FMT_CONSTEXPR constexpr -#else -# define FMT_CONSTEXPR -#endif - -#if (FMT_CPLUSPLUS >= 202002L || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \ - ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \ - (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \ - (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1928)) && \ - defined(__cpp_lib_is_constant_evaluated) -# define FMT_CONSTEXPR20 constexpr -#else -# define FMT_CONSTEXPR20 -#endif - -// Check if constexpr std::char_traits<>::{compare,length} are supported. -#if defined(__GLIBCXX__) -# if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE. -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -# endif -#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \ - _LIBCPP_VERSION >= 4000 -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L -# define FMT_CONSTEXPR_CHAR_TRAITS constexpr -#endif -#ifndef FMT_CONSTEXPR_CHAR_TRAITS -# define FMT_CONSTEXPR_CHAR_TRAITS -#endif - -// Check if exceptions are disabled. -#ifndef FMT_EXCEPTIONS -# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ - (FMT_MSC_VERSION && !_HAS_EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -# else -# define FMT_EXCEPTIONS 1 -# endif -#endif - -// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings. -#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \ - !defined(__NVCC__) -# define FMT_NORETURN [[noreturn]] -#else -# define FMT_NORETURN -#endif - -#ifndef FMT_NODISCARD -# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) -# define FMT_NODISCARD [[nodiscard]] -# else -# define FMT_NODISCARD -# endif -#endif - -#ifndef FMT_INLINE -# if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_INLINE inline __attribute__((always_inline)) -# else -# define FMT_INLINE inline -# endif -#endif - -#ifdef _MSC_VER -# define FMT_UNCHECKED_ITERATOR(It) \ - using _Unchecked_type = It // Mark iterator as checked. -#else -# define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It -#endif - -#ifndef FMT_BEGIN_NAMESPACE -# define FMT_BEGIN_NAMESPACE \ - namespace fmt { \ - inline namespace v10 { -# define FMT_END_NAMESPACE \ - } \ - } -#endif - -#ifndef FMT_EXPORT -# define FMT_EXPORT -# define FMT_BEGIN_EXPORT -# define FMT_END_EXPORT -#endif - -#if FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_VISIBILITY(value) __attribute__((visibility(value))) -#else -# define FMT_VISIBILITY(value) -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# if defined(FMT_LIB_EXPORT) -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED) -# define FMT_API FMT_VISIBILITY("default") -#endif -#ifndef FMT_API -# define FMT_API -#endif - -// libc++ supports string_view in pre-c++17. -#if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -# include -# define FMT_USE_STRING_VIEW -#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L -# include -# define FMT_USE_EXPERIMENTAL_STRING_VIEW -#endif - -#ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VERSION -#endif - -#ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - (!defined(__apple_build_version__) || \ - __apple_build_version__ >= 14000029L) && \ - FMT_CPLUSPLUS >= 202002L) || \ - (defined(__cpp_consteval) && \ - (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1929)) -// consteval is broken in MSVC before VS2019 version 16.10 and Apple clang -// before 14. -# define FMT_CONSTEVAL consteval -# define FMT_HAS_CONSTEVAL -# else -# define FMT_CONSTEVAL -# endif -#endif - -#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS -# if defined(__cpp_nontype_template_args) && \ - ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \ - __cpp_nontype_template_args >= 201911L) && \ - !defined(__NVCOMPILER) && !defined(__LCC__) -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 -# else -# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 -# endif -#endif - -// GCC < 5 requires this-> in decltype -#ifndef FMT_DECLTYPE_THIS -# if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -# define FMT_DECLTYPE_THIS this-> -# else -# define FMT_DECLTYPE_THIS -# endif -#endif - -FMT_GCC_PRAGMA("GCC push_options") - -FMT_BEGIN_NAMESPACE - -// Implementations of enable_if_t and other metafunctions for older systems. -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; -template using bool_constant = std::integral_constant; -template -using remove_reference_t = typename std::remove_reference::type; -template -using remove_const_t = typename std::remove_const::type; -template -using remove_cvref_t = typename std::remove_cv>::type; -template struct type_identity { - using type = T; -}; -template using type_identity_t = typename type_identity::type; -template -using underlying_t = typename std::underlying_type::type; - -// Checks whether T is a container with contiguous storage. -template struct is_contiguous : std::false_type {}; -template -struct is_contiguous> : std::true_type {}; - -struct monostate { - constexpr monostate() {} -}; - -// An enable_if helper to be used in template parameters which results in much -// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed -// to workaround a bug in MSVC 2019 (see #1140 and #1186). -#ifdef FMT_DOC -# define FMT_ENABLE_IF(...) -#else -# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 -#endif - -// This is defined in core.h instead of format.h to avoid injecting in std. -// It is a template to avoid undesirable implicit conversions to std::byte. -#ifdef __cpp_lib_byte -template ::value)> -inline auto format_as(T b) -> unsigned char { - return static_cast(b); -} -#endif - -namespace detail { -// Suppresses "unused variable" warnings with the method described in -// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -// (void)var does not work on many Intel compilers. -template FMT_CONSTEXPR void ignore_unused(const T&...) {} - -constexpr FMT_INLINE auto is_constant_evaluated( - bool default_value = false) noexcept -> bool { -// Workaround for incompatibility between libstdc++ consteval-based -// std::is_constant_evaluated() implementation and clang-14. -// https://github.com/fmtlib/fmt/issues/3247 -#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ - _GLIBCXX_RELEASE >= 12 && \ - (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) - ignore_unused(default_value); - return __builtin_is_constant_evaluated(); -#elif defined(__cpp_lib_is_constant_evaluated) - ignore_unused(default_value); - return std::is_constant_evaluated(); -#else - return default_value; -#endif -} - -// Suppresses "conditional expression is constant" warnings. -template constexpr FMT_INLINE auto const_check(T value) -> T { - return value; -} - -FMT_NORETURN FMT_API void assert_fail(const char* file, int line, - const char* message); - -#ifndef FMT_ASSERT -# ifdef NDEBUG -// FMT_ASSERT is not empty to avoid -Wempty-body. -# define FMT_ASSERT(condition, message) \ - fmt::detail::ignore_unused((condition), (message)) -# else -# define FMT_ASSERT(condition, message) \ - ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ - ? (void)0 \ - : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) -# endif -#endif - -#if defined(FMT_USE_STRING_VIEW) -template using std_string_view = std::basic_string_view; -#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) -template -using std_string_view = std::experimental::basic_string_view; -#else -template struct std_string_view {}; -#endif - -#ifdef FMT_USE_INT128 -// Do nothing. -#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \ - !(FMT_CLANG_VERSION && FMT_MSC_VERSION) -# define FMT_USE_INT128 1 -using int128_opt = __int128_t; // An optional native 128-bit integer. -using uint128_opt = __uint128_t; -template inline auto convert_for_visit(T value) -> T { - return value; -} -#else -# define FMT_USE_INT128 0 -#endif -#if !FMT_USE_INT128 -enum class int128_opt {}; -enum class uint128_opt {}; -// Reduce template instantiations. -template auto convert_for_visit(T) -> monostate { return {}; } -#endif - -// Casts a nonnegative integer to unsigned. -template -FMT_CONSTEXPR auto to_unsigned(Int value) -> - typename std::make_unsigned::type { - FMT_ASSERT(std::is_unsigned::value || value >= 0, "negative value"); - return static_cast::type>(value); -} - -FMT_CONSTEXPR inline auto is_utf8() -> bool { - FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; - - // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). - using uchar = unsigned char; - return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && - uchar(section[1]) == 0xA7); -} -} // namespace detail - -/** - An implementation of ``std::basic_string_view`` for pre-C++17. It provides a - subset of the API. ``fmt::basic_string_view`` is used for format strings even - if ``std::string_view`` is available to prevent issues when a library is - compiled with a different ``-std`` option than the client code (which is not - recommended). - */ -FMT_EXPORT -template class basic_string_view { - private: - const Char* data_; - size_t size_; - - public: - using value_type = Char; - using iterator = const Char*; - - constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} - - /** Constructs a string reference object from a C string and a size. */ - constexpr basic_string_view(const Char* s, size_t count) noexcept - : data_(s), size_(count) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - FMT_CONSTEXPR_CHAR_TRAITS - FMT_INLINE - basic_string_view(const Char* s) - : data_(s), - size_(detail::const_check(std::is_same::value && - !detail::is_constant_evaluated(true)) - ? std::strlen(reinterpret_cast(s)) - : std::char_traits::length(s)) {} - - /** Constructs a string reference from a ``std::basic_string`` object. */ - template - FMT_CONSTEXPR basic_string_view( - const std::basic_string& s) noexcept - : data_(s.data()), size_(s.size()) {} - - template >::value)> - FMT_CONSTEXPR basic_string_view(S s) noexcept - : data_(s.data()), size_(s.size()) {} - - /** Returns a pointer to the string data. */ - constexpr auto data() const noexcept -> const Char* { return data_; } - - /** Returns the string size. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - constexpr auto begin() const noexcept -> iterator { return data_; } - constexpr auto end() const noexcept -> iterator { return data_ + size_; } - - constexpr auto operator[](size_t pos) const noexcept -> const Char& { - return data_[pos]; - } - - FMT_CONSTEXPR void remove_prefix(size_t n) noexcept { - data_ += n; - size_ -= n; - } - - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with( - basic_string_view sv) const noexcept -> bool { - return size_ >= sv.size_ && - std::char_traits::compare(data_, sv.data_, sv.size_) == 0; - } - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(Char c) const noexcept -> bool { - return size_ >= 1 && std::char_traits::eq(*data_, c); - } - FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(const Char* s) const -> bool { - return starts_with(basic_string_view(s)); - } - - // Lexicographically compare this string reference to other. - FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int { - size_t str_size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, str_size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; - } - - FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, - basic_string_view rhs) - -> bool { - return lhs.compare(rhs) == 0; - } - friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) != 0; - } - friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) < 0; - } - friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) <= 0; - } - friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) > 0; - } - friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { - return lhs.compare(rhs) >= 0; - } -}; - -FMT_EXPORT -using string_view = basic_string_view; - -/** Specifies if ``T`` is a character type. Can be specialized by users. */ -FMT_EXPORT -template struct is_char : std::false_type {}; -template <> struct is_char : std::true_type {}; - -namespace detail { - -// A base class for compile-time strings. -struct compile_string {}; - -template -struct is_compile_string : std::is_base_of {}; - -template ::value)> -FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { - return s; -} -template -inline auto to_string_view(const std::basic_string& s) - -> basic_string_view { - return s; -} -template -constexpr auto to_string_view(basic_string_view s) - -> basic_string_view { - return s; -} -template >::value)> -inline auto to_string_view(std_string_view s) -> basic_string_view { - return s; -} -template ::value)> -constexpr auto to_string_view(const S& s) - -> basic_string_view { - return basic_string_view(s); -} -void to_string_view(...); - -// Specifies whether S is a string type convertible to fmt::basic_string_view. -// It should be a constexpr function but MSVC 2017 fails to compile it in -// enable_if and MSVC 2015 fails to compile it as an alias template. -// ADL is intentionally disabled as to_string_view is not an extension point. -template -struct is_string - : std::is_class()))> {}; - -template struct char_t_impl {}; -template struct char_t_impl::value>> { - using result = decltype(to_string_view(std::declval())); - using type = typename result::value_type; -}; - -enum class type { - none_type, - // Integer types should go first, - int_type, - uint_type, - long_long_type, - ulong_long_type, - int128_type, - uint128_type, - bool_type, - char_type, - last_integer_type = char_type, - // followed by floating-point types. - float_type, - double_type, - long_double_type, - last_numeric_type = long_double_type, - cstring_type, - string_type, - pointer_type, - custom_type -}; - -// Maps core type T to the corresponding type enum constant. -template -struct type_constant : std::integral_constant {}; - -#define FMT_TYPE_CONSTANT(Type, constant) \ - template \ - struct type_constant \ - : std::integral_constant {} - -FMT_TYPE_CONSTANT(int, int_type); -FMT_TYPE_CONSTANT(unsigned, uint_type); -FMT_TYPE_CONSTANT(long long, long_long_type); -FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); -FMT_TYPE_CONSTANT(int128_opt, int128_type); -FMT_TYPE_CONSTANT(uint128_opt, uint128_type); -FMT_TYPE_CONSTANT(bool, bool_type); -FMT_TYPE_CONSTANT(Char, char_type); -FMT_TYPE_CONSTANT(float, float_type); -FMT_TYPE_CONSTANT(double, double_type); -FMT_TYPE_CONSTANT(long double, long_double_type); -FMT_TYPE_CONSTANT(const Char*, cstring_type); -FMT_TYPE_CONSTANT(basic_string_view, string_type); -FMT_TYPE_CONSTANT(const void*, pointer_type); - -constexpr auto is_integral_type(type t) -> bool { - return t > type::none_type && t <= type::last_integer_type; -} -constexpr auto is_arithmetic_type(type t) -> bool { - return t > type::none_type && t <= type::last_numeric_type; -} - -constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } -constexpr auto in(type t, int set) -> bool { - return ((set >> static_cast(t)) & 1) != 0; -} - -// Bitsets of types. -enum { - sint_set = - set(type::int_type) | set(type::long_long_type) | set(type::int128_type), - uint_set = set(type::uint_type) | set(type::ulong_long_type) | - set(type::uint128_type), - bool_set = set(type::bool_type), - char_set = set(type::char_type), - float_set = set(type::float_type) | set(type::double_type) | - set(type::long_double_type), - string_set = set(type::string_type), - cstring_set = set(type::cstring_type), - pointer_set = set(type::pointer_type) -}; - -// DEPRECATED! -FMT_NORETURN FMT_API void throw_format_error(const char* message); - -struct error_handler { - constexpr error_handler() = default; - - // This function is intentionally not constexpr to give a compile-time error. - FMT_NORETURN void on_error(const char* message) { - throw_format_error(message); - } -}; -} // namespace detail - -/** Throws ``format_error`` with a given message. */ -using detail::throw_format_error; - -/** String's character type. */ -template using char_t = typename detail::char_t_impl::type; - -/** - \rst - Parsing context consisting of a format string range being parsed and an - argument counter for automatic indexing. - You can use the ``format_parse_context`` type alias for ``char`` instead. - \endrst - */ -FMT_EXPORT -template class basic_format_parse_context { - private: - basic_string_view format_str_; - int next_arg_id_; - - FMT_CONSTEXPR void do_check_arg_id(int id); - - public: - using char_type = Char; - using iterator = const Char*; - - explicit constexpr basic_format_parse_context( - basic_string_view format_str, int next_arg_id = 0) - : format_str_(format_str), next_arg_id_(next_arg_id) {} - - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ - constexpr auto begin() const noexcept -> iterator { - return format_str_.begin(); - } - - /** - Returns an iterator past the end of the format string range being parsed. - */ - constexpr auto end() const noexcept -> iterator { return format_str_.end(); } - - /** Advances the begin iterator to ``it``. */ - FMT_CONSTEXPR void advance_to(iterator it) { - format_str_.remove_prefix(detail::to_unsigned(it - begin())); - } - - /** - Reports an error if using the manual argument indexing; otherwise returns - the next argument index and switches to the automatic indexing. - */ - FMT_CONSTEXPR auto next_arg_id() -> int { - if (next_arg_id_ < 0) { - detail::throw_format_error( - "cannot switch from manual to automatic argument indexing"); - return 0; - } - int id = next_arg_id_++; - do_check_arg_id(id); - return id; - } - - /** - Reports an error if using the automatic argument indexing; otherwise - switches to the manual indexing. - */ - FMT_CONSTEXPR void check_arg_id(int id) { - if (next_arg_id_ > 0) { - detail::throw_format_error( - "cannot switch from automatic to manual argument indexing"); - return; - } - next_arg_id_ = -1; - do_check_arg_id(id); - } - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - FMT_CONSTEXPR void check_dynamic_spec(int arg_id); -}; - -FMT_EXPORT -using format_parse_context = basic_format_parse_context; - -namespace detail { -// A parse context with extra data used only in compile-time checks. -template -class compile_parse_context : public basic_format_parse_context { - private: - int num_args_; - const type* types_; - using base = basic_format_parse_context; - - public: - explicit FMT_CONSTEXPR compile_parse_context( - basic_string_view format_str, int num_args, const type* types, - int next_arg_id = 0) - : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} - - constexpr auto num_args() const -> int { return num_args_; } - constexpr auto arg_type(int id) const -> type { return types_[id]; } - - FMT_CONSTEXPR auto next_arg_id() -> int { - int id = base::next_arg_id(); - if (id >= num_args_) throw_format_error("argument not found"); - return id; - } - - FMT_CONSTEXPR void check_arg_id(int id) { - base::check_arg_id(id); - if (id >= num_args_) throw_format_error("argument not found"); - } - using base::check_arg_id; - - FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { - detail::ignore_unused(arg_id); -#if !defined(__LCC__) - if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) - throw_format_error("width/precision is not integer"); -#endif - } -}; - -// Extracts a reference to the container from back_insert_iterator. -template -inline auto get_container(std::back_insert_iterator it) - -> Container& { - using base = std::back_insert_iterator; - struct accessor : base { - accessor(base b) : base(b) {} - using base::container; - }; - return *accessor(it).container; -} - -template -FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) - -> OutputIt { - while (begin != end) *out++ = static_cast(*begin++); - return out; -} - -template , U>::value&& is_char::value)> -FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { - if (is_constant_evaluated()) return copy_str(begin, end, out); - auto size = to_unsigned(end - begin); - if (size > 0) memcpy(out, begin, size * sizeof(U)); - return out + size; -} - -/** - \rst - A contiguous memory buffer with an optional growing ability. It is an internal - class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. - \endrst - */ -template class buffer { - private: - T* ptr_; - size_t size_; - size_t capacity_; - - protected: - // Don't initialize ptr_ since it is not accessed to save a few cycles. - FMT_MSC_WARNING(suppress : 26495) - FMT_CONSTEXPR buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} - - FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept - : ptr_(p), size_(sz), capacity_(cap) {} - - FMT_CONSTEXPR20 ~buffer() = default; - buffer(buffer&&) = default; - - /** Sets the buffer data and capacity. */ - FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { - ptr_ = buf_data; - capacity_ = buf_capacity; - } - - /** Increases the buffer capacity to hold at least *capacity* elements. */ - // DEPRECATED! - virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; - - public: - using value_type = T; - using const_reference = const T&; - - buffer(const buffer&) = delete; - void operator=(const buffer&) = delete; - - FMT_INLINE auto begin() noexcept -> T* { return ptr_; } - FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } - - FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } - FMT_INLINE auto end() const noexcept -> const T* { return ptr_ + size_; } - - /** Returns the size of this buffer. */ - constexpr auto size() const noexcept -> size_t { return size_; } - - /** Returns the capacity of this buffer. */ - constexpr auto capacity() const noexcept -> size_t { return capacity_; } - - /** Returns a pointer to the buffer data (not null-terminated). */ - FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } - FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } - - /** Clears this buffer. */ - void clear() { size_ = 0; } - - // Tries resizing the buffer to contain *count* elements. If T is a POD type - // the new elements may not be initialized. - FMT_CONSTEXPR20 void try_resize(size_t count) { - try_reserve(count); - size_ = count <= capacity_ ? count : capacity_; - } - - // Tries increasing the buffer capacity to *new_capacity*. It can increase the - // capacity by a smaller amount than requested but guarantees there is space - // for at least one additional element either by increasing the capacity or by - // flushing the buffer if it is full. - FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { - if (new_capacity > capacity_) grow(new_capacity); - } - - FMT_CONSTEXPR20 void push_back(const T& value) { - try_reserve(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end); - - template FMT_CONSTEXPR auto operator[](Idx index) -> T& { - return ptr_[index]; - } - template - FMT_CONSTEXPR auto operator[](Idx index) const -> const T& { - return ptr_[index]; - } -}; - -struct buffer_traits { - explicit buffer_traits(size_t) {} - auto count() const -> size_t { return 0; } - auto limit(size_t size) -> size_t { return size; } -}; - -class fixed_buffer_traits { - private: - size_t count_ = 0; - size_t limit_; - - public: - explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} - auto count() const -> size_t { return count_; } - auto limit(size_t size) -> size_t { - size_t n = limit_ > count_ ? limit_ - count_ : 0; - count_ += size; - return size < n ? size : n; - } -}; - -// A buffer that writes to an output iterator when flushed. -template -class iterator_buffer final : public Traits, public buffer { - private: - OutputIt out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == buffer_size) flush(); - } - - void flush() { - auto size = this->size(); - this->clear(); - out_ = copy_str(data_, data_ + this->limit(size), out_); - } - - public: - explicit iterator_buffer(OutputIt out, size_t n = buffer_size) - : Traits(n), buffer(data_, 0, buffer_size), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : Traits(other), buffer(data_, 0, buffer_size), out_(other.out_) {} - ~iterator_buffer() { flush(); } - - auto out() -> OutputIt { - flush(); - return out_; - } - auto count() const -> size_t { return Traits::count() + this->size(); } -}; - -template -class iterator_buffer final - : public fixed_buffer_traits, - public buffer { - private: - T* out_; - enum { buffer_size = 256 }; - T data_[buffer_size]; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() == this->capacity()) flush(); - } - - void flush() { - size_t n = this->limit(this->size()); - if (this->data() == out_) { - out_ += n; - this->set(data_, buffer_size); - } - this->clear(); - } - - public: - explicit iterator_buffer(T* out, size_t n = buffer_size) - : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} - iterator_buffer(iterator_buffer&& other) - : fixed_buffer_traits(other), - buffer(std::move(other)), - out_(other.out_) { - if (this->data() != out_) { - this->set(data_, buffer_size); - this->clear(); - } - } - ~iterator_buffer() { flush(); } - - auto out() -> T* { - flush(); - return out_; - } - auto count() const -> size_t { - return fixed_buffer_traits::count() + this->size(); - } -}; - -template class iterator_buffer final : public buffer { - protected: - FMT_CONSTEXPR20 void grow(size_t) override {} - - public: - explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} - - auto out() -> T* { return &*this->end(); } -}; - -// A buffer that writes to a container with the contiguous storage. -template -class iterator_buffer, - enable_if_t::value, - typename Container::value_type>> - final : public buffer { - private: - Container& container_; - - protected: - FMT_CONSTEXPR20 void grow(size_t capacity) override { - container_.resize(capacity); - this->set(&container_[0], capacity); - } - - public: - explicit iterator_buffer(Container& c) - : buffer(c.size()), container_(c) {} - explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) - : iterator_buffer(get_container(out)) {} - - auto out() -> std::back_insert_iterator { - return std::back_inserter(container_); - } -}; - -// A buffer that counts the number of code units written discarding the output. -template class counting_buffer final : public buffer { - private: - enum { buffer_size = 256 }; - T data_[buffer_size]; - size_t count_ = 0; - - protected: - FMT_CONSTEXPR20 void grow(size_t) override { - if (this->size() != buffer_size) return; - count_ += this->size(); - this->clear(); - } - - public: - counting_buffer() : buffer(data_, 0, buffer_size) {} - - auto count() -> size_t { return count_ + this->size(); } -}; -} // namespace detail - -template -FMT_CONSTEXPR void basic_format_parse_context::do_check_arg_id(int id) { - // Argument id is only checked at compile-time during parsing because - // formatting has its own validation. - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - if (id >= static_cast(this)->num_args()) - detail::throw_format_error("argument not found"); - } -} - -template -FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( - int arg_id) { - if (detail::is_constant_evaluated() && - (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { - using context = detail::compile_parse_context; - static_cast(this)->check_dynamic_spec(arg_id); - } -} - -FMT_EXPORT template class basic_format_arg; -FMT_EXPORT template class basic_format_args; -FMT_EXPORT template class dynamic_format_arg_store; - -// A formatter for objects of type T. -FMT_EXPORT -template -struct formatter { - // A deleted default constructor indicates a disabled formatter. - formatter() = delete; -}; - -// Specifies if T has an enabled formatter specialization. A type can be -// formattable even if it doesn't have a formatter e.g. via a conversion. -template -using has_formatter = - std::is_constructible>; - -// An output iterator that appends to a buffer. -// It is used to reduce symbol sizes for the common case. -class appender : public std::back_insert_iterator> { - using base = std::back_insert_iterator>; - - public: - using std::back_insert_iterator>::back_insert_iterator; - appender(base it) noexcept : base(it) {} - FMT_UNCHECKED_ITERATOR(appender); - - auto operator++() noexcept -> appender& { return *this; } - auto operator++(int) noexcept -> appender { return *this; } -}; - -namespace detail { - -template -constexpr auto has_const_formatter_impl(T*) - -> decltype(typename Context::template formatter_type().format( - std::declval(), std::declval()), - true) { - return true; -} -template -constexpr auto has_const_formatter_impl(...) -> bool { - return false; -} -template -constexpr auto has_const_formatter() -> bool { - return has_const_formatter_impl(static_cast(nullptr)); -} - -template -using buffer_appender = conditional_t::value, appender, - std::back_insert_iterator>>; - -// Maps an output iterator to a buffer. -template -auto get_buffer(OutputIt out) -> iterator_buffer { - return iterator_buffer(out); -} -template , Buf>::value)> -auto get_buffer(std::back_insert_iterator out) -> buffer& { - return get_container(out); -} - -template -FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { - return buf.out(); -} -template -auto get_iterator(buffer&, OutputIt out) -> OutputIt { - return out; -} - -struct view {}; - -template struct named_arg : view { - const Char* name; - const T& value; - named_arg(const Char* n, const T& v) : name(n), value(v) {} -}; - -template struct named_arg_info { - const Char* name; - int id; -}; - -template -struct arg_data { - // args_[0].named_args points to named_args_ to avoid bloating format_args. - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; - named_arg_info named_args_[NUM_NAMED_ARGS]; - - template - arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} - arg_data(const arg_data& other) = delete; - auto args() const -> const T* { return args_ + 1; } - auto named_args() -> named_arg_info* { return named_args_; } -}; - -template -struct arg_data { - // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. - T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; - - template - FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {} - FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; } - FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { - return nullptr; - } -}; - -template -inline void init_named_args(named_arg_info*, int, int) {} - -template struct is_named_arg : std::false_type {}; -template struct is_statically_named_arg : std::false_type {}; - -template -struct is_named_arg> : std::true_type {}; - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T&, const Tail&... args) { - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template ::value)> -void init_named_args(named_arg_info* named_args, int arg_count, - int named_arg_count, const T& arg, const Tail&... args) { - named_args[named_arg_count++] = {arg.name, arg_count}; - init_named_args(named_args, arg_count + 1, named_arg_count, args...); -} - -template -FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, - const Args&...) {} - -template constexpr auto count() -> size_t { return B ? 1 : 0; } -template constexpr auto count() -> size_t { - return (B1 ? 1 : 0) + count(); -} - -template constexpr auto count_named_args() -> size_t { - return count::value...>(); -} - -template -constexpr auto count_statically_named_args() -> size_t { - return count::value...>(); -} - -struct unformattable {}; -struct unformattable_char : unformattable {}; -struct unformattable_pointer : unformattable {}; - -template struct string_value { - const Char* data; - size_t size; -}; - -template struct named_arg_value { - const named_arg_info* data; - size_t size; -}; - -template struct custom_value { - using parse_context = typename Context::parse_context_type; - void* value; - void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); -}; - -// A formatting argument value. -template class value { - public: - using char_type = typename Context::char_type; - - union { - monostate no_value; - int int_value; - unsigned uint_value; - long long long_long_value; - unsigned long long ulong_long_value; - int128_opt int128_value; - uint128_opt uint128_value; - bool bool_value; - char_type char_value; - float float_value; - double double_value; - long double long_double_value; - const void* pointer; - string_value string; - custom_value custom; - named_arg_value named_args; - }; - - constexpr FMT_INLINE value() : no_value() {} - constexpr FMT_INLINE value(int val) : int_value(val) {} - constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} - constexpr FMT_INLINE value(long long val) : long_long_value(val) {} - constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} - FMT_INLINE value(int128_opt val) : int128_value(val) {} - FMT_INLINE value(uint128_opt val) : uint128_value(val) {} - constexpr FMT_INLINE value(float val) : float_value(val) {} - constexpr FMT_INLINE value(double val) : double_value(val) {} - FMT_INLINE value(long double val) : long_double_value(val) {} - constexpr FMT_INLINE value(bool val) : bool_value(val) {} - constexpr FMT_INLINE value(char_type val) : char_value(val) {} - FMT_CONSTEXPR FMT_INLINE value(const char_type* val) { - string.data = val; - if (is_constant_evaluated()) string.size = {}; - } - FMT_CONSTEXPR FMT_INLINE value(basic_string_view val) { - string.data = val.data(); - string.size = val.size(); - } - FMT_INLINE value(const void* val) : pointer(val) {} - FMT_INLINE value(const named_arg_info* args, size_t size) - : named_args{args, size} {} - - template FMT_CONSTEXPR20 FMT_INLINE value(T& val) { - using value_type = remove_const_t; - custom.value = const_cast(std::addressof(val)); - // Get the formatter type through the context to allow different contexts - // have different extension points, e.g. `formatter` for `format` and - // `printf_formatter` for `printf`. - custom.format = format_custom_arg< - value_type, typename Context::template formatter_type>; - } - value(unformattable); - value(unformattable_char); - value(unformattable_pointer); - - private: - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg(void* arg, - typename Context::parse_context_type& parse_ctx, - Context& ctx) { - auto f = Formatter(); - parse_ctx.advance_to(f.parse(parse_ctx)); - using qualified_type = - conditional_t(), const T, T>; - // Calling format through a mutable reference is deprecated. - ctx.advance_to(f.format(*static_cast(arg), ctx)); - } -}; - -// To minimize the number of types we need to deal with, long is translated -// either to int or to long long depending on its size. -enum { long_short = sizeof(long) == sizeof(int) }; -using long_type = conditional_t; -using ulong_type = conditional_t; - -template struct format_as_result { - template ::value || std::is_class::value)> - static auto map(U*) -> remove_cvref_t()))>; - static auto map(...) -> void; - - using type = decltype(map(static_cast(nullptr))); -}; -template using format_as_t = typename format_as_result::type; - -template -struct has_format_as - : bool_constant, void>::value> {}; - -// Maps formatting arguments to core types. -// arg_mapper reports errors by returning unformattable instead of using -// static_assert because it's used in the is_formattable trait. -template struct arg_mapper { - using char_type = typename Context::char_type; - - FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) - -> unsigned long long { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } - - template ::value || - std::is_same::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { - return val; - } - template ::value || -#ifdef __cpp_char8_t - std::is_same::value || -#endif - std::is_same::value || - std::is_same::value) && - !std::is_same::value, - int> = 0> - FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double { - return val; - } - - FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { - return val; - } - template ::value && !std::is_pointer::value && - std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> basic_string_view { - return to_string_view(val); - } - template ::value && !std::is_pointer::value && - !std::is_same>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { - return {}; - } - - FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } - FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* { - return val; - } - FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* { - return val; - } - - // Use SFINAE instead of a const T* parameter to avoid a conflict with the - // array overload. - template < - typename T, - FMT_ENABLE_IF( - std::is_pointer::value || std::is_member_pointer::value || - std::is_function::type>::value || - (std::is_array::value && - !std::is_convertible::value))> - FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { - return {}; - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { - return values; - } - - // Only map owning types because mapping views can be unsafe. - template , - FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) - -> decltype(FMT_DECLTYPE_THIS map(U())) { - return map(format_as(val)); - } - - template > - struct formattable : bool_constant() || - (has_formatter::value && - !std::is_const::value)> {}; - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T& val) -> T& { - return val; - } - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto do_map(T&) -> unformattable { - return {}; - } - - template , - FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || - std::is_union::value) && - !is_string::value && !is_char::value && - !is_named_arg::value && - !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) - -> decltype(FMT_DECLTYPE_THIS do_map(val)) { - return do_map(val); - } - - template ::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) { - return map(named_arg.value); - } - - auto map(...) -> unformattable { return {}; } -}; - -// A type constant after applying arg_mapper. -template -using mapped_type_constant = - type_constant().map(std::declval())), - typename Context::char_type>; - -enum { packed_arg_bits = 4 }; -// Maximum number of arguments with packed types. -enum { max_packed_args = 62 / packed_arg_bits }; -enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; -enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; - -template -auto copy_str(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} -template -auto copy_str(InputIt begin, InputIt end, - std::back_insert_iterator out) - -> std::back_insert_iterator { - get_container(out).append(begin, end); - return out; -} - -template -FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { - return detail::copy_str(rng.begin(), rng.end(), out); -} - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -// A workaround for gcc 4.8 to make void_t work in a SFINAE context. -template struct void_t_impl { - using type = void; -}; -template using void_t = typename void_t_impl::type; -#else -template using void_t = void; -#endif - -template -struct is_output_iterator : std::false_type {}; - -template -struct is_output_iterator< - It, T, - void_t::iterator_category, - decltype(*std::declval() = std::declval())>> - : std::true_type {}; - -template struct is_back_insert_iterator : std::false_type {}; -template -struct is_back_insert_iterator> - : std::true_type {}; - -// A type-erased reference to an std::locale to avoid a heavy include. -class locale_ref { - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr FMT_INLINE locale_ref() : locale_(nullptr) {} - template explicit locale_ref(const Locale& loc); - - explicit operator bool() const noexcept { return locale_ != nullptr; } - - template auto get() const -> Locale; -}; - -template constexpr auto encode_types() -> unsigned long long { - return 0; -} - -template -constexpr auto encode_types() -> unsigned long long { - return static_cast(mapped_type_constant::value) | - (encode_types() << packed_arg_bits); -} - -#if defined(__cpp_if_constexpr) -// This type is intentionally undefined, only used for errors -template struct type_is_unformattable_for; -#endif - -template -FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value { - using arg_type = remove_cvref_t().map(val))>; - - constexpr bool formattable_char = - !std::is_same::value; - static_assert(formattable_char, "Mixing character types is disallowed."); - - // Formatting of arbitrary pointers is disallowed. If you want to format a - // pointer cast it to `void*` or `const void*`. In particular, this forbids - // formatting of `[const] volatile char*` printed as bool by iostreams. - constexpr bool formattable_pointer = - !std::is_same::value; - static_assert(formattable_pointer, - "Formatting of non-void pointers is disallowed."); - - constexpr bool formattable = !std::is_same::value; -#if defined(__cpp_if_constexpr) - if constexpr (!formattable) { - type_is_unformattable_for _; - } -#endif - static_assert( - formattable, - "Cannot format an argument. To make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return {arg_mapper().map(val)}; -} - -template -FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg { - auto arg = basic_format_arg(); - arg.type_ = mapped_type_constant::value; - arg.value_ = make_arg(val); - return arg; -} - -template -FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { - return make_arg(val); -} -} // namespace detail -FMT_BEGIN_EXPORT - -// A formatting argument. Context is a template parameter for the compiled API -// where output can be unbuffered. -template class basic_format_arg { - private: - detail::value value_; - detail::type type_; - - template - friend FMT_CONSTEXPR auto detail::make_arg(T& value) - -> basic_format_arg; - - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)); - - friend class basic_format_args; - friend class dynamic_format_arg_store; - - using char_type = typename Context::char_type; - - template - friend struct detail::arg_data; - - basic_format_arg(const detail::named_arg_info* args, size_t size) - : value_(args, size) {} - - public: - class handle { - public: - explicit handle(detail::custom_value custom) : custom_(custom) {} - - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { - custom_.format(custom_.value, parse_ctx, ctx); - } - - private: - detail::custom_value custom_; - }; - - constexpr basic_format_arg() : type_(detail::type::none_type) {} - - constexpr explicit operator bool() const noexcept { - return type_ != detail::type::none_type; - } - - auto type() const -> detail::type { return type_; } - - auto is_integral() const -> bool { return detail::is_integral_type(type_); } - auto is_arithmetic() const -> bool { - return detail::is_arithmetic_type(type_); - } - - FMT_INLINE auto format_custom(const char_type* parse_begin, - typename Context::parse_context_type& parse_ctx, - Context& ctx) -> bool { - if (type_ != detail::type::custom_type) return false; - parse_ctx.advance_to(parse_begin); - value_.custom.format(value_.custom.value, parse_ctx, ctx); - return true; - } -}; - -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -// DEPRECATED! -template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); -} - -// Formatting context. -template class basic_format_context { - private: - OutputIt out_; - basic_format_args args_; - detail::locale_ref loc_; - - public: - using iterator = OutputIt; - using format_arg = basic_format_arg; - using format_args = basic_format_args; - using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; - - /** The character type for the output. */ - using char_type = Char; - - basic_format_context(basic_format_context&&) = default; - basic_format_context(const basic_format_context&) = delete; - void operator=(const basic_format_context&) = delete; - /** - Constructs a ``basic_format_context`` object. References to the arguments - are stored in the object so make sure they have appropriate lifetimes. - */ - constexpr basic_format_context(OutputIt out, format_args ctx_args, - detail::locale_ref loc = {}) - : out_(out), args_(ctx_args), loc_(loc) {} - - constexpr auto arg(int id) const -> format_arg { return args_.get(id); } - FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { - return args_.get(name); - } - FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { - return args_.get_id(name); - } - auto args() const -> const format_args& { return args_; } - - // DEPRECATED! - FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } - void on_error(const char* message) { error_handler().on_error(message); } - - // Returns an iterator to the beginning of the output range. - FMT_CONSTEXPR auto out() -> iterator { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { - if (!detail::is_back_insert_iterator()) out_ = it; - } - - FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } -}; - -template -using buffer_context = - basic_format_context, Char>; -using format_context = buffer_context; - -template -using is_formattable = bool_constant>() - .map(std::declval()))>::value>; - -/** - \rst - An array of references to arguments. It can be implicitly converted into - `~fmt::basic_format_args` for passing into type-erased formatting functions - such as `~fmt::vformat`. - \endrst - */ -template -class format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - static const size_t num_args = sizeof...(Args); - static constexpr size_t num_named_args = detail::count_named_args(); - static const bool is_packed = num_args <= detail::max_packed_args; - - using value_type = conditional_t, - basic_format_arg>; - - detail::arg_data - data_; - - friend class basic_format_args; - - static constexpr unsigned long long desc = - (is_packed ? detail::encode_types() - : detail::is_unpacked_bit | num_args) | - (num_named_args != 0 - ? static_cast(detail::has_named_args_bit) - : 0); - - public: - template - FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) - : -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - basic_format_args(*this), -#endif - data_{detail::make_arg(args)...} { - if (detail::const_check(num_named_args != 0)) - detail::init_named_args(data_.named_args(), 0, 0, args...); - } -}; - -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::format_context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ -// Arguments are taken by lvalue references to avoid some lifetime issues. -template -constexpr auto make_format_args(T&... args) - -> format_arg_store...> { - return {args...}; -} - -/** - \rst - Returns a named argument to be used in a formatting function. - It should only be used in a call to a formatting function or - `dynamic_format_arg_store::push_back`. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst - */ -template -inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); - return {name, arg}; -} -FMT_END_EXPORT - -/** - \rst - A view of a collection of formatting arguments. To avoid lifetime issues it - should only be used as a parameter type in type-erased functions such as - ``vformat``:: - - void vlog(string_view format_str, format_args args); // OK - format_args args = make_format_args(); // Error: dangling reference - \endrst - */ -template class basic_format_args { - public: - using size_type = int; - using format_arg = basic_format_arg; - - private: - // A descriptor that contains information about formatting arguments. - // If the number of arguments is less or equal to max_packed_args then - // argument types are passed in the descriptor. This reduces binary code size - // per formatting function call. - unsigned long long desc_; - union { - // If is_packed() returns true then argument values are stored in values_; - // otherwise they are stored in args_. This is done to improve cache - // locality and reduce compiled code size since storing larger objects - // may require more code (at least on x86-64) even if the same amount of - // data is actually copied to stack. It saves ~10% on the bloat test. - const detail::value* values_; - const format_arg* args_; - }; - - constexpr auto is_packed() const -> bool { - return (desc_ & detail::is_unpacked_bit) == 0; - } - auto has_named_args() const -> bool { - return (desc_ & detail::has_named_args_bit) != 0; - } - - FMT_CONSTEXPR auto type(int index) const -> detail::type { - int shift = index * detail::packed_arg_bits; - unsigned int mask = (1 << detail::packed_arg_bits) - 1; - return static_cast((desc_ >> shift) & mask); - } - - constexpr FMT_INLINE basic_format_args(unsigned long long desc, - const detail::value* values) - : desc_(desc), values_(values) {} - constexpr basic_format_args(unsigned long long desc, const format_arg* args) - : desc_(desc), args_(args) {} - - public: - constexpr basic_format_args() : desc_(0), args_(nullptr) {} - - /** - \rst - Constructs a `basic_format_args` object from `~fmt::format_arg_store`. - \endrst - */ - template - constexpr FMT_INLINE basic_format_args( - const format_arg_store& store) - : basic_format_args(format_arg_store::desc, - store.data_.args()) {} - - /** - \rst - Constructs a `basic_format_args` object from - `~fmt::dynamic_format_arg_store`. - \endrst - */ - constexpr FMT_INLINE basic_format_args( - const dynamic_format_arg_store& store) - : basic_format_args(store.get_types(), store.data()) {} - - /** - \rst - Constructs a `basic_format_args` object from a dynamic set of arguments. - \endrst - */ - constexpr basic_format_args(const format_arg* args, int count) - : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), - args) {} - - /** Returns the argument with the specified id. */ - FMT_CONSTEXPR auto get(int id) const -> format_arg { - format_arg arg; - if (!is_packed()) { - if (id < max_size()) arg = args_[id]; - return arg; - } - if (id >= detail::max_packed_args) return arg; - arg.type_ = type(id); - if (arg.type_ == detail::type::none_type) return arg; - arg.value_ = values_[id]; - return arg; - } - - template - auto get(basic_string_view name) const -> format_arg { - int id = get_id(name); - return id >= 0 ? get(id) : format_arg(); - } - - template - auto get_id(basic_string_view name) const -> int { - if (!has_named_args()) return -1; - const auto& named_args = - (is_packed() ? values_[-1] : args_[-1].value_).named_args; - for (size_t i = 0; i < named_args.size; ++i) { - if (named_args.data[i].name == name) return named_args.data[i].id; - } - return -1; - } - - auto max_size() const -> int { - unsigned long long max_packed = detail::max_packed_args; - return static_cast(is_packed() ? max_packed - : desc_ & ~detail::is_unpacked_bit); - } -}; - -/** An alias to ``basic_format_args``. */ -// A separate type would result in shorter symbols but break ABI compatibility -// between clang and gcc on ARM (#1919). -FMT_EXPORT using format_args = basic_format_args; - -// We cannot use enum classes as bit fields because of a gcc bug, so we put them -// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414). -// Additionally, if an underlying type is specified, older gcc incorrectly warns -// that the type is too small. Both bugs are fixed in gcc 9.3. -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903 -# define FMT_ENUM_UNDERLYING_TYPE(type) -#else -# define FMT_ENUM_UNDERLYING_TYPE(type) : type -#endif -namespace align { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, - numeric}; -} -using align_t = align::type; -namespace sign { -enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space}; -} -using sign_t = sign::type; - -namespace detail { - -// Workaround an array initialization issue in gcc 4.8. -template struct fill_t { - private: - enum { max_size = 4 }; - Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; - unsigned char size_ = 1; - - public: - FMT_CONSTEXPR void operator=(basic_string_view s) { - auto size = s.size(); - FMT_ASSERT(size <= max_size, "invalid fill"); - for (size_t i = 0; i < size; ++i) data_[i] = s[i]; - size_ = static_cast(size); - } - - constexpr auto size() const -> size_t { return size_; } - constexpr auto data() const -> const Char* { return data_; } - - FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; } - FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& { - return data_[index]; - } -}; -} // namespace detail - -enum class presentation_type : unsigned char { - none, - dec, // 'd' - oct, // 'o' - hex_lower, // 'x' - hex_upper, // 'X' - bin_lower, // 'b' - bin_upper, // 'B' - hexfloat_lower, // 'a' - hexfloat_upper, // 'A' - exp_lower, // 'e' - exp_upper, // 'E' - fixed_lower, // 'f' - fixed_upper, // 'F' - general_lower, // 'g' - general_upper, // 'G' - chr, // 'c' - string, // 's' - pointer, // 'p' - debug // '?' -}; - -// Format specifiers for built-in and string types. -template struct format_specs { - int width; - int precision; - presentation_type type; - align_t align : 4; - sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). - bool localized : 1; - detail::fill_t fill; - - constexpr format_specs() - : width(0), - precision(-1), - type(presentation_type::none), - align(align::none), - sign(sign::none), - alt(false), - localized(false) {} -}; - -namespace detail { - -enum class arg_id_kind { none, index, name }; - -// An argument reference. -template struct arg_ref { - FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {} - - FMT_CONSTEXPR explicit arg_ref(int index) - : kind(arg_id_kind::index), val(index) {} - FMT_CONSTEXPR explicit arg_ref(basic_string_view name) - : kind(arg_id_kind::name), val(name) {} - - FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& { - kind = arg_id_kind::index; - val.index = idx; - return *this; - } - - arg_id_kind kind; - union value { - FMT_CONSTEXPR value(int idx = 0) : index(idx) {} - FMT_CONSTEXPR value(basic_string_view n) : name(n) {} - - int index; - basic_string_view name; - } val; -}; - -// Format specifiers with width and precision resolved at formatting rather -// than parsing time to allow reusing the same parsed specifiers with -// different sets of arguments (precompilation of format strings). -template -struct dynamic_format_specs : format_specs { - arg_ref width_ref; - arg_ref precision_ref; -}; - -// Converts a character to ASCII. Returns '\0' on conversion failure. -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} -template ::value)> -constexpr auto to_ascii(Char c) -> char { - return c <= 0xff ? static_cast(c) : '\0'; -} - -// Returns the number of code units in a code point or 1 on error. -template -FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { - if (const_check(sizeof(Char) != 1)) return 1; - auto c = static_cast(*begin); - return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; -} - -// Return the result via the out param to workaround gcc bug 77539. -template -FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { - for (out = first; out != last; ++out) { - if (*out == value) return true; - } - return false; -} - -template <> -inline auto find(const char* first, const char* last, char value, - const char*& out) -> bool { - out = static_cast( - std::memchr(first, value, to_unsigned(last - first))); - return out != nullptr; -} - -// Parses the range [begin, end) as an unsigned integer. This function assumes -// that the range is non-empty and the first character is a digit. -template -FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end, - int error_value) noexcept -> int { - FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); - unsigned value = 0, prev = 0; - auto p = begin; - do { - prev = value; - value = value * 10 + unsigned(*p - '0'); - ++p; - } while (p != end && '0' <= *p && *p <= '9'); - auto num_digits = p - begin; - begin = p; - if (num_digits <= std::numeric_limits::digits10) - return static_cast(value); - // Check for overflow. - const unsigned max = to_unsigned((std::numeric_limits::max)()); - return num_digits == std::numeric_limits::digits10 + 1 && - prev * 10ull + unsigned(p[-1] - '0') <= max - ? static_cast(value) - : error_value; -} - -FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { - switch (c) { - case '<': - return align::left; - case '>': - return align::right; - case '^': - return align::center; - } - return align::none; -} - -template constexpr auto is_name_start(Char c) -> bool { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; -} - -template -FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - Char c = *begin; - if (c >= '0' && c <= '9') { - int index = 0; - constexpr int max = (std::numeric_limits::max)(); - if (c != '0') - index = parse_nonnegative_int(begin, end, max); - else - ++begin; - if (begin == end || (*begin != '}' && *begin != ':')) - throw_format_error("invalid format string"); - else - handler.on_index(index); - return begin; - } - if (!is_name_start(c)) { - throw_format_error("invalid format string"); - return begin; - } - auto it = begin; - do { - ++it; - } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); - handler.on_name({begin, to_unsigned(it - begin)}); - return it; -} - -template -FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); - Char c = *begin; - if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); - handler.on_auto(); - return begin; -} - -template struct dynamic_spec_id_handler { - basic_format_parse_context& ctx; - arg_ref& ref; - - FMT_CONSTEXPR void on_auto() { - int id = ctx.next_arg_id(); - ref = arg_ref(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_index(int id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - ctx.check_dynamic_spec(id); - } - FMT_CONSTEXPR void on_name(basic_string_view id) { - ref = arg_ref(id); - ctx.check_arg_id(id); - } -}; - -// Parses [integer | "{" [arg_id] "}"]. -template -FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - FMT_ASSERT(begin != end, ""); - if ('0' <= *begin && *begin <= '9') { - int val = parse_nonnegative_int(begin, end, -1); - if (val != -1) - value = val; - else - throw_format_error("number is too big"); - } else if (*begin == '{') { - ++begin; - auto handler = dynamic_spec_id_handler{ctx, ref}; - if (begin != end) begin = parse_arg_id(begin, end, handler); - if (begin != end && *begin == '}') return ++begin; - throw_format_error("invalid format string"); - } - return begin; -} - -template -FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, - int& value, arg_ref& ref, - basic_format_parse_context& ctx) - -> const Char* { - ++begin; - if (begin == end || *begin == '}') { - throw_format_error("invalid precision"); - return begin; - } - return parse_dynamic_spec(begin, end, value, ref, ctx); -} - -enum class state { start, align, sign, hash, zero, width, precision, locale }; - -// Parses standard format specifiers. -template -FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( - const Char* begin, const Char* end, dynamic_format_specs& specs, - basic_format_parse_context& ctx, type arg_type) -> const Char* { - auto c = '\0'; - if (end - begin > 1) { - auto next = to_ascii(begin[1]); - c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; - } else { - if (begin == end) return begin; - c = to_ascii(*begin); - } - - struct { - state current_state = state::start; - FMT_CONSTEXPR void operator()(state s, bool valid = true) { - if (current_state >= s || !valid) - throw_format_error("invalid format specifier"); - current_state = s; - } - } enter_state; - - using pres = presentation_type; - constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; - struct { - const Char*& begin; - dynamic_format_specs& specs; - type arg_type; - - FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { - if (!in(arg_type, set)) { - if (arg_type == type::none_type) return begin; - throw_format_error("invalid format specifier"); - } - specs.type = pres_type; - return begin + 1; - } - } parse_presentation_type{begin, specs, arg_type}; - - for (;;) { - switch (c) { - case '<': - case '>': - case '^': - enter_state(state::align); - specs.align = parse_align(c); - ++begin; - break; - case '+': - case '-': - case ' ': - if (arg_type == type::none_type) return begin; - enter_state(state::sign, in(arg_type, sint_set | float_set)); - switch (c) { - case '+': - specs.sign = sign::plus; - break; - case '-': - specs.sign = sign::minus; - break; - case ' ': - specs.sign = sign::space; - break; - } - ++begin; - break; - case '#': - if (arg_type == type::none_type) return begin; - enter_state(state::hash, is_arithmetic_type(arg_type)); - specs.alt = true; - ++begin; - break; - case '0': - enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) { - if (arg_type == type::none_type) return begin; - throw_format_error("format specifier requires numeric argument"); - } - if (specs.align == align::none) { - // Ignore 0 if align is specified for compatibility with std::format. - specs.align = align::numeric; - specs.fill[0] = Char('0'); - } - ++begin; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '{': - enter_state(state::width); - begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); - break; - case '.': - if (arg_type == type::none_type) return begin; - enter_state(state::precision, - in(arg_type, float_set | string_set | cstring_set)); - begin = parse_precision(begin, end, specs.precision, specs.precision_ref, - ctx); - break; - case 'L': - if (arg_type == type::none_type) return begin; - enter_state(state::locale, is_arithmetic_type(arg_type)); - specs.localized = true; - ++begin; - break; - case 'd': - return parse_presentation_type(pres::dec, integral_set); - case 'o': - return parse_presentation_type(pres::oct, integral_set); - case 'x': - return parse_presentation_type(pres::hex_lower, integral_set); - case 'X': - return parse_presentation_type(pres::hex_upper, integral_set); - case 'b': - return parse_presentation_type(pres::bin_lower, integral_set); - case 'B': - return parse_presentation_type(pres::bin_upper, integral_set); - case 'a': - return parse_presentation_type(pres::hexfloat_lower, float_set); - case 'A': - return parse_presentation_type(pres::hexfloat_upper, float_set); - case 'e': - return parse_presentation_type(pres::exp_lower, float_set); - case 'E': - return parse_presentation_type(pres::exp_upper, float_set); - case 'f': - return parse_presentation_type(pres::fixed_lower, float_set); - case 'F': - return parse_presentation_type(pres::fixed_upper, float_set); - case 'g': - return parse_presentation_type(pres::general_lower, float_set); - case 'G': - return parse_presentation_type(pres::general_upper, float_set); - case 'c': - if (arg_type == type::bool_type) - throw_format_error("invalid format specifier"); - return parse_presentation_type(pres::chr, integral_set); - case 's': - return parse_presentation_type(pres::string, - bool_set | string_set | cstring_set); - case 'p': - return parse_presentation_type(pres::pointer, pointer_set | cstring_set); - case '?': - return parse_presentation_type(pres::debug, - char_set | string_set | cstring_set); - case '}': - return begin; - default: { - if (*begin == '}') return begin; - // Parse fill and alignment. - auto fill_end = begin + code_point_length(begin); - if (end - fill_end <= 0) { - throw_format_error("invalid format specifier"); - return begin; - } - if (*begin == '{') { - throw_format_error("invalid fill character '{'"); - return begin; - } - auto align = parse_align(to_ascii(*fill_end)); - enter_state(state::align, align != align::none); - specs.fill = {begin, to_unsigned(fill_end - begin)}; - specs.align = align; - begin = fill_end + 1; - } - } - if (begin == end) return begin; - c = to_ascii(*begin); - } -} - -template -FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - struct id_adapter { - Handler& handler; - int arg_id; - - FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void on_name(basic_string_view id) { - arg_id = handler.on_arg_id(id); - } - }; - - ++begin; - if (begin == end) return handler.on_error("invalid format string"), end; - if (*begin == '}') { - handler.on_replacement_field(handler.on_arg_id(), begin); - } else if (*begin == '{') { - handler.on_text(begin, begin + 1); - } else { - auto adapter = id_adapter{handler, 0}; - begin = parse_arg_id(begin, end, adapter); - Char c = begin != end ? *begin : Char(); - if (c == '}') { - handler.on_replacement_field(adapter.arg_id, begin); - } else if (c == ':') { - begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); - if (begin == end || *begin != '}') - return handler.on_error("unknown format specifier"), end; - } else { - return handler.on_error("missing '}' in format string"), end; - } - } - return begin + 1; -} - -template -FMT_CONSTEXPR FMT_INLINE void parse_format_string( - basic_string_view format_str, Handler&& handler) { - auto begin = format_str.data(); - auto end = begin + format_str.size(); - if (end - begin < 32) { - // Use a simple loop instead of memchr for small strings. - const Char* p = begin; - while (p != end) { - auto c = *p++; - if (c == '{') { - handler.on_text(begin, p - 1); - begin = p = parse_replacement_field(p - 1, end, handler); - } else if (c == '}') { - if (p == end || *p != '}') - return handler.on_error("unmatched '}' in format string"); - handler.on_text(begin, p); - begin = ++p; - } - } - handler.on_text(begin, end); - return; - } - struct writer { - FMT_CONSTEXPR void operator()(const Char* from, const Char* to) { - if (from == to) return; - for (;;) { - const Char* p = nullptr; - if (!find(from, to, Char('}'), p)) - return handler_.on_text(from, to); - ++p; - if (p == to || *p != '}') - return handler_.on_error("unmatched '}' in format string"); - handler_.on_text(from, p); - from = p + 1; - } - } - Handler& handler_; - } write = {handler}; - while (begin != end) { - // Doing two passes with memchr (one for '{' and another for '}') is up to - // 2.5x faster than the naive one-pass implementation on big format strings. - const Char* p = begin; - if (*begin != '{' && !find(begin + 1, end, Char('{'), p)) - return write(begin, end); - write(begin, p); - begin = parse_replacement_field(p, end, handler); - } -} - -template ::value> struct strip_named_arg { - using type = T; -}; -template struct strip_named_arg { - using type = remove_cvref_t; -}; - -template -FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) - -> decltype(ctx.begin()) { - using char_type = typename ParseContext::char_type; - using context = buffer_context; - using mapped_type = conditional_t< - mapped_type_constant::value != type::custom_type, - decltype(arg_mapper().map(std::declval())), - typename strip_named_arg::type>; -#if defined(__cpp_if_constexpr) - if constexpr (std::is_default_constructible< - formatter>::value) { - return formatter().parse(ctx); - } else { - type_is_unformattable_for _; - return ctx.begin(); - } -#else - return formatter().parse(ctx); -#endif -} - -// Checks char specs and returns true iff the presentation type is char-like. -template -FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { - if (specs.type != presentation_type::none && - specs.type != presentation_type::chr && - specs.type != presentation_type::debug) { - return false; - } - if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) - throw_format_error("invalid format specifier for char"); - return true; -} - -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -template -constexpr auto get_arg_index_by_name(basic_string_view name) -> int { - if constexpr (is_statically_named_arg()) { - if (name == T::name) return N; - } - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name(name); - (void)name; // Workaround an MSVC bug about "unused" parameter. - return -1; -} -#endif - -template -FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - if constexpr (sizeof...(Args) > 0) - return get_arg_index_by_name<0, Args...>(name); -#endif - (void)name; - return -1; -} - -template class format_string_checker { - private: - using parse_context_type = compile_parse_context; - static constexpr int num_args = sizeof...(Args); - - // Format specifier parsing function. - // In the future basic_format_parse_context will replace compile_parse_context - // here and will use is_constant_evaluated and downcasting to access the data - // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1. - using parse_func = const Char* (*)(parse_context_type&); - - type types_[num_args > 0 ? static_cast(num_args) : 1]; - parse_context_type context_; - parse_func parse_funcs_[num_args > 0 ? static_cast(num_args) : 1]; - - public: - explicit FMT_CONSTEXPR format_string_checker(basic_string_view fmt) - : types_{mapped_type_constant>::value...}, - context_(fmt, num_args, types_), - parse_funcs_{&parse_format_specs...} {} - - FMT_CONSTEXPR void on_text(const Char*, const Char*) {} - - FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return context_.check_arg_id(id), id; - } - FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS - auto index = get_arg_index_by_name(id); - if (index < 0) on_error("named argument is not found"); - return index; -#else - (void)id; - on_error("compile-time checks for named arguments require C++20 support"); - return 0; -#endif - } - - FMT_CONSTEXPR void on_replacement_field(int id, const Char* begin) { - on_format_specs(id, begin, begin); // Call parse() on empty specs. - } - - FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*) - -> const Char* { - context_.advance_to(begin); - // id >= 0 check is a workaround for gcc 10 bug (#2065). - return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin; - } - - FMT_CONSTEXPR void on_error(const char* message) { - throw_format_error(message); - } -}; - -// Reports a compile-time error if S is not a valid format string. -template ::value)> -FMT_INLINE void check_format_string(const S&) { -#ifdef FMT_ENFORCE_COMPILE_STRING - static_assert(is_compile_string::value, - "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " - "FMT_STRING."); -#endif -} -template ::value)> -void check_format_string(S format_str) { - using char_t = typename S::char_type; - FMT_CONSTEXPR auto s = basic_string_view(format_str); - using checker = format_string_checker...>; - FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); - ignore_unused(error); -} - -template struct vformat_args { - using type = basic_format_args< - basic_format_context>, Char>>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -// Use vformat_args and avoid type_identity to keep symbols short. -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}); - -FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); -#ifndef _WIN32 -inline void vprint_mojibake(std::FILE*, string_view, format_args) {} -#endif -} // namespace detail - -FMT_BEGIN_EXPORT - -// A formatter specialization for natively supported types. -template -struct formatter::value != - detail::type::custom_type>> { - private: - detail::dynamic_format_specs specs_; - - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - auto type = detail::type_constant::value; - auto end = - detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); - if (type == detail::type::char_type) detail::check_char_specs(specs_); - return end; - } - - template ::value, - FMT_ENABLE_IF(U == detail::type::string_type || - U == detail::type::cstring_type || - U == detail::type::char_type)> - FMT_CONSTEXPR void set_debug_format(bool set = true) { - specs_.type = set ? presentation_type::debug : presentation_type::none; - } - - template - FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const - -> decltype(ctx.out()); -}; - -template struct runtime_format_string { - basic_string_view str; -}; - -/** A compile-time format string. */ -template class basic_format_string { - private: - basic_string_view str_; - - public: - template >::value)> - FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { - static_assert( - detail::count< - (std::is_base_of>::value && - std::is_reference::value)...>() == 0, - "passing views as lvalues is disallowed"); -#ifdef FMT_HAS_CONSTEVAL - if constexpr (detail::count_named_args() == - detail::count_statically_named_args()) { - using checker = - detail::format_string_checker...>; - detail::parse_format_string(str_, checker(s)); - } -#else - detail::check_format_string(s); -#endif - } - basic_format_string(runtime_format_string fmt) : str_(fmt.str) {} - - FMT_INLINE operator basic_string_view() const { return str_; } - FMT_INLINE auto get() const -> basic_string_view { return str_; } -}; - -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 -// Workaround broken conversion on older gcc. -template using format_string = string_view; -inline auto runtime(string_view s) -> string_view { return s; } -#else -template -using format_string = basic_format_string...>; -/** - \rst - Creates a runtime format string. - - **Example**:: - - // Check format string at runtime instead of compile-time. - fmt::print(fmt::runtime("{:d}"), "I am not a number"); - \endrst - */ -inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } -#endif - -FMT_API auto vformat(string_view fmt, format_args args) -> std::string; - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and returns the result - as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}.", 42); - \endrst -*/ -template -FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) - -> std::string { - return vformat(fmt, fmt::make_format_args(args...)); -} - -/** Formats a string and writes the output to ``out``. */ -template ::value)> -auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { - auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, fmt, args, {}); - return detail::get_iterator(buf, out); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes the result to - the output iterator ``out`` and returns the iterator past the end of the output - range. `format_to` does not append a terminating null character. - - **Example**:: - - auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); - \endrst - */ -template ::value)> -FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) - -> OutputIt { - return vformat_to(out, fmt, fmt::make_format_args(args...)); -} - -template struct format_to_n_result { - /** Iterator past the end of the output range. */ - OutputIt out; - /** Total (not truncated) output size. */ - size_t size; -}; - -template ::value)> -auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) - -> format_to_n_result { - using traits = detail::fixed_buffer_traits; - auto buf = detail::iterator_buffer(out, n); - detail::vformat_to(buf, fmt, args, {}); - return {buf.out(), buf.count()}; -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` - characters of the result to the output iterator ``out`` and returns the total - (not truncated) output size and the iterator past the end of the output range. - `format_to_n` does not append a terminating null character. - \endrst - */ -template ::value)> -FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, - T&&... args) -> format_to_n_result { - return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); -} - -/** Returns the number of chars in the output of ``format(fmt, args...)``. */ -template -FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, - T&&... args) -> size_t { - auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt, fmt::make_format_args(args...), {}); - return buf.count(); -} - -FMT_API void vprint(string_view fmt, format_args args); -FMT_API void vprint(std::FILE* f, string_view fmt, format_args args); - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout``. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -template -FMT_INLINE void print(format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(fmt, vargs) - : detail::vprint_mojibake(stdout, fmt, vargs); -} - -/** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f``. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst - */ -template -FMT_INLINE void print(std::FILE* f, format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprint(f, fmt, vargs) - : detail::vprint_mojibake(f, fmt, vargs); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f`` followed by a newline. - */ -template -FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { - return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); -} - -/** - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout`` followed by a newline. - */ -template -FMT_INLINE void println(format_string fmt, T&&... args) { - return fmt::println(stdout, fmt, std::forward(args)...); -} - -FMT_END_EXPORT -FMT_GCC_PRAGMA("GCC pop_options") -FMT_END_NAMESPACE - -#ifdef FMT_HEADER_ONLY -# include "format.h" -#endif -#endif // FMT_CORE_H_ +#include "format.h" diff --git a/src/3rdparty/fmt/format-inl.h b/src/3rdparty/fmt/format-inl.h index e9a4ca453a..a5b79dbe49 100644 --- a/src/3rdparty/fmt/format-inl.h +++ b/src/3rdparty/fmt/format-inl.h @@ -8,36 +8,36 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#include -#include // errno -#include -#include -#include - -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -# include +#ifndef FMT_MODULE +# include +# include // errno +# include +# include +# include #endif -#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) # include // _isatty #endif #include "format.h" +#if FMT_USE_LOCALE +# include +#endif + +#ifndef FMT_FUNC +# define FMT_FUNC +#endif + FMT_BEGIN_NAMESPACE namespace detail { FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when - // writing to stderr fails - std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); - // Chosen instead of std::abort to satisfy Clang in CUDA mode during device - // code pass. - std::terminate(); -} - -FMT_FUNC void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); + // writing to stderr fails. + fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + abort(); } FMT_FUNC void format_error_code(detail::buffer& out, int error_code, @@ -56,89 +56,105 @@ FMT_FUNC void format_error_code(detail::buffer& out, int error_code, ++error_code_size; } error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); - auto it = buffer_appender(out); + auto it = appender(out); if (message.size() <= inline_buffer_size - error_code_size) 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, ""); } -FMT_FUNC void report_error(format_func func, int error_code, - const char* message) noexcept { +FMT_FUNC void do_report_error(format_func func, int error_code, + const char* message) noexcept { memory_buffer full_message; func(full_message, error_code, message); - // Don't use fwrite_fully because the latter may throw. + // Don't use fwrite_all because the latter may throw. if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) std::fputc('\n', stderr); } // A wrapper around fwrite that throws on error. -inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) { +inline void fwrite_all(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"))); } -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +#if FMT_USE_LOCALE +using std::locale; +using std::numpunct; +using std::use_facet; + template locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same::value, ""); + static_assert(std::is_same::value, ""); } +#else +struct locale {}; +template struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template Facet use_facet(locale) { return {}; } +#endif // FMT_USE_LOCALE template auto locale_ref::get() const -> Locale { - static_assert(std::is_same::value, ""); - return locale_ ? *static_cast(locale_) : std::locale(); + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); } template FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { - auto& facet = std::use_facet>(loc.get()); + auto&& facet = use_facet>(loc.get()); auto grouping = facet.grouping(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } template FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { - return std::use_facet>(loc.get()) - .decimal_point(); + return use_facet>(loc.get()).decimal_point(); } -#else -template -FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result { - return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; -} -template FMT_FUNC Char decimal_point_impl(locale_ref) { - return '.'; -} -#endif +#if FMT_USE_LOCALE FMT_FUNC auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + const format_specs& specs, locale_ref loc) -> bool { auto locale = loc.get(); // We cannot use the num_put facet because it may produce output in // a wrong encoding. using facet = format_facet; if (std::has_facet(locale)) - return std::use_facet(locale).put(out, value, specs); + return use_facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs); -#endif - return false; } +#endif } // namespace detail +FMT_FUNC void report_error(const char* message) { +#if FMT_USE_EXCEPTIONS + // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings + // from MSVC. + FMT_THROW(format_error(message)); +#else + fputs(message, stderr); + abort(); +#endif +} + template typename Locale::id format_facet::id; -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR template format_facet::format_facet(Locale& loc) { - auto& numpunct = std::use_facet>(loc); - grouping_ = numpunct.grouping(); - if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); + auto& np = detail::use_facet>(loc); + grouping_ = np.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); } +#if FMT_USE_LOCALE template <> FMT_API FMT_FUNC auto format_facet::do_put( - appender out, loc_value val, const format_specs<>& specs) const -> bool { + appender out, loc_value val, const format_specs& specs) const -> bool { return val.visit( detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); } @@ -1411,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept { FMT_TRY { auto ec = std::error_code(error_code, std::generic_category()); - write(std::back_inserter(out), std::system_error(ec, message).what()); + detail::write(appender(out), std::system_error(ec, message).what()); return; } FMT_CATCH(...) {} @@ -1420,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, FMT_FUNC void report_system_error(int error_code, const char* message) noexcept { - report_error(format_system_error, error_code, message); + do_report_error(format_system_error, error_code, message); } FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { @@ -1432,7 +1448,251 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { } namespace detail { -#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) + +FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, + locale_ref loc) { + auto out = appender(buf); + if (fmt.size() == 2 && equal2(fmt.data(), "{}")) + return args.get(0).visit(default_arg_formatter{out}); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} + +template struct span { + T* data; + size_t size; +}; + +template auto flockfile(F* f) -> decltype(_lock_file(f)) { + _lock_file(f); +} +template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { + _unlock_file(f); +} + +#ifndef getc_unlocked +template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { + return _fgetc_nolock(f); +} +#endif + +template +struct has_flockfile : std::false_type {}; + +template +struct has_flockfile()))>> + : std::true_type {}; + +// A FILE wrapper. F is FILE defined as a template parameter to make system API +// detection work. +template class file_base { + public: + F* file_; + + public: + file_base(F* file) : file_(file) {} + operator F*() const { return file_; } + + // Reads a code unit from the stream. + auto get() -> int { + int result = getc_unlocked(file_); + if (result == EOF && ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); + return result; + } + + // Puts the code unit back into the stream buffer. + void unget(char c) { + if (ungetc(c, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); + } + + void flush() { fflush(this->file_); } +}; + +// A FILE wrapper for glibc. +template class glibc_file : public file_base { + private: + enum { + line_buffered = 0x200, // _IO_LINE_BUF + unbuffered = 2 // _IO_UNBUFFERED + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_IO_write_ptr) return; + // Force buffer initialization by placing and removing a char in a buffer. + assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end); + putc_unlocked(0, this->file_); + --this->file_->_IO_write_ptr; + } + + // Returns the file's read buffer. + auto get_read_buffer() const -> span { + auto ptr = this->file_->_IO_read_ptr; + return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; + } + + // Returns the file's write buffer. + auto get_write_buffer() const -> span { + auto ptr = this->file_->_IO_write_ptr; + return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; + } + + void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + char* end = this->file_->_IO_write_end; + return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); + } + + void flush() { fflush_unlocked(this->file_); } +}; + +// A FILE wrapper for Apple's libc. +template class apple_file : public file_base { + private: + enum { + line_buffered = 1, // __SNBF + unbuffered = 2 // __SLBF + }; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { + return (this->file_->_flags & unbuffered) == 0; + } + + void init_buffer() { + if (this->file_->_p) return; + // Force buffer initialization by placing and removing a char in a buffer. + putc_unlocked(0, this->file_); + --this->file_->_p; + ++this->file_->_w; + } + + auto get_read_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_r)}; + } + + auto get_write_buffer() const -> span { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_bf._base + this->file_->_bf._size - + this->file_->_p)}; + } + + void advance_write_buffer(size_t size) { + this->file_->_p += size; + this->file_->_w -= size; + } + + bool needs_flush() const { + if ((this->file_->_flags & line_buffered) == 0) return false; + return memchr(this->file_->_p + this->file_->_w, '\n', + to_unsigned(-this->file_->_w)); + } +}; + +// A fallback FILE wrapper. +template class fallback_file : public file_base { + private: + char next_; // The next unconsumed character in the buffer. + bool has_next_ = false; + + public: + using file_base::file_base; + + auto is_buffered() const -> bool { return false; } + auto needs_flush() const -> bool { return false; } + void init_buffer() {} + + auto get_read_buffer() const -> span { + return {&next_, has_next_ ? 1u : 0u}; + } + + auto get_write_buffer() const -> span { return {nullptr, 0}; } + + void advance_write_buffer(size_t) {} + + auto get() -> int { + has_next_ = false; + return file_base::get(); + } + + void unget(char c) { + file_base::unget(c); + next_ = c; + has_next_ = true; + } +}; + +#ifndef FMT_USE_FALLBACK_FILE +# define FMT_USE_FALLBACK_FILE 0 +#endif + +template +auto get_file(F* f, int) -> apple_file { + return f; +} +template +inline auto get_file(F* f, int) -> glibc_file { + return f; +} + +inline auto get_file(FILE* f, ...) -> fallback_file { return f; } + +using file_ref = decltype(get_file(static_cast(nullptr), 0)); + +template +class file_print_buffer : public buffer { + public: + explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} +}; + +template +class file_print_buffer::value>> + : public buffer { + private: + file_ref file_; + + static void grow(buffer& base, size_t) { + auto& self = static_cast(base); + self.file_.advance_write_buffer(self.size()); + if (self.file_.get_write_buffer().size == 0) self.file_.flush(); + auto buf = self.file_.get_write_buffer(); + FMT_ASSERT(buf.size > 0, ""); + self.set(buf.data, buf.size); + self.clear(); + } + + public: + explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { + flockfile(f); + file_.init_buffer(); + auto buf = file_.get_write_buffer(); + set(buf.data, buf.size); + } + ~file_print_buffer() { + file_.advance_write_buffer(size()); + bool flush = file_.needs_flush(); + F* f = file_; // Make funlockfile depend on the template parameter F + funlockfile(f); // for the system API detection to work. + if (flush) fflush(file_); + } +}; + +#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) FMT_FUNC auto write_console(int, string_view) -> bool { return false; } #else using dword = conditional_t; @@ -1448,31 +1708,47 @@ FMT_FUNC bool write_console(int fd, string_view text) { #ifdef _WIN32 // Print assuming legacy (non-Unicode) encoding. -FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, + bool newline) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); - fwrite_fully(buffer.data(), buffer.size(), f); + if (newline) buffer.push_back('\n'); + fwrite_all(buffer.data(), buffer.size(), f); } #endif FMT_FUNC void print(std::FILE* f, string_view text) { -#ifdef _WIN32 +#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) int fd = _fileno(f); if (_isatty(fd)) { std::fflush(f); if (write_console(fd, text)) return; } #endif - fwrite_fully(text.data(), text.size(), f); + fwrite_all(text.data(), text.size(), f); } } // namespace detail -FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); detail::print(f, {buffer.data(), buffer.size()}); } +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) + return vprint_buffered(f, fmt, args); + auto&& buffer = detail::file_print_buffer<>(f); + return detail::vformat_to(buffer, fmt, args); +} + +FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + buffer.push_back('\n'); + detail::print(f, {buffer.data(), buffer.size()}); +} + FMT_FUNC void vprint(string_view fmt, format_args args) { vprint(stdout, fmt, args); } diff --git a/src/3rdparty/fmt/format.cc b/src/3rdparty/fmt/format.cc new file mode 100644 index 0000000000..b5ff07508b --- /dev/null +++ b/src/3rdparty/fmt/format.cc @@ -0,0 +1,46 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "format-inl.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +template FMT_API auto dragonbox::to_decimal(float x) noexcept + -> dragonbox::decimal_fp; +template FMT_API auto dragonbox::to_decimal(double x) noexcept + -> dragonbox::decimal_fp; + +#if FMT_USE_LOCALE +// DEPRECATED! locale_ref in the detail namespace +template FMT_API locale_ref::locale_ref(const std::locale& loc); +template FMT_API auto locale_ref::get() const -> std::locale; +#endif + +// Explicit instantiations for char. + +template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +template FMT_API auto decimal_point_impl(locale_ref) -> char; + +// DEPRECATED! +template FMT_API void buffer::append(const char*, const char*); + +// DEPRECATED! +template FMT_API void vformat_to(buffer&, string_view, + typename vformat_args<>::type, locale_ref); + +// Explicit instantiations for wchar_t. + +template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; + +template FMT_API void buffer::append(const wchar_t*, const wchar_t*); + +} // namespace detail +FMT_END_NAMESPACE diff --git a/src/3rdparty/fmt/format.h b/src/3rdparty/fmt/format.h index 97f0e1fb12..287e71631e 100644 --- a/src/3rdparty/fmt/format.h +++ b/src/3rdparty/fmt/format.h @@ -33,20 +33,59 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include // std::signbit -#include // uint32_t -#include // std::memcpy -#include // std::initializer_list -#include // std::numeric_limits -#include // std::uninitialized_copy -#include // std::runtime_error -#include // std::system_error - -#ifdef __cpp_lib_bit_cast -# include // std::bit_cast +#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES +# define FMT_REMOVE_TRANSITIVE_INCLUDES #endif -#include "core.h" +#include "base.h" + +#ifndef FMT_MODULE +# include // std::signbit +# include // std::byte +# include // uint32_t +# include // std::memcpy +# include // std::numeric_limits +# include // std::bad_alloc +# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI) +// Workaround for pre gcc 5 libstdc++. +# include // std::allocator_traits +# endif +# include // std::runtime_error +# include // std::string +# include // std::system_error + +// Check FMT_CPLUSPLUS to avoid a warning in MSVC. +# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L +# include // std::bit_cast +# endif + +// libc++ supports string_view in pre-c++17. +# if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) +# include +# define FMT_USE_STRING_VIEW +# endif + +# if FMT_MSC_VERSION +# include // _BitScanReverse[64], _umul128 +# endif +#endif // FMT_MODULE + +#if defined(FMT_USE_NONTYPE_TEMPLATE_ARGS) +// Use the provided definition. +#elif defined(__NVCOMPILER) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#elif FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif defined(__cpp_nontype_template_args) && \ + __cpp_nontype_template_args >= 201911L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +#endif #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline @@ -54,43 +93,15 @@ # define FMT_INLINE_VARIABLE #endif -#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough) -# define FMT_FALLTHROUGH [[fallthrough]] -#elif defined(__clang__) -# define FMT_FALLTHROUGH [[clang::fallthrough]] -#elif FMT_GCC_VERSION >= 700 && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) -# define FMT_FALLTHROUGH [[gnu::fallthrough]] +// Check if RTTI is disabled. +#ifdef FMT_USE_RTTI +// Use the provided definition. +#elif defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ + defined(__INTEL_RTTI__) || defined(__RTTI) +// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. +# define FMT_USE_RTTI 1 #else -# define FMT_FALLTHROUGH -#endif - -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VERSION -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - -#ifndef FMT_NO_UNIQUE_ADDRESS -# if FMT_CPLUSPLUS >= 202002L -# if FMT_HAS_CPP_ATTRIBUTE(no_unique_address) -# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]] -// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485) -# elif (FMT_MSC_VERSION >= 1929) && !FMT_CLANG_VERSION -# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] -# endif -# endif -#endif -#ifndef FMT_NO_UNIQUE_ADDRESS -# define FMT_NO_UNIQUE_ADDRESS +# define FMT_USE_RTTI 0 #endif // Visibility when compiled as a shared library/object. @@ -100,20 +111,25 @@ # define FMT_SO_VISIBILITY(value) #endif -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_NOINLINE __attribute__((noinline)) #else # define FMT_NOINLINE #endif +namespace std { +template struct iterator_traits> { + using iterator_category = output_iterator_tag; + using value_type = T; + using difference_type = + decltype(static_cast(nullptr) - static_cast(nullptr)); + using pointer = void; + using reference = void; +}; +} // namespace std + #ifndef FMT_THROW -# if FMT_EXCEPTIONS +# if FMT_USE_EXCEPTIONS # if FMT_MSC_VERSION || defined(__NVCC__) FMT_BEGIN_NAMESPACE namespace detail { @@ -132,38 +148,8 @@ FMT_END_NAMESPACE # else # define FMT_THROW(x) \ ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) -# endif -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif -#endif - -#ifndef FMT_USE_USER_DEFINED_LITERALS -// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. -// -// GCC before 4.9 requires a space in `operator"" _a` which is invalid in later -// compiler versions. -# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 409 || \ - FMT_MSC_VERSION >= 1900) && \ - (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) -# define FMT_USE_USER_DEFINED_LITERALS 1 -# else -# define FMT_USE_USER_DEFINED_LITERALS 0 -# endif -#endif +# endif // FMT_USE_EXCEPTIONS +#endif // FMT_THROW // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the @@ -173,7 +159,15 @@ FMT_END_NAMESPACE # define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif -// __builtin_clz is broken in clang with Microsoft CodeGen: +FMT_BEGIN_NAMESPACE + +template +struct is_contiguous> + : std::true_type {}; + +namespace detail { + +// __builtin_clz is broken in clang with Microsoft codegen: // https://github.com/fmtlib/fmt/issues/519. #if !FMT_MSC_VERSION # if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION @@ -184,53 +178,30 @@ FMT_END_NAMESPACE # endif #endif -// __builtin_ctz is broken in Intel Compiler Classic on Windows: -// https://github.com/fmtlib/fmt/issues/2510. -#ifndef __ICL -# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \ - defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) -# endif -# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \ - FMT_ICC_VERSION || defined(__NVCOMPILER) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) -# endif -#endif - -#if FMT_MSC_VERSION -# include // _BitScanReverse[64], _BitScanForward[64], _umul128 -#endif - -// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// Some compilers masquerade as both MSVC and GCC but otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. -#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \ - !defined(FMT_BUILTIN_CTZLL) -FMT_BEGIN_NAMESPACE -namespace detail { +#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. -# if !defined(__clang__) -# pragma intrinsic(_BitScanForward) +# ifndef __clang__ # pragma intrinsic(_BitScanReverse) -# if defined(_WIN64) -# pragma intrinsic(_BitScanForward64) +# ifdef _WIN64 # pragma intrinsic(_BitScanReverse64) # endif # endif inline auto clz(uint32_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. unsigned long r = 0; _BitScanReverse(&r, x); - FMT_ASSERT(x != 0, ""); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. - FMT_MSC_WARNING(suppress : 6102) return 31 ^ static_cast(r); } # define FMT_BUILTIN_CLZ(n) detail::clz(n) inline auto clzll(uint64_t x) -> int { + FMT_ASSERT(x != 0, ""); + FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. unsigned long r = 0; # ifdef _WIN64 _BitScanReverse64(&r, x); @@ -241,43 +212,10 @@ inline auto clzll(uint64_t x) -> int { // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); # endif - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. return 63 ^ static_cast(r); } # define FMT_BUILTIN_CLZLL(n) detail::clzll(n) - -inline auto ctz(uint32_t x) -> int { - unsigned long r = 0; - _BitScanForward(&r, x); - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. - return static_cast(r); -} -# define FMT_BUILTIN_CTZ(n) detail::ctz(n) - -inline auto ctzll(uint64_t x) -> int { - unsigned long r = 0; - FMT_ASSERT(x != 0, ""); - FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning. -# ifdef _WIN64 - _BitScanForward64(&r, x); -# else - // Scan the low 32 bits. - if (_BitScanForward(&r, static_cast(x))) return static_cast(r); - // Scan the high 32 bits. - _BitScanForward(&r, static_cast(x >> 32)); - r += 32; -# endif - return static_cast(r); -} -# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) -} // namespace detail -FMT_END_NAMESPACE -#endif - -FMT_BEGIN_NAMESPACE -namespace detail { +#endif // FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { ignore_unused(condition); @@ -286,16 +224,23 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { #endif } -template struct string_literal { - static constexpr CharT value[sizeof...(C)] = {C...}; - constexpr operator basic_string_view() const { +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#else +template struct std_string_view { + operator basic_string_view() const; +}; +#endif + +template struct string_literal { + static constexpr Char value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { return {value, sizeof...(C)}; } }; - #if FMT_CPLUSPLUS < 201703L -template -constexpr CharT string_literal::value[sizeof...(C)]; +template +constexpr Char string_literal::value[sizeof...(C)]; #endif // Implementation of std::bit_cast for pre-C++20. @@ -367,13 +312,14 @@ class uint128_fallback { -> uint128_fallback { return {~n.hi_, ~n.lo_}; } - friend auto operator+(const uint128_fallback& lhs, - const uint128_fallback& rhs) -> uint128_fallback { + friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs, + const uint128_fallback& rhs) + -> uint128_fallback { auto result = uint128_fallback(lhs); result += rhs; return result; } - friend auto operator*(const uint128_fallback& lhs, uint32_t rhs) + friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs) -> uint128_fallback { FMT_ASSERT(lhs.hi_ == 0, ""); uint64_t hi = (lhs.lo_ >> 32) * rhs; @@ -381,7 +327,7 @@ class uint128_fallback { uint64_t new_lo = (hi << 32) + lo; return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo}; } - friend auto operator-(const uint128_fallback& lhs, uint64_t rhs) + friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs) -> uint128_fallback { return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs}; } @@ -454,23 +400,24 @@ template constexpr auto num_bits() -> int { } // std::numeric_limits::digits may return 0 for 128-bit ints. template <> constexpr auto num_bits() -> int { return 128; } -template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } +template <> constexpr auto num_bits() -> int { return 128; } // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t // and 128-bit pointers to uint128_fallback. template sizeof(From))> inline auto bit_cast(const From& from) -> To { - constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned)); + constexpr auto size = static_cast(sizeof(From) / sizeof(unsigned short)); struct data_t { - unsigned value[static_cast(size)]; + unsigned short value[static_cast(size)]; } data = bit_cast(from); auto result = To(); if (const_check(is_big_endian())) { for (int i = 0; i < size; ++i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } else { for (int i = size - 1; i >= 0; --i) - result = (result << num_bits()) | data.value[i]; + result = (result << num_bits()) | data.value[i]; } return result; } @@ -506,38 +453,25 @@ FMT_INLINE void assume(bool condition) { #endif } -// An approximation of iterator_t for pre-C++20 systems. -template -using iterator_t = decltype(std::begin(std::declval())); -template using sentinel_t = decltype(std::end(std::declval())); - -// A workaround for std::string not having mutable data() until C++17. -template -inline auto get_data(std::basic_string& s) -> Char* { - return &s[0]; -} -template -inline auto get_data(Container& c) -> typename Container::value_type* { - return c.data(); -} - // Attempts to reserve space for n extra characters in the output range. // Returns a pointer to the reserved range or a reference to it. -template ::value)> +template ::value&& + is_contiguous::value)> #if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline auto -reserve(std::back_insert_iterator it, size_t n) -> - typename Container::value_type* { - Container& c = get_container(it); +FMT_CONSTEXPR20 inline auto +reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { + auto& c = get_container(it); size_t size = c.size(); c.resize(size + n); - return get_data(c) + size; + return &c[size]; } template -inline auto reserve(buffer_appender it, size_t n) -> buffer_appender { +FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) + -> basic_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; @@ -556,18 +490,22 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template auto to_pointer(buffer_appender it, size_t n) -> T* { +template +FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); auto size = buf.size(); if (buf.capacity() < size + n) return nullptr; buf.try_resize(size + n); return buf.data() + size; } -template ::value)> -inline auto base_iterator(std::back_insert_iterator it, - typename Container::value_type*) - -> std::back_insert_iterator { +template ::value&& + is_contiguous::value)> +inline auto base_iterator(OutputIt it, + typename OutputIt::container_type::value_type*) + -> OutputIt { return it; } @@ -586,23 +524,15 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) } template FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { - if (is_constant_evaluated()) { - return fill_n(out, count, value); - } + if (is_constant_evaluated()) return fill_n(out, count, value); std::memset(out, value, to_unsigned(count)); return out + count; } -#ifdef __cpp_char8_t -using char8_type = char8_t; -#else -enum char8_type : unsigned char {}; -#endif - template -FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, - OutputIt out) -> OutputIt { - return copy_str(begin, end, out); +FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, + OutputIt out) -> OutputIt { + return copy(begin, end, out); } // A public domain branchless UTF-8 decoder by Christopher Wellons: @@ -673,6 +603,7 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr))); return result ? (error ? buf_ptr + 1 : end) : nullptr; }; + auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. if (s.size() >= block_size) { @@ -681,17 +612,20 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { if (!p) return; } } - if (auto num_chars_left = s.data() + s.size() - p) { - char buf[2 * block_size - 1] = {}; - copy_str(p, p + num_chars_left, buf); - const char* buf_ptr = buf; - do { - auto end = decode(buf_ptr, p); - if (!end) return; - p += end - buf_ptr; - buf_ptr = end; - } while (buf_ptr - buf < num_chars_left); - } + auto num_chars_left = to_unsigned(s.data() + s.size() - p); + if (num_chars_left == 0) return; + + // Suppress bogus -Wstringop-overflow. + if (FMT_GCC_VERSION) num_chars_left &= 3; + char buf[2 * block_size - 1] = {}; + copy(p, p + num_chars_left, buf); + const char* buf_ptr = buf; + do { + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr < buf + num_chars_left); } template @@ -706,7 +640,7 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { struct count_code_points { size_t* count; FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { - *count += detail::to_unsigned( + *count += to_unsigned( 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants @@ -734,15 +668,9 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { return num_code_points; } -inline auto compute_width(basic_string_view s) -> size_t { - return compute_width( - string_view(reinterpret_cast(s.data()), s.size())); -} - template inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - size_t size = s.size(); - return n < size ? n : size; + return min_of(n, s.size()); } // Calculates the index of the nth code point in a UTF-8 string. @@ -760,12 +688,6 @@ inline auto code_point_index(string_view s, size_t n) -> size_t { return result; } -inline auto code_point_index(basic_string_view s, size_t n) - -> size_t { - return code_point_index( - string_view(reinterpret_cast(s.data()), s.size()), n); -} - template struct is_integral : std::is_integral {}; template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; @@ -781,38 +703,22 @@ using is_integer = !std::is_same::value && !std::is_same::value>; -#ifndef FMT_USE_FLOAT -# define FMT_USE_FLOAT 1 +#if defined(FMT_USE_FLOAT128) +// Use the provided definition. +#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ + !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +#else +# define FMT_USE_FLOAT128 0 #endif -#ifndef FMT_USE_DOUBLE -# define FMT_USE_DOUBLE 1 -#endif -#ifndef FMT_USE_LONG_DOUBLE -# define FMT_USE_LONG_DOUBLE 1 -#endif - -#ifndef FMT_USE_FLOAT128 -# ifdef __clang__ -// Clang emulates GCC, so it has to appear early. -# if FMT_HAS_INCLUDE() -# define FMT_USE_FLOAT128 1 -# endif -# elif defined(__GNUC__) -// GNU C++: -# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) -# define FMT_USE_FLOAT128 1 -# endif -# endif -# ifndef FMT_USE_FLOAT128 -# define FMT_USE_FLOAT128 0 -# endif -#endif - #if FMT_USE_FLOAT128 using float128 = __float128; #else -using float128 = void; +struct float128 {}; #endif + template using is_float128 = std::is_same; template @@ -831,24 +737,21 @@ using is_double_double = bool_constant::digits == 106>; # define FMT_USE_FULL_CACHE_DRAGONBOX 0 #endif -template -template -void buffer::append(const U* begin, const U* end) { - while (begin != end) { - auto count = to_unsigned(end - begin); - try_reserve(size_ + count); - auto free_cap = capacity_ - size_; - if (free_cap < count) count = free_cap; - std::uninitialized_copy_n(begin, count, ptr_ + size_); - size_ += count; - begin += count; - } -} +// An allocator that uses malloc/free to allow removing dependency on the C++ +// standard libary runtime. +template struct allocator { + using value_type = T; + + T* allocate(size_t n) { + FMT_ASSERT(n <= max_value() / sizeof(T), ""); + T* p = static_cast(malloc(n * sizeof(T))); + if (!p) FMT_THROW(std::bad_alloc()); + return p; + } + + void deallocate(T* p, size_t) { free(p); } +}; -template -struct is_locale : std::false_type {}; -template -struct is_locale> : std::true_type {}; } // namespace detail FMT_BEGIN_EXPORT @@ -858,29 +761,21 @@ FMT_BEGIN_EXPORT enum { inline_buffer_size = 500 }; /** - \rst - A dynamically growing memory buffer for trivially copyable/constructible types - with the first ``SIZE`` elements stored in the object itself. - - You can use the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - - auto out = fmt::memory_buffer(); - fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); - - This will append the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42. - - The output can be converted to an ``std::string`` with ``to_string(out)``. - \endrst + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. */ template > -class basic_memory_buffer final : public detail::buffer { + typename Allocator = detail::allocator> +class basic_memory_buffer : public detail::buffer { private: T store_[SIZE]; @@ -893,37 +788,37 @@ class basic_memory_buffer final : public detail::buffer { if (data != store_) alloc_.deallocate(data, this->capacity()); } - protected: - FMT_CONSTEXPR20 void grow(size_t size) override { + static FMT_CONSTEXPR20 void grow(detail::buffer& buf, size_t size) { detail::abort_fuzzing_if(size > 5000); - const size_t max_size = std::allocator_traits::max_size(alloc_); - size_t old_capacity = this->capacity(); + auto& self = static_cast(buf); + const size_t max_size = + std::allocator_traits::max_size(self.alloc_); + size_t old_capacity = buf.capacity(); size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; else if (new_capacity > max_size) - new_capacity = size > max_size ? size : max_size; - T* old_data = this->data(); - T* new_data = - std::allocator_traits::allocate(alloc_, new_capacity); + new_capacity = max_of(size, max_size); + T* old_data = buf.data(); + T* new_data = self.alloc_.allocate(new_capacity); // Suppress a bogus -Wstringop-overflow in gcc 13.1 (#3481). - detail::assume(this->size() <= new_capacity); + detail::assume(buf.size() <= new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy_n(old_data, this->size(), new_data); - this->set(new_data, new_capacity); + memcpy(new_data, old_data, buf.size() * sizeof(T)); + self.set(new_data, new_capacity); // deallocate must not throw according to the standard, but even if it does, // the buffer already uses the new storage and will deallocate it in // destructor. - if (old_data != store_) alloc_.deallocate(old_data, old_capacity); + if (old_data != self.store_) self.alloc_.deallocate(old_data, old_capacity); } public: using value_type = T; using const_reference = const T&; - FMT_CONSTEXPR20 explicit basic_memory_buffer( + FMT_CONSTEXPR explicit basic_memory_buffer( const Allocator& alloc = Allocator()) - : alloc_(alloc) { + : detail::buffer(grow), alloc_(alloc) { this->set(store_, SIZE); if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T()); } @@ -937,7 +832,7 @@ class basic_memory_buffer final : public detail::buffer { size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - detail::copy_str(other.store_, other.store_ + size, store_); + detail::copy(other.store_, other.store_ + size, store_); } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called @@ -949,21 +844,14 @@ class basic_memory_buffer final : public detail::buffer { } public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ - FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept { + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept + : detail::buffer(grow) { move(other); } - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ + /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); @@ -974,119 +862,108 @@ class basic_memory_buffer final : public detail::buffer { // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } - /** - Resizes the buffer to contain *count* elements. If T is a POD type new - elements may not be initialized. - */ - FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } + /// Resizes the buffer to contain `count` elements. If T is a POD type new + /// elements may not be initialized. + FMT_CONSTEXPR void resize(size_t count) { this->try_resize(count); } - /** Increases the buffer capacity to *new_capacity*. */ + /// Increases the buffer capacity to `new_capacity`. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } using detail::buffer::append; template - void append(const ContiguousRange& range) { + FMT_CONSTEXPR20 void append(const ContiguousRange& range) { append(range.data(), range.data() + range.size()); } }; using memory_buffer = basic_memory_buffer; +template +FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) + -> std::string { + auto size = buf.size(); + detail::assume(size < std::string().max_size()); + return {buf.data(), size}; +} + +// A writer to a buffered stream. It doesn't own the underlying stream. +class writer { + private: + detail::buffer* buf_; + + // We cannot create a file buffer in advance because any write to a FILE may + // invalidate it. + FILE* file_; + + public: + inline writer(FILE* f) : buf_(nullptr), file_(f) {} + inline writer(detail::buffer& buf) : buf_(&buf) {} + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + if (buf_) + fmt::format_to(appender(*buf_), fmt, std::forward(args)...); + else + fmt::print(file_, fmt, std::forward(args)...); + } +}; + +class string_buffer { + private: + std::string str_; + detail::container_buffer buf_; + + public: + inline string_buffer() : buf_(str_) {} + + inline operator writer() { return buf_; } + inline std::string& str() { return str_; } +}; + template struct is_contiguous> : std::true_type { }; -FMT_END_EXPORT -namespace detail { -FMT_API auto write_console(int fd, string_view text) -> bool; -FMT_API void print(std::FILE*, string_view); -} // namespace detail - -FMT_BEGIN_EXPORT - // Suppress a misleading warning in older versions of clang. -#if FMT_CLANG_VERSION -# pragma clang diagnostic ignored "-Wweak-vtables" -#endif +FMT_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") -/** An error reported from a formatting function. */ +/// An error reported from a formatting function. class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; }; -namespace detail_exported { -#if FMT_USE_NONTYPE_TEMPLATE_ARGS +class loc_value; + +FMT_END_EXPORT +namespace detail { +FMT_API auto write_console(int fd, string_view text) -> bool; +FMT_API void print(FILE*, string_view); +} // namespace detail + +namespace detail { template struct fixed_string { - constexpr fixed_string(const Char (&str)[N]) { - detail::copy_str(static_cast(str), - str + N, data); + FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) { + detail::copy(static_cast(s), s + N, + data); } Char data[N] = {}; }; -#endif // Converts a compile-time string to basic_string_view. -template +FMT_EXPORT template constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view { // Remove trailing NUL character if needed. Won't be present if this is used // with a raw character array (i.e. not defined as a string). return {s, N - (std::char_traits::to_int_type(s[N - 1]) == 0 ? 1 : 0)}; } -template -constexpr auto compile_string_to_view(detail::std_string_view s) +FMT_EXPORT template +constexpr auto compile_string_to_view(basic_string_view s) -> basic_string_view { - return {s.data(), s.size()}; + return s; } -} // namespace detail_exported - -class loc_value { - private: - basic_format_arg value_; - - public: - template ::value)> - loc_value(T value) : value_(detail::make_arg(value)) {} - - template ::value)> - loc_value(T) {} - - template auto visit(Visitor&& vis) -> decltype(vis(0)) { - return visit_format_arg(vis, value_); - } -}; - -// A locale facet that formats values in UTF-8. -// It is parameterized on the locale to avoid the heavy include. -template class format_facet : public Locale::facet { - private: - std::string separator_; - std::string grouping_; - std::string decimal_point_; - - protected: - virtual auto do_put(appender out, loc_value val, - const format_specs<>& specs) const -> bool; - - public: - static FMT_API typename Locale::id id; - - explicit format_facet(Locale& loc); - explicit format_facet(string_view sep = "", - std::initializer_list g = {3}, - std::string decimal_point = ".") - : separator_(sep.data(), sep.size()), - grouping_(g.begin(), g.end()), - decimal_point_(decimal_point) {} - - auto put(appender out, loc_value val, const format_specs<>& specs) const - -> bool { - return do_put(out, val, specs); - } -}; - -namespace detail { // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. @@ -1099,14 +976,6 @@ constexpr auto is_negative(T) -> bool { return false; } -template -FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool { - if (std::is_same()) return FMT_USE_FLOAT; - if (std::is_same()) return FMT_USE_DOUBLE; - if (std::is_same()) return FMT_USE_LONG_DOUBLE; - return true; -} - // Smallest of uint32_t, uint64_t, uint128_t that is large enough to // represent all values of an integral type T. template @@ -1123,21 +992,22 @@ using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr auto digits2(size_t value) -> const char* { - // GCC generates slightly better code when value is pointer-size. - return &"0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"[value * 2]; +// GCC generates slightly better code when value is pointer-size. +inline auto digits2(size_t value) -> const char* { + // Align data since unaligned access may be slower when crossing a + // hardware-specific boundary. + alignas(2) static const char data[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + return &data[value * 2]; } -// Sign is a template parameter to workaround a bug in gcc 4.8. -template constexpr auto sign(Sign s) -> Char { -#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 - static_assert(std::is_same::value, ""); -#endif - return static_cast("\0-+ "[s]); +template constexpr auto getsign(sign s) -> Char { + return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> + (static_cast(s) * 8)); } template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { @@ -1185,9 +1055,7 @@ inline auto do_count_digits(uint64_t n) -> int { // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1237,9 +1105,7 @@ FMT_INLINE auto do_count_digits(uint32_t n) -> int { // Optional version of count_digits for better performance on 32-bit platforms. FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated() && !FMT_OPTIMIZE_SIZE) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1276,6 +1142,17 @@ template <> inline auto decimal_point(locale_ref loc) -> wchar_t { return decimal_point_impl(loc); } +#ifndef FMT_HEADER_ONLY +FMT_BEGIN_EXPORT +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto thousands_sep_impl(locale_ref) + -> thousands_sep_result; +extern template FMT_API auto decimal_point_impl(locale_ref) -> char; +extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +FMT_END_EXPORT +#endif // FMT_HEADER_ONLY + // Compares two characters for equality. template auto equal2(const Char* lhs, const char* rhs) -> bool { return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]); @@ -1284,83 +1161,99 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } -// Copies two characters from src to dst. +// Writes a two-digit value to out. template -FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { - if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { - memcpy(dst, src, 2); +FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { + if (!is_constant_evaluated() && std::is_same::value && + !FMT_OPTIMIZE_SIZE) { + memcpy(out, digits2(value), 2); return; } - *dst++ = static_cast(*src++); - *dst = static_cast(*src); + *out++ = static_cast('0' + value / 10); + *out = static_cast('0' + value % 10); } -template struct format_decimal_result { - Iterator begin; - Iterator end; -}; - -// Formats a decimal unsigned integer value writing into out pointing to a -// buffer of specified size. The caller must ensure that the buffer is large -// enough. +// Formats a decimal unsigned integer value writing to out pointing to a buffer +// of specified size. The caller must ensure that the buffer is large enough. template -FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) - -> format_decimal_result { +FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) + -> Char* { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); - out += size; - Char* end = out; + unsigned n = to_unsigned(size); while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. - out -= 2; - copy2(out, digits2(static_cast(value % 100))); + n -= 2; + write2digits(out + n, static_cast(value % 100)); value /= 100; } - if (value < 10) { - *--out = static_cast('0' + value); - return {out, end}; + if (value >= 10) { + n -= 2; + write2digits(out + n, static_cast(value)); + } else { + out[--n] = static_cast('0' + value); } - out -= 2; - copy2(out, digits2(static_cast(value))); - return {out, end}; + return out + n; } -template >::value)> -FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) - -> format_decimal_result { - // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1] = {}; - auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_str_noinline(buffer, end, out)}; +template +FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, + int num_digits) -> Char* { + do_format_decimal(out, value, num_digits); + return out + num_digits; } -template -FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, - bool upper = false) -> Char* { - buffer += num_digits; - Char* end = buffer; - do { - const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << BASE_BITS) - 1)); - *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) - : digits[digit]); - } while ((value >>= BASE_BITS) != 0); - return end; -} - -template -FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits, - bool upper = false) -> It { +template >::value)> +FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) + -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { - format_uint(ptr, value, num_digits, upper); + do_format_decimal(ptr, value, num_digits); return out; } - // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). - char buffer[num_bits() / BASE_BITS + 1] = {}; - format_uint(buffer, value, num_digits, upper); - return detail::copy_str_noinline(buffer, buffer + num_digits, out); + // Buffer is large enough to hold all digits (digits10 + 1). + char buffer[digits10() + 1]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + do_format_decimal(buffer, value, num_digits); + return copy_noinline(buffer, buffer + num_digits, out); +} + +template +FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, + int size, bool upper = false) -> Char* { + out += size; + do { + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= base_bits) != 0); + return out; +} + +// Formats an unsigned integer in the power of two base (binary, octal, hex). +template +FMT_CONSTEXPR auto format_base2e(int base_bits, Char* out, UInt value, + int num_digits, bool upper = false) -> Char* { + do_format_base2e(base_bits, out, value, num_digits, upper); + return out + num_digits; +} + +template ::value)> +FMT_CONSTEXPR inline auto format_base2e(int base_bits, OutputIt out, UInt value, + int num_digits, bool upper = false) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_base2e(base_bits, ptr, value, num_digits, upper); + return out; + } + // Make buffer large enough for any base. + char buffer[num_bits()]; + if (is_constant_evaluated()) fill_n(buffer, sizeof(buffer), '\0'); + format_base2e(base_bits, buffer, value, num_digits, upper); + return detail::copy_noinline(buffer, buffer + num_digits, out); } // A converter from UTF-8 to UTF-16. @@ -1370,10 +1263,12 @@ class utf8_to_utf16 { public: FMT_API explicit utf8_to_utf16(string_view s); - operator basic_string_view() const { return {&buffer_[0], size()}; } - auto size() const -> size_t { return buffer_.size() - 1; } - auto c_str() const -> const wchar_t* { return &buffer_[0]; } - auto str() const -> std::wstring { return {&buffer_[0], size()}; } + inline operator basic_string_view() const { + return {&buffer_[0], size()}; + } + inline auto size() const -> size_t { return buffer_.size() - 1; } + inline auto c_str() const -> const wchar_t* { return &buffer_[0]; } + inline auto str() const -> std::wstring { return {&buffer_[0], size()}; } }; enum class to_utf8_error_policy { abort, replace }; @@ -1420,10 +1315,12 @@ template class to_utf8 { if (policy == to_utf8_error_policy::abort) return false; buf.append(string_view("\xEF\xBF\xBD")); --p; + continue; } else { c = (c << 10) + static_cast(*p) - 0x35fdc00; } - } else if (c < 0x80) { + } + if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); @@ -1591,25 +1488,30 @@ template constexpr auto exponent_bias() -> int { } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template -FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { +template +FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { - *it++ = static_cast('-'); + *out++ = static_cast('-'); exp = -exp; } else { - *it++ = static_cast('+'); + *out++ = static_cast('+'); } - if (exp >= 100) { - const char* top = digits2(to_unsigned(exp / 100)); - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; + auto uexp = static_cast(exp); + if (is_constant_evaluated()) { + if (uexp < 10) *out++ = '0'; + return format_decimal(out, uexp, count_digits(uexp)); } - const char* d = digits2(to_unsigned(exp)); - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; + if (uexp >= 100u) { + const char* top = digits2(uexp / 100); + if (uexp >= 1000u) *out++ = static_cast(top[0]); + *out++ = static_cast(top[1]); + uexp %= 100; + } + const char* d = digits2(uexp); + *out++ = static_cast(d[0]); + *out++ = static_cast(d[1]); + return out; } // A floating-point number f * pow(2, e) where F is an unsigned type. @@ -1710,67 +1612,69 @@ constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } -template +template FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, - const fill_t& fill) -> OutputIt { - auto fill_size = fill.size(); - if (fill_size == 1) return detail::fill_n(it, n, fill[0]); - auto data = fill.data(); - for (size_t i = 0; i < n; ++i) - it = copy_str(data, data + fill_size, it); + const basic_specs& specs) -> OutputIt { + auto fill_size = specs.fill_size(); + if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); + if (const Char* data = specs.fill()) { + for (size_t i = 0; i < n; ++i) it = copy(data, data + fill_size, it); + } return it; } // Writes the output of f, padded according to format specifications in specs. // size: output size in code units. // width: output display width in (terminal) column positions. -template -FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, +FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs, size_t size, size_t width, F&& f) -> OutputIt { - static_assert(align == align::left || align == align::right, ""); + static_assert(default_align == align::left || default_align == align::right, + ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; // Shifts are encoded as string literals because static constexpr is not // supported in constexpr functions. - auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; - size_t left_padding = padding >> shifts[specs.align]; + auto* shifts = + default_align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; + size_t left_padding = padding >> shifts[static_cast(specs.align())]; size_t right_padding = padding - left_padding; - auto it = reserve(out, size + padding * specs.fill.size()); - if (left_padding != 0) it = fill(it, left_padding, specs.fill); + auto it = reserve(out, size + padding * specs.fill_size()); + if (left_padding != 0) it = fill(it, left_padding, specs); it = f(it); - if (right_padding != 0) it = fill(it, right_padding, specs.fill); + if (right_padding != 0) it = fill(it, right_padding, specs); return base_iterator(out, it); } -template -constexpr auto write_padded(OutputIt out, const format_specs& specs, +constexpr auto write_padded(OutputIt out, const format_specs& specs, size_t size, F&& f) -> OutputIt { - return write_padded(out, specs, size, size, f); + return write_padded(out, specs, size, size, f); } -template +template FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, - const format_specs& specs) -> OutputIt { - return write_padded( + const format_specs& specs = {}) -> OutputIt { + return write_padded( out, specs, bytes.size(), [bytes](reserve_iterator it) { const char* data = bytes.data(); - return copy_str(data, data + bytes.size(), it); + return copy(data, data + bytes.size(), it); }); } template -auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) +auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) -> OutputIt { int num_digits = count_digits<4>(value); auto size = to_unsigned(num_digits) + size_t(2); auto write = [=](reserve_iterator it) { *it++ = static_cast('0'); *it++ = static_cast('x'); - return format_uint<4, Char>(it, value, num_digits); + return format_base2e(4, it, value, num_digits); }; - return specs ? write_padded(out, *specs, size, write) + return specs ? write_padded(out, *specs, size, write) : base_iterator(out, write(reserve(out, size))); } @@ -1778,8 +1682,9 @@ auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs) FMT_API auto is_printable(uint32_t cp) -> bool; inline auto needs_escape(uint32_t cp) -> bool { - return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || - !is_printable(cp); + if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true; + if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false; + return !is_printable(cp); } template struct find_escape_result { @@ -1788,17 +1693,11 @@ template struct find_escape_result { uint32_t cp; }; -template -using make_unsigned_char = - typename conditional_t::value, - std::make_unsigned, - type_identity>::type; - template auto find_escape(const Char* begin, const Char* end) -> find_escape_result { for (; begin != end; ++begin) { - uint32_t cp = static_cast>(*begin); + uint32_t cp = static_cast>(*begin); if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue; if (needs_escape(cp)) return {begin, begin + 1, cp}; } @@ -1807,7 +1706,7 @@ auto find_escape(const Char* begin, const Char* end) inline auto find_escape(const char* begin, const char* end) -> find_escape_result { - if (!is_utf8()) return find_escape(begin, end); + if (const_check(!use_utf8)) return find_escape(begin, end); auto result = find_escape_result{end, nullptr, 0}; for_each_codepoint(string_view(begin, to_unsigned(end - begin)), [&](uint32_t cp, string_view sv) { @@ -1820,40 +1719,14 @@ inline auto find_escape(const char* begin, const char* end) return result; } -#define FMT_STRING_IMPL(s, base, explicit) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ - using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t; \ - FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \ - operator fmt::basic_string_view() const { \ - return fmt::detail_exported::compile_string_to_view(s); \ - } \ - }; \ - return FMT_COMPILE_STRING(); \ - }() - -/** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst - */ -#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) - template auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { *out++ = static_cast('\\'); *out++ = static_cast(prefix); Char buf[width]; fill_n(buf, width, static_cast('0')); - format_uint<4>(buf, cp, width); - return copy_str(buf, buf + width, out); + format_base2e(4, buf, cp, width); + return copy(buf, buf + width, out); } template @@ -1873,23 +1746,15 @@ auto write_escaped_cp(OutputIt out, const find_escape_result& escape) *out++ = static_cast('\\'); c = static_cast('t'); break; - case '"': - FMT_FALLTHROUGH; - case '\'': - FMT_FALLTHROUGH; - case '\\': - *out++ = static_cast('\\'); - break; + case '"': FMT_FALLTHROUGH; + case '\'': FMT_FALLTHROUGH; + case '\\': *out++ = static_cast('\\'); break; default: - if (escape.cp < 0x100) { - return write_codepoint<2, Char>(out, 'x', escape.cp); - } - if (escape.cp < 0x10000) { + if (escape.cp < 0x100) return write_codepoint<2, Char>(out, 'x', escape.cp); + if (escape.cp < 0x10000) return write_codepoint<4, Char>(out, 'u', escape.cp); - } - if (escape.cp < 0x110000) { + if (escape.cp < 0x110000) return write_codepoint<8, Char>(out, 'U', escape.cp); - } for (Char escape_char : basic_string_view( escape.begin, to_unsigned(escape.end - escape.begin))) { out = write_codepoint<2, Char>(out, 'x', @@ -1908,7 +1773,7 @@ auto write_escaped_string(OutputIt out, basic_string_view str) auto begin = str.begin(), end = str.end(); do { auto escape = find_escape(begin, end); - out = copy_str(begin, escape.begin, out); + out = copy(begin, escape.begin, out); begin = escape.end; if (!begin) break; out = write_escaped_cp(out, escape); @@ -1935,74 +1800,23 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt { template FMT_CONSTEXPR auto write_char(OutputIt out, Char value, - const format_specs& specs) -> OutputIt { - bool is_debug = specs.type == presentation_type::debug; - return write_padded(out, specs, 1, [=](reserve_iterator it) { + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + return write_padded(out, specs, 1, [=](reserve_iterator it) { if (is_debug) return write_escaped_char(it, value); *it++ = value; return it; }); } template -FMT_CONSTEXPR auto write(OutputIt out, Char value, - const format_specs& specs, locale_ref loc = {}) - -> OutputIt { +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { // char is formatted as unsigned char for consistency across platforms. using unsigned_type = conditional_t::value, unsigned char, unsigned>; return check_char_specs(specs) - ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); -} - -// Data for write_int that doesn't depend on output iterator type. It is used to -// avoid template code bloat. -template struct write_int_data { - size_t size; - size_t padding; - - FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, - const format_specs& specs) - : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { - if (specs.align == align::numeric) { - auto width = to_unsigned(specs.width); - if (width > size) { - padding = width - size; - size = width; - } - } else if (specs.precision > num_digits) { - size = (prefix >> 24) + to_unsigned(specs.precision); - padding = to_unsigned(specs.precision - num_digits); - } - } -}; - -// Writes an integer in the format -// -// where are written by write_digits(it). -// prefix contains chars in three lower bytes and the size in the fourth byte. -template -FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, - unsigned prefix, - const format_specs& specs, - W write_digits) -> OutputIt { - // Slightly faster check for specs.width == 0 && specs.precision == -1. - if ((specs.width | (specs.precision + 1)) == 0) { - auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); - if (prefix != 0) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - } - return base_iterator(out, write_digits(it)); - } - auto data = write_int_data(num_digits, prefix, specs); - return write_padded( - out, specs, data.size, [=](reserve_iterator it) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); - it = detail::fill_n(it, data.padding, static_cast('0')); - return write_digits(it); - }); + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); } template class digit_grouping { @@ -2027,7 +1841,9 @@ template class digit_grouping { } public: - explicit digit_grouping(locale_ref loc, bool localized = true) { + template ::value)> + explicit digit_grouping(Locale loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep(loc); grouping_ = sep.grouping; @@ -2059,9 +1875,8 @@ template class digit_grouping { for (int i = 0, sep_index = static_cast(separators.size() - 1); i < num_digits; ++i) { if (num_digits - i == separators[sep_index]) { - out = - copy_str(thousands_sep_.data(), - thousands_sep_.data() + thousands_sep_.size(), out); + out = copy(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); --sep_index; } *out++ = static_cast(digits[to_unsigned(i)]); @@ -2078,54 +1893,45 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, - const format_specs& specs, - const digit_grouping& grouping) -> OutputIt { + const format_specs& specs, const digit_grouping& grouping) + -> OutputIt { static_assert(std::is_same, UInt>::value, ""); int num_digits = 0; auto buffer = memory_buffer(); - switch (specs.type) { + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { + case presentation_type::dec: num_digits = count_digits(value); format_decimal(appender(buffer), value, num_digits); break; - } - case presentation_type::hex_lower: - case presentation_type::hex_upper: { - bool upper = specs.type == presentation_type::hex_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + case presentation_type::hex: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); - format_uint<4, char>(appender(buffer), value, num_digits, upper); + format_base2e(4, appender(buffer), value, num_digits, specs.upper()); break; - } - case presentation_type::bin_lower: - case presentation_type::bin_upper: { - bool upper = specs.type == presentation_type::bin_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); - num_digits = count_digits<1>(value); - format_uint<1, char>(appender(buffer), value, num_digits); - break; - } - case presentation_type::oct: { + case presentation_type::oct: num_digits = count_digits<3>(value); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. - if (specs.alt && specs.precision <= num_digits && value != 0) + if (specs.alt() && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); - format_uint<3, char>(appender(buffer), value, num_digits); + format_base2e(3, appender(buffer), value, num_digits); + break; + case presentation_type::bin: + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_base2e(1, appender(buffer), value, num_digits); break; - } case presentation_type::chr: - return write_char(out, static_cast(value), specs); - default: - throw_format_error("invalid format specifier"); + return write_char(out, static_cast(value), specs); } unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + to_unsigned(grouping.count_separators(num_digits)); - return write_padded( + return write_padded( out, specs, size, size, [&](reserve_iterator it) { for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) *it++ = static_cast(p & 0xff); @@ -2133,11 +1939,13 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, }); } +#if FMT_USE_LOCALE // Writes a localized value. -FMT_API auto write_loc(appender out, loc_value value, - const format_specs<>& specs, locale_ref loc) -> bool; -template -inline auto write_loc(OutputIt, loc_value, const format_specs&, +FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, + locale_ref loc) -> bool; +#endif +template +inline auto write_loc(OutputIt, const loc_value&, const format_specs&, locale_ref) -> bool { return false; } @@ -2148,7 +1956,7 @@ template struct write_int_arg { }; template -FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) +FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) -> write_int_arg> { auto prefix = 0u; auto abs_value = static_cast>(value); @@ -2158,21 +1966,21 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) } else { constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; - prefix = prefixes[sign]; + prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; } template struct loc_writer { - buffer_appender out; - const format_specs& specs; + basic_appender out; + const format_specs& specs; std::basic_string sep; std::string grouping; std::basic_string decimal_point; template ::value)> auto operator()(T value) -> bool { - auto arg = make_write_int_arg(value, specs.sign); + auto arg = make_write_int_arg(value, specs.sign()); write_int(out, static_cast>(arg.abs_value), arg.prefix, specs, digit_grouping(grouping, sep)); return true; @@ -2184,167 +1992,162 @@ template struct loc_writer { } }; +// Size and padding computation separate from write_int to avoid template bloat. +struct size_padding { + unsigned size; + unsigned padding; + + FMT_CONSTEXPR size_padding(int num_digits, unsigned prefix, + const format_specs& specs) + : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { + if (specs.align() == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = (prefix >> 24) + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; + template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, - const format_specs& specs, - locale_ref) -> OutputIt { + const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); + + constexpr int buffer_size = num_bits(); + char buffer[buffer_size]; + if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); + const char* begin = nullptr; + const char* end = buffer + buffer_size; + auto abs_value = arg.abs_value; auto prefix = arg.prefix; - switch (specs.type) { + switch (specs.type()) { + default: FMT_ASSERT(false, ""); FMT_FALLTHROUGH; case presentation_type::none: - case presentation_type::dec: { - auto num_digits = count_digits(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits).end; - }); - } - case presentation_type::hex_lower: - case presentation_type::hex_upper: { - bool upper = specs.type == presentation_type::hex_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); - int num_digits = count_digits<4>(abs_value); - return write_int( - out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_uint<4, Char>(it, abs_value, num_digits, upper); - }); - } - case presentation_type::bin_lower: - case presentation_type::bin_upper: { - bool upper = specs.type == presentation_type::bin_upper; - if (specs.alt) - prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); - int num_digits = count_digits<1>(abs_value); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } + case presentation_type::dec: + begin = do_format_decimal(buffer, abs_value, buffer_size); + break; + case presentation_type::hex: + begin = do_format_base2e(4, buffer, abs_value, buffer_size, specs.upper()); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0'); + break; case presentation_type::oct: { - int num_digits = count_digits<3>(abs_value); + begin = do_format_base2e(3, buffer, abs_value, buffer_size); // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. - if (specs.alt && specs.precision <= num_digits && abs_value != 0) + auto num_digits = end - begin; + if (specs.alt() && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); - return write_int(out, num_digits, prefix, specs, - [=](reserve_iterator it) { - return format_uint<3, Char>(it, abs_value, num_digits); - }); + break; } + case presentation_type::bin: + begin = do_format_base2e(1, buffer, abs_value, buffer_size); + if (specs.alt()) + prefix_append(prefix, unsigned(specs.upper() ? 'B' : 'b') << 8 | '0'); + break; case presentation_type::chr: - return write_char(out, static_cast(abs_value), specs); - default: - throw_format_error("invalid format specifier"); + return write_char(out, static_cast(abs_value), specs); } - return out; + + // Write an integer in the format + // + // prefix contains chars in three lower bytes and the size in the fourth byte. + int num_digits = static_cast(end - begin); + // Slightly faster check for specs.width == 0 && specs.precision == -1. + if ((specs.width | (specs.precision + 1)) == 0) { + auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24)); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return base_iterator(out, copy(begin, end, it)); + } + auto sp = size_padding(num_digits, prefix, specs); + unsigned padding = sp.padding; + return write_padded( + out, specs, sp.size, [=](reserve_iterator it) { + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + it = detail::fill_n(it, padding, static_cast('0')); + return copy(begin, end, it); + }); } + template -FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( - OutputIt out, write_int_arg arg, const format_specs& specs, - locale_ref loc) -> OutputIt { - return write_int(out, arg, specs, loc); +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out, + write_int_arg arg, + const format_specs& specs) + -> OutputIt { + return write_int(out, arg, specs); } -template ::value && !std::is_same::value && - std::is_same>::value)> -FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, - loc); + !std::is_same::value)> +FMT_CONSTEXPR FMT_INLINE auto write(basic_appender out, T value, + const format_specs& specs, locale_ref loc) + -> basic_appender { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int_noinline(out, make_write_int_arg(value, specs.sign()), + specs); } + // An inlined version of write used in format string compilation. template ::value && !std::is_same::value && - !std::is_same>::value)> + !std::is_same::value && + !std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, - const format_specs& specs, - locale_ref loc) -> OutputIt { - if (specs.localized && write_loc(out, value, specs, loc)) return out; - return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); + const format_specs& specs, locale_ref loc) + -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; + return write_int(out, make_write_int_arg(value, specs.sign()), specs); } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - FMT_UNCHECKED_ITERATOR(counting_iterator); - - struct value_type { - template FMT_CONSTEXPR void operator=(const T&) {} - }; - - FMT_CONSTEXPR counting_iterator() : count_(0) {} - - FMT_CONSTEXPR auto count() const -> size_t { return count_; } - - FMT_CONSTEXPR auto operator++() -> counting_iterator& { - ++count_; - return *this; - } - FMT_CONSTEXPR auto operator++(int) -> counting_iterator { - auto it = *this; - ++*this; - return it; - } - - FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n) - -> counting_iterator { - it.count_ += static_cast(n); - return it; - } - - FMT_CONSTEXPR auto operator*() const -> value_type { return {}; } -}; - template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, - const format_specs& specs) -> OutputIt { + const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) size = code_point_index(s, to_unsigned(specs.precision)); - bool is_debug = specs.type == presentation_type::debug; + + bool is_debug = specs.type() == presentation_type::debug; + if (is_debug) { + auto buf = counting_buffer(); + write_escaped_string(basic_appender(buf), s); + size = buf.count(); + } + size_t width = 0; if (specs.width != 0) { - if (is_debug) - width = write_escaped_string(counting_iterator{}, s).count(); - else - width = compute_width(basic_string_view(data, size)); + width = + is_debug ? size : compute_width(basic_string_view(data, size)); } - return write_padded(out, specs, size, width, - [=](reserve_iterator it) { - if (is_debug) return write_escaped_string(it, s); - return copy_str(data, data + size, it); - }); + return write_padded( + out, specs, size, width, [=](reserve_iterator it) { + return is_debug ? write_escaped_string(it, s) + : copy(data, data + size, it); + }); } template -FMT_CONSTEXPR auto write(OutputIt out, - basic_string_view> s, - const format_specs& specs, locale_ref) - -> OutputIt { - return write(out, s, specs); +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const format_specs& specs, locale_ref) -> OutputIt { + return write(out, s, specs); } template -FMT_CONSTEXPR auto write(OutputIt out, const Char* s, - const format_specs& specs, locale_ref) - -> OutputIt { - if (specs.type == presentation_type::pointer) +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, + locale_ref) -> OutputIt { + if (specs.type() == presentation_type::pointer) return write_ptr(out, bit_cast(s), &specs); - if (!s) throw_format_error("string pointer is null"); - return write(out, basic_string_view(s), specs, {}); + if (!s) report_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); } template OutputIt { if (negative) abs_value = ~abs_value + 1; int num_digits = count_digits(abs_value); auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { + if (auto ptr = to_pointer(out, size)) { if (negative) *ptr++ = static_cast('-'); format_decimal(ptr, abs_value, num_digits); return out; } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); + if (negative) *out++ = static_cast('-'); + return format_decimal(out, abs_value, num_digits); } -// DEPRECATED! template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, - format_specs& specs) -> const Char* { + format_specs& specs) -> const Char* { FMT_ASSERT(begin != end, ""); - auto align = align::none; + auto alignment = 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; + case '<': alignment = align::left; break; + case '>': alignment = align::right; break; + case '^': alignment = align::center; break; } - if (align != align::none) { + if (alignment != align::none) { if (p != begin) { auto c = *begin; if (c == '}') return begin; if (c == '{') { - throw_format_error("invalid fill character '{'"); + report_error("invalid fill character '{'"); return begin; } - specs.fill = {begin, to_unsigned(p - begin)}; + specs.set_fill(basic_string_view(begin, to_unsigned(p - begin))); begin = p + 1; } else { ++begin; @@ -2408,88 +2202,27 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, } p = begin; } - specs.align = align; + specs.set_align(alignment); return begin; } -// A floating-point presentation format. -enum class float_format : unsigned char { - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed, // Fixed point with the default precision of 6, e.g. 0.0012. - hex -}; - -struct float_specs { - int precision; - float_format format : 8; - sign_t sign : 8; - bool upper : 1; - bool locale : 1; - bool binary32 : 1; - bool showpoint : 1; -}; - -template -FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs) - -> float_specs { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - case presentation_type::none: - result.format = float_format::general; - break; - case presentation_type::general_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::general_lower: - result.format = float_format::general; - break; - case presentation_type::exp_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::exp_lower: - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::fixed_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::fixed_lower: - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::hexfloat_upper: - result.upper = true; - FMT_FALLTHROUGH; - case presentation_type::hexfloat_lower: - result.format = float_format::hex; - break; - default: - throw_format_error("invalid format specifier"); - break; - } - return result; -} - template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, - format_specs specs, - const float_specs& fspecs) -> OutputIt { + format_specs specs, sign s) -> OutputIt { auto str = - isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); + isnan ? (specs.upper() ? "NAN" : "nan") : (specs.upper() ? "INF" : "inf"); constexpr size_t str_size = 3; - auto sign = fspecs.sign; - auto size = str_size + (sign ? 1 : 0); + auto size = str_size + (s != sign::none ? 1 : 0); // Replace '0'-padding with space for non-finite values. const bool is_zero_fill = - specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); - if (is_zero_fill) specs.fill[0] = static_cast(' '); - return write_padded(out, specs, size, [=](reserve_iterator it) { - if (sign) *it++ = detail::sign(sign); - return copy_str(str, str + str_size, it); - }); + specs.fill_size() == 1 && specs.fill_unit() == '0'; + if (is_zero_fill) specs.set_fill(' '); + return write_padded(out, specs, size, + [=](reserve_iterator it) { + if (s != sign::none) + *it++ = detail::getsign(s); + return copy(str, str + str_size, it); + }); } // A decimal floating-point number significand * pow(10, exp). @@ -2510,12 +2243,12 @@ inline auto get_significand_size(const dragonbox::decimal_fp& f) -> int { template constexpr auto write_significand(OutputIt out, const char* significand, int significand_size) -> OutputIt { - return copy_str(significand, significand + significand_size, out); + return copy(significand, significand + significand_size, out); } template inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { - return format_decimal(out, significand, significand_size).end; + return format_decimal(out, significand, significand_size); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, @@ -2535,14 +2268,13 @@ template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; + if (!decimal_point) return format_decimal(out, significand, significand_size); out += significand_size + 1; Char* end = out; int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(static_cast(significand % 100))); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2563,19 +2295,19 @@ inline auto write_significand(OutputIt out, UInt significand, Char buffer[digits10() + 2]; auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point); - return detail::copy_str_noinline(buffer, end, out); + return detail::copy_noinline(buffer, end, out); } template FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, int significand_size, int integral_size, Char decimal_point) -> OutputIt { - out = detail::copy_str_noinline(significand, - significand + integral_size, out); + out = detail::copy_noinline(significand, significand + integral_size, + out); if (!decimal_point) return out; *out++ = decimal_point; - return detail::copy_str_noinline(significand + integral_size, - significand + significand_size, out); + return detail::copy_noinline(significand + integral_size, + significand + significand_size, out); } template @@ -2588,44 +2320,42 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, decimal_point); } auto buffer = basic_memory_buffer(); - write_significand(buffer_appender(buffer), significand, - significand_size, integral_size, decimal_point); + write_significand(basic_appender(buffer), significand, significand_size, + integral_size, decimal_point); grouping.apply( out, basic_string_view(buffer.data(), to_unsigned(integral_size))); - return detail::copy_str_noinline(buffer.data() + integral_size, - buffer.end(), out); + return detail::copy_noinline(buffer.data() + integral_size, + buffer.end(), out); } -template > FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { auto significand = f.significand; int significand_size = get_significand_size(f); const Char zero = static_cast('0'); - auto sign = fspecs.sign; - size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); using iterator = reserve_iterator; - Char decimal_point = - fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + Char decimal_point = specs.localized() ? detail::decimal_point(loc) + : static_cast('.'); int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { - if (fspecs.format == float_format::exp) return true; - if (fspecs.format != float_format::general) return false; + if (specs.type() == presentation_type::exp) return true; + if (specs.type() == presentation_type::fixed) return false; // Use the fixed notation if the exponent is in [exp_lower, exp_upper), // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. - const int exp_lower = -4, exp_upper = 16; + const int exp_lower = -4; return output_exp < exp_lower || - output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); }; if (use_exp_format()) { int num_zeros = 0; - if (fspecs.showpoint) { - num_zeros = fspecs.precision - significand_size; + if (specs.alt()) { + num_zeros = specs.precision - significand_size; if (num_zeros < 0) num_zeros = 0; size += to_unsigned(num_zeros); } else if (significand_size == 1) { @@ -2636,9 +2366,9 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); - char exp_char = fspecs.upper ? 'E' : 'e'; + char exp_char = specs.upper() ? 'E' : 'e'; auto write = [=](iterator it) { - if (sign) *it++ = detail::sign(sign); + if (s != sign::none) *it++ = detail::getsign(s); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, significand, significand_size, 1, decimal_point); @@ -2646,39 +2376,41 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, *it++ = static_cast(exp_char); return write_exponent(output_exp, it); }; - return specs.width > 0 ? write_padded(out, specs, size, write) - : base_iterator(out, write(reserve(out, size))); + return specs.width > 0 + ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); } int exp = f.exponent + significand_size; if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] size += to_unsigned(f.exponent); - int num_zeros = fspecs.precision - exp; + int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); - if (fspecs.showpoint) { + if (specs.alt()) { ++size; - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; + if (num_zeros <= 0 && specs.type() != presentation_type::fixed) + num_zeros = 0; if (num_zeros > 0) size += to_unsigned(num_zeros); } - auto grouping = Grouping(loc, fspecs.locale); + auto grouping = Grouping(loc, specs.localized()); size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, significand, significand_size, f.exponent, grouping); - if (!fspecs.showpoint) return it; + if (!specs.alt()) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } else if (exp > 0) { // 1234e-2 -> 12.34[0+] - int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; - size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); - auto grouping = Grouping(loc, fspecs.locale); + int num_zeros = specs.alt() ? specs.precision - significand_size : 0; + size += 1 + static_cast(max_of(num_zeros, 0)); + auto grouping = Grouping(loc, specs.localized()); size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); it = write_significand(it, significand, significand_size, exp, decimal_point, grouping); return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -2686,14 +2418,14 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, } // 1234e-6 -> 0.001234 int num_zeros = -exp; - if (significand_size == 0 && fspecs.precision >= 0 && - fspecs.precision < num_zeros) { - num_zeros = fspecs.precision; + if (significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { + num_zeros = specs.precision; } - bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); - return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = detail::sign(sign); + return write_padded(out, specs, size, [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); *it++ = zero; if (!pointy) return it; *it++ = decimal_point; @@ -2716,22 +2448,21 @@ template class fallback_digit_grouping { } }; -template +template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float>(out, f, specs, fspecs, - loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float(out, f, specs, fspecs, loc); + return do_write_float(out, f, specs, s, exp_upper, loc); } } template constexpr auto isnan(T value) -> bool { - return !(value >= value); // std::isnan doesn't support __float128. + return value != value; // std::isnan doesn't support __float128. } template @@ -2779,52 +2510,48 @@ inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) { class bigint { private: - // A bigint is stored as an array of bigits (big digits), with bigit at index - // 0 being the least significant one. - using bigit = uint32_t; + // A bigint is a number in the form bigit_[N - 1] ... bigit_[0] * 32^exp_. + using bigit = uint32_t; // A big digit. using double_bigit = uint64_t; + enum { bigit_bits = num_bits() }; enum { bigits_capacity = 32 }; basic_memory_buffer bigits_; int exp_; - FMT_CONSTEXPR20 auto operator[](int index) const -> bigit { - return bigits_[to_unsigned(index)]; - } - FMT_CONSTEXPR20 auto operator[](int index) -> bigit& { - return bigits_[to_unsigned(index)]; - } - - static constexpr const int bigit_bits = num_bits(); - friend struct formatter; - FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { - auto result = static_cast((*this)[index]) - other - borrow; - (*this)[index] = static_cast(result); + FMT_CONSTEXPR auto get_bigit(int i) const -> bigit { + return i >= exp_ && i < num_bigits() ? bigits_[i - exp_] : 0; + } + + FMT_CONSTEXPR void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = double_bigit(bigits_[index]) - other - borrow; + bigits_[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } - FMT_CONSTEXPR20 void remove_leading_zeros() { + FMT_CONSTEXPR void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; - while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits; bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. - FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { + FMT_CONSTEXPR void subtract_aligned(const bigint& other) { FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); FMT_ASSERT(compare(*this, other) >= 0, ""); bigit borrow = 0; int i = other.exp_ - exp_; for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) subtract_bigits(i, other.bigits_[j], borrow); - while (borrow > 0) subtract_bigits(i, 0, borrow); + if (borrow != 0) subtract_bigits(i, 0, borrow); + FMT_ASSERT(borrow == 0, ""); remove_leading_zeros(); } - FMT_CONSTEXPR20 void multiply(uint32_t value) { - const double_bigit wide_value = value; + FMT_CONSTEXPR void multiply(uint32_t value) { bigit carry = 0; + const double_bigit wide_value = value; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { double_bigit result = bigits_[i] * wide_value + carry; bigits_[i] = static_cast(result); @@ -2835,7 +2562,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void multiply(UInt value) { + FMT_CONSTEXPR void multiply(UInt value) { using half_uint = conditional_t::value, uint64_t, uint32_t>; const int shift = num_bits() - bigit_bits; @@ -2856,7 +2583,7 @@ class bigint { template ::value || std::is_same::value)> - FMT_CONSTEXPR20 void assign(UInt n) { + FMT_CONSTEXPR void assign(UInt n) { size_t num_bigits = 0; do { bigits_[num_bigits++] = static_cast(n); @@ -2867,30 +2594,30 @@ class bigint { } public: - FMT_CONSTEXPR20 bigint() : exp_(0) {} + FMT_CONSTEXPR bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; - FMT_CONSTEXPR20 void assign(const bigint& other) { + FMT_CONSTEXPR void assign(const bigint& other) { auto size = other.bigits_.size(); bigits_.resize(size); auto data = other.bigits_.data(); - copy_str(data, data + size, bigits_.data()); + copy(data, data + size, bigits_.data()); exp_ = other.exp_; } - template FMT_CONSTEXPR20 void operator=(Int n) { + template FMT_CONSTEXPR void operator=(Int n) { FMT_ASSERT(n > 0, ""); assign(uint64_or_128_t(n)); } - FMT_CONSTEXPR20 auto num_bigits() const -> int { + FMT_CONSTEXPR auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } - FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& { + FMT_CONSTEXPR auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -2905,49 +2632,39 @@ class bigint { return *this; } - template - FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& { + template FMT_CONSTEXPR auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs) - -> int { - int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); - if (num_lhs_bigits != num_rhs_bigits) - return num_lhs_bigits > num_rhs_bigits ? 1 : -1; - int i = static_cast(lhs.bigits_.size()) - 1; - int j = static_cast(rhs.bigits_.size()) - 1; + friend FMT_CONSTEXPR auto compare(const bigint& b1, const bigint& b2) -> int { + int num_bigits1 = b1.num_bigits(), num_bigits2 = b2.num_bigits(); + if (num_bigits1 != num_bigits2) return num_bigits1 > num_bigits2 ? 1 : -1; + int i = static_cast(b1.bigits_.size()) - 1; + int j = static_cast(b2.bigits_.size()) - 1; int end = i - j; if (end < 0) end = 0; for (; i >= end; --i, --j) { - bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; - if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + bigit b1_bigit = b1.bigits_[i], b2_bigit = b2.bigits_[j]; + if (b1_bigit != b2_bigit) return b1_bigit > b2_bigit ? 1 : -1; } if (i != j) return i > j ? 1 : -1; return 0; } // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1, - const bigint& lhs2, const bigint& rhs) - -> int { - auto minimum = [](int a, int b) { return a < b ? a : b; }; - auto maximum = [](int a, int b) { return a > b ? a : b; }; - int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); + friend FMT_CONSTEXPR auto add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) -> int { + int max_lhs_bigits = max_of(lhs1.num_bigits(), lhs2.num_bigits()); int num_rhs_bigits = rhs.num_bigits(); if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; if (max_lhs_bigits > num_rhs_bigits) return 1; - auto get_bigit = [](const bigint& n, int i) -> bigit { - return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; - }; double_bigit borrow = 0; - int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_); + int min_exp = min_of(min_of(lhs1.exp_, lhs2.exp_), rhs.exp_); for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { - double_bigit sum = - static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); - bigit rhs_bigit = get_bigit(rhs, i); + double_bigit sum = double_bigit(lhs1.get_bigit(i)) + lhs2.get_bigit(i); + bigit rhs_bigit = rhs.get_bigit(i); if (sum > rhs_bigit + borrow) return 1; borrow = rhs_bigit + borrow - sum; if (borrow > 1) return -1; @@ -2960,10 +2677,8 @@ class bigint { FMT_CONSTEXPR20 void assign_pow10(int exp) { FMT_ASSERT(exp >= 0, ""); if (exp == 0) return *this = 1; - // Find the top bit. - int bitmask = 1; - while (exp >= bitmask) bitmask <<= 1; - bitmask >>= 1; + int bitmask = 1 << (num_bits() - + countl_zero(static_cast(exp)) - 1); // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by // repeated squaring and multiplication. *this = 5; @@ -2987,17 +2702,17 @@ class bigint { // cross-product terms n[i] * n[j] such that i + j == bigit_index. for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { // Most terms are multiplied twice which can be optimized in the future. - sum += static_cast(n[i]) * n[j]; + sum += double_bigit(n[i]) * n[j]; } - (*this)[bigit_index] = static_cast(sum); + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); // Compute the carry. } // Do the same for the top half. for (int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index) { for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) - sum += static_cast(n[i++]) * n[j--]; - (*this)[bigit_index] = static_cast(sum); + sum += double_bigit(n[i++]) * n[j--]; + bigits_[bigit_index] = static_cast(sum); sum >>= num_bits(); } remove_leading_zeros(); @@ -3006,20 +2721,20 @@ class bigint { // If this bigint has a bigger exponent than other, adds trailing zero to make // exponents equal. This simplifies some operations such as subtraction. - FMT_CONSTEXPR20 void align(const bigint& other) { + FMT_CONSTEXPR void align(const bigint& other) { int exp_difference = exp_ - other.exp_; if (exp_difference <= 0) return; int num_bigits = static_cast(bigits_.size()); bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; - std::uninitialized_fill_n(bigits_.data(), exp_difference, 0u); + memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); exp_ -= exp_difference; } // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int { + FMT_CONSTEXPR auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -3138,8 +2853,11 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Generate the given number of digits. exp10 -= num_digits - 1; if (num_digits <= 0) { - denominator *= 10; - auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + auto digit = '0'; + if (num_digits == 0) { + denominator *= 10; + digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + } buf.push_back(digit); return; } @@ -3176,8 +2894,8 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, // Formats a floating-point number using the hexfloat format. template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { // float is passed as double to reduce the number of instantiations and to // simplify implementation. static_assert(!std::is_same::value, ""); @@ -3187,26 +2905,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, // Assume Float is in the format [sign][exponent][significand]. using carrier_uint = typename info::carrier_uint; - constexpr auto num_float_significand_bits = - detail::num_significand_bits(); + const auto num_float_significand_bits = detail::num_significand_bits(); basic_fp f(value); f.e += num_float_significand_bits; if (!has_implicit_bit()) --f.e; - constexpr auto num_fraction_bits = + const auto num_fraction_bits = num_float_significand_bits + (has_implicit_bit() ? 1 : 0); - constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; + const auto num_xdigits = (num_fraction_bits + 3) / 4; - constexpr auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_shift = ((num_xdigits - 1) * 4); const auto leading_mask = carrier_uint(0xF) << leading_shift; const auto leading_xdigit = static_cast((f.f & leading_mask) >> leading_shift); if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); int print_xdigits = num_xdigits - 1; - if (precision >= 0 && print_xdigits > precision) { - const int shift = ((print_xdigits - precision - 1) * 4); + if (specs.precision >= 0 && print_xdigits > specs.precision) { + const int shift = ((print_xdigits - specs.precision - 1) * 4); const auto mask = carrier_uint(0xF) << shift; const auto v = static_cast((f.f & mask) >> shift); @@ -3225,25 +2942,25 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } } - print_xdigits = precision; + print_xdigits = specs.precision; } char xdigits[num_bits() / 4]; detail::fill_n(xdigits, sizeof(xdigits), '0'); - format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); + format_base2e(4, xdigits, f.f, num_xdigits, specs.upper()); // Remove zero tail while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; buf.push_back('0'); - buf.push_back(specs.upper ? 'X' : 'x'); + buf.push_back(specs.upper() ? 'X' : 'x'); buf.push_back(xdigits[0]); - if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) + if (specs.alt() || print_xdigits > 0 || print_xdigits < specs.precision) buf.push_back('.'); buf.append(xdigits + 1, xdigits + 1 + print_xdigits); - for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); + for (; print_xdigits < specs.precision; ++print_xdigits) buf.push_back('0'); - buf.push_back(specs.upper ? 'P' : 'p'); + buf.push_back(specs.upper() ? 'P' : 'p'); uint32_t abs_e; if (f.e < 0) { @@ -3257,9 +2974,9 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, } template ::value)> -FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, - float_specs specs, buffer& buf) { - format_hexfloat(static_cast(value), precision, specs, buf); +FMT_CONSTEXPR20 void format_hexfloat(Float value, format_specs specs, + buffer& buf) { + format_hexfloat(static_cast(value), specs, buf); } constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { @@ -3274,15 +2991,15 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { } template -FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, +FMT_CONSTEXPR20 auto format_float(Float value, int precision, + const format_specs& specs, bool binary32, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); auto converted_value = convert_float(value); - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. + const bool fixed = specs.type() == presentation_type::fixed; + if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; @@ -3307,16 +3024,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; - } else if (precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(buffer_appender(buf), dec.significand); - return dec.exponent; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; @@ -3415,7 +3122,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, uint64_t prod; uint32_t digits; bool should_round_up; - int number_of_digits_to_print = precision > 9 ? 9 : precision; + int number_of_digits_to_print = min_of(precision, 9); // Print a 9-digits subsegment, either the first or the second. auto print_subsegment = [&](uint32_t subsegment, char* buffer) { @@ -3443,7 +3150,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // for details. prod = ((subsegment * static_cast(450359963)) >> 20) + 1; digits = static_cast(prod >> 32); - copy2(buffer, digits2(digits)); + write2digits(buffer, digits); number_of_digits_printed += 2; } @@ -3451,7 +3158,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, while (number_of_digits_printed < number_of_digits_to_print) { prod = static_cast(prod) * static_cast(100); digits = static_cast(prod >> 32); - copy2(buffer + number_of_digits_printed, digits2(digits)); + write2digits(buffer + number_of_digits_printed, digits); number_of_digits_printed += 2; } }; @@ -3560,9 +3267,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } if (use_dragon) { auto f = basic_fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(converted_value); + bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) + : f.assign(converted_value); if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; if (fixed) dragon_flags |= dragon::fixed; // Limit precision to the maximum possible number of significant digits in @@ -3571,7 +3277,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, if (precision > max_double_digits) precision = max_double_digits; format_dragon(f, dragon_flags, precision, buf, exp); } - if (!fixed && !specs.showpoint) { + if (!fixed && !specs.alt()) { // Remove trailing zeros. auto num_digits = buf.size(); while (num_digits > 0 && buf[num_digits - 1] == '0') { @@ -3582,97 +3288,106 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } return exp; } + +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template constexpr auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} + template -FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, - format_specs specs, locale_ref loc) - -> OutputIt { - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = specs.sign; - if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. - fspecs.sign = sign::minus; - value = -value; - } else if (fspecs.sign == sign::minus) { - fspecs.sign = sign::none; - } +FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, + locale_ref loc) -> OutputIt { + // Use signbit because value < 0 is false for NaN. + sign s = detail::signbit(value) ? sign::minus : specs.sign(); if (!detail::isfinite(value)) - return write_nonfinite(out, detail::isnan(value), specs, fspecs); + return write_nonfinite(out, detail::isnan(value), specs, s); - if (specs.align == align::numeric && fspecs.sign) { - auto it = reserve(out, 1); - *it++ = detail::sign(fspecs.sign); - out = base_iterator(out, it); - fspecs.sign = sign::none; + if (specs.align() == align::numeric && s != sign::none) { + *out++ = detail::getsign(s); + s = sign::none; if (specs.width != 0) --specs.width; } - memory_buffer buffer; - if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); - format_hexfloat(convert_float(value), specs.precision, fspecs, buffer); - return write_bytes(out, {buffer.data(), buffer.size()}, - specs); + constexpr int exp_upper = detail::exp_upper(); + int precision = specs.precision; + if (precision < 0) { + if (specs.type() != presentation_type::none) { + precision = 6; + } else if (is_fast_float::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + using floaty = conditional_t= sizeof(double), double, float>; + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, s, exp_upper, loc); + } } - int precision = specs.precision >= 0 || specs.type == presentation_type::none - ? specs.precision - : 6; - if (fspecs.format == float_format::exp) { + + memory_buffer buffer; + if (specs.type() == presentation_type::hexfloat) { + if (s != sign::none) buffer.push_back(detail::getsign(s)); + format_hexfloat(convert_float(value), specs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, + specs); + } + + if (specs.type() == presentation_type::exp) { if (precision == max_value()) - throw_format_error("number is too big"); + report_error("number is too big"); else ++precision; - } else if (fspecs.format != float_format::fixed && precision == 0) { + if (specs.precision != 0) specs.set_alt(); + } else if (specs.type() == presentation_type::fixed) { + if (specs.precision != 0) specs.set_alt(); + } else if (precision == 0) { precision = 1; } - if (const_check(std::is_same())) fspecs.binary32 = true; - int exp = format_float(convert_float(value), precision, fspecs, buffer); - fspecs.precision = precision; + int exp = format_float(convert_float(value), precision, specs, + std::is_same(), buffer); + + specs.precision = precision; auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, f, specs, fspecs, loc); + return write_float(out, f, specs, s, exp_upper, loc); } template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, locale_ref loc = {}) -> OutputIt { - if (const_check(!is_supported_floating_point(value))) return out; - return specs.localized && write_loc(out, value, specs, loc) + return specs.localized() && write_loc(out, value, specs, loc) ? out - : write_float(out, value, specs, loc); + : write_float(out, value, specs, loc); } template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { - if (is_constant_evaluated()) return write(out, value, format_specs()); - if (const_check(!is_supported_floating_point(value))) return out; + if (is_constant_evaluated()) return write(out, value, format_specs()); - auto fspecs = float_specs(); - if (detail::signbit(value)) { - fspecs.sign = sign::minus; - value = -value; - } + auto s = detail::signbit(value) ? sign::minus : sign::none; - constexpr auto specs = format_specs(); - using floaty = conditional_t::value, double, T>; + constexpr auto specs = format_specs(); + using floaty = conditional_t= sizeof(double), double, float>; using floaty_uint = typename dragonbox::float_info::carrier_uint; floaty_uint mask = exponent_mask(); if ((bit_cast(value) & mask) == mask) - return write_nonfinite(out, std::isnan(value), specs, fspecs); + return write_nonfinite(out, std::isnan(value), specs, s); auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, {}); + return write_float(out, dec, specs, s, exp_upper(), {}); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, format_specs()); + return write(out, value, format_specs()); } template -auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) +auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) -> OutputIt { FMT_ASSERT(false, ""); return out; @@ -3681,13 +3396,11 @@ auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {}) template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { - auto it = reserve(out, value.size()); - it = copy_str_noinline(value.begin(), value.end(), it); - return base_iterator(out, it); + return copy_noinline(value.begin(), value.end(), out); } template ::value)> + FMT_ENABLE_IF(has_to_string_view::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -3695,10 +3408,8 @@ constexpr auto write(OutputIt out, const T& value) -> OutputIt { // FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, - bool check = - std::is_enum::value && !std::is_same::value && - mapped_type_constant>::value != - type::custom_type, + bool check = std::is_enum::value && !std::is_same::value && + mapped_type_constant::value != type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { return write(out, static_cast>(value)); @@ -3706,13 +3417,12 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { template ::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value, - const format_specs& specs = {}, locale_ref = {}) - -> OutputIt { - return specs.type != presentation_type::none && - specs.type != presentation_type::string - ? write(out, value ? 1 : 0, specs, {}) - : write_bytes(out, value ? "true" : "false", specs); +FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + return specs.type() != presentation_type::none && + specs.type() != presentation_type::string + ? write(out, value ? 1 : 0, specs, {}) + : write_bytes(out, value ? "true" : "false", specs); } template @@ -3723,171 +3433,150 @@ FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt { } template -FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) - -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt { if (value) return write(out, basic_string_view(value)); - throw_format_error("string pointer is null"); + report_error("string pointer is null"); return out; } template ::value)> -auto write(OutputIt out, const T* value, const format_specs& specs = {}, +auto write(OutputIt out, const T* value, const format_specs& specs = {}, locale_ref = {}) -> OutputIt { return write_ptr(out, bit_cast(value), &specs); } -// A write overload that handles implicit conversions. template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< - std::is_class::value && !is_string::value && - !is_floating_point::value && !std::is_same::value && - !std::is_same().map( - value))>>::value, - OutputIt> { - return write(out, arg_mapper().map(value)); + FMT_ENABLE_IF(mapped_type_constant::value == + type::custom_type && + !std::is_fundamental::value)> +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> OutputIt { + auto f = formatter(); + auto parse_ctx = parse_context({}); + f.parse(parse_ctx); + auto ctx = basic_format_context(out, {}, {}); + return f.format(value, ctx); } -template > -FMT_CONSTEXPR auto write(OutputIt out, const T& value) - -> enable_if_t::value == type::custom_type, - OutputIt> { - auto formatter = typename Context::template formatter_type(); - auto parse_ctx = typename Context::parse_context_type({}); - formatter.parse(parse_ctx); - auto ctx = Context(out, {}, {}); - return formatter.format(value, ctx); -} +template +using is_builtin = + bool_constant::value || FMT_BUILTIN_TYPES>; // An argument visitor that formats the argument and writes it via the output // iterator. It's a class and not a generic lambda for compatibility with C++11. template struct default_arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; + using context = buffered_context; - iterator out; - basic_format_args args; - locale_ref loc; + basic_appender out; - template auto operator()(T value) -> iterator { - return write(out, value); + void operator()(monostate) { report_error("argument not found"); } + + template ::value)> + void operator()(T value) { + write(out, value); } - auto operator()(typename basic_format_arg::handle h) -> iterator { - basic_format_parse_context parse_ctx({}); - context format_ctx(out, args, loc); + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg::handle h) { + // Use a null locale since the default format must be unlocalized. + auto parse_ctx = parse_context({}); + auto format_ctx = context(out, {}, {}); h.format(parse_ctx, format_ctx); - return format_ctx.out(); } }; template struct arg_formatter { - using iterator = buffer_appender; - using context = buffer_context; + basic_appender out; + const format_specs& specs; + FMT_NO_UNIQUE_ADDRESS locale_ref locale; - iterator out; - const format_specs& specs; - locale_ref locale; - - template - FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { - return detail::write(out, value, specs, locale); + template ::value)> + FMT_CONSTEXPR FMT_INLINE void operator()(T value) { + detail::write(out, value, specs, locale); } - auto operator()(typename basic_format_arg::handle) -> iterator { + + template ::value)> + void operator()(T) { + FMT_ASSERT(false, ""); + } + + void operator()(typename basic_format_arg>::handle) { // User-defined types are handled separately because they require access // to the parse context. - return out; } }; -struct width_checker { +struct dynamic_spec_getter { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative width"); - return static_cast(value); + return is_negative(value) ? ~0ull : static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("width is not integer"); + report_error("width/precision is not integer"); return 0; } }; -struct precision_checker { - template ::value)> - FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) throw_format_error("negative precision"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - throw_format_error("precision is not integer"); - return 0; - } -}; - -template -FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int { - unsigned long long value = visit_format_arg(Handler(), arg); - if (value > to_unsigned(max_value())) - throw_format_error("number is too big"); - return static_cast(value); -} - template -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) { +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { auto arg = ctx.arg(id); - if (!arg) ctx.on_error("argument not found"); + if (!arg) report_error("argument not found"); return arg; } -template -FMT_CONSTEXPR void handle_dynamic_spec(int& value, - arg_ref ref, - Context& ctx) { - switch (ref.kind) { - case arg_id_kind::none: - break; - case arg_id_kind::index: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.index)); - break; - case arg_id_kind::name: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.name)); - break; - } +template +FMT_CONSTEXPR int get_dynamic_spec( + arg_id_kind kind, const arg_ref& ref, + Context& ctx) { + FMT_ASSERT(kind != arg_id_kind::none, ""); + auto arg = + kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); + if (!arg) report_error("argument not found"); + unsigned long long value = arg.visit(dynamic_spec_getter()); + if (value > to_unsigned(max_value())) + report_error("width/precision is out of range"); + return static_cast(value); } -#if FMT_USE_USER_DEFINED_LITERALS -# if FMT_USE_NONTYPE_TEMPLATE_ARGS +template +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref& ref, Context& ctx) { + if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); +} + +#if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> -struct statically_named_arg : view { + fmt::detail::fixed_string Str> +struct static_named_arg : view { static constexpr auto name = Str.data; const T& value; - statically_named_arg(const T& v) : value(v) {} + static_named_arg(const T& v) : value(v) {} }; template Str> -struct is_named_arg> : std::true_type {}; + fmt::detail::fixed_string Str> +struct is_named_arg> : std::true_type {}; template Str> -struct is_statically_named_arg> - : std::true_type {}; + fmt::detail::fixed_string Str> +struct is_static_named_arg> : std::true_type { +}; -template Str> +template Str> struct udl_arg { template auto operator=(T&& value) const { - return statically_named_arg(std::forward(value)); + return static_named_arg(std::forward(value)); } }; -# else +#else template struct udl_arg { const Char* str; @@ -3895,149 +3584,198 @@ template struct udl_arg { return {str, std::forward(value)}; } }; -# endif -#endif // FMT_USE_USER_DEFINED_LITERALS +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS -template -auto vformat(const Locale& loc, basic_string_view fmt, - basic_format_args>> args) - -> std::basic_string { - auto buf = basic_memory_buffer(); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); - return {buf.data(), buf.size()}; -} +template struct format_handler { + parse_context parse_ctx; + buffered_context ctx; + + void on_text(const Char* begin, const Char* end) { + copy_noinline(begin, end, ctx.out()); + } + + FMT_CONSTEXPR auto on_arg_id() -> int { return parse_ctx.next_arg_id(); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { + parse_ctx.check_arg_id(id); + return id; + } + FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { + parse_ctx.check_arg_id(id); + int arg_id = ctx.arg_id(id); + if (arg_id < 0) report_error("argument not found"); + return arg_id; + } + + FMT_INLINE void on_replacement_field(int id, const Char*) { + ctx.arg(id).visit(default_arg_formatter{ctx.out()}); + } + + auto on_format_specs(int id, const Char* begin, const Char* end) + -> const Char* { + auto arg = get_arg(ctx, id); + // Not using a visitor for custom types gives better codegen. + if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); + + auto specs = dynamic_format_specs(); + begin = parse_format_specs(begin, end, specs, parse_ctx, arg.type()); + if (specs.dynamic()) { + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + arg.visit(arg_formatter{ctx.out(), specs, ctx.locale()}); + return begin; + } + + FMT_NORETURN void on_error(const char* message) { report_error(message); } +}; using format_func = void (*)(detail::buffer&, int, const char*); +FMT_API void do_report_error(format_func func, int error_code, + const char* message) noexcept; FMT_API void format_error_code(buffer& out, int error_code, string_view message) noexcept; -FMT_API void report_error(format_func func, int error_code, - const char* message) noexcept; -} // namespace detail - -FMT_API auto vsystem_error(int error_code, string_view format_str, - format_args args) -> std::system_error; - -/** - \rst - Constructs :class:`std::system_error` with a message formatted with - ``fmt::format(fmt, args...)``. - *error_code* is a system error code as given by ``errno``. - - **Example**:: - - // This throws std::system_error with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char* filename = "madeup"; - std::FILE* file = std::fopen(filename, "r"); - if (!file) - throw fmt::system_error(errno, "cannot open file '{}'", filename); - \endrst - */ -template -auto system_error(int error_code, format_string fmt, T&&... args) - -> std::system_error { - return vsystem_error(error_code, fmt, fmt::make_format_args(args...)); +template +template +FMT_CONSTEXPR auto native_formatter::format( + const T& val, FormatContext& ctx) const -> decltype(ctx.out()) { + if (!specs_.dynamic()) + return write(ctx.out(), val, specs_, ctx.locale()); + auto specs = format_specs(specs_); + handle_dynamic_spec(specs.dynamic_width(), specs.width, specs_.width_ref, + ctx); + handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs_.precision_ref, ctx); + return write(ctx.out(), val, specs, ctx.locale()); } -/** - \rst - Formats an error message for an error returned by an operating system or a - language runtime, for example a file opening error, and writes it to *out*. - The format is the same as the one used by ``std::system_error(ec, message)`` - where ``ec`` is ``std::error_code(error_code, std::generic_category()})``. - It is implementation-defined but normally looks like: +// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; - .. parsed-literal:: - **: ** +// DEPRECATED! +template struct vformat_args { + using type = basic_format_args>; +}; +template <> struct vformat_args { + using type = format_args; +}; - where ** is the passed message and ** is the system - message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - \endrst - */ -FMT_API void format_system_error(detail::buffer& out, int error_code, - const char* message) noexcept; +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}) { + auto out = basic_appender(buf); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} +} // namespace detail -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -FMT_API void report_system_error(int error_code, const char* message) noexcept; +FMT_BEGIN_EXPORT -/** Fast integer formatter. */ -class format_int { +// A generic formatting context with custom output iterator and character +// (code unit) support. Char is the format string code unit type which can be +// different from OutputIt::value_type. +template class generic_context { private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum { buffer_size = std::numeric_limits::digits10 + 3 }; - mutable char buffer_[buffer_size]; - char* str_; - - template auto format_unsigned(UInt value) -> char* { - auto n = static_cast>(value); - return detail::format_decimal(buffer_, n, buffer_size - 1).begin; - } - - template auto format_signed(Int value) -> char* { - auto abs_value = static_cast>(value); - bool negative = value < 0; - if (negative) abs_value = 0 - abs_value; - auto begin = format_unsigned(abs_value); - if (negative) *--begin = '-'; - return begin; - } + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; public: - explicit format_int(int value) : str_(format_signed(value)) {} - explicit format_int(long value) : str_(format_signed(value)) {} - explicit format_int(long long value) : str_(format_signed(value)) {} - explicit format_int(unsigned value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long long value) - : str_(format_unsigned(value)) {} + using char_type = Char; + using iterator = OutputIt; + using parse_context_type FMT_DEPRECATED = parse_context; + template + using formatter_type FMT_DEPRECATED = formatter; + enum { builtin_types = FMT_BUILTIN_TYPES }; - /** Returns the number of characters written to the output buffer. */ - auto size() const -> size_t { - return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + constexpr generic_context(OutputIt out, + basic_format_args args, + detail::locale_ref loc = {}) + : out_(out), args_(args), loc_(loc) {} + generic_context(generic_context&&) = default; + generic_context(const generic_context&) = delete; + void operator=(const generic_context&) = delete; + + constexpr auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } + auto arg(basic_string_view name) const + -> basic_format_arg { + return args_.get(name); + } + constexpr auto arg_id(basic_string_view name) const -> int { + return args_.get_id(name); } - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - auto data() const -> const char* { return str_; } + constexpr auto out() const -> iterator { return out_; } - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - auto c_str() const -> const char* { - buffer_[buffer_size - 1] = '\0'; - return str_; + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; } - /** - \rst - Returns the content of the output buffer as an ``std::string``. - \endrst - */ - auto str() const -> std::string { return std::string(str_, size()); } + constexpr auto locale() const -> detail::locale_ref { return loc_; } }; -template -struct formatter::value>> - : formatter, Char> { - template - auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { - using base = formatter, Char>; - return base::format(format_as(value), ctx); +class loc_value { + private: + basic_format_arg value_; + + public: + template ::value)> + loc_value(T value) : value_(value) {} + + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return value_.visit(vis); } }; -#define FMT_FORMAT_AS(Type, Base) \ - template \ - struct formatter : formatter {} +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; + + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs& specs) const -> bool; + + public: + static FMT_API typename Locale::id id; + + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", std::string grouping = "\3", + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(grouping), + decimal_point_(decimal_point) {} + + auto put(appender out, loc_value val, const format_specs& specs) const + -> bool { + return do_put(out, val, specs); + } +}; + +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + FMT_CONSTEXPR auto format(Type value, FormatContext& ctx) const \ + -> decltype(ctx.out()) { \ + return formatter::format(value, ctx); \ + } \ + } FMT_FORMAT_AS(signed char, int); FMT_FORMAT_AS(unsigned char, unsigned); @@ -4046,44 +3784,58 @@ FMT_FORMAT_AS(unsigned short, unsigned); FMT_FORMAT_AS(long, detail::long_type); FMT_FORMAT_AS(unsigned long, detail::ulong_type); FMT_FORMAT_AS(Char*, const Char*); -FMT_FORMAT_AS(std::basic_string, basic_string_view); -FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(detail::std_string_view, basic_string_view); +FMT_FORMAT_AS(std::nullptr_t, const void*); FMT_FORMAT_AS(void*, const void*); template struct formatter : formatter, Char> {}; +template +class formatter, Char> + : public formatter, Char> {}; + +template +struct formatter, Char> : formatter {}; +template +struct formatter, Char> + : formatter {}; + +template +struct formatter + : detail::native_formatter {}; + +template +struct formatter>> + : formatter, Char> { + template + FMT_CONSTEXPR auto format(const T& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto&& val = format_as(value); // Make an lvalue reference for format. + return formatter, Char>::format(val, ctx); + } +}; + /** - \rst - Converts ``p`` to ``const void*`` for pointer formatting. - - **Example**:: - - auto s = fmt::format("{}", fmt::ptr(p)); - \endrst + * Converts `p` to `const void*` for pointer formatting. + * + * **Example**: + * + * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { static_assert(std::is_pointer::value, ""); return detail::bit_cast(p); } -template -auto ptr(const std::unique_ptr& p) -> const void* { - return p.get(); -} -template auto ptr(const std::shared_ptr& p) -> const void* { - return p.get(); -} /** - \rst - Converts ``e`` to the underlying type. - - **Example**:: - - enum class color { red, green, blue }; - auto s = fmt::format("{}", fmt::underlying(color::red)); - \endrst + * Converts `e` to the underlying type. + * + * **Example**: + * + * enum class color { red, green, blue }; + * auto s = fmt::format("{}", fmt::underlying(color::red)); // s == "0" */ template constexpr auto underlying(Enum e) noexcept -> underlying_t { @@ -4097,13 +3849,22 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t { } } // namespace enums -class bytes { - private: - string_view data_; - friend struct formatter; +#ifdef __cpp_lib_byte +template <> struct formatter : formatter { + static auto format_as(std::byte b) -> unsigned char { + return static_cast(b); + } + template + auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { + return formatter::format(format_as(b), ctx); + } +}; +#endif - public: - explicit bytes(string_view data) : data_(data) {} +struct bytes { + string_view data; + + inline explicit bytes(string_view s) : data(s) {} }; template <> struct formatter { @@ -4111,19 +3872,19 @@ template <> struct formatter { detail::dynamic_format_specs<> specs_; public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::string_type); } template - auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) { - detail::handle_dynamic_spec(specs_.width, - specs_.width_ref, ctx); - detail::handle_dynamic_spec( - specs_.precision, specs_.precision_ref, ctx); - return detail::write_bytes(ctx.out(), b.data_, specs_); + auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + return detail::write_bytes(ctx.out(), b.data, specs); } }; @@ -4133,15 +3894,13 @@ template struct group_digits_view { }; /** - \rst - Returns a view that formats an integer value using ',' as a locale-independent - thousands separator. - - **Example**:: - - fmt::print("{}", fmt::group_digits(12345)); - // Output: "12,345" - \endrst + * Returns a view that formats an integer value using ',' as a + * locale-independent thousands separator. + * + * **Example**: + * + * fmt::print("{}", fmt::group_digits(12345)); + * // Output: "12,345" */ template auto group_digits(T value) -> group_digits_view { return {value}; @@ -4152,331 +3911,255 @@ template struct formatter> : formatter { detail::dynamic_format_specs<> specs_; public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type::int_type); } template - auto format(group_digits_view t, FormatContext& ctx) + auto format(group_digits_view view, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::handle_dynamic_spec(specs_.width, - specs_.width_ref, ctx); - detail::handle_dynamic_spec( - specs_.precision, specs_.precision_ref, ctx); + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + auto arg = detail::make_write_int_arg(view.value, specs.sign()); return detail::write_int( - ctx.out(), static_cast>(t.value), 0, specs_, - detail::digit_grouping("\3", ",")); + ctx.out(), static_cast>(arg.abs_value), + arg.prefix, specs, detail::digit_grouping("\3", ",")); } }; -template struct nested_view { - const formatter* fmt; +template struct nested_view { + const formatter* fmt; const T* value; }; -template struct formatter> { - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { +template +struct formatter, Char> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } - auto format(nested_view view, format_context& ctx) const + template + auto format(nested_view view, FormatContext& ctx) const -> decltype(ctx.out()) { return view.fmt->format(*view.value, ctx); } }; -template struct nested_formatter { +template struct nested_formatter { private: + basic_specs specs_; int width_; - detail::fill_t fill_; - align_t align_ : 4; - formatter formatter_; + formatter formatter_; public: - constexpr nested_formatter() : width_(0), align_(align_t::none) {} + constexpr nested_formatter() : width_(0) {} - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* { - auto specs = detail::dynamic_format_specs(); - auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx, - detail::type::none_type); - width_ = specs.width; - fill_ = specs.fill; - align_ = specs.align; + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + auto specs = format_specs(); + it = detail::parse_align(it, end, specs); + specs_ = specs; + Char c = *it; + auto width_ref = detail::arg_ref(); + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs, width_ref, ctx); + width_ = specs.width; + } ctx.advance_to(it); return formatter_.parse(ctx); } - template - auto write_padded(format_context& ctx, F write) const -> decltype(ctx.out()) { + template + auto write_padded(FormatContext& ctx, F write) const -> decltype(ctx.out()) { if (width_ == 0) return write(ctx.out()); - auto buf = memory_buffer(); - write(std::back_inserter(buf)); - auto specs = format_specs<>(); + auto buf = basic_memory_buffer(); + write(basic_appender(buf)); + auto specs = format_specs(); specs.width = width_; - specs.fill = fill_; - specs.align = align_; - return detail::write(ctx.out(), string_view(buf.data(), buf.size()), specs); + specs.copy_fill_from(specs_); + specs.set_align(specs_.align()); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); } - auto nested(const T& value) const -> nested_view { - return nested_view{&formatter_, &value}; + auto nested(const T& value) const -> nested_view { + return nested_view{&formatter_, &value}; } }; -// DEPRECATED! join_view will be moved to ranges.h. -template -struct join_view : detail::view { - It begin; - Sentinel end; - basic_string_view sep; - - join_view(It b, Sentinel e, basic_string_view s) - : begin(b), end(e), sep(s) {} -}; - -template -struct formatter, Char> { - private: - using value_type = -#ifdef __cpp_lib_ranges - std::iter_value_t; -#else - typename std::iterator_traits::value_type; -#endif - formatter, Char> value_formatter_; - - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - return value_formatter_.parse(ctx); - } - - template - auto format(const join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { - auto it = value.begin; - auto out = ctx.out(); - if (it != value.end) { - out = value_formatter_.format(*it, ctx); - ++it; - while (it != value.end) { - out = detail::copy_str(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - out = value_formatter_.format(*it, ctx); - ++it; - } - } - return out; - } -}; - -/** - Returns a view that formats the iterator range `[begin, end)` with elements - separated by `sep`. - */ -template -auto join(It begin, Sentinel end, string_view sep) -> join_view { - return {begin, end, sep}; -} - -/** - \rst - Returns a view that formats `range` with elements separated by `sep`. - - **Example**:: - - std::vector v = {1, 2, 3}; - fmt::print("{}", fmt::join(v, ", ")); - // Output: "1, 2, 3" - - ``fmt::join`` applies passed format specifiers to the range elements:: - - fmt::print("{:02}", fmt::join(v, ", ")); - // Output: "01, 02, 03" - \endrst - */ -template -auto join(Range&& range, string_view sep) - -> join_view, detail::sentinel_t> { - return join(std::begin(range), std::end(range), sep); -} - -/** - \rst - Converts *value* to ``std::string`` using the default format for type *T*. - - **Example**:: - - #include - - std::string answer = fmt::to_string(42); - \endrst - */ -template ::value && - !detail::has_format_as::value)> -inline auto to_string(const T& value) -> std::string { - auto buffer = memory_buffer(); - detail::write(appender(buffer), value); - return {buffer.data(), buffer.size()}; -} - -template ::value)> -FMT_NODISCARD inline auto to_string(T value) -> std::string { - // The buffer should be large enough to store the number including the sign - // or "false" for bool. - constexpr int max_size = detail::digits10() + 2; - char buffer[max_size > 5 ? static_cast(max_size) : 5]; - char* begin = buffer; - return std::string(begin, detail::write(begin, value)); -} - -template -FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) - -> std::basic_string { - auto size = buf.size(); - detail::assume(size < std::basic_string().max_size()); - return std::basic_string(buf.data(), size); -} - -template ::value && - detail::has_format_as::value)> -inline auto to_string(const T& value) -> std::string { - return to_string(format_as(value)); -} - -FMT_END_EXPORT - -namespace detail { - -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc) { - auto out = buffer_appender(buf); - if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { - auto arg = args.get(0); - if (!arg) throw_format_error("argument not found"); - visit_format_arg(default_arg_formatter{out, args, loc}, arg); - return; - } - - struct format_handler : error_handler { - basic_format_parse_context parse_context; - buffer_context context; - - format_handler(buffer_appender p_out, basic_string_view str, - basic_format_args> p_args, - locale_ref p_loc) - : parse_context(str), context(p_out, p_args, p_loc) {} - - void on_text(const Char* begin, const Char* end) { - auto text = basic_string_view(begin, to_unsigned(end - begin)); - context.advance_to(write(context.out(), text)); - } - - FMT_CONSTEXPR auto on_arg_id() -> int { - return parse_context.next_arg_id(); - } - FMT_CONSTEXPR auto on_arg_id(int id) -> int { - return parse_context.check_arg_id(id), id; - } - FMT_CONSTEXPR auto on_arg_id(basic_string_view id) -> int { - int arg_id = context.arg_id(id); - if (arg_id < 0) throw_format_error("argument not found"); - return arg_id; - } - - FMT_INLINE void on_replacement_field(int id, const Char*) { - auto arg = get_arg(context, id); - context.advance_to(visit_format_arg( - default_arg_formatter{context.out(), context.args(), - context.locale()}, - arg)); - } - - auto on_format_specs(int id, const Char* begin, const Char* end) - -> const Char* { - auto arg = get_arg(context, id); - // Not using a visitor for custom types gives better codegen. - if (arg.format_custom(begin, parse_context, context)) - return parse_context.begin(); - auto specs = detail::dynamic_format_specs(); - begin = parse_format_specs(begin, end, specs, parse_context, arg.type()); - detail::handle_dynamic_spec( - specs.width, specs.width_ref, context); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, context); - if (begin == end || *begin != '}') - throw_format_error("missing '}' in format string"); - auto f = arg_formatter{context.out(), specs, context.locale()}; - context.advance_to(visit_format_arg(f, arg)); - return begin; - } - }; - detail::parse_format_string(fmt, format_handler(out, fmt, args, loc)); -} - -FMT_BEGIN_EXPORT - -#ifndef FMT_HEADER_ONLY -extern template FMT_API void vformat_to(buffer&, string_view, - typename vformat_args<>::type, - locale_ref); -extern template FMT_API auto thousands_sep_impl(locale_ref) - -> thousands_sep_result; -extern template FMT_API auto thousands_sep_impl(locale_ref) - -> thousands_sep_result; -extern template FMT_API auto decimal_point_impl(locale_ref) -> char; -extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; -#endif // FMT_HEADER_ONLY - -} // namespace detail - -#if FMT_USE_USER_DEFINED_LITERALS inline namespace literals { -/** - \rst - User-defined literal equivalent of :func:`fmt::arg`. - - **Example**:: - - using namespace fmt::literals; - fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); - \endrst - */ -# if FMT_USE_NONTYPE_TEMPLATE_ARGS -template constexpr auto operator""_a() { - using char_t = remove_cvref_t; - return detail::udl_arg(); +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +template constexpr auto operator""_a() { + using char_t = remove_cvref_t; + return detail::udl_arg(); } -# else +#else +/** + * User-defined literal equivalent of `fmt::arg`. + * + * **Example**: + * + * using namespace fmt::literals; + * fmt::print("The answer is {answer}.", "answer"_a=42); + */ constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { return {s}; } -# endif +#endif // FMT_USE_NONTYPE_TEMPLATE_ARGS } // namespace literals -#endif // FMT_USE_USER_DEFINED_LITERALS + +/// A fast integer formatter. +class format_int { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum { buffer_size = std::numeric_limits::digits10 + 3 }; + mutable char buffer_[buffer_size]; + char* str_; + + template + FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* { + auto n = static_cast>(value); + return detail::do_format_decimal(buffer_, n, buffer_size - 1); + } + + template + FMT_CONSTEXPR20 auto format_signed(Int value) -> char* { + auto abs_value = static_cast>(value); + bool negative = value < 0; + if (negative) abs_value = 0 - abs_value; + auto begin = format_unsigned(abs_value); + if (negative) *--begin = '-'; + return begin; + } + + public: + FMT_CONSTEXPR20 explicit format_int(int value) : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(long long value) + : str_(format_signed(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long value) + : str_(format_unsigned(value)) {} + FMT_CONSTEXPR20 explicit format_int(unsigned long long value) + : str_(format_unsigned(value)) {} + + /// Returns the number of characters written to the output buffer. + FMT_CONSTEXPR20 auto size() const -> size_t { + return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); + } + + /// Returns a pointer to the output buffer content. No terminating null + /// character is appended. + FMT_CONSTEXPR20 auto data() const -> const char* { return str_; } + + /// Returns a pointer to the output buffer content with terminating null + /// character appended. + FMT_CONSTEXPR20 auto c_str() const -> const char* { + buffer_[buffer_size - 1] = '\0'; + return str_; + } + + /// Returns the content of the output buffer as an `std::string`. + inline auto str() const -> std::string { return {str_, size()}; } +}; + +#define FMT_STRING_IMPL(s, base) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + constexpr explicit operator fmt::basic_string_view() const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() + +/** + * Constructs a legacy compile-time format string from a string literal `s`. + * + * **Example**: + * + * // A compile-time error because 'd' is an invalid specifier for strings. + * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); + */ +#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) + +FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error; + +/** + * Constructs `std::system_error` with a message formatted with + * `fmt::format(fmt, args...)`. + * `error_code` is a system error code as given by `errno`. + * + * **Example**: + * + * // This throws std::system_error with the description + * // cannot open file 'madeup': No such file or directory + * // or similar (system message may vary). + * const char* filename = "madeup"; + * FILE* file = fopen(filename, "r"); + * if (!file) + * throw fmt::system_error(errno, "cannot open file '{}'", filename); + */ +template +auto system_error(int error_code, format_string fmt, T&&... args) + -> std::system_error { + return vsystem_error(error_code, fmt.str, vargs{{args...}}); +} + +/** + * Formats an error message for an error returned by an operating system or a + * language runtime, for example a file opening error, and writes it to `out`. + * The format is the same as the one used by `std::system_error(ec, message)` + * where `ec` is `std::error_code(error_code, std::generic_category())`. + * It is implementation-defined but normally looks like: + * + * : + * + * where `` is the passed message and `` is the system + * message corresponding to the error code. + * `error_code` is a system error code as given by `errno`. + */ +FMT_API void format_system_error(detail::buffer& out, int error_code, + const char* message) noexcept; + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_system_error(int error_code, const char* message) noexcept; template ::value)> inline auto vformat(const Locale& loc, string_view fmt, format_args args) -> std::string { - return detail::vformat(loc, fmt, args); + auto buf = memory_buffer(); + detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + return {buf.data(), buf.size()}; } template ::value)> -inline auto format(const Locale& loc, format_string fmt, T&&... args) +FMT_INLINE auto format(const Locale& loc, format_string fmt, T&&... args) -> std::string { - return fmt::vformat(loc, string_view(fmt), fmt::make_format_args(args...)); + return vformat(loc, fmt.str, vargs{{args...}}); } template ::value&& - detail::is_locale::value)> + FMT_ENABLE_IF(detail::is_output_iterator::value)> auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, format_args args) -> OutputIt { - using detail::get_buffer; - auto&& buf = get_buffer(out); + auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); return detail::get_iterator(buf, out); } @@ -4486,7 +4169,7 @@ template ::value)> FMT_INLINE auto format_to(OutputIt out, const Locale& loc, format_string fmt, T&&... args) -> OutputIt { - return vformat_to(out, loc, fmt, fmt::make_format_args(args...)); + return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); } template fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt, fmt::make_format_args(args...), - detail::locale_ref(loc)); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, + detail::locale_ref(loc)); return buf.count(); } -FMT_END_EXPORT +FMT_API auto vformat(string_view fmt, format_args args) -> std::string; -template -template -FMT_CONSTEXPR FMT_INLINE auto -formatter::value != - detail::type::custom_type>>::format(const T& val, - FormatContext& ctx) - const -> decltype(ctx.out()) { - if (specs_.width_ref.kind == detail::arg_id_kind::none && - specs_.precision_ref.kind == detail::arg_id_kind::none) { - return detail::write(ctx.out(), val, specs_, ctx.locale()); - } - auto specs = specs_; - detail::handle_dynamic_spec(specs.width, - specs.width_ref, ctx); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, ctx); - return detail::write(ctx.out(), val, specs, ctx.locale()); +/** + * Formats `args` according to specifications in `fmt` and returns the result + * as a string. + * + * **Example**: + * + * #include + * std::string message = fmt::format("The answer is {}.", 42); + */ +template +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { + return vformat(fmt.str, vargs{{args...}}); } +/** + * Converts `value` to `std::string` using the default format for type `T`. + * + * **Example**: + * + * std::string answer = fmt::to_string(42); + */ +template ::value)> +FMT_NODISCARD auto to_string(T value) -> std::string { + // The buffer should be large enough to store the number including the sign + // or "false" for bool. + char buffer[max_of(detail::digits10() + 2, 5)]; + return {buffer, detail::write(buffer, value)}; +} + +template ::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + return to_string(format_as(value)); +} + +template ::value && + !detail::use_format_as::value)> +FMT_NODISCARD auto to_string(const T& value) -> std::string { + auto buffer = memory_buffer(); + detail::write(appender(buffer), value); + return {buffer.data(), buffer.size()}; +} + +FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY # define FMT_FUNC inline # include "format-inl.h" -#else -# define FMT_FUNC +#endif + +// Restore _LIBCPP_REMOVE_TRANSITIVE_INCLUDES. +#ifdef FMT_REMOVE_TRANSITIVE_INCLUDES +# undef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES #endif #endif // FMT_FORMAT_H_ diff --git a/src/3rdparty/fmt/os.cc b/src/3rdparty/fmt/os.cc new file mode 100644 index 0000000000..00a89e930c --- /dev/null +++ b/src/3rdparty/fmt/os.cc @@ -0,0 +1,398 @@ +// Formatting library for C++ - optional OS-specific functionality +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +// Disable bogus MSVC warnings. +#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "os.h" + +#ifndef FMT_MODULE +# include + +# if FMT_USE_FCNTL +# include +# include + +# ifdef _WRS_KERNEL // VxWorks7 kernel +# include // getpagesize +# endif + +# ifndef _WIN32 +# include +# else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# endif // _WIN32 +# endif // FMT_USE_FCNTL + +# ifdef _WIN32 +# include +# endif +#endif + +#ifdef _WIN32 +# ifndef S_IRUSR +# define S_IRUSR _S_IREAD +# endif +# ifndef S_IWUSR +# define S_IWUSR _S_IWRITE +# endif +# ifndef S_IRGRP +# define S_IRGRP 0 +# endif +# ifndef S_IWGRP +# define S_IWGRP 0 +# endif +# ifndef S_IROTH +# define S_IROTH 0 +# endif +# ifndef S_IWOTH +# define S_IWOTH 0 +# endif +#endif + +namespace { +#ifdef _WIN32 +// Return type of read and write functions. +using rwresult = int; + +// On Windows the count argument to read and write is unsigned, so convert +// it from size_t preventing integer overflow. +inline unsigned convert_rwcount(std::size_t count) { + return count <= UINT_MAX ? static_cast(count) : UINT_MAX; +} +#elif FMT_USE_FCNTL +// Return type of read and write functions. +using rwresult = ssize_t; + +inline std::size_t convert_rwcount(std::size_t count) { return count; } +#endif +} // namespace + +FMT_BEGIN_NAMESPACE + +#ifdef _WIN32 +namespace detail { + +class system_message { + system_message(const system_message&) = delete; + void operator=(const system_message&) = delete; + + unsigned long result_; + wchar_t* message_; + + static bool is_whitespace(wchar_t c) noexcept { + return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; + } + + public: + explicit system_message(unsigned long error_code) + : result_(0), message_(nullptr) { + result_ = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message_), 0, nullptr); + if (result_ != 0) { + while (result_ != 0 && is_whitespace(message_[result_ - 1])) { + --result_; + } + } + } + ~system_message() { LocalFree(message_); } + explicit operator bool() const noexcept { return result_ != 0; } + operator basic_string_view() const noexcept { + return basic_string_view(message_, result_); + } +}; + +class utf8_system_category final : public std::error_category { + public: + const char* name() const noexcept override { return "system"; } + std::string message(int error_code) const override { + auto&& msg = system_message(error_code); + if (msg) { + auto utf8_message = to_utf8(); + if (utf8_message.convert(msg)) { + return utf8_message.str(); + } + } + return "unknown error"; + } +}; + +} // namespace detail + +FMT_API const std::error_category& system_category() noexcept { + static const detail::utf8_system_category category; + return category; +} + +std::system_error vwindows_error(int err_code, string_view format_str, + format_args args) { + auto ec = std::error_code(err_code, system_category()); + return std::system_error(ec, vformat(format_str, args)); +} + +void detail::format_windows_error(detail::buffer& out, int error_code, + const char* message) noexcept { + FMT_TRY { + auto&& msg = system_message(error_code); + if (msg) { + auto utf8_message = to_utf8(); + if (utf8_message.convert(msg)) { + fmt::format_to(appender(out), FMT_STRING("{}: {}"), message, + string_view(utf8_message)); + return; + } + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +void report_windows_error(int error_code, const char* message) noexcept { + do_report_error(detail::format_windows_error, error_code, message); +} +#endif // _WIN32 + +buffered_file::~buffered_file() noexcept { + if (file_ && FMT_SYSTEM(fclose(file_)) != 0) + report_system_error(errno, "cannot close file"); +} + +buffered_file::buffered_file(cstring_view filename, cstring_view mode) { + FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), + nullptr); + if (!file_) + FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"), + filename.c_str())); +} + +void buffered_file::close() { + if (!file_) return; + int result = FMT_SYSTEM(fclose(file_)); + file_ = nullptr; + if (result != 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); +} + +int buffered_file::descriptor() const { +#ifdef FMT_HAS_SYSTEM + // fileno is a macro on OpenBSD. +# ifdef fileno +# undef fileno +# endif + int fd = FMT_POSIX_CALL(fileno(file_)); +#elif defined(_WIN32) + int fd = _fileno(file_); +#else + int fd = fileno(file_); +#endif + if (fd == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor"))); + return fd; +} + +#if FMT_USE_FCNTL +# ifdef _WIN32 +using mode_t = int; +# endif + +constexpr mode_t default_open_mode = + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + +file::file(cstring_view path, int oflag) { +# if defined(_WIN32) && !defined(__MINGW32__) + fd_ = -1; + auto converted = detail::utf8_to_utf16(string_view(path.c_str())); + *this = file::open_windows_file(converted.c_str(), oflag); +# else + FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode))); + if (fd_ == -1) + FMT_THROW( + system_error(errno, FMT_STRING("cannot open file {}"), path.c_str())); +# endif +} + +file::~file() noexcept { + // Don't retry close in case of EINTR! + // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html + if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) + report_system_error(errno, "cannot close file"); +} + +void file::close() { + if (fd_ == -1) return; + // Don't retry close in case of EINTR! + // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html + int result = FMT_POSIX_CALL(close(fd_)); + fd_ = -1; + if (result != 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot close file"))); +} + +long long file::size() const { +# ifdef _WIN32 + // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT + // is less than 0x0500 as is the case with some default MinGW builds. + // Both functions support large file sizes. + DWORD size_upper = 0; + HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); + DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); + if (size_lower == INVALID_FILE_SIZE) { + DWORD error = GetLastError(); + if (error != NO_ERROR) + FMT_THROW(windows_error(GetLastError(), "cannot get file size")); + } + unsigned long long long_size = size_upper; + return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; +# else + using Stat = struct stat; + Stat file_stat = Stat(); + if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes"))); + static_assert(sizeof(long long) >= sizeof(file_stat.st_size), + "return type of file::size is not large enough"); + return file_stat.st_size; +# endif +} + +std::size_t file::read(void* buffer, std::size_t count) { + rwresult result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot read from file"))); + return detail::to_unsigned(result); +} + +std::size_t file::write(const void* buffer, std::size_t count) { + rwresult result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); + return detail::to_unsigned(result); +} + +file file::dup(int fd) { + // Don't retry as dup doesn't return EINTR. + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html + int new_fd = FMT_POSIX_CALL(dup(fd)); + if (new_fd == -1) + FMT_THROW(system_error( + errno, FMT_STRING("cannot duplicate file descriptor {}"), fd)); + return file(new_fd); +} + +void file::dup2(int fd) { + int result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); + if (result == -1) { + FMT_THROW(system_error( + errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_, + fd)); + } +} + +void file::dup2(int fd, std::error_code& ec) noexcept { + int result = 0; + FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); + if (result == -1) ec = std::error_code(errno, std::generic_category()); +} + +buffered_file file::fdopen(const char* mode) { +// Don't retry as fdopen doesn't return EINTR. +# if defined(__MINGW32__) && defined(_POSIX_) + FILE* f = ::fdopen(fd_, mode); +# else + FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); +# endif + if (!f) { + FMT_THROW(system_error( + errno, FMT_STRING("cannot associate stream with file descriptor"))); + } + buffered_file bf(f); + fd_ = -1; + return bf; +} + +# if defined(_WIN32) && !defined(__MINGW32__) +file file::open_windows_file(wcstring_view path, int oflag) { + int fd = -1; + auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode); + if (fd == -1) { + FMT_THROW(system_error(err, FMT_STRING("cannot open file {}"), + detail::to_utf8(path.c_str()).c_str())); + } + return file(fd); +} +# endif + +pipe::pipe() { + int fds[2] = {}; +# ifdef _WIN32 + // Make the default pipe capacity same as on Linux 2.6.11+. + enum { DEFAULT_CAPACITY = 65536 }; + int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); +# else + // Don't retry as the pipe function doesn't return EINTR. + // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html + int result = FMT_POSIX_CALL(pipe(fds)); +# endif + if (result != 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); + // The following assignments don't throw. + read_end = file(fds[0]); + write_end = file(fds[1]); +} + +# if !defined(__MSDOS__) +long getpagesize() { +# ifdef _WIN32 + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwPageSize; +# else +# ifdef _WRS_KERNEL + long size = FMT_POSIX_CALL(getpagesize()); +# else + long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); +# endif + + if (size < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size"))); + return size; +# endif +} +# endif + +void ostream::grow(buffer& buf, size_t) { + if (buf.size() == buf.capacity()) static_cast(buf).flush(); +} + +ostream::ostream(cstring_view path, const detail::ostream_params& params) + : buffer(grow), file_(path, params.oflag) { + set(new char[params.buffer_size], params.buffer_size); +} + +ostream::ostream(ostream&& other) noexcept + : buffer(grow, other.data(), other.size(), other.capacity()), + file_(std::move(other.file_)) { + other.clear(); + other.set(nullptr, 0); +} + +ostream::~ostream() { + flush(); + delete[] data(); +} +#endif // FMT_USE_FCNTL +FMT_END_NAMESPACE diff --git a/src/3rdparty/fmt/os.h b/src/3rdparty/fmt/os.h new file mode 100644 index 0000000000..b2cc5e4b85 --- /dev/null +++ b/src/3rdparty/fmt/os.h @@ -0,0 +1,427 @@ +// Formatting library for C++ - optional OS-specific functionality +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_OS_H_ +#define FMT_OS_H_ + +#include "format.h" + +#ifndef FMT_MODULE +# include +# include +# include +# include // std::system_error + +# if FMT_HAS_INCLUDE() +# include // LC_NUMERIC_MASK on macOS +# endif +#endif // FMT_MODULE + +#ifndef FMT_USE_FCNTL +// UWP doesn't provide _pipe. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include // for O_RDONLY +# define FMT_USE_FCNTL 1 +# else +# define FMT_USE_FCNTL 0 +# endif +#endif + +#ifndef FMT_POSIX +# if defined(_WIN32) && !defined(__MINGW32__) +// Fix warnings about deprecated symbols. +# define FMT_POSIX(call) _##call +# else +# define FMT_POSIX(call) call +# endif +#endif + +// Calls to system functions are wrapped in FMT_SYSTEM for testability. +#ifdef FMT_SYSTEM +# define FMT_HAS_SYSTEM +# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) +#else +# define FMT_SYSTEM(call) ::call +# ifdef _WIN32 +// Fix warnings about deprecated symbols. +# define FMT_POSIX_CALL(call) ::_##call +# else +# define FMT_POSIX_CALL(call) ::call +# endif +#endif + +// Retries the expression while it evaluates to error_result and errno +// equals to EINTR. +#ifndef _WIN32 +# define FMT_RETRY_VAL(result, expression, error_result) \ + do { \ + (result) = (expression); \ + } while ((result) == (error_result) && errno == EINTR) +#else +# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) +#endif + +#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) + +FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT + +/** + * A reference to a null-terminated string. It can be constructed from a C + * string or `std::string`. + * + * You can use one of the following type aliases for common character types: + * + * +---------------+-----------------------------+ + * | Type | Definition | + * +===============+=============================+ + * | cstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * | wcstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * + * This class is most useful as a parameter type for functions that wrap C APIs. + */ +template class basic_cstring_view { + private: + const Char* data_; + + public: + /// Constructs a string reference object from a C string. + basic_cstring_view(const Char* s) : data_(s) {} + + /// Constructs a string reference from an `std::string` object. + basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} + + /// Returns the pointer to a C string. + auto c_str() const -> const Char* { return data_; } +}; + +using cstring_view = basic_cstring_view; +using wcstring_view = basic_cstring_view; + +#ifdef _WIN32 +FMT_API const std::error_category& system_category() noexcept; + +namespace detail { +FMT_API void format_windows_error(buffer& out, int error_code, + const char* message) noexcept; +} + +FMT_API std::system_error vwindows_error(int error_code, string_view fmt, + format_args args); + +/** + * Constructs a `std::system_error` object with the description of the form + * + * : + * + * where `` is the formatted message and `` is the + * system message corresponding to the error code. + * `error_code` is a Windows error code as given by `GetLastError`. + * If `error_code` is not a valid error code such as -1, the system message + * will look like "error -1". + * + * **Example**: + * + * // This throws a system_error with the description + * // cannot open file 'madeup': The system cannot find the file + * specified. + * // or similar (system message may vary). + * const char *filename = "madeup"; + * LPOFSTRUCT of = LPOFSTRUCT(); + * HFILE file = OpenFile(filename, &of, OF_READ); + * if (file == HFILE_ERROR) { + * throw fmt::windows_error(GetLastError(), + * "cannot open file '{}'", filename); + * } + */ +template +auto windows_error(int error_code, string_view message, const T&... args) + -> std::system_error { + return vwindows_error(error_code, message, vargs{{args...}}); +} + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +FMT_API void report_windows_error(int error_code, const char* message) noexcept; +#else +inline auto system_category() noexcept -> const std::error_category& { + return std::system_category(); +} +#endif // _WIN32 + +// std::system is not available on some platforms such as iOS (#2248). +#ifdef __OSX__ +template > +void say(const S& fmt, Args&&... args) { + std::system(format("say \"{}\"", format(fmt, args...)).c_str()); +} +#endif + +// A buffered file. +class buffered_file { + private: + FILE* file_; + + friend class file; + + inline explicit buffered_file(FILE* f) : file_(f) {} + + public: + buffered_file(const buffered_file&) = delete; + void operator=(const buffered_file&) = delete; + + // Constructs a buffered_file object which doesn't represent any file. + inline buffered_file() noexcept : file_(nullptr) {} + + // Destroys the object closing the file it represents if any. + FMT_API ~buffered_file() noexcept; + + public: + inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { + other.file_ = nullptr; + } + + inline auto operator=(buffered_file&& other) -> buffered_file& { + close(); + file_ = other.file_; + other.file_ = nullptr; + return *this; + } + + // Opens a file. + FMT_API buffered_file(cstring_view filename, cstring_view mode); + + // Closes the file. + FMT_API void close(); + + // Returns the pointer to a FILE object representing this file. + inline auto get() const noexcept -> FILE* { return file_; } + + FMT_API auto descriptor() const -> int; + + template + inline void print(string_view fmt, const T&... args) { + fmt::vargs vargs = {{args...}}; + detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) + : fmt::vprint(file_, fmt, vargs); + } +}; + +#if FMT_USE_FCNTL + +// A file. Closed file is represented by a file object with descriptor -1. +// Methods that are not declared with noexcept may throw +// fmt::system_error in case of failure. Note that some errors such as +// closing the file multiple times will cause a crash on Windows rather +// than an exception. You can get standard behavior by overriding the +// invalid parameter handler with _set_invalid_parameter_handler. +class FMT_API file { + private: + int fd_; // File descriptor. + + // Constructs a file object with a given descriptor. + explicit file(int fd) : fd_(fd) {} + + friend struct pipe; + + public: + // Possible values for the oflag argument to the constructor. + enum { + RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. + WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. + RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. + CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. + APPEND = FMT_POSIX(O_APPEND), // Open in append mode. + TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. + }; + + // Constructs a file object which doesn't represent any file. + inline file() noexcept : fd_(-1) {} + + // Opens a file and constructs a file object representing this file. + file(cstring_view path, int oflag); + + public: + file(const file&) = delete; + void operator=(const file&) = delete; + + inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } + + // Move assignment is not noexcept because close may throw. + inline auto operator=(file&& other) -> file& { + close(); + fd_ = other.fd_; + other.fd_ = -1; + return *this; + } + + // Destroys the object closing the file it represents if any. + ~file() noexcept; + + // Returns the file descriptor. + inline auto descriptor() const noexcept -> int { return fd_; } + + // Closes the file. + void close(); + + // Returns the file size. The size has signed type for consistency with + // stat::st_size. + auto size() const -> long long; + + // Attempts to read count bytes from the file into the specified buffer. + auto read(void* buffer, size_t count) -> size_t; + + // Attempts to write count bytes from the specified buffer to the file. + auto write(const void* buffer, size_t count) -> size_t; + + // Duplicates a file descriptor with the dup function and returns + // the duplicate as a file object. + static auto dup(int fd) -> file; + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd); + + // Makes fd be the copy of this file descriptor, closing fd first if + // necessary. + void dup2(int fd, std::error_code& ec) noexcept; + + // Creates a buffered_file object associated with this file and detaches + // this file object from the file. + auto fdopen(const char* mode) -> buffered_file; + +# if defined(_WIN32) && !defined(__MINGW32__) + // Opens a file and constructs a file object representing this file by + // wcstring_view filename. Windows only. + static file open_windows_file(wcstring_view path, int oflag); +# endif +}; + +struct FMT_API pipe { + file read_end; + file write_end; + + // Creates a pipe setting up read_end and write_end file objects for reading + // and writing respectively. + pipe(); +}; + +// Returns the memory page size. +auto getpagesize() -> long; + +namespace detail { + +struct buffer_size { + constexpr buffer_size() = default; + size_t value = 0; + FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { + auto bs = buffer_size(); + bs.value = val; + return bs; + } +}; + +struct ostream_params { + int oflag = file::WRONLY | file::CREATE | file::TRUNC; + size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; + + constexpr ostream_params() {} + + template + ostream_params(T... params, int new_oflag) : ostream_params(params...) { + oflag = new_oflag; + } + + template + ostream_params(T... params, detail::buffer_size bs) + : ostream_params(params...) { + this->buffer_size = bs.value; + } + +// Intel has a bug that results in failure to deduce a constructor +// for empty parameter packs. +# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 + ostream_params(int new_oflag) : oflag(new_oflag) {} + ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} +# endif +}; + +} // namespace detail + +FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); + +/// A fast buffered output stream for writing from a single thread. Writing from +/// multiple threads without external synchronization may result in a data race. +class FMT_API ostream : private detail::buffer { + private: + file file_; + + ostream(cstring_view path, const detail::ostream_params& params); + + static void grow(buffer& buf, size_t); + + public: + ostream(ostream&& other) noexcept; + ~ostream(); + + operator writer() { + detail::buffer& buf = *this; + return buf; + } + + inline void flush() { + if (size() == 0) return; + file_.write(data(), size() * sizeof(data()[0])); + clear(); + } + + template + friend auto output_file(cstring_view path, T... params) -> ostream; + + inline void close() { + flush(); + file_.close(); + } + + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. + template void print(format_string fmt, T&&... args) { + vformat_to(appender(*this), fmt.str, vargs{{args...}}); + } +}; + +/** + * Opens a file for writing. Supported parameters passed in `params`: + * + * - ``: Flags passed to [open]( + * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) + * (`file::WRONLY | file::CREATE | file::TRUNC` by default) + * - `buffer_size=`: Output buffer size + * + * **Example**: + * + * auto out = fmt::output_file("guide.txt"); + * out.print("Don't {}", "Panic"); + */ +template +inline auto output_file(cstring_view path, T... params) -> ostream { + return {path, detail::ostream_params(params...)}; +} +#endif // FMT_USE_FCNTL + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_OS_H_ diff --git a/src/3rdparty/fmt/ostream.h b/src/3rdparty/fmt/ostream.h index 26fb3b5ac0..71fd6c887e 100644 --- a/src/3rdparty/fmt/ostream.h +++ b/src/3rdparty/fmt/ostream.h @@ -8,7 +8,9 @@ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ -#include // std::filebuf +#ifndef FMT_MODULE +# include // std::filebuf +#endif #ifdef _WIN32 # ifdef __GLIBCXX__ @@ -18,42 +20,19 @@ # include #endif -#include "format.h" +#include "chrono.h" // formatbuf + +#ifdef _MSVC_STL_UPDATE +# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE +#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5 +# define FMT_MSVC_STL_UPDATE _MSVC_LANG +#else +# define FMT_MSVC_STL_UPDATE 0 +#endif FMT_BEGIN_NAMESPACE namespace detail { -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& 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(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 { @@ -64,53 +43,18 @@ class file_access { friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } }; -#if FMT_MSC_VERSION +#if FMT_MSVC_STL_UPDATE template class file_access; auto get_file(std::filebuf&) -> FILE*; #endif -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(os.rdbuf())) - f = get_file(*buf); - else - return false; -#elif defined(_WIN32) && defined(__GLIBCXX__) - auto* rdbuf = os.rdbuf(); - if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) - f = sfbuf->file(); - else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) - f = fbuf->file(); - else - return false; -#else - 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 auto write_ostream_unicode(std::wostream&, - fmt::basic_string_view) -> bool { - return false; -} - // Write the content of buf to os. // It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { const Char* buf_data = buf.data(); - using unsigned_streamsize = std::make_unsigned::type; + using unsigned_streamsize = make_unsigned_t; unsigned_streamsize size = buf.size(); unsigned_streamsize max_size = to_unsigned(max_value()); do { @@ -121,21 +65,9 @@ void write_buffer(std::basic_ostream& os, buffer& buf) { } while (size != 0); } -template -void format_value(buffer& buf, const T& value) { - auto&& format_buf = formatbuf>(buf); - auto&& output = std::basic_ostream(&format_buf); -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) - output.imbue(std::locale::classic()); // The default is always unlocalized. -#endif - output << value; - output.exceptions(std::ios_base::failbit | std::ios_base::badbit); -} - template struct streamed_view { const T& value; }; - } // namespace detail // Formats an object of type T that has an overloaded ostream operator<<. @@ -143,11 +75,14 @@ template struct basic_ostream_formatter : formatter, Char> { void set_debug_format() = delete; - template - auto format(const T& value, basic_format_context& ctx) const - -> OutputIt { + template + auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { auto buffer = basic_memory_buffer(); - detail::format_value(buffer, value); + auto&& formatbuf = detail::formatbuf>(buffer); + auto&& output = std::basic_ostream(&formatbuf); + output.imbue(std::locale::classic()); // The default is always unlocalized. + output << value; + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } @@ -158,73 +93,67 @@ using ostream_formatter = basic_ostream_formatter; template struct formatter, Char> : basic_ostream_formatter { - template - auto format(detail::streamed_view view, - basic_format_context& ctx) const -> OutputIt { + template + auto format(detail::streamed_view view, Context& ctx) const + -> decltype(ctx.out()) { return basic_ostream_formatter::format(view.value, ctx); } }; /** - \rst - Returns a view that formats `value` via an ostream ``operator<<``. - - **Example**:: - - fmt::print("Current thread id: {}\n", - fmt::streamed(std::this_thread::get_id())); - \endrst + * Returns a view that formats `value` via an ostream `operator<<`. + * + * **Example**: + * + * fmt::print("Current thread id: {}\n", + * fmt::streamed(std::this_thread::get_id())); */ template constexpr auto streamed(const T& value) -> detail::streamed_view { return {value}; } -namespace detail { - -inline void vprint_directly(std::ostream& os, string_view format_str, - format_args args) { +inline void vprint(std::ostream& os, string_view fmt, format_args args) { auto buffer = memory_buffer(); - detail::vformat_to(buffer, format_str, args); - detail::write_buffer(os, buffer); -} - -} // namespace detail - -FMT_EXPORT template -void vprint(std::basic_ostream& os, - basic_string_view> format_str, - basic_format_args>> args) { - auto buffer = basic_memory_buffer(); - detail::vformat_to(buffer, format_str, args); - if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; + detail::vformat_to(buffer, fmt, args); + FILE* f = nullptr; +#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI + if (auto* buf = dynamic_cast(os.rdbuf())) + f = detail::get_file(*buf); +#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI + auto* rdbuf = os.rdbuf(); + if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) + f = sfbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) + f = fbuf->file(); +#endif +#ifdef _WIN32 + if (f) { + int fd = _fileno(f); + if (_isatty(fd)) { + os.flush(); + if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; + } + } +#endif + detail::ignore_unused(f); detail::write_buffer(os, buffer); } /** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fmt::print(cerr, "Don't {}!", "panic"); - \endrst + * Prints formatted data to the stream `os`. + * + * **Example**: + * + * fmt::print(cerr, "Don't {}!", "panic"); */ FMT_EXPORT template void print(std::ostream& os, format_string fmt, T&&... args) { - const auto& vargs = fmt::make_format_args(args...); - if (detail::is_utf8()) - vprint(os, fmt, vargs); - else - detail::vprint_directly(os, fmt, vargs); -} - -FMT_EXPORT -template -void print(std::wostream& os, - basic_format_string...> fmt, - Args&&... args) { - vprint(os, fmt, fmt::make_format_args>(args...)); + fmt::vargs vargs = {{args...}}; + if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs); + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt.str, vargs); + detail::write_buffer(os, buffer); } FMT_EXPORT template @@ -232,14 +161,6 @@ void println(std::ostream& os, format_string fmt, T&&... args) { fmt::print(os, "{}\n", fmt::format(fmt, std::forward(args)...)); } -FMT_EXPORT -template -void println(std::wostream& os, - basic_format_string...> fmt, - Args&&... args) { - print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); -} - FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ diff --git a/src/3rdparty/fmt/printf.h b/src/3rdparty/fmt/printf.h new file mode 100644 index 0000000000..e726840185 --- /dev/null +++ b/src/3rdparty/fmt/printf.h @@ -0,0 +1,633 @@ +// Formatting library for C++ - legacy printf implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_PRINTF_H_ +#define FMT_PRINTF_H_ + +#ifndef FMT_MODULE +# include // std::max +# include // std::numeric_limits +#endif + +#include "format.h" + +FMT_BEGIN_NAMESPACE +FMT_BEGIN_EXPORT + +template struct printf_formatter { + printf_formatter() = delete; +}; + +template class basic_printf_context { + private: + basic_appender out_; + basic_format_args args_; + + static_assert(std::is_same::value || + std::is_same::value, + "Unsupported code unit type."); + + public: + using char_type = Char; + using parse_context_type = parse_context; + template using formatter_type = printf_formatter; + enum { builtin_types = 1 }; + + /// Constructs a `printf_context` object. References to the arguments are + /// stored in the context object so make sure they have appropriate lifetimes. + basic_printf_context(basic_appender out, + basic_format_args args) + : out_(out), args_(args) {} + + auto out() -> basic_appender { return out_; } + void advance_to(basic_appender) {} + + auto locale() -> detail::locale_ref { return {}; } + + auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } +}; + +namespace detail { + +// Return the result via the out param to workaround gcc bug 77539. +template +FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { + for (out = first; out != last; ++out) { + if (*out == value) return true; + } + return false; +} + +template <> +inline auto find(const char* first, const char* last, char value, + const char*& out) -> bool { + out = + static_cast(memchr(first, value, to_unsigned(last - first))); + return out != nullptr; +} + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template struct int_checker { + template static auto fits_in_int(T value) -> bool { + unsigned max = to_unsigned(max_value()); + return value <= max; + } + inline static auto fits_in_int(bool) -> bool { return true; } +}; + +template <> struct int_checker { + template static auto fits_in_int(T value) -> bool { + return value >= (std::numeric_limits::min)() && + value <= max_value(); + } + inline static auto fits_in_int(int) -> bool { return true; } +}; + +struct printf_precision_handler { + template ::value)> + auto operator()(T value) -> int { + if (!int_checker::is_signed>::fits_in_int(value)) + report_error("number is too big"); + return (std::max)(static_cast(value), 0); + } + + template ::value)> + auto operator()(T) -> int { + report_error("precision is not integer"); + return 0; + } +}; + +// An argument visitor that returns true iff arg is a zero integer. +struct is_zero_int { + template ::value)> + auto operator()(T value) -> bool { + return value == 0; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + +template struct make_unsigned_or_bool : std::make_unsigned {}; + +template <> struct make_unsigned_or_bool { + using type = bool; +}; + +template class arg_converter { + private: + using char_type = typename Context::char_type; + + basic_format_arg& arg_; + char_type type_; + + public: + arg_converter(basic_format_arg& arg, char_type type) + : arg_(arg), type_(type) {} + + void operator()(bool value) { + if (type_ != 's') operator()(value); + } + + template ::value)> + void operator()(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using target_type = conditional_t::value, U, T>; + if (const_check(sizeof(target_type) <= sizeof(int))) { + // Extra casts are used to silence warnings. + using unsigned_type = typename make_unsigned_or_bool::type; + if (is_signed) + arg_ = static_cast(static_cast(value)); + else + arg_ = static_cast(static_cast(value)); + } else { + // glibc's printf doesn't sign extend arguments of smaller types: + // std::printf("%lld", -42); // prints "4294967254" + // but we don't have to do the same because it's a UB. + if (is_signed) + arg_ = static_cast(value); + else + arg_ = static_cast::type>(value); + } + } + + template ::value)> + void operator()(U) {} // No conversion needed for non-integral types. +}; + +// Converts an integer argument to T for printf, if T is an integral type. +// If T is void, the argument is converted to corresponding signed or unsigned +// type depending on the type specifier: 'd' and 'i' - signed, other - +// unsigned). +template +void convert_arg(basic_format_arg& arg, Char type) { + arg.visit(arg_converter(arg, type)); +} + +// Converts an integer argument to char for printf. +template class char_converter { + private: + basic_format_arg& arg_; + + public: + explicit char_converter(basic_format_arg& arg) : arg_(arg) {} + + template ::value)> + void operator()(T value) { + arg_ = static_cast(value); + } + + template ::value)> + void operator()(T) {} // No conversion needed for non-integral types. +}; + +// An argument visitor that return a pointer to a C string if argument is a +// string or null otherwise. +template struct get_cstring { + template auto operator()(T) -> const Char* { return nullptr; } + auto operator()(const Char* s) -> const Char* { return s; } +}; + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class printf_width_handler { + private: + format_specs& specs_; + + public: + inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {} + + template ::value)> + auto operator()(T value) -> unsigned { + auto width = static_cast>(value); + if (detail::is_negative(value)) { + specs_.set_align(align::left); + width = 0 - width; + } + unsigned int_max = to_unsigned(max_value()); + if (width > int_max) report_error("number is too big"); + return static_cast(width); + } + + template ::value)> + auto operator()(T) -> unsigned { + report_error("width is not integer"); + return 0; + } +}; + +// Workaround for a bug with the XL compiler when initializing +// printf_arg_formatter's base class. +template +auto make_arg_formatter(basic_appender iter, format_specs& s) + -> arg_formatter { + return {iter, s, locale_ref()}; +} + +// The `printf` argument formatter. +template +class printf_arg_formatter : public arg_formatter { + private: + using base = arg_formatter; + using context_type = basic_printf_context; + + context_type& context_; + + void write_null_pointer(bool is_string = false) { + auto s = this->specs; + s.set_type(presentation_type::none); + write_bytes(this->out, is_string ? "(null)" : "(nil)", s); + } + + template void write(T value) { + detail::write(this->out, value, this->specs, this->locale); + } + + public: + printf_arg_formatter(basic_appender iter, format_specs& s, + context_type& ctx) + : base(make_arg_formatter(iter, s)), context_(ctx) {} + + void operator()(monostate value) { write(value); } + + template ::value)> + void operator()(T value) { + // MSVC2013 fails to compile separate overloads for bool and Char so use + // std::is_same instead. + if (!std::is_same::value) { + write(value); + return; + } + format_specs s = this->specs; + if (s.type() != presentation_type::none && + s.type() != presentation_type::chr) { + return (*this)(static_cast(value)); + } + s.set_sign(sign::none); + s.clear_alt(); + s.set_fill(' '); // Ignore '0' flag for char types. + // align::numeric needs to be overwritten here since the '0' flag is + // ignored for non-numeric types + if (s.align() == align::none || s.align() == align::numeric) + s.set_align(align::right); + detail::write(this->out, static_cast(value), s); + } + + template ::value)> + void operator()(T value) { + write(value); + } + + void operator()(const char* value) { + if (value) + write(value); + else + write_null_pointer(this->specs.type() != presentation_type::pointer); + } + + void operator()(const wchar_t* value) { + if (value) + write(value); + else + write_null_pointer(this->specs.type() != presentation_type::pointer); + } + + void operator()(basic_string_view value) { write(value); } + + void operator()(const void* value) { + if (value) + write(value); + else + write_null_pointer(); + } + + void operator()(typename basic_format_arg::handle handle) { + auto parse_ctx = parse_context({}); + handle.format(parse_ctx, context_); + } +}; + +template +void parse_flags(format_specs& specs, const Char*& it, const Char* end) { + for (; it != end; ++it) { + switch (*it) { + case '-': specs.set_align(align::left); break; + case '+': specs.set_sign(sign::plus); break; + case '0': specs.set_fill('0'); break; + case ' ': + if (specs.sign() != sign::plus) specs.set_sign(sign::space); + break; + case '#': specs.set_alt(); break; + default: return; + } + } +} + +template +auto parse_header(const Char*& it, const Char* end, format_specs& specs, + GetArg get_arg) -> int { + int arg_index = -1; + Char c = *it; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + int value = parse_nonnegative_int(it, end, -1); + if (it != end && *it == '$') { // value is an argument index + ++it; + arg_index = value != -1 ? value : max_value(); + } else { + if (c == '0') specs.set_fill('0'); + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + if (value == -1) report_error("number is too big"); + specs.width = value; + return arg_index; + } + } + } + parse_flags(specs, it, end); + // Parse width. + if (it != end) { + if (*it >= '0' && *it <= '9') { + specs.width = parse_nonnegative_int(it, end, -1); + if (specs.width == -1) report_error("number is too big"); + } else if (*it == '*') { + ++it; + specs.width = static_cast( + get_arg(-1).visit(detail::printf_width_handler(specs))); + } + } + return arg_index; +} + +inline auto parse_printf_presentation_type(char c, type t, bool& upper) + -> presentation_type { + using pt = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + switch (c) { + case 'd': return in(t, integral_set) ? pt::dec : pt::none; + case 'o': return in(t, integral_set) ? pt::oct : pt::none; + case 'X': upper = true; FMT_FALLTHROUGH; + case 'x': return in(t, integral_set) ? pt::hex : pt::none; + case 'E': upper = true; FMT_FALLTHROUGH; + case 'e': return in(t, float_set) ? pt::exp : pt::none; + case 'F': upper = true; FMT_FALLTHROUGH; + case 'f': return in(t, float_set) ? pt::fixed : pt::none; + case 'G': upper = true; FMT_FALLTHROUGH; + case 'g': return in(t, float_set) ? pt::general : pt::none; + case 'A': upper = true; FMT_FALLTHROUGH; + case 'a': return in(t, float_set) ? pt::hexfloat : pt::none; + case 'c': return in(t, integral_set) ? pt::chr : pt::none; + case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none; + case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; + default: return pt::none; + } +} + +template +void vprintf(buffer& buf, basic_string_view format, + basic_format_args args) { + using iterator = basic_appender; + auto out = iterator(buf); + auto context = basic_printf_context(out, args); + auto parse_ctx = parse_context(format); + + // Returns the argument with specified index or, if arg_index is -1, the next + // argument. + auto get_arg = [&](int arg_index) { + if (arg_index < 0) + arg_index = parse_ctx.next_arg_id(); + else + parse_ctx.check_arg_id(--arg_index); + return detail::get_arg(context, arg_index); + }; + + const Char* start = parse_ctx.begin(); + const Char* end = parse_ctx.end(); + auto it = start; + while (it != end) { + if (!find(it, end, '%', it)) { + it = end; // find leaves it == nullptr if it doesn't find '%'. + break; + } + Char c = *it++; + if (it != end && *it == c) { + write(out, basic_string_view(start, to_unsigned(it - start))); + start = ++it; + continue; + } + write(out, basic_string_view(start, to_unsigned(it - 1 - start))); + + auto specs = format_specs(); + specs.set_align(align::right); + + // Parse argument index, flags and width. + int arg_index = parse_header(it, end, specs, get_arg); + if (arg_index == 0) report_error("argument not found"); + + // Parse precision. + if (it != end && *it == '.') { + ++it; + c = it != end ? *it : 0; + if ('0' <= c && c <= '9') { + specs.precision = parse_nonnegative_int(it, end, 0); + } else if (c == '*') { + ++it; + specs.precision = + static_cast(get_arg(-1).visit(printf_precision_handler())); + } else { + specs.precision = 0; + } + } + + auto arg = get_arg(arg_index); + // For d, i, o, u, x, and X conversion specifiers, if a precision is + // specified, the '0' flag is ignored + if (specs.precision >= 0 && is_integral_type(arg.type())) { + // Ignore '0' for non-numeric types or if '-' present. + specs.set_fill(' '); + } + if (specs.precision >= 0 && arg.type() == type::cstring_type) { + auto str = arg.visit(get_cstring()); + auto str_end = str + specs.precision; + auto nul = std::find(str, str_end, Char()); + auto sv = basic_string_view( + str, to_unsigned(nul != str_end ? nul - str : specs.precision)); + arg = sv; + } + if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt(); + if (specs.fill_unit() == '0') { + if (is_arithmetic_type(arg.type()) && specs.align() != align::left) { + specs.set_align(align::numeric); + } else { + // Ignore '0' flag for non-numeric types or if '-' flag is also present. + specs.set_fill(' '); + } + } + + // Parse length and convert the argument to the required type. + c = it != end ? *it++ : 0; + Char t = it != end ? *it : 0; + switch (c) { + case 'h': + if (t == 'h') { + ++it; + t = it != end ? *it : 0; + convert_arg(arg, t); + } else { + convert_arg(arg, t); + } + break; + case 'l': + if (t == 'l') { + ++it; + t = it != end ? *it : 0; + convert_arg(arg, t); + } else { + convert_arg(arg, t); + } + break; + case 'j': convert_arg(arg, t); break; + case 'z': convert_arg(arg, t); break; + case 't': convert_arg(arg, t); break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: --it; convert_arg(arg, c); + } + + // Parse type. + if (it == end) report_error("invalid format string"); + char type = static_cast(*it++); + if (is_integral_type(arg.type())) { + // Normalize type. + switch (type) { + case 'i': + case 'u': type = 'd'; break; + case 'c': + arg.visit(char_converter>(arg)); + break; + } + } + bool upper = false; + specs.set_type(parse_printf_presentation_type(type, arg.type(), upper)); + if (specs.type() == presentation_type::none) + report_error("invalid format specifier"); + if (upper) specs.set_upper(); + + start = it; + + // Format argument. + arg.visit(printf_arg_formatter(out, specs, context)); + } + write(out, basic_string_view(start, to_unsigned(it - start))); +} +} // namespace detail + +using printf_context = basic_printf_context; +using wprintf_context = basic_printf_context; + +using printf_args = basic_format_args; +using wprintf_args = basic_format_args; + +/// Constructs an `format_arg_store` object that contains references to +/// arguments and can be implicitly converted to `printf_args`. +template +inline auto make_printf_args(T&... args) + -> decltype(fmt::make_format_args>(args...)) { + return fmt::make_format_args>(args...); +} + +template struct vprintf_args { + using type = basic_format_args>; +}; + +template +inline auto vsprintf(basic_string_view fmt, + typename vprintf_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vprintf(buf, fmt, args); + return {buf.data(), buf.size()}; +} + +/** + * Formats `args` according to specifications in `fmt` and returns the result + * as as string. + * + * **Example**: + * + * std::string message = fmt::sprintf("The answer is %d", 42); + */ +template > +inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { + return vsprintf(detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template +inline auto vfprintf(std::FILE* f, basic_string_view fmt, + typename vprintf_args::type args) -> int { + auto buf = basic_memory_buffer(); + detail::vprintf(buf, fmt, args); + size_t size = buf.size(); + return std::fwrite(buf.data(), sizeof(Char), size, f) < size + ? -1 + : static_cast(size); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `f`. + * + * **Example**: + * + * fmt::fprintf(stderr, "Don't %s!", "panic"); + */ +template > +inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { + return vfprintf(f, detail::to_string_view(fmt), + make_printf_args(args...)); +} + +template +FMT_DEPRECATED inline auto vprintf(basic_string_view fmt, + typename vprintf_args::type args) + -> int { + return vfprintf(stdout, fmt, args); +} + +/** + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::printf("Elapsed time: %.2f seconds", 1.23); + */ +template +inline auto printf(string_view fmt, const T&... args) -> int { + return vfprintf(stdout, fmt, make_printf_args(args...)); +} +template +FMT_DEPRECATED inline auto printf(basic_string_view fmt, + const T&... args) -> int { + return vfprintf(stdout, fmt, make_printf_args(args...)); +} + +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_PRINTF_H_ diff --git a/src/3rdparty/fmt/ranges.h b/src/3rdparty/fmt/ranges.h index 3638fffb83..77d645f8e1 100644 --- a/src/3rdparty/fmt/ranges.h +++ b/src/3rdparty/fmt/ranges.h @@ -8,67 +8,31 @@ #ifndef FMT_RANGES_H_ #define FMT_RANGES_H_ -#include -#include -#include +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +#endif #include "format.h" FMT_BEGIN_NAMESPACE +FMT_EXPORT +enum class range_format { disabled, map, set, sequence, string, debug_string }; + namespace detail { -template -auto copy(const Range& range, OutputIt out) -> OutputIt { - for (auto it = range.begin(), end = range.end(); it != end; ++it) - *out++ = *it; - return out; -} - -template -auto copy(const char* str, OutputIt out) -> OutputIt { - while (*str) *out++ = *str++; - return out; -} - -template auto copy(char ch, OutputIt out) -> OutputIt { - *out++ = ch; - return out; -} - -template auto copy(wchar_t ch, OutputIt out) -> OutputIt { - *out++ = ch; - return out; -} - -// Returns true if T has a std::string-like interface, like std::string_view. -template class is_std_string_like { - template - static auto check(U* p) - -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); - template static void check(...); - - public: - static constexpr const bool value = - is_string::value || - std::is_convertible>::value || - !std::is_void(nullptr))>::value; -}; - -template -struct is_std_string_like> : std::true_type {}; - template class is_map { template static auto check(U*) -> typename U::mapped_type; template static void check(...); public: -#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED! - static constexpr const bool value = false; -#else static constexpr const bool value = !std::is_void(nullptr))>::value; -#endif }; template class is_set { @@ -76,26 +40,10 @@ template class is_set { template static void check(...); public: -#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED! - static constexpr const bool value = false; -#else static constexpr const bool value = !std::is_void(nullptr))>::value && !is_map::value; -#endif }; -template struct conditional_helper {}; - -template struct is_range_ : std::false_type {}; - -#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 - -# define FMT_DECLTYPE_RETURN(val) \ - ->decltype(val) { return val; } \ - static_assert( \ - true, "") // This makes it so that a semicolon is required after the - // macro, which helps clang-format handle the formatting. - // C array overload template auto range_begin(const T (&arr)[N]) -> const T* { @@ -110,17 +58,21 @@ template struct has_member_fn_begin_end_t : std::false_type {}; template -struct has_member_fn_begin_end_t().begin()), +struct has_member_fn_begin_end_t().begin()), decltype(std::declval().end())>> : std::true_type {}; -// Member function overload +// Member function overloads. template -auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).begin()); +auto range_begin(T&& rng) -> decltype(static_cast(rng).begin()) { + return static_cast(rng).begin(); +} template -auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).end()); +auto range_end(T&& rng) -> decltype(static_cast(rng).end()) { + return static_cast(rng).end(); +} -// ADL overload. Only participates in overload resolution if member functions +// ADL overloads. Only participate in overload resolution if member functions // are not found. template auto range_begin(T&& rng) @@ -141,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {}; template struct has_const_begin_end< - T, - void_t< - decltype(detail::range_begin(std::declval&>())), - decltype(detail::range_end(std::declval&>()))>> + T, void_t&>())), + decltype(detail::range_end( + std::declval&>()))>> : std::true_type {}; template struct has_mutable_begin_end< - T, void_t())), - decltype(detail::range_end(std::declval())), + T, void_t())), + decltype(detail::range_end(std::declval())), // the extra int here is because older versions of MSVC don't // SFINAE properly unless there are distinct types int>> : std::true_type {}; +template struct is_range_ : std::false_type {}; template struct is_range_ : std::integral_constant::value || has_mutable_begin_end::value)> {}; -# undef FMT_DECLTYPE_RETURN -#endif // tuple_size and tuple_element check. template class is_tuple_like_ { - template - static auto check(U* p) -> decltype(std::tuple_size::value, int()); + template ::type> + static auto check(U* p) -> decltype(std::tuple_size::value, 0); template static void check(...); public: @@ -206,12 +157,13 @@ class is_tuple_formattable_ { static constexpr const bool value = false; }; template class is_tuple_formattable_ { - template - static auto check2(index_sequence, - integer_sequence) -> std::true_type; - static auto check2(...) -> std::false_type; - template - static auto check(index_sequence) -> decltype(check2( + template + static auto all_true(index_sequence, + integer_sequence= 0)...>) -> std::true_type; + static auto all_true(...) -> std::false_type; + + template + static auto check(index_sequence) -> decltype(all_true( index_sequence{}, integer_sequence::type, @@ -292,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) template FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} +template +struct range_format_kind_ + : std::integral_constant, T>::value + ? range_format::disabled + : is_map::value ? range_format::map + : is_set::value ? range_format::set + : range_format::sequence> {}; + +template +using range_format_constant = std::integral_constant; + // These are not generic lambdas for compatibility with C++11. -template struct parse_empty_specs { +template struct parse_empty_specs { template FMT_CONSTEXPR void operator()(Formatter& f) { f.parse(ctx); detail::maybe_set_debug_format(f, true); } - ParseContext& ctx; + parse_context& ctx; }; template struct format_tuple_element { using char_type = typename FormatContext::char_type; template void operator()(const formatter& f, const T& v) { - if (i > 0) - ctx.advance_to(detail::copy_str(separator, ctx.out())); + if (i > 0) ctx.advance_to(detail::copy(separator, ctx.out())); ctx.advance_to(f.format(v, ctx)); ++i; } @@ -355,66 +318,48 @@ struct formatter - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); - if (it != ctx.end() && *it != '}') - FMT_THROW(format_error("invalid format specifier")); - detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + auto end = ctx.end(); + if (it != end && detail::to_ascii(*it) == 'n') { + ++it; + set_brackets({}, {}); + set_separator({}); + } + if (it != end && *it != '}') report_error("invalid format specifier"); + ctx.advance_to(it); + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); return it; } template auto format(const Tuple& value, FormatContext& ctx) const -> decltype(ctx.out()) { - ctx.advance_to(detail::copy_str(opening_bracket_, ctx.out())); + ctx.advance_to(detail::copy(opening_bracket_, ctx.out())); detail::for_each2( formatters_, value, detail::format_tuple_element{0, ctx, separator_}); - return detail::copy_str(closing_bracket_, ctx.out()); + return detail::copy(closing_bracket_, ctx.out()); } }; template struct is_range { static constexpr const bool value = - detail::is_range_::value && !detail::is_std_string_like::value && - !std::is_convertible>::value && - !std::is_convertible>::value; + detail::is_range_::value && !detail::has_to_string_view::value; }; namespace detail { -template struct range_mapper { - using mapper = arg_mapper; - - template , Context>::value)> - static auto map(T&& value) -> T&& { - return static_cast(value); - } - template , Context>::value)> - static auto map(T&& value) - -> decltype(mapper().map(static_cast(value))) { - return mapper().map(static_cast(value)); - } -}; template -using range_formatter_type = - formatter>{}.map( - std::declval()))>, - Char>; +using range_formatter_type = formatter, Char>; template using maybe_const_range = conditional_t::value, const R, R>; -// Workaround a bug in MSVC 2015 and earlier. -#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 template struct is_formattable_delayed : is_formattable>, Char> {}; -#endif } // namespace detail template struct conjunction : std::true_type {}; @@ -438,6 +383,24 @@ struct range_formatter< detail::string_literal{}; basic_string_view closing_bracket_ = detail::string_literal{}; + bool is_debug = false; + + template ::value)> + auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { + auto buf = basic_memory_buffer(); + for (; it != end; ++it) buf.push_back(*it); + auto specs = format_specs(); + specs.set_type(presentation_type::debug); + return detail::write( + out, basic_string_view(buf.data(), buf.size()), specs); + } + + template ::value)> + auto write_debug_string(Output& out, It, Sentinel) const -> Output { + return out; + } public: FMT_CONSTEXPR range_formatter() {} @@ -456,21 +419,40 @@ struct range_formatter< closing_bracket_ = close; } - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); + detail::maybe_set_debug_format(underlying_, true); + if (it == end) return underlying_.parse(ctx); - if (it != end && *it == 'n') { + switch (detail::to_ascii(*it)) { + case 'n': set_brackets({}, {}); ++it; + break; + case '?': + is_debug = true; + set_brackets({}, {}); + ++it; + if (it == end || *it != 's') report_error("invalid format specifier"); + FMT_FALLTHROUGH; + case 's': + if (!std::is_same::value) + report_error("invalid format specifier"); + if (!is_debug) { + set_brackets(detail::string_literal{}, + detail::string_literal{}); + set_separator({}); + detail::maybe_set_debug_format(underlying_, false); + } + ++it; + return it; } if (it != end && *it != '}') { - if (*it != ':') FMT_THROW(format_error("invalid format specifier")); + if (*it != ':') report_error("invalid format specifier"); + detail::maybe_set_debug_format(underlying_, false); ++it; - } else { - detail::maybe_set_debug_format(underlying_, true); } ctx.advance_to(it); @@ -479,80 +461,26 @@ struct range_formatter< template auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::range_mapper> mapper; auto out = ctx.out(); - out = detail::copy_str(opening_bracket_, out); - int i = 0; auto it = detail::range_begin(range); auto end = detail::range_end(range); + if (is_debug) return write_debug_string(out, std::move(it), end); + + out = detail::copy(opening_bracket_, out); + int i = 0; for (; it != end; ++it) { - if (i > 0) out = detail::copy_str(separator_, out); + if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); - auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + auto&& item = *it; // Need an lvalue + out = underlying_.format(item, ctx); ++i; } - out = detail::copy_str(closing_bracket_, out); + out = detail::copy(closing_bracket_, out); return out; } }; -enum class range_format { disabled, map, set, sequence, string, debug_string }; - -namespace detail { -template -struct range_format_kind_ - : std::integral_constant, T>::value - ? range_format::disabled - : is_map::value ? range_format::map - : is_set::value ? range_format::set - : range_format::sequence> {}; - -template -struct range_default_formatter; - -template -using range_format_constant = std::integral_constant; - -template -struct range_default_formatter< - K, R, Char, - enable_if_t<(K == range_format::sequence || K == range_format::map || - K == range_format::set)>> { - using range_type = detail::maybe_const_range; - range_formatter, Char> underlying_; - - FMT_CONSTEXPR range_default_formatter() { init(range_format_constant()); } - - FMT_CONSTEXPR void init(range_format_constant) { - underlying_.set_brackets(detail::string_literal{}, - detail::string_literal{}); - } - - FMT_CONSTEXPR void init(range_format_constant) { - underlying_.set_brackets(detail::string_literal{}, - detail::string_literal{}); - underlying_.underlying().set_brackets({}, {}); - underlying_.underlying().set_separator( - detail::string_literal{}); - } - - FMT_CONSTEXPR void init(range_format_constant) {} - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return underlying_.parse(ctx); - } - - template - auto format(range_type& range, FormatContext& ctx) const - -> decltype(ctx.out()) { - return underlying_.format(range, ctx); - } -}; -} // namespace detail - +FMT_EXPORT template struct range_format_kind : conditional_t< @@ -562,23 +490,191 @@ struct range_format_kind template struct formatter< R, Char, - enable_if_t::value != - range_format::disabled> -// Workaround a bug in MSVC 2015 and earlier. -#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 - , - detail::is_formattable_delayed -#endif - >::value>> - : detail::range_default_formatter::value, R, - Char> { + enable_if_t::value != range_format::disabled && + range_format_kind::value != range_format::map && + range_format_kind::value != range_format::string && + range_format_kind::value != range_format::debug_string>, + detail::is_formattable_delayed>::value>> { + private: + using range_type = detail::maybe_const_range; + range_formatter, Char> range_formatter_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR formatter() { + if (detail::const_check(range_format_kind::value != + range_format::set)) + return; + range_formatter_.set_brackets(detail::string_literal{}, + detail::string_literal{}); + } + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return range_formatter_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + return range_formatter_.format(range, ctx); + } }; -template struct tuple_join_view : detail::view { - const std::tuple& tuple; +// A map formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::map>, + detail::is_formattable_delayed>::value>> { + private: + using map_type = detail::maybe_const_range; + using element_type = detail::uncvref_type; + + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; + bool no_delimiters_ = false; + + public: + FMT_CONSTEXPR formatter() {} + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + auto it = ctx.begin(); + auto end = ctx.end(); + if (it != end) { + if (detail::to_ascii(*it) == 'n') { + no_delimiters_ = true; + ++it; + } + if (it != end && *it != '}') { + if (*it != ':') report_error("invalid format specifier"); + ++it; + } + ctx.advance_to(it); + } + detail::for_each(formatters_, detail::parse_empty_specs{ctx}); + return it; + } + + template + auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { + auto out = ctx.out(); + basic_string_view open = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(open, out); + int i = 0; + basic_string_view sep = detail::string_literal{}; + for (auto&& value : map) { + if (i > 0) out = detail::copy(sep, out); + ctx.advance_to(out); + detail::for_each2(formatters_, value, + detail::format_tuple_element{ + 0, ctx, detail::string_literal{}}); + ++i; + } + basic_string_view close = detail::string_literal{}; + if (!no_delimiters_) out = detail::copy(close, out); + return out; + } +}; + +// A (debug_)string formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::string || + range_format_kind::value == + range_format::debug_string>> { + private: + using range_type = detail::maybe_const_range; + using string_type = + conditional_t, + decltype(detail::range_begin(std::declval())), + decltype(detail::range_end(std::declval()))>::value, + detail::std_string_view, std::basic_string>; + + formatter underlying_; + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return underlying_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + out = underlying_.format( + string_type{detail::range_begin(range), detail::range_end(range)}, ctx); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + return out; + } +}; + +template +struct join_view : detail::view { + It begin; + Sentinel end; basic_string_view sep; - tuple_join_view(const std::tuple& t, basic_string_view s) + join_view(It b, Sentinel e, basic_string_view s) + : begin(std::move(b)), end(e), sep(s) {} +}; + +template +struct formatter, Char> { + private: + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t; +#else + typename std::iterator_traits::value_type; +#endif + formatter, Char> value_formatter_; + + using view = conditional_t::value, + const join_view, + join_view>; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return value_formatter_.parse(ctx); + } + + template + auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { + using iter = + conditional_t::value, It, It&>; + iter it = value.begin; + auto out = ctx.out(); + if (it == value.end) return out; + out = value_formatter_.format(*it, ctx); + ++it; + while (it != value.end) { + out = detail::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = value_formatter_.format(*it, ctx); + ++it; + } + return out; + } +}; + +template struct tuple_join_view : detail::view { + const Tuple& tuple; + basic_string_view sep; + + tuple_join_view(const Tuple& t, basic_string_view s) : tuple(t), sep{s} {} }; @@ -589,65 +685,64 @@ template struct tuple_join_view : detail::view { # define FMT_TUPLE_JOIN_SPECIFIERS 0 #endif -template -struct formatter, Char> { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return do_parse(ctx, std::integral_constant()); +template +struct formatter, Char, + enable_if_t::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return do_parse(ctx, std::tuple_size()); } template - auto format(const tuple_join_view& value, + auto format(const tuple_join_view& value, FormatContext& ctx) const -> typename FormatContext::iterator { - return do_format(value, ctx, - std::integral_constant()); + return do_format(value, ctx, std::tuple_size()); } private: - std::tuple::type, Char>...> formatters_; + decltype(detail::tuple::get_formatters( + detail::tuple_index_sequence())) formatters_; - template - FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) - -> decltype(ctx.begin()) { + -> const Char* { return ctx.begin(); } - template - FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + template + FMT_CONSTEXPR auto do_parse(parse_context& ctx, std::integral_constant) - -> decltype(ctx.begin()) { + -> const Char* { auto end = ctx.begin(); #if FMT_TUPLE_JOIN_SPECIFIERS - end = std::get(formatters_).parse(ctx); + end = std::get::value - N>(formatters_).parse(ctx); if (N > 1) { auto end1 = do_parse(ctx, std::integral_constant()); if (end != end1) - FMT_THROW(format_error("incompatible format specs for tuple elements")); + report_error("incompatible format specs for tuple elements"); } #endif return end; } template - auto do_format(const tuple_join_view&, FormatContext& ctx, + auto do_format(const tuple_join_view&, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { return ctx.out(); } template - auto do_format(const tuple_join_view& value, FormatContext& ctx, + auto do_format(const tuple_join_view& value, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { - auto out = std::get(formatters_) - .format(std::get(value.tuple), ctx); - if (N > 1) { - out = std::copy(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - return do_format(value, ctx, std::integral_constant()); - } - return out; + using std::get; + auto out = + std::get::value - N>(formatters_) + .format(get::value - N>(value.tuple), ctx); + if (N <= 1) return out; + out = detail::copy(value.sep, out); + ctx.advance_to(out); + return do_format(value, ctx, std::integral_constant()); } }; @@ -691,40 +786,57 @@ struct formatter< FMT_BEGIN_EXPORT +/// Returns a view that formats the iterator range `[begin, end)` with elements +/// separated by `sep`. +template +auto join(It begin, Sentinel end, string_view sep) -> join_view { + return {std::move(begin), end, sep}; +} + /** - \rst - Returns an object that formats `tuple` with elements separated by `sep`. - - **Example**:: - - std::tuple t = {1, 'a'}; - fmt::print("{}", fmt::join(t, ", ")); - // Output: "1, a" - \endrst + * Returns a view that formats `range` with elements separated by `sep`. + * + * **Example**: + * + * auto v = std::vector{1, 2, 3}; + * fmt::print("{}", fmt::join(v, ", ")); + * // Output: 1, 2, 3 + * + * `fmt::join` applies passed format specifiers to the range elements: + * + * fmt::print("{:02}", fmt::join(v, ", ")); + * // Output: 01, 02, 03 */ -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) - -> tuple_join_view { - return {tuple, sep}; +template ::value)> +auto join(Range&& r, string_view sep) + -> join_view { + return {detail::range_begin(r), detail::range_end(r), sep}; } -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, - basic_string_view sep) - -> tuple_join_view { +/** + * Returns an object that formats `std::tuple` with elements separated by `sep`. + * + * **Example**: + * + * auto t = std::tuple{1, 'a'}; + * fmt::print("{}", fmt::join(t, ", ")); + * // Output: 1, a + */ +template ::value)> +FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) + -> tuple_join_view { return {tuple, sep}; } /** - \rst - Returns an object that formats `initializer_list` with elements separated by - `sep`. - - **Example**:: - - fmt::print("{}", fmt::join({1, 2, 3}, ", ")); - // Output: "1, 2, 3" - \endrst + * Returns an object that formats `std::initializer_list` with elements + * separated by `sep`. + * + * **Example**: + * + * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); + * // Output: "1, 2, 3" */ template auto join(std::initializer_list list, string_view sep) diff --git a/src/3rdparty/fmt/std.h b/src/3rdparty/fmt/std.h index 7cff115920..54eb2c2a73 100644 --- a/src/3rdparty/fmt/std.h +++ b/src/3rdparty/fmt/std.h @@ -8,39 +8,49 @@ #ifndef FMT_STD_H_ #define FMT_STD_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "format.h" #include "ostream.h" +#ifndef FMT_MODULE +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. +# if FMT_CPLUSPLUS >= 201703L +# if FMT_HAS_INCLUDE() && \ + (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +# if FMT_HAS_INCLUDE() +# include +# endif +# endif +// Use > instead of >= in the version check because may be +// available after C++17 but before C++20 is marked as implemented. +# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() +# include +# endif +# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() +# include +# endif +#endif // FMT_MODULE + #if FMT_HAS_INCLUDE() # include #endif -// Checking FMT_CPLUSPLUS for warning suppression in MSVC. -#if FMT_CPLUSPLUS >= 201703L -# if FMT_HAS_INCLUDE() -# include -# endif -# if FMT_HAS_INCLUDE() -# include -# endif -# if FMT_HAS_INCLUDE() -# include -# endif -#endif - -#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() -# include -#endif // GCC 4 does not support FMT_HAS_INCLUDE. #if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) @@ -52,17 +62,6 @@ # endif #endif -// 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 @@ -117,7 +116,7 @@ void write_escaped_path(basic_memory_buffer& quoted, FMT_EXPORT template struct formatter { private: - format_specs specs_; + format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; char path_type_ = 0; @@ -125,33 +124,33 @@ template struct formatter { public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + FMT_CONSTEXPR auto parse(parse_context& ctx) { 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); + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } - if (it != end && (*it == 'g')) path_type_ = *it++; + if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); return it; } template 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 + auto path_string = + !path_type_ ? p.native() + : p.generic_string(); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); if (!debug_) { auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); @@ -163,13 +162,30 @@ template struct formatter { specs); } }; + +class path : public std::filesystem::path { + public: + auto display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{}"), base); + } + auto system_string() const -> std::string { return string(); } + + auto generic_display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{:g}"), base); + } + auto generic_system_string() const -> std::string { return generic_string(); } +}; + FMT_END_NAMESPACE #endif // FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE FMT_EXPORT template -struct formatter, Char> : nested_formatter { +struct formatter, Char> + : nested_formatter, Char> { private: // Functor because C++11 doesn't support generic lambdas. struct writer { @@ -189,7 +205,7 @@ struct formatter, Char> : nested_formatter { template auto format(const std::bitset& bs, FormatContext& ctx) const -> decltype(ctx.out()) { - return write_padded(ctx, writer{bs}); + return this->write_padded(ctx, writer{bs}); } }; @@ -222,7 +238,7 @@ struct formatter, Char, FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} public: - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + FMT_CONSTEXPR auto parse(parse_context& ctx) { maybe_set_debug_format(underlying_, true); return underlying_.parse(ctx); } @@ -242,13 +258,62 @@ struct formatter, Char, FMT_END_NAMESPACE #endif // __cpp_lib_optional +#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (has_to_string_view::value) + return write_escaped_string(out, detail::to_string_view(v)); + if constexpr (std::is_same_v) return write_escaped_char(out, v); + return write(out, v); +} + +} // namespace detail + +FMT_END_NAMESPACE +#endif + +#ifdef __cpp_lib_expected +FMT_BEGIN_NAMESPACE + +FMT_EXPORT +template +struct formatter, Char, + std::enable_if_t<(std::is_void::value || + is_formattable::value) && + is_formattable::value>> { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::expected& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + + if (value.has_value()) { + out = detail::write(out, "expected("); + if constexpr (!std::is_void::value) + out = detail::write_escaped_alternative(out, *value); + } else { + out = detail::write(out, "unexpected("); + out = detail::write_escaped_alternative(out, value.error()); + } + *out++ = ')'; + return out; + } +}; +FMT_END_NAMESPACE +#endif // __cpp_lib_expected + #ifdef __cpp_lib_source_location FMT_BEGIN_NAMESPACE FMT_EXPORT template <> struct formatter { - template FMT_CONSTEXPR auto parse(ParseContext& ctx) { - return ctx.begin(); - } + FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } template auto format(const std::source_location& loc, FormatContext& ctx) const @@ -291,16 +356,6 @@ template class is_variant_formattable_ { decltype(check(variant_index_sequence{}))::value; }; -template -auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { - if constexpr (is_string::value) - return write_escaped_string(out, detail::to_string_view(v)); - else if constexpr (std::is_same_v) - return write_escaped_char(out, v); - else - return write(out, v); -} - } // namespace detail template struct is_variant_like { @@ -314,8 +369,7 @@ template struct is_variant_formattable { FMT_EXPORT template struct formatter { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } @@ -332,8 +386,7 @@ struct formatter< Variant, Char, std::enable_if_t, is_variant_formattable>>> { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } @@ -346,7 +399,7 @@ struct formatter< FMT_TRY { std::visit( [&](const auto& v) { - out = detail::write_variant_alternative(out, v); + out = detail::write_escaped_alternative(out, v); }, value); } @@ -362,23 +415,128 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE FMT_EXPORT -template struct formatter { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); +template <> struct formatter { + private: + format_specs specs_; + detail::arg_ref width_ref_; + + public: + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + + it = detail::parse_align(it, end, specs_); + if (it == end) return it; + + char c = *it; + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + return it; } template - FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const - -> decltype(ctx.out()) { - auto out = ctx.out(); - out = detail::write_bytes(out, ec.category().name(), format_specs()); - out = detail::write(out, Char(':')); - out = detail::write(out, ec.value()); - return out; + FMT_CONSTEXPR20 auto format(const std::error_code& ec, + FormatContext& ctx) const -> decltype(ctx.out()) { + auto specs = specs_; + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, + ctx); + memory_buffer buf; + buf.append(string_view(ec.category().name())); + buf.push_back(':'); + detail::write(appender(buf), ec.value()); + return detail::write(ctx.out(), string_view(buf.data(), buf.size()), + specs); } }; +#if FMT_USE_RTTI +namespace detail { + +template +auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { +# ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + std::size_t size = 0; + std::unique_ptr demangled_name_ptr( + abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); + + string_view demangled_name_view; + if (demangled_name_ptr) { + demangled_name_view = demangled_name_ptr.get(); + + // Normalization of stdlib inline namespace names. + // libc++ inline namespaces. + // std::__1::* -> std::* + // std::__1::__fs::* -> std::* + // libstdc++ inline namespaces. + // std::__cxx11::* -> std::* + // std::filesystem::__cxx11::* -> std::filesystem::* + if (demangled_name_view.starts_with("std::")) { + char* begin = demangled_name_ptr.get(); + char* to = begin + 5; // std:: + for (char *from = to, *end = begin + demangled_name_view.size(); + from < end;) { + // This is safe, because demangled_name is NUL-terminated. + if (from[0] == '_' && from[1] == '_') { + char* next = from + 1; + while (next < end && *next != ':') next++; + if (next[0] == ':' && next[1] == ':') { + from = next + 2; + continue; + } + } + *to++ = *from++; + } + demangled_name_view = {begin, detail::to_unsigned(to - begin)}; + } + } else { + demangled_name_view = string_view(ti.name()); + } + return detail::write_bytes(out, demangled_name_view); +# elif FMT_MSC_VERSION + const string_view demangled_name(ti.name()); + for (std::size_t i = 0; i < demangled_name.size(); ++i) { + auto sub = demangled_name; + sub.remove_prefix(i); + if (sub.starts_with("enum ")) { + i += 4; + continue; + } + if (sub.starts_with("class ") || sub.starts_with("union ")) { + i += 5; + continue; + } + if (sub.starts_with("struct ")) { + i += 6; + continue; + } + if (*sub.begin() != ' ') *out++ = *sub.begin(); + } + return out; +# else + return detail::write_bytes(out, string_view(ti.name())); +# endif +} + +} // namespace detail + +FMT_EXPORT +template +struct formatter { + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return ctx.begin(); + } + + template + auto format(const std::type_info& ti, Context& ctx) const + -> decltype(ctx.out()) { + return detail::write_demangled_name(ctx.out(), ti); + } +}; +#endif + FMT_EXPORT template struct formatter< @@ -388,81 +546,29 @@ struct formatter< bool with_typename_ = false; public: - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { auto it = ctx.begin(); auto end = ctx.end(); if (it == end || *it == '}') return it; if (*it == 't') { ++it; - with_typename_ = FMT_USE_TYPEID != 0; + with_typename_ = FMT_USE_RTTI != 0; } return it; } - template - auto format(const std::exception& ex, - basic_format_context& ctx) const -> OutputIt { - format_specs spec; + template + auto format(const std::exception& ex, Context& ctx) const + -> decltype(ctx.out()) { auto out = ctx.out(); - 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 - int status = 0; - std::size_t size = 0; - std::unique_ptr demangled_name_ptr( - abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); - - string_view demangled_name_view; - if (demangled_name_ptr) { - demangled_name_view = demangled_name_ptr.get(); - - // Normalization of stdlib inline namespace names. - // libc++ inline namespaces. - // std::__1::* -> std::* - // std::__1::__fs::* -> std::* - // libstdc++ inline namespaces. - // std::__cxx11::* -> std::* - // std::filesystem::__cxx11::* -> std::filesystem::* - if (demangled_name_view.starts_with("std::")) { - char* begin = demangled_name_ptr.get(); - char* to = begin + 5; // std:: - for (char *from = to, *end = begin + demangled_name_view.size(); - from < end;) { - // This is safe, because demangled_name is NUL-terminated. - if (from[0] == '_' && from[1] == '_') { - char* next = from + 1; - while (next < end && *next != ':') next++; - if (next[0] == ':' && next[1] == ':') { - from = next + 2; - continue; - } - } - *to++ = *from++; - } - demangled_name_view = {begin, detail::to_unsigned(to - begin)}; - } - } else { - demangled_name_view = string_view(ti.name()); +#if FMT_USE_RTTI + if (with_typename_) { + out = detail::write_demangled_name(out, typeid(ex)); + *out++ = ':'; + *out++ = ' '; } - out = detail::write_bytes(out, demangled_name_view, spec); -# 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 - out = detail::write_bytes(out, string_view(ti.name()), spec); -# endif - *out++ = ':'; - *out++ = ' '; - return detail::write_bytes(out, string_view(ex.what()), spec); #endif + return detail::write_bytes(out, string_view(ex.what())); } }; @@ -509,6 +615,14 @@ struct formatter +auto ptr(const std::unique_ptr& p) -> const void* { + return p.get(); +} +template auto ptr(const std::shared_ptr& p) -> const void* { + return p.get(); +} + FMT_EXPORT template struct formatter, Char, @@ -533,5 +647,80 @@ struct formatter : formatter { }; #endif // __cpp_lib_atomic_flag_test +FMT_EXPORT +template struct formatter, Char> { + private: + detail::dynamic_format_specs specs_; + + template + FMT_CONSTEXPR auto do_format(const std::complex& c, + detail::dynamic_format_specs& specs, + FormatContext& ctx, OutputIt out) const + -> OutputIt { + if (c.real() != 0) { + *out++ = Char('('); + out = detail::write(out, c.real(), specs, ctx.locale()); + specs.set_sign(sign::plus); + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + *out++ = Char(')'); + return out; + } + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + return out; + } + + public: + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type_constant::value); + } + + template + auto format(const std::complex& c, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto specs = specs_; + if (specs.dynamic()) { + detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, + specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, + specs.precision_ref, ctx); + } + + if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); + auto buf = basic_memory_buffer(); + + auto outer_specs = format_specs(); + outer_specs.width = specs.width; + outer_specs.copy_fill_from(specs); + outer_specs.set_align(specs.align()); + + specs.width = 0; + specs.set_fill({}); + specs.set_align(align::none); + + do_format(c, specs, ctx, basic_appender(buf)); + return detail::write(ctx.out(), + basic_string_view(buf.data(), buf.size()), + outer_specs); + } +}; + +FMT_EXPORT +template +struct formatter, Char, + enable_if_t, Char>::value>> + : formatter, Char> { + template + auto format(std::reference_wrapper ref, FormatContext& ctx) const + -> decltype(ctx.out()) { + return formatter, Char>::format(ref.get(), ctx); + } +}; + FMT_END_NAMESPACE #endif // FMT_STD_H_ diff --git a/src/3rdparty/fmt/xchar.h b/src/3rdparty/fmt/xchar.h new file mode 100644 index 0000000000..9f7f889d64 --- /dev/null +++ b/src/3rdparty/fmt/xchar.h @@ -0,0 +1,373 @@ +// Formatting library for C++ - optional wchar_t and exotic character support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_XCHAR_H_ +#define FMT_XCHAR_H_ + +#include "color.h" +#include "format.h" +#include "ostream.h" +#include "ranges.h" + +#ifndef FMT_MODULE +# include +# if FMT_USE_LOCALE +# include +# endif +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +template +using is_exotic_char = bool_constant::value>; + +template struct format_string_char {}; + +template +struct format_string_char< + S, void_t())))>> { + using type = char_t; +}; + +template +struct format_string_char< + S, enable_if_t::value>> { + using type = typename S::char_type; +}; + +template +using format_string_char_t = typename format_string_char::type; + +inline auto write_loc(basic_appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool { +#if FMT_USE_LOCALE + auto& numpunct = + std::use_facet>(loc.get()); + auto separator = std::wstring(); + auto grouping = numpunct.grouping(); + if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); + return value.visit(loc_writer{out, specs, separator, grouping, {}}); +#endif + return false; +} +} // namespace detail + +FMT_BEGIN_EXPORT + +using wstring_view = basic_string_view; +using wformat_parse_context = parse_context; +using wformat_context = buffered_context; +using wformat_args = basic_format_args; +using wmemory_buffer = basic_memory_buffer; + +template struct basic_fstring { + private: + basic_string_view str_; + + static constexpr int num_static_named_args = + detail::count_static_named_args(); + + using checker = detail::format_string_checker< + Char, static_cast(sizeof...(T)), num_static_named_args, + num_static_named_args != detail::count_named_args()>; + + using arg_pack = detail::arg_pack; + + public: + using t = basic_fstring; + + template >::value)> + FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { + if (FMT_USE_CONSTEVAL) + detail::parse_format_string(s, checker(s, arg_pack())); + } + template ::value&& + std::is_same::value)> + FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { + FMT_CONSTEXPR auto sv = basic_string_view(S()); + FMT_CONSTEXPR int ignore = + (parse_format_string(sv, checker(sv, arg_pack())), 0); + detail::ignore_unused(ignore); + } + basic_fstring(runtime_format_string fmt) : str_(fmt.str) {} + + operator basic_string_view() const { return str_; } + auto get() const -> basic_string_view { return str_; } +}; + +template +using basic_format_string = basic_fstring; + +template +using wformat_string = typename basic_format_string::t; +inline auto runtime(wstring_view s) -> runtime_format_string { + return {{s}}; +} + +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; + +#ifdef __cpp_char8_t +template <> struct is_char : bool_constant {}; +#endif + +template +constexpr auto make_wformat_args(T&... args) + -> decltype(fmt::make_format_args(args...)) { + return fmt::make_format_args(args...); +} + +#if !FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg { + return {s}; +} +} // namespace literals +#endif + +template +auto join(It begin, Sentinel end, wstring_view sep) + -> join_view { + return {begin, end, sep}; +} + +template ::value)> +auto join(Range&& range, wstring_view sep) + -> join_view { + return join(std::begin(range), std::end(range), sep); +} + +template +auto join(std::initializer_list list, wstring_view sep) + -> join_view { + return join(std::begin(list), std::end(list), sep); +} + +template ::value)> +auto join(const Tuple& tuple, basic_string_view sep) + -> tuple_join_view { + return {tuple, sep}; +} + +template ::value)> +auto vformat(basic_string_view fmt, + typename detail::vformat_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vformat_to(buf, fmt, args); + return {buf.data(), buf.size()}; +} + +template +auto format(wformat_string fmt, T&&... args) -> std::wstring { + return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template +auto format_to(OutputIt out, wformat_string fmt, T&&... args) + -> OutputIt { + return vformat_to(out, fmt::wstring_view(fmt), + fmt::make_wformat_args(args...)); +} + +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template , + FMT_ENABLE_IF(!std::is_same::value && + !std::is_same::value)> +auto format(const S& fmt, T&&... args) -> std::basic_string { + return vformat(detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto vformat(const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) + -> std::basic_string { + auto buf = basic_memory_buffer(); + detail::vformat_to(buf, detail::to_string_view(fmt), args, + detail::locale_ref(loc)); + return {buf.data(), buf.size()}; +} + +template , + FMT_ENABLE_IF(detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto format(const Locale& loc, const S& fmt, T&&... args) + -> std::basic_string { + return vformat(loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_exotic_char::value)> +auto vformat_to(OutputIt out, const S& fmt, + typename detail::vformat_args::type args) -> OutputIt { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, detail::to_string_view(fmt), args); + return detail::get_iterator(buf, out); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value && + !std::is_same::value && + !std::is_same::value)> +inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { + return vformat_to(out, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_locale::value&& + detail::is_exotic_char::value)> +inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, + typename detail::vformat_args::type args) + -> OutputIt { + auto&& buf = detail::get_buffer(out); + vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); + return detail::get_iterator(buf, out); +} + +template , + bool enable = detail::is_output_iterator::value && + detail::is_locale::value && + detail::is_exotic_char::value> +inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, + T&&... args) -> + typename std::enable_if::type { + return vformat_to(out, loc, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template ::value&& + detail::is_exotic_char::value)> +inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, + typename detail::vformat_args::type args) + -> format_to_n_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); + detail::vformat_to(buf, fmt, args); + return {buf.out(), buf.count()}; +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_exotic_char::value)> +inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) + -> format_to_n_result { + return vformat_to_n(out, n, fmt::basic_string_view(fmt), + fmt::make_format_args>(args...)); +} + +template , + FMT_ENABLE_IF(detail::is_exotic_char::value)> +inline auto formatted_size(const S& fmt, T&&... args) -> size_t { + auto buf = detail::counting_buffer(); + detail::vformat_to(buf, detail::to_string_view(fmt), + fmt::make_format_args>(args...)); + return buf.count(); +} + +inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { + auto buf = wmemory_buffer(); + detail::vformat_to(buf, fmt, args); + buf.push_back(L'\0'); + if (std::fputws(buf.data(), f) == -1) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +inline void vprint(wstring_view fmt, wformat_args args) { + vprint(stdout, fmt, args); +} + +template +void print(std::FILE* f, wformat_string fmt, T&&... args) { + return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template void print(wformat_string fmt, T&&... args) { + return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); +} + +template +void println(std::FILE* f, wformat_string fmt, T&&... args) { + return print(f, L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +template void println(wformat_string fmt, T&&... args) { + return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args) + -> std::wstring { + auto buf = wmemory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + return {buf.data(), buf.size()}; +} + +template +inline auto format(const text_style& ts, wformat_string fmt, T&&... args) + -> std::wstring { + return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(std::FILE* f, const text_style& ts, + wformat_string fmt, const T&... args) { + vprint(f, ts, fmt, fmt::make_wformat_args(args...)); +} + +template +FMT_DEPRECATED void print(const text_style& ts, wformat_string fmt, + const T&... args) { + return print(stdout, ts, fmt, args...); +} + +inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { + auto buffer = basic_memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::write_buffer(os, buffer); +} + +template +void print(std::wostream& os, wformat_string fmt, T&&... args) { + vprint(os, fmt, fmt::make_format_args>(args...)); +} + +template +void println(std::wostream& os, wformat_string fmt, T&&... args) { + print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); +} + +/// Converts `value` to `std::wstring` using the default format for type `T`. +template inline auto to_wstring(const T& value) -> std::wstring { + return format(FMT_STRING(L"{}"), value); +} +FMT_END_EXPORT +FMT_END_NAMESPACE + +#endif // FMT_XCHAR_H_ diff --git a/src/3rdparty/icu/scriptrun.h b/src/3rdparty/icu/scriptrun.h index 0a00001052..792d5c55c9 100644 --- a/src/3rdparty/icu/scriptrun.h +++ b/src/3rdparty/icu/scriptrun.h @@ -24,15 +24,15 @@ U_NAMESPACE_BEGIN struct ScriptRecord { - UChar32 startChar; - UChar32 endChar; - UScriptCode scriptCode; + UChar32 startChar = 0; + UChar32 endChar = 0; + UScriptCode scriptCode{}; }; struct ParenStackEntry { - int32_t pairIndex; - UScriptCode scriptCode; + int32_t pairIndex = 0; + UScriptCode scriptCode{}; }; class ScriptRun : public UObject { @@ -75,16 +75,16 @@ private: static UBool sameScript(int32_t scriptOne, int32_t scriptTwo); - int32_t charStart; - int32_t charLimit; - const char16_t *charArray; + int32_t charStart = 0; + int32_t charLimit = 0; + const char16_t *charArray = nullptr; - int32_t scriptStart; - int32_t scriptEnd; - UScriptCode scriptCode; + int32_t scriptStart = 0; + int32_t scriptEnd = 0; + UScriptCode scriptCode{}; - ParenStackEntry parenStack[128]; - int32_t parenSP; + std::array parenStack{}; + int32_t parenSP = 0; static int8_t highBit(int32_t value); static int32_t getPairIndex(UChar32 ch); diff --git a/src/3rdparty/squirrel/squirrel/CMakeLists.txt b/src/3rdparty/squirrel/squirrel/CMakeLists.txt index a86a92b7dd..237c3f6527 100644 --- a/src/3rdparty/squirrel/squirrel/CMakeLists.txt +++ b/src/3rdparty/squirrel/squirrel/CMakeLists.txt @@ -13,7 +13,6 @@ add_files( sqfuncstate.h sqlexer.cpp sqlexer.h - sqmem.cpp sqobject.cpp sqobject.h sqopcodes.h diff --git a/src/3rdparty/squirrel/squirrel/sqdebug.cpp b/src/3rdparty/squirrel/squirrel/sqdebug.cpp index 6ea958bbdf..8a6f15121a 100644 --- a/src/3rdparty/squirrel/squirrel/sqdebug.cpp +++ b/src/3rdparty/squirrel/squirrel/sqdebug.cpp @@ -12,7 +12,6 @@ #include "sqclosure.h" #include "sqstring.h" -#include "../../../core/alloc_func.hpp" #include "../../../string_func.h" #include "../../../safeguards.h" diff --git a/src/3rdparty/squirrel/squirrel/sqlexer.cpp b/src/3rdparty/squirrel/squirrel/sqlexer.cpp index 96e43f4af7..89eae0026e 100644 --- a/src/3rdparty/squirrel/squirrel/sqlexer.cpp +++ b/src/3rdparty/squirrel/squirrel/sqlexer.cpp @@ -11,7 +11,7 @@ #include "sqcompiler.h" #include "sqlexer.h" -#include "../../../string_func.h" +#include "../../../core/utf8.hpp" #include "../../../safeguards.h" @@ -28,8 +28,7 @@ SQLexer::~SQLexer() void SQLexer::APPEND_CHAR(char32_t c) { - char buf[4]; - size_t chars = Utf8Encode(buf, c); + auto [buf, chars] = EncodeUtf8(c); for (size_t i = 0; i < chars; i++) { _longstr.push_back(buf[i]); } diff --git a/src/3rdparty/squirrel/squirrel/sqmem.cpp b/src/3rdparty/squirrel/squirrel/sqmem.cpp deleted file mode 100644 index f4fa6309ed..0000000000 --- a/src/3rdparty/squirrel/squirrel/sqmem.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * see copyright notice in squirrel.h - */ - -#include "../../../stdafx.h" - -#include "sqpcheader.h" - -#include "../../../core/alloc_func.hpp" -#include "../../../safeguards.h" - -#ifdef SQUIRREL_DEFAULT_ALLOCATOR -void *sq_vm_malloc(SQUnsignedInteger size) { return MallocT((size_t)size); } - -void *sq_vm_realloc(void *p, SQUnsignedInteger oldsize, SQUnsignedInteger size) { return ReallocT(static_cast(p), (size_t)size); } - -void sq_vm_free(void *p, SQUnsignedInteger size) { free(p); } -#endif diff --git a/src/3rdparty/squirrel/squirrel/sqobject.h b/src/3rdparty/squirrel/squirrel/sqobject.h index 67f74deb7a..3b3c516067 100644 --- a/src/3rdparty/squirrel/squirrel/sqobject.h +++ b/src/3rdparty/squirrel/squirrel/sqobject.h @@ -56,11 +56,11 @@ enum SQMetaMethod{ struct SQRefCounted { - SQRefCounted() { _uiRef = 0; _weakref = nullptr; } + SQRefCounted() {} virtual ~SQRefCounted(); SQWeakRef *GetWeakRef(SQObjectType type); - SQUnsignedInteger _uiRef; - struct SQWeakRef *_weakref; + SQUnsignedInteger _uiRef = 0; + struct SQWeakRef *_weakref = nullptr; virtual void Release()=0; /* Placement new/delete to prevent memory leaks if constructor throws an exception. */ @@ -79,7 +79,7 @@ struct SQRefCounted inline void operator delete(void *) { NOT_REACHED(); } private: - size_t size; + size_t size = 0; }; struct SQWeakRef : SQRefCounted @@ -161,6 +161,14 @@ struct SQObjectPtr : public SQObject _unVal=o._unVal; __AddRef(_type,_unVal); } + SQObjectPtr(SQObjectPtr &&o) + { + SQ_OBJECT_RAWINIT() + this->_type = OT_NULL; + this->_unVal.pUserPointer = nullptr; + std::swap(this->_type, o._type); + std::swap(this->_unVal, o._unVal); + } SQObjectPtr(const SQObject &o) { SQ_OBJECT_RAWINIT() @@ -330,6 +338,12 @@ struct SQObjectPtr : public SQObject __Release(tOldType,unOldVal); return *this; } + inline SQObjectPtr& operator=(SQObjectPtr &&obj) + { + std::swap(this->_type, obj._type); + std::swap(this->_unVal, obj._unVal); + return *this; + } inline SQObjectPtr& operator=(const SQObject& obj) { SQObjectType tOldType; diff --git a/src/3rdparty/squirrel/squirrel/sqvm.cpp b/src/3rdparty/squirrel/squirrel/sqvm.cpp index 52a4dfe85c..bb50dc2244 100644 --- a/src/3rdparty/squirrel/squirrel/sqvm.cpp +++ b/src/3rdparty/squirrel/squirrel/sqvm.cpp @@ -1480,7 +1480,6 @@ bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool raiseerror,SQBool can_suspend) { - [[maybe_unused]] SQInteger prevstackbase = _stackbase; switch(type(closure)) { case OT_CLOSURE: { assert(!can_suspend || this->_can_suspend); @@ -1490,13 +1489,12 @@ bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObj this->_can_suspend = backup_suspend; return ret; } - break; + case OT_NATIVECLOSURE: { bool suspend; return CallNative(_nativeclosure(closure), nparams, stackbase, outres,suspend); - } - break; + case OT_CLASS: { SQObjectPtr constr; SQObjectPtr temp; @@ -1507,14 +1505,10 @@ bool SQVM::Call(SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObj } return true; } - break; + default: return false; } - if(!_suspended) { - assert(_stackbase == prevstackbase); - } - return true; } bool SQVM::CallMetaMethod(SQDelegable *del,SQMetaMethod mm,SQInteger nparams,SQObjectPtr &outres) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8b282ad6e..c2207476f8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ add_subdirectory(linkgraph) add_subdirectory(misc) add_subdirectory(music) add_subdirectory(network) +add_subdirectory(newgrf) add_subdirectory(os) add_subdirectory(pathfinder) add_subdirectory(saveload) @@ -40,6 +41,11 @@ add_files( CONDITION ICU_i18n_FOUND AND HARFBUZZ_FOUND ) +add_files( + screenshot_png.cpp + CONDITION PNG_FOUND +) + add_files( soundloader_opus.cpp CONDITION OpusFile_FOUND @@ -74,6 +80,9 @@ add_files( base_consist.h base_media_base.h base_media_func.h + base_media_graphics.h + base_media_music.h + base_media_sounds.h base_station_base.h bitmap_type.h bmp.cpp @@ -269,6 +278,9 @@ add_files( newgrf_airporttiles.h newgrf_animation_base.h newgrf_animation_type.h + newgrf_badge.cpp + newgrf_badge.h + newgrf_badge_type.h newgrf_callbacks.h newgrf_canal.cpp newgrf_canal.h @@ -382,10 +394,15 @@ add_files( roadveh_cmd.h roadveh_gui.cpp safeguards.h + screenshot_bmp.cpp screenshot_gui.cpp screenshot_gui.h screenshot.cpp screenshot.h + screenshot_type.h + screenshot_pcx.cpp + settingentry_gui.cpp + settingentry_gui.h settings.cpp settings_cmd.h settings_func.h @@ -428,6 +445,7 @@ add_files( soundloader_type.h soundloader_raw.cpp soundloader_wav.cpp + source_type.h sprite.cpp sprite.h spritecache.cpp diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp index 5750ae6c53..7a2d9fcb3d 100644 --- a/src/ai/ai.hpp +++ b/src/ai/ai.hpp @@ -103,7 +103,7 @@ public: /** * Broadcast a new event to all active AIs. */ - static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company = MAX_COMPANIES); + static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company = CompanyID::Invalid()); /** * Save data from an AI to a savegame. @@ -135,12 +135,12 @@ public: static AIScannerLibrary *GetScannerLibrary(); /** Wrapper function for AIScanner::HasAI */ - static bool HasAI(const struct ContentInfo *ci, bool md5sum); - static bool HasAILibrary(const ContentInfo *ci, bool md5sum); + static bool HasAI(const ContentInfo &ci, bool md5sum); + static bool HasAILibrary(const ContentInfo &ci, bool md5sum); private: - static uint frame_counter; ///< Tick counter for the AI code - static class AIScannerInfo *scanner_info; ///< ScriptScanner instance that is used to find AIs - static class AIScannerLibrary *scanner_library; ///< ScriptScanner instance that is used to find AI Libraries + static uint frame_counter; ///< Tick counter for the AI code + static std::unique_ptr scanner_info; ///< ScriptScanner instance that is used to find AIs + static std::unique_ptr scanner_library; ///< ScriptScanner instance that is used to find AI Libraries }; #endif /* AI_HPP */ diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp index bdb986692e..e7b8f2a8b0 100644 --- a/src/ai/ai_config.cpp +++ b/src/ai/ai_config.cpp @@ -21,18 +21,17 @@ { assert(company < MAX_COMPANIES); - AIConfig **config; - if (source == SSS_FORCE_NEWGAME || (source == SSS_DEFAULT && _game_mode == GM_MENU)) { - config = &_settings_newgame.ai_config[company]; - } else { - if (source != SSS_FORCE_GAME) { - Company *c = Company::GetIfValid(company); - if (c != nullptr && c->ai_config != nullptr) return c->ai_config.get(); - } - config = &_settings_game.ai_config[company]; + if (_game_mode == GM_MENU) source = SSS_FORCE_NEWGAME; + + if (source == SSS_DEFAULT) { + Company *c = Company::GetIfValid(company); + if (c != nullptr && c->ai_config != nullptr) return c->ai_config.get(); } - if (*config == nullptr) *config = new AIConfig(); - return *config; + + auto &config = (source == SSS_FORCE_NEWGAME) ? _settings_newgame.script_config.ai[company] : _settings_game.script_config.ai[company]; + if (config == nullptr) config = std::make_unique(); + + return config.get(); } class AIInfo *AIConfig::GetInfo() const diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp index b64020b4f0..6a3723fc85 100644 --- a/src/ai/ai_config.hpp +++ b/src/ai/ai_config.hpp @@ -24,7 +24,7 @@ public: ScriptConfig() {} - AIConfig(const AIConfig *config) : + AIConfig(const AIConfig &config) : ScriptConfig(config) {} diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp index 358ab4f834..0283b38f66 100644 --- a/src/ai/ai_core.cpp +++ b/src/ai/ai_core.cpp @@ -24,8 +24,8 @@ #include "../safeguards.h" /* static */ uint AI::frame_counter = 0; -/* static */ AIScannerInfo *AI::scanner_info = nullptr; -/* static */ AIScannerLibrary *AI::scanner_library = nullptr; +/* static */ std::unique_ptr AI::scanner_info = nullptr; +/* static */ std::unique_ptr AI::scanner_library = nullptr; /* static */ bool AI::CanStartNew() { @@ -45,7 +45,7 @@ AIConfig *config = c->ai_config.get(); if (config == nullptr) { - c->ai_config = std::make_unique(AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME)); + c->ai_config = std::make_unique(*AIConfig::GetConfig(company, AIConfig::SSS_FORCE_GAME)); config = c->ai_config.get(); } @@ -168,10 +168,10 @@ AI::frame_counter = 0; if (AI::scanner_info == nullptr) { - TarScanner::DoScan(TarScanner::AI); - AI::scanner_info = new AIScannerInfo(); + TarScanner::DoScan(TarScanner::Mode::AI); + AI::scanner_info = std::make_unique(); AI::scanner_info->Initialize(); - AI::scanner_library = new AIScannerLibrary(); + AI::scanner_library = std::make_unique(); AI::scanner_library->Initialize(); } } @@ -185,20 +185,12 @@ * still load all the AIS, while keeping the configs in place */ Rescan(); } else { - delete AI::scanner_info; - delete AI::scanner_library; - AI::scanner_info = nullptr; - AI::scanner_library = nullptr; + AI::scanner_info.reset(); + AI::scanner_library.reset(); - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { - if (_settings_game.ai_config[c] != nullptr) { - delete _settings_game.ai_config[c]; - _settings_game.ai_config[c] = nullptr; - } - if (_settings_newgame.ai_config[c] != nullptr) { - delete _settings_newgame.ai_config[c]; - _settings_newgame.ai_config[c] = nullptr; - } + for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) { + _settings_game.script_config.ai[c].reset(); + _settings_newgame.script_config.ai[c].reset(); } } } @@ -208,18 +200,18 @@ /* Check for both newgame as current game if we can reload the AIInfo inside * the AIConfig. If not, remove the AI from the list (which will assign * a random new AI on reload). */ - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { - if (_settings_game.ai_config[c] != nullptr && _settings_game.ai_config[c]->HasScript()) { - if (!_settings_game.ai_config[c]->ResetInfo(true)) { - Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_game.ai_config[c]->GetName()); - _settings_game.ai_config[c]->Change(std::nullopt); + for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) { + if (_settings_game.script_config.ai[c] != nullptr && _settings_game.script_config.ai[c]->HasScript()) { + if (!_settings_game.script_config.ai[c]->ResetInfo(true)) { + Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_game.script_config.ai[c]->GetName()); + _settings_game.script_config.ai[c]->Change(std::nullopt); } } - if (_settings_newgame.ai_config[c] != nullptr && _settings_newgame.ai_config[c]->HasScript()) { - if (!_settings_newgame.ai_config[c]->ResetInfo(false)) { - Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_newgame.ai_config[c]->GetName()); - _settings_newgame.ai_config[c]->Change(std::nullopt); + if (_settings_newgame.script_config.ai[c] != nullptr && _settings_newgame.script_config.ai[c]->HasScript()) { + if (!_settings_newgame.script_config.ai[c]->ResetInfo(false)) { + Debug(script, 0, "After a reload, the AI by the name '{}' was no longer found, and removed from the list.", _settings_newgame.script_config.ai[c]->GetName()); + _settings_newgame.script_config.ai[c]->Change(std::nullopt); } } @@ -270,7 +262,7 @@ } /* Try to send the event to all AIs */ - for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { + for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) { if (c != skip_company) AI::NewEvent(c, event); } } @@ -325,7 +317,7 @@ /* static */ void AI::Rescan() { - TarScanner::DoScan(TarScanner::AI); + TarScanner::DoScan(TarScanner::Mode::AI); AI::scanner_info->RescanDir(); AI::scanner_library->RescanDir(); @@ -342,23 +334,23 @@ * @param md5sum whether to check the MD5 checksum * @return true iff we have an AI (library) matching. */ -/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum) +/* static */ bool AI::HasAI(const ContentInfo &ci, bool md5sum) { return AI::scanner_info->HasScript(ci, md5sum); } -/* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum) +/* static */ bool AI::HasAILibrary(const ContentInfo &ci, bool md5sum) { return AI::scanner_library->HasScript(ci, md5sum); } /* static */ AIScannerInfo *AI::GetScannerInfo() { - return AI::scanner_info; + return AI::scanner_info.get(); } /* static */ AIScannerLibrary *AI::GetScannerLibrary() { - return AI::scanner_library; + return AI::scanner_library.get(); } diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index cb4223699b..becef23813 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -21,6 +21,7 @@ #include "ai_config.hpp" #include "ai_info.hpp" #include "../script/script_gui.h" + #include "table/strings.h" #include "../citymania/cm_hotkeys.hpp" @@ -32,50 +33,50 @@ static constexpr NWidgetPart _nested_ai_config_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_MAUVE), - NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_AI_CONFIG_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_MAUVE), SetStringTip(STR_AI_CONFIG_CAPTION_AI, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), EndContainer(), NWidget(WWT_PANEL, COLOUR_MAUVE, WID_AIC_BACKGROUND), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), SetPadding(WidgetDimensions::unscaled.sparse), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_NUMBER), SetDataTip(AWV_DECREASE, STR_NULL), - NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_NUMBER), SetDataTip(AWV_INCREASE, STR_NULL), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_NUMBER), SetArrowWidgetTypeTip(AWV_DECREASE), + NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_NUMBER), SetArrowWidgetTypeTip(AWV_INCREASE), EndContainer(), - NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_NUMBER), SetDataTip(STR_AI_CONFIG_MAX_COMPETITORS, STR_NULL), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_AIC_NUMBER), SetFill(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_INTERVAL), SetDataTip(AWV_DECREASE, STR_NULL), - NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_INTERVAL), SetDataTip(AWV_INCREASE, STR_NULL), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_DECREASE_INTERVAL), SetArrowWidgetTypeTip(AWV_DECREASE), + NWidget(WWT_PUSHARROWBTN, COLOUR_YELLOW, WID_AIC_INCREASE_INTERVAL), SetArrowWidgetTypeTip(AWV_INCREASE), EndContainer(), - NWidget(WWT_TEXT, COLOUR_MAUVE, WID_AIC_INTERVAL), SetDataTip(STR_AI_CONFIG_COMPETITORS_INTERVAL, STR_NULL), SetFill(1, 0), + NWidget(WWT_TEXT, INVALID_COLOUR, WID_AIC_INTERVAL), SetFill(1, 0), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_DOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_MOVE_DOWN, STR_AI_CONFIG_MOVE_DOWN_TOOLTIP), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_UP), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_UP, STR_AI_CONFIG_MOVE_UP_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_MOVE_DOWN), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_MOVE_DOWN, STR_AI_CONFIG_MOVE_DOWN_TOOLTIP), EndContainer(), EndContainer(), - NWidget(WWT_FRAME, COLOUR_MAUVE), SetDataTip(STR_AI_CONFIG_AI, STR_NULL), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0), + NWidget(WWT_FRAME, COLOUR_MAUVE), SetStringTip(STR_AI_CONFIG_AI), SetPIP(0, WidgetDimensions::unscaled.vsep_sparse, 0), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_MAUVE, WID_AIC_LIST), SetMinimalSize(288, 112), SetFill(1, 0), SetMatrixDataTip(1, 8, STR_AI_CONFIG_AILIST_TOOLTIP), SetScrollbar(WID_AIC_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_MAUVE, WID_AIC_SCROLLBAR), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetDataTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONFIGURE), SetFill(1, 0), SetStringTip(STR_AI_CONFIG_CONFIGURE, STR_AI_CONFIG_CONFIGURE_TOOLTIP), EndContainer(), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), - NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 1), SetDataTip(STR_AI_CONFIG_CHANGE_AI, STR_AI_CONFIG_CHANGE_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 1), SetDataTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), + NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CHANGE), SetFill(1, 1), SetStringTip(STR_AI_CONFIG_CHANGE_AI, STR_AI_CONFIG_CHANGE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_CONTENT_DOWNLOAD), SetFill(1, 1), SetStringTip(STR_INTRO_ONLINE_CONTENT, STR_INTRO_TOOLTIP_ONLINE_CONTENT), EndContainer(), - NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_OPEN_URL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP), + NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_OPEN_URL), SetResize(1, 0), SetFill(1, 0), SetStringTip(STR_CONTENT_OPEN_URL, STR_CONTENT_OPEN_URL_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_README), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_README, STR_TEXTFILE_VIEW_README_TOOLTIP), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_LICENSE), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_CHANGELOG), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_CHANGELOG, STR_TEXTFILE_VIEW_CHANGELOG_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_AIC_TEXTFILE + TFT_LICENSE), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TEXTFILE_VIEW_LICENCE, STR_TEXTFILE_VIEW_LICENCE_TOOLTIP), EndContainer(), EndContainer(), EndContainer(), @@ -87,7 +88,7 @@ static constexpr NWidgetPart _nested_ai_config_widgets[] = { static WindowDesc _ai_config_desc( WDP_CENTER, nullptr, 0, 0, WC_GAME_OPTIONS, WC_NONE, - 0, + {}, _nested_ai_config_widgets ); @@ -95,15 +96,15 @@ static WindowDesc _ai_config_desc( * Window to configure which AIs will start. */ struct AIConfigWindow : public Window { - CompanyID selected_slot; ///< The currently selected AI slot or \c INVALID_COMPANY. - int line_height; ///< Height of a single AI-name line. - Scrollbar *vscroll; ///< Cache of the vertical scrollbar. + CompanyID selected_slot = CompanyID::Invalid(); ///< The currently selected AI slot or \c CompanyID::Invalid(). + int line_height = 0; ///< Height of a single AI-name line. + Scrollbar *vscroll = nullptr; ///< Cache of the vertical scrollbar. AIConfigWindow() : Window(_ai_config_desc) { this->InitNested(WN_GAME_OPTIONS_AI); // Initializes 'this->line_height' as a side effect. this->vscroll = this->GetScrollbar(WID_AIC_SCROLLBAR); - this->selected_slot = INVALID_COMPANY; + this->selected_slot = CompanyID::Invalid(); NWidgetCore *nwi = this->GetWidget(WID_AIC_LIST); this->vscroll->SetCapacity(nwi->current_y / this->line_height); this->vscroll->SetCount(MAX_COMPANIES); @@ -117,16 +118,17 @@ struct AIConfigWindow : public Window { this->Window::Close(); } - void SetStringParameters(WidgetID widget) const override + std::string GetWidgetString(WidgetID widget, StringID stringid) const override { switch (widget) { case WID_AIC_NUMBER: - SetDParam(0, GetGameSettings().difficulty.max_no_competitors); - break; + return GetString(STR_AI_CONFIG_MAX_COMPETITORS, GetGameSettings().difficulty.max_no_competitors); case WID_AIC_INTERVAL: - SetDParam(0, GetGameSettings().difficulty.competitors_interval); - break; + return GetString(STR_AI_CONFIG_COMPETITORS_INTERVAL, GetGameSettings().difficulty.competitors_interval); + + default: + return this->Window::GetWidgetString(widget, stringid); } } @@ -161,6 +163,32 @@ struct AIConfigWindow : public Window { return slot < MAX_COMPANIES && !Company::IsValidID(slot); } + /** + * Get text to display for a given company slot. + * @param cid Company to display. + * @returns Text to display for company. + */ + std::string GetSlotText(CompanyID cid) const + { + if ((_game_mode != GM_NORMAL && cid == 0) || (_game_mode == GM_NORMAL && Company::IsValidHumanID(cid))) return GetString(STR_AI_CONFIG_HUMAN_PLAYER); + if (const AIInfo *info = AIConfig::GetConfig(cid)->GetInfo(); info != nullptr) return info->GetName(); + return GetString(STR_AI_CONFIG_RANDOM_AI); + } + + /** + * Get colour to display text in for a given company slot. + * @param cid Company to display. + * @param max_slot Maximum company ID that can be an AI. + * @returns Colour to display text for company. + */ + TextColour GetSlotColour(CompanyID cid, CompanyID max_slot) const + { + if (this->selected_slot == cid) return TC_WHITE; + if (IsEditable(cid)) return cid < max_slot ? TC_ORANGE : TC_SILVER; + if (Company::IsValidAiID(cid)) return TC_GREEN; + return TC_SILVER; + } + void DrawWidget(const Rect &r, WidgetID widget) const override { switch (widget) { @@ -171,33 +199,15 @@ struct AIConfigWindow : public Window { for (const Company *c : Company::Iterate()) { if (c->is_ai) max_slot--; } - for (CompanyID cid = COMPANY_FIRST; cid < (CompanyID)max_slot && cid < MAX_COMPANIES; cid++) { + for (CompanyID cid = CompanyID::Begin(); cid < max_slot && cid < MAX_COMPANIES; ++cid) { if (Company::IsValidID(cid)) max_slot++; } } else { max_slot++; // Slot 0 is human } for (int i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < MAX_COMPANIES; i++) { - StringID text; - - if ((_game_mode != GM_NORMAL && i == 0) || (_game_mode == GM_NORMAL && Company::IsValidHumanID(i))) { - text = STR_AI_CONFIG_HUMAN_PLAYER; - } else if (AIConfig::GetConfig((CompanyID)i)->GetInfo() != nullptr) { - SetDParamStr(0, AIConfig::GetConfig((CompanyID)i)->GetInfo()->GetName()); - text = STR_JUST_RAW_STRING; - } else { - text = STR_AI_CONFIG_RANDOM_AI; - } - - TextColour tc = TC_SILVER; - if (this->selected_slot == i) { - tc = TC_WHITE; - } else if (IsEditable((CompanyID)i)) { - if (i < max_slot) tc = TC_ORANGE; - } else if (Company::IsValidAiID(i)) { - tc = TC_GREEN; - } - DrawString(tr, text, tc); + CompanyID cid = static_cast(i); + DrawString(tr, this->GetSlotText(cid), this->GetSlotColour(cid, static_cast(max_slot))); tr.top += this->line_height; } break; @@ -208,7 +218,7 @@ struct AIConfigWindow : public Window { void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_CONTENT_END) { - if (this->selected_slot == INVALID_COMPANY || AIConfig::GetConfig(this->selected_slot) == nullptr) return; + if (this->selected_slot == CompanyID::Invalid() || AIConfig::GetConfig(this->selected_slot) == nullptr) return; ShowScriptTextfileWindow((TextfileType)(widget - WID_AIC_TEXTFILE), this->selected_slot); return; @@ -221,7 +231,7 @@ struct AIConfigWindow : public Window { if (widget == WID_AIC_DECREASE_NUMBER) { new_value = std::max(0, GetGameSettings().difficulty.max_no_competitors - 1); } else { - new_value = std::min(MAX_COMPANIES - 1, GetGameSettings().difficulty.max_no_competitors + 1); + new_value = std::min(MAX_COMPANIES - 1, GetGameSettings().difficulty.max_no_competitors + 1); } IConsoleSetSetting("difficulty.max_no_competitors", new_value); this->InvalidateData(); @@ -250,25 +260,25 @@ struct AIConfigWindow : public Window { case WID_AIC_MOVE_UP: if (IsEditable(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot - 1))) { - Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot - 1]); - this->selected_slot--; - this->vscroll->ScrollTowards(this->selected_slot); + std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot - 1]); + this->selected_slot = CompanyID(this->selected_slot - 1); + this->vscroll->ScrollTowards(this->selected_slot.base()); this->InvalidateData(); } break; case WID_AIC_MOVE_DOWN: if (IsEditable(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot + 1))) { - Swap(GetGameSettings().ai_config[this->selected_slot], GetGameSettings().ai_config[this->selected_slot + 1]); - this->selected_slot++; - this->vscroll->ScrollTowards(this->selected_slot); + std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot + 1]); + ++this->selected_slot; + this->vscroll->ScrollTowards(this->selected_slot.base()); this->InvalidateData(); } break; case WID_AIC_OPEN_URL: { const AIConfig *config = AIConfig::GetConfig(this->selected_slot); - if (this->selected_slot == INVALID_COMPANY || config == nullptr || config->GetInfo() == nullptr) return; + if (this->selected_slot == CompanyID::Invalid() || config == nullptr || config->GetInfo() == nullptr) return; OpenBrowser(config->GetInfo()->GetURL()); break; } @@ -283,7 +293,7 @@ struct AIConfigWindow : public Window { case WID_AIC_CONTENT_DOWNLOAD: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_AI); } @@ -299,25 +309,25 @@ struct AIConfigWindow : public Window { void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { if (!IsEditable(this->selected_slot) && !Company::IsValidAiID(this->selected_slot)) { - this->selected_slot = INVALID_COMPANY; + this->selected_slot = CompanyID::Invalid(); } if (!gui_scope) return; - AIConfig *config = this->selected_slot == INVALID_COMPANY ? nullptr : AIConfig::GetConfig(this->selected_slot); + AIConfig *config = this->selected_slot == CompanyID::Invalid() ? nullptr : AIConfig::GetConfig(this->selected_slot); this->SetWidgetDisabledState(WID_AIC_DECREASE_NUMBER, GetGameSettings().difficulty.max_no_competitors == 0); this->SetWidgetDisabledState(WID_AIC_INCREASE_NUMBER, GetGameSettings().difficulty.max_no_competitors == MAX_COMPANIES - 1); this->SetWidgetDisabledState(WID_AIC_DECREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MIN_COMPETITORS_INTERVAL); this->SetWidgetDisabledState(WID_AIC_INCREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MAX_COMPETITORS_INTERVAL); this->SetWidgetDisabledState(WID_AIC_CHANGE, !IsEditable(this->selected_slot)); - this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == INVALID_COMPANY || config->GetConfigList()->empty()); + this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == CompanyID::Invalid() || config->GetConfigList()->empty()); this->SetWidgetDisabledState(WID_AIC_MOVE_UP, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot - 1))); this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot + 1))); - this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == INVALID_COMPANY || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty()); + this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == CompanyID::Invalid() || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty()); for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) { - this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == INVALID_COMPANY || !config->GetTextfile(tft, this->selected_slot).has_value()); + this->SetWidgetDisabledState(WID_AIC_TEXTFILE + tft, this->selected_slot == CompanyID::Invalid() || !config->GetTextfile(tft, this->selected_slot).has_value()); } } }; diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp index fccfd94871..79d5ca96dd 100644 --- a/src/ai/ai_info.cpp +++ b/src/ai/ai_info.cpp @@ -24,14 +24,10 @@ */ static bool CheckAPIVersion(const std::string &api_version) { - static const std::set versions = { "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" }; - return versions.find(api_version) != versions.end(); + return std::ranges::find(AIInfo::ApiVersions, api_version) != std::end(AIInfo::ApiVersions); } -#if defined(_WIN32) -#undef GetClassName -#endif /* _WIN32 */ -template <> const char *GetClassName() { return "AIInfo"; } +template <> SQInteger PushClassName(HSQUIRRELVM vm) { sq_pushstring(vm, "AIInfo", -1); return 1; } /* static */ void AIInfo::RegisterAPI(Squirrel *engine) { @@ -41,17 +37,17 @@ template <> const char *GetClassName() { return "AIInfo" SQAIInfo.AddConstructor(engine, "x"); SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting"); SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_NONE"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "CONFIG_RANDOM"); // Deprecated, mapped to NONE. - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "CONFIG_BOOLEAN"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "CONFIG_INGAME"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_DEVELOPER, "CONFIG_DEVELOPER"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_NONE"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_RANDOM"); // Deprecated, mapped to NONE. + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Boolean}.base(), "CONFIG_BOOLEAN"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "CONFIG_INGAME"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Developer}.base(), "CONFIG_DEVELOPER"); /* Pre 1.2 had an AI prefix */ - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_NONE"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_NONE, "AICONFIG_RANDOM"); // Deprecated, mapped to NONE. - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_BOOLEAN, "AICONFIG_BOOLEAN"); - SQAIInfo.DefSQConst(engine, SCRIPTCONFIG_INGAME, "AICONFIG_INGAME"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "AICONFIG_NONE"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "AICONFIG_RANDOM"); // Deprecated, mapped to NONE. + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::Boolean}.base(), "AICONFIG_BOOLEAN"); + SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "AICONFIG_INGAME"); SQAIInfo.PostRegister(engine); engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx"); @@ -103,7 +99,7 @@ template <> const char *GetClassName() { return "AIInfo" SQUserPointer instance; sq_getinstanceup(vm, 2, &instance, nullptr); AIInfo *info = (AIInfo *)instance; - info->api_version = fmt::format("{}.{}", GB(_openttd_newgrf_version, 28, 4), GB(_openttd_newgrf_version, 24, 4)); + info->api_version = *std::rbegin(AIInfo::ApiVersions); SQInteger res = ScriptInfo::Constructor(vm, info); if (res != 0) return res; diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp index df8e6b65d7..dc2e0de500 100644 --- a/src/ai/ai_info.hpp +++ b/src/ai/ai_info.hpp @@ -15,6 +15,9 @@ /** All static information from an AI like name, version, etc. */ class AIInfo : public ScriptInfo { public: + /* All valid AI API versions, in order. */ + static constexpr std::string_view ApiVersions[]{ "0.7", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "1.10", "1.11", "12", "13", "14", "15" }; + AIInfo(); /** diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index db4db4b819..07dcd40023 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -10,6 +10,8 @@ #include "../stdafx.h" #include "../debug.h" #include "../error.h" +#include "../company_base.h" +#include "../company_func.h" #include "../script/squirrel_class.hpp" @@ -22,15 +24,14 @@ #include "ai_info.hpp" #include "ai_instance.hpp" +#include "table/strings.h" + /* Manually include the Text glue. */ #include "../script/api/template/template_text.hpp.sq" /* Convert all AI related classes to Squirrel data. */ #include "../script/api/ai/ai_includes.hpp" -#include "../company_base.h" -#include "../company_func.h" - #include "../safeguards.h" AIInstance::AIInstance() : @@ -39,7 +40,7 @@ AIInstance::AIInstance() : void AIInstance::Initialize(AIInfo *info) { - this->versionAPI = info->GetAPIVersion(); + this->api_version = info->GetAPIVersion(); /* Register the AIController (including the "import" command) */ SQAIController_Register(this->engine); @@ -54,7 +55,7 @@ void AIInstance::RegisterAPI() /* Register all classes */ SQAI_RegisterAll(this->engine); - if (!this->LoadCompatibilityScripts(this->versionAPI, AI_DIR)) this->Died(); + if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died(); } void AIInstance::Died() @@ -71,7 +72,7 @@ void AIInstance::Died() const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo(); if (info != nullptr) { - ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_AI_PLEASE_REPORT_CRASH), {}, WL_WARNING); if (!info->GetURL().empty()) { ScriptLog::Info("Please report the error to the following URL:"); diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp index 1b3fdf193b..843229e699 100644 --- a/src/ai/ai_scanner.cpp +++ b/src/ai/ai_scanner.cpp @@ -8,6 +8,7 @@ /** @file ai_scanner.cpp allows scanning AI scripts */ #include "../stdafx.h" +#include #include "../debug.h" #include "../network/network.h" #include "../openttd.h" @@ -65,31 +66,21 @@ AIInfo *AIScannerInfo::SelectRandomAI() const return this->info_dummy; } - uint num_random_ais = 0; - for (const auto &item : info_single_list) { - AIInfo *i = static_cast(item.second); - if (i->UseAsRandomAI()) num_random_ais++; - } + /* Filter for AIs suitable as Random AI. */ + auto random_ais = info_single_list | std::views::filter([](const auto &item) { return static_cast(item.second)->UseAsRandomAI(); }); + uint num_random_ais = std::ranges::distance(random_ais); if (num_random_ais == 0) { Debug(script, 0, "No suitable AI found, loading 'dummy' AI."); return this->info_dummy; } - /* Find a random AI */ + /* Pick a random AI */ uint pos = ScriptObject::GetRandomizer(OWNER_NONE).Next(num_random_ais); + auto it = std::ranges::next(std::begin(random_ais), pos, std::end(random_ais)); + assert(it != std::end(random_ais)); - /* Find the Nth item from the array */ - ScriptInfoList::const_iterator it = this->info_single_list.begin(); - -#define GetAIInfo(it) static_cast((*it).second) - while (!GetAIInfo(it)->UseAsRandomAI()) it++; - for (; pos > 0; pos--) { - it++; - while (!GetAIInfo(it)->UseAsRandomAI()) it++; - } - return GetAIInfo(it); -#undef GetAIInfo + return static_cast(it->second); } AIInfo *AIScannerInfo::FindInfo(const std::string &name, int version, bool force_exact_match) diff --git a/src/aircraft.h b/src/aircraft.h index a6b747d5cf..d3b9336f1f 100644 --- a/src/aircraft.h +++ b/src/aircraft.h @@ -25,7 +25,7 @@ static constexpr int HELICOPTER_HOLD_MAX_FLYING_ALTITUDE = 184; ///< holding fly struct Aircraft; /** An aircraft can be one of those types. */ -enum AircraftSubType { +enum AircraftSubType : uint8_t { AIR_HELICOPTER = 0, ///< an helicopter AIR_AIRCRAFT = 2, ///< an airplane AIR_SHADOW = 4, ///< shadow of the aircraft @@ -33,7 +33,7 @@ enum AircraftSubType { }; /** Flags for air vehicles; shared with disaster vehicles. */ -enum AirVehicleFlags { +enum AirVehicleFlags : uint8_t { VAF_DEST_TOO_FAR = 0, ///< Next destination is too far away. /* The next two flags are to prevent stair climbing of the aircraft. The idea is that the aircraft @@ -62,25 +62,25 @@ int GetAircraftFlightLevel(T *v, bool takeoff = false); /** Variables that are cached to improve performance and such. */ struct AircraftCache { - uint32_t cached_max_range_sqr; ///< Cached squared maximum range. - uint16_t cached_max_range; ///< Cached maximum range. + uint32_t cached_max_range_sqr = 0; ///< Cached squared maximum range. + uint16_t cached_max_range = 0; ///< Cached maximum range. }; /** * Aircraft, helicopters, rotors and their shadows belong to this class. */ struct Aircraft final : public SpecializedVehicle { - uint16_t crashed_counter; ///< Timer for handling crash animations. - uint8_t pos; ///< Next desired position of the aircraft. - uint8_t previous_pos; ///< Previous desired position of the aircraft. - StationID targetairport; ///< Airport to go to next. - uint8_t state; ///< State of the airport. @see AirportMovementStates - Direction last_direction; - uint8_t number_consecutive_turns; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point. - uint8_t turn_counter; ///< Ticks between each turn to prevent > 45 degree turns. - uint8_t flags; ///< Aircraft flags. @see AirVehicleFlags + uint16_t crashed_counter = 0; ///< Timer for handling crash animations. + uint8_t pos = 0; ///< Next desired position of the aircraft. + uint8_t previous_pos = 0; ///< Previous desired position of the aircraft. + StationID targetairport = StationID::Invalid(); ///< Airport to go to next. + uint8_t state = 0; ///< State of the airport. @see AirportMovementStates + Direction last_direction = INVALID_DIR; + uint8_t number_consecutive_turns = 0; ///< Protection to prevent the aircraft of making a lot of turns in order to reach a specific point. + uint8_t turn_counter = 0; ///< Ticks between each turn to prevent > 45 degree turns. + uint8_t flags = 0; ///< Aircraft flags. @see AirVehicleFlags - AircraftCache acache; + AircraftCache acache{}; /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ Aircraft() : SpecializedVehicleBase() {} @@ -101,7 +101,7 @@ struct Aircraft final : public SpecializedVehicle { bool IsInDepot() const override { assert(this->IsPrimaryVehicle()); - return (this->vehstatus & VS_HIDDEN) != 0 && IsHangarTile(this->tile); + return this->vehstatus.Test(VehState::Hidden) && IsHangarTile(this->tile); } bool Tick() override; diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index ba770daa60..5bd49928df 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -107,7 +107,7 @@ bool IsValidImageIndex(uint8_t image_index) } /** Helicopter rotor animation states */ -enum HelicopterRotorStates { +enum HelicopterRotorStates : uint8_t { HRS_ROTOR_STOPPED, HRS_ROTOR_MOVING_1, HRS_ROTOR_MOVING_2, @@ -116,15 +116,15 @@ enum HelicopterRotorStates { /** * Find the nearest hangar to v - * INVALID_STATION is returned, if the company does not have any suitable + * StationID::Invalid() is returned, if the company does not have any suitable * airports (like helipads only) * @param v vehicle looking for a hangar - * @return the StationID if one is found, otherwise, INVALID_STATION + * @return the StationID if one is found, otherwise, StationID::Invalid() */ static StationID FindNearestHangar(const Aircraft *v) { uint best = 0; - StationID index = INVALID_STATION; + StationID index = StationID::Invalid(); TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos); const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type); uint max_range = v->acache.cached_max_range_sqr; @@ -136,7 +136,7 @@ static StationID FindNearestHangar(const Aircraft *v) if (v->current_order.IsType(OT_GOTO_STATION) || (v->current_order.IsType(OT_GOTO_DEPOT) && (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) == 0)) { last_dest = Station::GetIfValid(v->last_station_visited); - next_dest = Station::GetIfValid(v->current_order.GetDestination()); + next_dest = Station::GetIfValid(v->current_order.GetDestination().ToStationID()); } else { last_dest = GetTargetAirportIfValid(v); next_dest = Station::GetIfValid(v->GetNextStoppingStation().value); @@ -144,15 +144,15 @@ static StationID FindNearestHangar(const Aircraft *v) } for (const Station *st : Station::Iterate()) { - if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT) || !st->airport.HasHangar()) continue; + if (st->owner != v->owner || !st->facilities.Test(StationFacility::Airport) || !st->airport.HasHangar()) continue; const AirportFTAClass *afc = st->airport.GetFTA(); /* don't crash the plane if we know it can't land at the airport */ - if ((afc->flags & AirportFTAClass::SHORT_STRIP) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue; + if (afc->flags.Test(AirportFTAClass::Flag::ShortStrip) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue; /* the plane won't land at any helicopter station */ - if (!(afc->flags & AirportFTAClass::AIRPLANES) && (avi->subtype & AIR_CTOL)) continue; + if (!afc->flags.Test(AirportFTAClass::Flag::Airplanes) && (avi->subtype & AIR_CTOL)) continue; /* Check if our last and next destinations can be reached from the depot airport. */ if (max_range != 0) { @@ -163,7 +163,7 @@ static StationID FindNearestHangar(const Aircraft *v) /* v->tile can't be used here, when aircraft is flying v->tile is set to 0 */ uint distance = DistanceSquare(vtile, st->airport.tile); - if (distance < best || index == INVALID_STATION) { + if (distance < best || index == StationID::Invalid()) { best = distance; index = st->index; } @@ -268,7 +268,7 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff * @param[out] ret the vehicle that has been built. * @return the cost of this operation or an error. */ -CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **ret) +CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) { const AircraftVehicleInfo *avi = &e->u.air; const Station *st = Station::GetByTile(tile); @@ -279,7 +279,7 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * /* Make sure all aircraft end up in the first tile of the hangar. */ tile = st->airport.GetHangarTile(st->airport.GetHangarNum(tile)); - if (flags & DC_EXEC) { + if (flags.Test(DoCommandFlag::Execute)) { Aircraft *v = new Aircraft(); // aircraft Aircraft *u = new Aircraft(); // shadow *ret = v; @@ -299,8 +299,8 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * u->z_pos = GetSlopePixelZ(x, y); v->z_pos = u->z_pos + 1; - v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; - u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW; + v->vehstatus = {VehState::Hidden, VehState::Stopped, VehState::DefaultPalette}; + u->vehstatus = {VehState::Hidden, VehState::Unclickable, VehState::Shadow}; v->spritenum = avi->image_index; @@ -309,17 +309,17 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * u->refit_cap = 0; v->cargo_type = e->GetDefaultCargoType(); - assert(IsValidCargoID(v->cargo_type)); + assert(IsValidCargoType(v->cargo_type)); - CargoID mail = GetCargoIDByLabel(CT_MAIL); - if (IsValidCargoID(mail)) { + CargoType mail = GetCargoTypeByLabel(CT_MAIL); + if (IsValidCargoType(mail)) { u->cargo_type = mail; u->cargo_cap = avi->mail_capacity; } v->name.clear(); - v->last_station_visited = INVALID_STATION; - v->last_loading_station = INVALID_STATION; + v->last_station_visited = StationID::Invalid(); + v->last_loading_station = StationID::Invalid(); v->acceleration = avi->acceleration; v->engine_type = e->index; @@ -354,8 +354,8 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * v->random_bits = Random(); u->random_bits = Random(); - v->vehicle_flags = 0; - if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); + v->vehicle_flags = {}; + if (e->flags.Test(EngineFlag::ExclusivePreview)) v->vehicle_flags.Set(VehicleFlag::BuiltAsPrototype); v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent); v->InvalidateNewGRFCacheOfChain(); @@ -378,7 +378,7 @@ CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine * w->x_pos = v->x_pos; w->y_pos = v->y_pos; w->z_pos = v->z_pos + ROTOR_Z_OFFSET; - w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE; + w->vehstatus = {VehState::Hidden, VehState::Unclickable}; w->spritenum = 0xFF; w->subtype = AIR_ROTOR; w->sprite_cache.sprite_seq.Set(SPR_ROTOR_STOPPED); @@ -404,7 +404,7 @@ ClosestDepot Aircraft::FindClosestDepot() /* the aircraft has to search for a hangar on its own */ StationID station = FindNearestHangar(this); - if (station == INVALID_STATION) return ClosestDepot(); + if (station == StationID::Invalid()) return ClosestDepot(); st = Station::Get(station); } @@ -424,7 +424,7 @@ static void CheckIfAircraftNeedsService(Aircraft *v) * we don't want to consider going to a depot too. */ if (!v->current_order.IsType(OT_GOTO_DEPOT) && !v->current_order.IsType(OT_GOTO_STATION)) return; - const Station *st = Station::Get(v->current_order.GetDestination()); + const Station *st = Station::Get(v->current_order.GetDestination().ToStationID()); assert(st != nullptr); @@ -482,11 +482,11 @@ static void HelicopterTickHandler(Aircraft *v) { Aircraft *u = v->Next()->Next(); - if (u->vehstatus & VS_HIDDEN) return; + if (u->vehstatus.Test(VehState::Hidden)) return; /* if true, helicopter rotors do not rotate. This should only be the case if a helicopter is * loading/unloading at a terminal or stopped */ - if (v->current_order.IsType(OT_LOADING) || (v->vehstatus & VS_STOPPED)) { + if (v->current_order.IsType(OT_LOADING) || v->vehstatus.Test(VehState::Stopped)) { if (u->cur_speed != 0) { u->cur_speed++; if (u->cur_speed >= 0x80 && u->state == HRS_ROTOR_MOVING_3) { @@ -576,10 +576,10 @@ void HandleAircraftEnterHangar(Aircraft *v) v->progress = 0; Aircraft *u = v->Next(); - u->vehstatus |= VS_HIDDEN; + u->vehstatus.Set(VehState::Hidden); u = u->Next(); if (u != nullptr) { - u->vehstatus |= VS_HIDDEN; + u->vehstatus.Set(VehState::Hidden); u->cur_speed = 0; } @@ -660,7 +660,7 @@ static int UpdateAircraftSpeed(Aircraft *v, uint speed_limit = SPEED_LIMIT_NONE, speed_limit *= _settings_game.vehicle.plane_speed; /* adjust speed for broken vehicles */ - if (v->vehstatus & VS_AIRCRAFT_BROKEN) { + if (v->vehstatus.Test(VehState::AircraftBroken)) { if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false; speed_limit = std::min(speed_limit, SPEED_LIMIT_BROKEN); } @@ -831,7 +831,7 @@ static uint8_t AircraftGetEntryPoint(const Aircraft *v, const AirportFTAClass *a /* In the case the station doesn't exit anymore, set target tile 0. * It doesn't hurt much, aircraft will go to next order, nearest hangar * or it will simply crash in next tick */ - TileIndex tile = 0; + TileIndex tile{}; const Station *st = Station::GetIfValid(v->targetairport); if (st != nullptr) { @@ -908,7 +908,7 @@ static bool AircraftController(Aircraft *v) int y = TileY(tile) * TILE_SIZE; /* Helicopter raise */ - if (amd.flag & AMED_HELI_RAISE) { + if (amd.flags.Test(AirportMovingDataFlag::HeliRaise)) { Aircraft *u = v->Next()->Next(); /* Make sure the rotors don't rotate too fast */ @@ -927,7 +927,7 @@ static bool AircraftController(Aircraft *v) u->cur_speed = 32; int count = UpdateAircraftSpeed(v); if (count > 0) { - v->tile = 0; + v->tile = TileIndex{}; int z_dest; GetAircraftFlightLevelBounds(v, &z_dest, nullptr); @@ -944,13 +944,10 @@ static bool AircraftController(Aircraft *v) } /* Helicopter landing. */ - if (amd.flag & AMED_HELI_LOWER) { + if (amd.flags.Test(AirportMovingDataFlag::HeliLower)) { SetBit(v->flags, VAF_HELI_DIRECT_DESCENT); if (st == nullptr) { - /* FIXME - AircraftController -> if station no longer exists, do not land - * helicopter will circle until sign disappears, then go to next order - * what to do when it is the only order left, right now it just stays in 1 place */ v->state = FLYING; UpdateAircraftCache(v); AircraftNextAirportPos_and_Order(v); @@ -996,7 +993,7 @@ static bool AircraftController(Aircraft *v) uint dist = abs(x + amd.x - v->x_pos) + abs(y + amd.y - v->y_pos); /* Need exact position? */ - if (!(amd.flag & AMED_EXACTPOS) && dist <= (amd.flag & AMED_SLOWTURN ? 8U : 4U)) return true; + if (!amd.flags.Test(AirportMovingDataFlag::ExactPosition) && dist <= (amd.flags.Test(AirportMovingDataFlag::SlowTurn) ? 8U : 4U)) return true; /* At final pos? */ if (dist == 0) { @@ -1018,18 +1015,18 @@ static bool AircraftController(Aircraft *v) return false; } - if (amd.flag & AMED_BRAKE && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) { + if (amd.flags.Test(AirportMovingDataFlag::Brake) && v->cur_speed > SPEED_LIMIT_TAXI * _settings_game.vehicle.plane_speed) { MaybeCrashAirplane(v); - if ((v->vehstatus & VS_CRASHED) != 0) return false; + if (v->vehstatus.Test(VehState::Crashed)) return false; } uint speed_limit = SPEED_LIMIT_TAXI; bool hard_limit = true; - if (amd.flag & AMED_NOSPDCLAMP) speed_limit = SPEED_LIMIT_NONE; - if (amd.flag & AMED_HOLD) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; } - if (amd.flag & AMED_LAND) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; } - if (amd.flag & AMED_BRAKE) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; } + if (amd.flags.Test(AirportMovingDataFlag::NoSpeedClamp)) speed_limit = SPEED_LIMIT_NONE; + if (amd.flags.Test(AirportMovingDataFlag::Hold)) { speed_limit = SPEED_LIMIT_HOLD; hard_limit = false; } + if (amd.flags.Test(AirportMovingDataFlag::Land)) { speed_limit = SPEED_LIMIT_APPROACH; hard_limit = false; } + if (amd.flags.Test(AirportMovingDataFlag::Brake)) { speed_limit = SPEED_LIMIT_TAXI; hard_limit = false; } int count = UpdateAircraftSpeed(v, speed_limit, hard_limit); if (count == 0) return false; @@ -1047,7 +1044,7 @@ static bool AircraftController(Aircraft *v) GetNewVehiclePosResult gp; - if (nudge_towards_target || (amd.flag & AMED_LAND)) { + if (nudge_towards_target || amd.flags.Test(AirportMovingDataFlag::Land)) { /* move vehicle one pixel towards target */ gp.x = (v->x_pos != (x + amd.x)) ? v->x_pos + ((x + amd.x > v->x_pos) ? 1 : -1) : @@ -1064,7 +1061,7 @@ static bool AircraftController(Aircraft *v) /* Turn. Do it slowly if in the air. */ Direction newdir = GetDirectionTowards(v, x + amd.x, y + amd.y); if (newdir != v->direction) { - if (amd.flag & AMED_SLOWTURN && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) { + if (amd.flags.Test(AirportMovingDataFlag::SlowTurn) && v->number_consecutive_turns < 8 && v->subtype == AIR_AIRCRAFT) { if (v->turn_counter == 0 || newdir == v->last_direction) { if (newdir == v->last_direction) { v->number_consecutive_turns = 0; @@ -1100,17 +1097,17 @@ static bool AircraftController(Aircraft *v) v->tile = gp.new_tile; /* If vehicle is in the air, use tile coordinate 0. */ - if (amd.flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0; + if (amd.flags.Any({AirportMovingDataFlag::Takeoff, AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::Land})) v->tile = TileIndex{}; /* Adjust Z for land or takeoff? */ int z = v->z_pos; - if (amd.flag & AMED_TAKEOFF) { + if (amd.flags.Test(AirportMovingDataFlag::Takeoff)) { z = GetAircraftFlightLevel(v, true); - } else if (amd.flag & AMED_HOLD) { + } else if (amd.flags.Test(AirportMovingDataFlag::Hold)) { /* Let the plane drop from normal flight altitude to holding pattern altitude */ if (z > GetAircraftHoldMaxAltitude(v)) z--; - } else if ((amd.flag & AMED_SLOWTURN) && (amd.flag & AMED_NOSPDCLAMP)) { + } else if (amd.flags.All({AirportMovingDataFlag::SlowTurn, AirportMovingDataFlag::NoSpeedClamp})) { z = GetAircraftFlightLevel(v); } @@ -1121,13 +1118,13 @@ static bool AircraftController(Aircraft *v) * We also know that the airport itself has to be completely flat (otherwise it is not a valid airport). * Therefore, use the height of this hangar to calculate our z-value. */ int airport_z = v->z_pos; - if ((amd.flag & (AMED_LAND | AMED_BRAKE)) && st != nullptr) { + if (amd.flags.Any({AirportMovingDataFlag::Land, AirportMovingDataFlag::Brake}) && st != nullptr) { assert(st->airport.HasHangar()); TileIndex hangar_tile = st->airport.GetHangarTile(0); airport_z = GetTileMaxPixelZ(hangar_tile) + 1; // To avoid clashing with the shadow } - if (amd.flag & AMED_LAND) { + if (amd.flags.Test(AirportMovingDataFlag::Land)) { if (st->airport.tile == INVALID_TILE) { /* Airport has been removed, abort the landing procedure */ v->state = FLYING; @@ -1151,7 +1148,7 @@ static bool AircraftController(Aircraft *v) } /* We've landed. Decrease speed when we're reaching end of runway. */ - if (amd.flag & AMED_BRAKE) { + if (amd.flags.Test(AirportMovingDataFlag::Brake)) { if (z > airport_z) { z--; @@ -1209,12 +1206,12 @@ static bool HandleCrashedAircraft(Aircraft *v) /* remove rubble of crashed airplane */ /* clear runway-in on all airports, set by crashing plane - * small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc. + * small airports use AIRPORT_BUSY, city airports use AirportBlock::RunwayInOut, etc. * but they all share the same number */ if (st != nullptr) { - CLRBITS(st->airport.flags, RUNWAY_IN_block); - CLRBITS(st->airport.flags, RUNWAY_IN_OUT_block); // commuter airport - CLRBITS(st->airport.flags, RUNWAY_IN2_block); // intercontinental + st->airport.blocks.Reset(AirportBlock::RunwayIn); + st->airport.blocks.Reset(AirportBlock::RunwayInOut); // commuter airport + st->airport.blocks.Reset(AirportBlock::RunwayIn2); // intercontinental } delete v; @@ -1247,11 +1244,11 @@ static void HandleAircraftSmoke(Aircraft *v, bool mode) { 0, 6 } }; - if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return; + if (!v->vehstatus.Test(VehState::AircraftBroken)) return; /* Stop smoking when landed */ if (v->cur_speed < 10) { - v->vehstatus &= ~VS_AIRCRAFT_BROKEN; + v->vehstatus.Reset(VehState::AircraftBroken); v->breakdown_ctr = 0; return; } @@ -1287,7 +1284,7 @@ void HandleMissingAircraftOrders(Aircraft *v) const Station *st = GetTargetAirportIfValid(v); if (st == nullptr) { Backup cur_company(_current_company, v->owner); - CommandCost ret = Command::Do(DC_EXEC, v->index, DepotCommand::None, {}); + CommandCost ret = Command::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag{}, {}); cur_company.Restore(); if (ret.Failed()) CrashAirplane(v); @@ -1305,7 +1302,7 @@ TileIndex Aircraft::GetOrderStationLocation(StationID) } /* Aircraft do not use dest-tile */ - return 0; + return TileIndex{}; } void Aircraft::MarkDirty() @@ -1335,29 +1332,28 @@ static void CrashAirplane(Aircraft *v) CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); uint victims = v->Crash(); - SetDParam(0, victims); v->cargo.Truncate(); v->Next()->cargo.Truncate(); const Station *st = GetTargetAirportIfValid(v); - StringID newsitem; TileIndex vt = TileVirtXY(v->x_pos, v->y_pos); + + EncodedString headline; if (st == nullptr) { - newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL; + headline = GetEncodedString(STR_NEWS_PLANE_CRASH_OUT_OF_FUEL, victims); } else { - SetDParam(1, st->index); - newsitem = STR_NEWS_AIRCRAFT_CRASH; + headline = GetEncodedString(STR_NEWS_AIRCRAFT_CRASH, victims, st->index); } - AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims)); - Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims)); + AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner)); + Game::NewEvent(new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims, v->owner)); - NewsType newstype = NT_ACCIDENT; + NewsType newstype = NewsType::Accident; if (v->owner != _local_company) { - newstype = NT_ACCIDENT_OTHER; + newstype = NewsType::AccidentOther; } - AddTileNewsItem(newsitem, newstype, vt, nullptr, st != nullptr ? st->index : INVALID_STATION); + AddTileNewsItem(std::move(headline), newstype, vt, st != nullptr ? st->index : StationID::Invalid()); ModifyStationRatingAround(vt, v->owner, -160, 30); if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v); @@ -1373,7 +1369,7 @@ static void MaybeCrashAirplane(Aircraft *v) Station *st = Station::Get(v->targetairport); uint32_t prob; - if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) && + if (st->airport.GetFTA()->flags.Test(AirportFTAClass::Flag::ShortStrip) && (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) { prob = 3276; @@ -1387,7 +1383,7 @@ static void MaybeCrashAirplane(Aircraft *v) /* Crash the airplane. Remove all goods stored at the station. */ for (GoodsEntry &ge : st->goods) { ge.rating = 1; - ge.cargo.Truncate(); + if (ge.HasData()) ge.GetData().cargo.Truncate(); } CrashAirplane(v); @@ -1408,11 +1404,10 @@ static void AircraftEntersTerminal(Aircraft *v) /* Check if station was ever visited before */ if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) { st->had_vehicle_of_type |= HVOT_AIRCRAFT; - SetDParam(0, st->index); /* show newsitem of celebrating citizens */ AddVehicleNewsItem( - STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, - (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER, + GetEncodedString(STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, st->index), + (v->owner == _local_company) ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index ); @@ -1447,7 +1442,7 @@ static void AircraftLandAirplane(Aircraft *v) void AircraftNextAirportPos_and_Order(Aircraft *v) { if (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT)) { - v->targetairport = v->current_order.GetDestination(); + v->targetairport = v->current_order.GetDestination().ToStationID(); } const Station *st = GetTargetAirportIfValid(v); @@ -1470,15 +1465,15 @@ void AircraftLeaveHangar(Aircraft *v, Direction exit_dir) v->subspeed = 0; v->progress = 0; v->direction = exit_dir; - v->vehstatus &= ~VS_HIDDEN; + v->vehstatus.Reset(VehState::Hidden); { Vehicle *u = v->Next(); - u->vehstatus &= ~VS_HIDDEN; + u->vehstatus.Reset(VehState::Hidden); /* Rotor blades */ u = u->Next(); if (u != nullptr) { - u->vehstatus &= ~VS_HIDDEN; + u->vehstatus.Reset(VehState::Hidden); u->cur_speed = 80; } } @@ -1524,7 +1519,7 @@ static void AircraftEventHandler_InHangar(Aircraft *v, const AirportFTAClass *ap } /* if we were sent to the depot, stay there */ - if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) { + if (v->current_order.IsType(OT_GOTO_DEPOT) && v->vehstatus.Test(VehState::Stopped)) { v->current_order.Free(); return; } @@ -1653,7 +1648,7 @@ static void AircraftEventHandler_HeliTakeOff(Aircraft *v, const AirportFTAClass /* Send the helicopter to a hangar if needed for replacement */ if (v->NeedsAutomaticServicing()) { Backup cur_company(_current_company, v->owner); - Command::Do(DC_EXEC, v->index, DepotCommand::Service | DepotCommand::LocateHangar, {}); + Command::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {}); cur_company.Restore(); } } @@ -1663,12 +1658,12 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc) Station *st = Station::Get(v->targetairport); /* Runway busy, not allowed to use this airstation or closed, circle. */ - if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !(st->airport.flags & AIRPORT_CLOSED_block)) { - /* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41}, + if (CanVehicleUseStation(v, st) && (st->owner == OWNER_NONE || st->owner == v->owner) && !st->airport.blocks.Test(AirportBlock::AirportClosed)) { + /* {32,FLYING,AirportBlock::Nothing,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41}, * if it is an airplane, look for LANDING, for helicopter HELILANDING * it is possible to choose from multiple landing runways, so loop until a free one is found */ uint8_t landingtype = (v->subtype == AIR_HELICOPTER) ? HELILANDING : LANDING; - const AirportFTA *current = apc->layout[v->pos].next; + const AirportFTA *current = apc->layout[v->pos].next.get(); while (current != nullptr) { if (current->heading == landingtype) { /* save speed before, since if AirportHasBlock is false, it resets them to 0 @@ -1683,13 +1678,13 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc) * if there are multiple runways, plane won't know which one it took (because * they all have heading LANDING). And also occupy that block! */ v->pos = current->next_position; - SETBITS(st->airport.flags, apc->layout[v->pos].block); + st->airport.blocks.Set(apc->layout[v->pos].blocks); return; } v->cur_speed = tcur_speed; v->subspeed = tsubspeed; } - current = current->next; + current = current->next.get(); } } v->state = FLYING; @@ -1704,7 +1699,7 @@ static void AircraftEventHandler_Landing(Aircraft *v, const AirportFTAClass *) /* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed */ if (v->NeedsAutomaticServicing()) { Backup cur_company(_current_company, v->owner); - Command::Do(DC_EXEC, v->index, DepotCommand::Service, {}); + Command::Do(DoCommandFlag::Execute, v->index, DepotCommandFlag::Service, {}); cur_company.Restore(); } } @@ -1784,10 +1779,10 @@ static AircraftStateHandler * const _aircraft_state_handlers[] = { static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc) { /* we have left the previous block, and entered the new one. Free the previous block */ - if (apc->layout[v->previous_pos].block != apc->layout[v->pos].block) { + if (apc->layout[v->previous_pos].blocks != apc->layout[v->pos].blocks) { Station *st = Station::Get(v->targetairport); - CLRBITS(st->airport.flags, apc->layout[v->previous_pos].block); + st->airport.blocks.Reset(apc->layout[v->previous_pos].blocks); } } @@ -1843,7 +1838,7 @@ static bool AirportMove(Aircraft *v, const AirportFTAClass *apc) } // move to next position return false; } - current = current->next; + current = current->next.get(); } while (current != nullptr); Debug(misc, 0, "[Ap] cannot move further on Airport! (pos {} state {}) for vehicle {}", v->pos, v->state, v->index); @@ -1857,16 +1852,16 @@ static bool AirportHasBlock(Aircraft *v, const AirportFTA *current_pos, const Ai const AirportFTA *next = &apc->layout[current_pos->next_position]; /* same block, then of course we can move */ - if (apc->layout[current_pos->position].block != next->block) { + if (apc->layout[current_pos->position].blocks != next->blocks) { const Station *st = Station::Get(v->targetairport); - uint64_t airport_flags = next->block; + AirportBlocks blocks = next->blocks; /* check additional possible extra blocks */ - if (current_pos != reference && current_pos->block != NOTHING_block) { - airport_flags |= current_pos->block; + if (current_pos != reference && current_pos->blocks != AirportBlock::Nothing) { + blocks.Set(current_pos->blocks); } - if (st->airport.flags & airport_flags) { + if (st->airport.blocks.Any(blocks)) { v->cur_speed = 0; v->subspeed = 0; return true; @@ -1888,33 +1883,33 @@ static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const A const AirportFTA *reference = &apc->layout[v->pos]; /* if the next position is in another block, check it and wait until it is free */ - if ((apc->layout[current_pos->position].block & next->block) != next->block) { - uint64_t airport_flags = next->block; + if (!apc->layout[current_pos->position].blocks.All(next->blocks)) { + AirportBlocks blocks = next->blocks; /* search for all all elements in the list with the same state, and blocks != N * this means more blocks should be checked/set */ const AirportFTA *current = current_pos; - if (current == reference) current = current->next; + if (current == reference) current = current->next.get(); while (current != nullptr) { - if (current->heading == current_pos->heading && current->block != 0) { - airport_flags |= current->block; + if (current->heading == current_pos->heading && current->blocks.Any()) { + blocks.Set(current->blocks); break; } - current = current->next; + current = current->next.get(); } /* if the block to be checked is in the next position, then exclude that from * checking, because it has been set by the airplane before */ - if (current_pos->block == next->block) airport_flags ^= next->block; + if (current_pos->blocks == next->blocks) blocks.Flip(next->blocks); Station *st = Station::Get(v->targetairport); - if (st->airport.flags & airport_flags) { + if (st->airport.blocks.Any(blocks)) { v->cur_speed = 0; v->subspeed = 0; return false; } - if (next->block != NOTHING_block) { - SETBITS(st->airport.flags, airport_flags); // occupy next block + if (next->blocks != AirportBlock::Nothing) { + st->airport.blocks.Set(blocks); // occupy next block } } return true; @@ -1926,22 +1921,22 @@ static bool AirportSetBlocks(Aircraft *v, const AirportFTA *current_pos, const A */ struct MovementTerminalMapping { AirportMovementStates state; ///< Aircraft movement state when going to this terminal. - uint64_t airport_flag; ///< Bitmask in the airport flags that need to be free for this terminal. + AirportBlock blocks; ///< Bitmask in the airport flags that need to be free for this terminal. }; /** A list of all valid terminals and their associated blocks. */ static const MovementTerminalMapping _airport_terminal_mapping[] = { - {TERM1, TERM1_block}, - {TERM2, TERM2_block}, - {TERM3, TERM3_block}, - {TERM4, TERM4_block}, - {TERM5, TERM5_block}, - {TERM6, TERM6_block}, - {TERM7, TERM7_block}, - {TERM8, TERM8_block}, - {HELIPAD1, HELIPAD1_block}, - {HELIPAD2, HELIPAD2_block}, - {HELIPAD3, HELIPAD3_block}, + {TERM1, AirportBlock::Term1}, + {TERM2, AirportBlock::Term2}, + {TERM3, AirportBlock::Term3}, + {TERM4, AirportBlock::Term4}, + {TERM5, AirportBlock::Term5}, + {TERM6, AirportBlock::Term6}, + {TERM7, AirportBlock::Term7}, + {TERM8, AirportBlock::Term8}, + {HELIPAD1, AirportBlock::Helipad1}, + {HELIPAD2, AirportBlock::Helipad2}, + {HELIPAD3, AirportBlock::Helipad3}, }; /** @@ -1956,10 +1951,10 @@ static bool FreeTerminal(Aircraft *v, uint8_t i, uint8_t last_terminal) assert(last_terminal <= lengthof(_airport_terminal_mapping)); Station *st = Station::Get(v->targetairport); for (; i < last_terminal; i++) { - if ((st->airport.flags & _airport_terminal_mapping[i].airport_flag) == 0) { + if (!st->airport.blocks.Any(_airport_terminal_mapping[i].blocks)) { /* TERMINAL# HELIPAD# */ v->state = _airport_terminal_mapping[i].state; // start moving to that terminal/helipad - SETBITS(st->airport.flags, _airport_terminal_mapping[i].airport_flag); // occupy terminal/helipad + st->airport.blocks.Set(_airport_terminal_mapping[i].blocks); // occupy terminal/helipad return true; } } @@ -1989,22 +1984,22 @@ static uint GetNumTerminals(const AirportFTAClass *apc) static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc) { /* example of more terminalgroups - * {0,HANGAR,NOTHING_block,1}, {0,TERMGROUP,TERM_GROUP1_block,0}, {0,TERMGROUP,TERM_GROUP2_ENTER_block,1}, {0,0,N,1}, + * {0,HANGAR,AirportBlock::Nothing,1}, {0,TERMGROUP,AirportBlock::TermGroup1,0}, {0,TERMGROUP,TERM_GROUP2_ENTER_block,1}, {0,0,N,1}, * Heading TERMGROUP denotes a group. We see 2 groups here: - * 1. group 0 -- TERM_GROUP1_block (check block) + * 1. group 0 -- AirportBlock::TermGroup1 (check block) * 2. group 1 -- TERM_GROUP2_ENTER_block (check block) - * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it + * First in line is checked first, group 0. If the block (AirportBlock::TermGroup1) is free, it * looks at the corresponding terminals of that group. If no free ones are found, other * possible groups are checked (in this case group 1, since that is after group 0). If that * fails, then attempt fails and plane waits */ if (apc->terminals[0] > 1) { const Station *st = Station::Get(v->targetairport); - const AirportFTA *temp = apc->layout[v->pos].next; + const AirportFTA *temp = apc->layout[v->pos].next.get(); while (temp != nullptr) { if (temp->heading == TERMGROUP) { - if (!(st->airport.flags & temp->block)) { + if (!st->airport.blocks.Any(temp->blocks)) { /* read which group do we want to go to? * (the first free group) */ uint target_group = temp->next_position + 1; @@ -2025,7 +2020,7 @@ static bool AirportFindFreeTerminal(Aircraft *v, const AirportFTAClass *apc) * So we cannot move */ return false; } - temp = temp->next; + temp = temp->next.get(); } } @@ -2063,8 +2058,7 @@ static void AircraftHandleDestTooFar(Aircraft *v, bool too_far) AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index)); if (v->owner == _local_company) { /* Post a news message. */ - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index); + AddVehicleAdviceNewsItem(AdviceType::AircraftDestinationTooFar, GetEncodedString(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index), v->index); } } return; @@ -2074,17 +2068,17 @@ static void AircraftHandleDestTooFar(Aircraft *v, bool too_far) /* Not too far anymore, clear flag and message. */ ClrBit(v->flags, VAF_DEST_TOO_FAR); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); - DeleteVehicleNews(v->index, STR_NEWS_AIRCRAFT_DEST_TOO_FAR); + DeleteVehicleNews(v->index, AdviceType::AircraftDestinationTooFar); } } static bool AircraftEventHandler(Aircraft *v, int loop) { - if (v->vehstatus & VS_CRASHED) { + if (v->vehstatus.Test(VehState::Crashed)) { return HandleCrashedAircraft(v); } - if (v->vehstatus & VS_STOPPED) return true; + if (v->vehstatus.Test(VehState::Stopped)) return true; v->HandleBreakdown(); @@ -2101,7 +2095,7 @@ static bool AircraftEventHandler(Aircraft *v, int loop) /* Check the distance to the next destination. This code works because the target * airport is only updated after take off and not on the ground. */ Station *cur_st = Station::GetIfValid(v->targetairport); - Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination()) : nullptr; + Station *next_st = v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_GOTO_DEPOT) ? Station::GetIfValid(v->current_order.GetDestination().ToStationID()) : nullptr; if (cur_st != nullptr && cur_st->airport.tile != INVALID_TILE && next_st != nullptr && next_st->airport.tile != INVALID_TILE) { uint dist = DistanceSquare(cur_st->airport.tile, next_st->airport.tile); @@ -2122,7 +2116,7 @@ bool Aircraft::Tick() this->tick_counter++; - if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++; + if (!this->vehstatus.Test(VehState::Stopped)) this->running_ticks++; if (this->subtype == AIR_HELICOPTER) HelicopterTickHandler(this); diff --git a/src/aircraft_cmd.h b/src/aircraft_cmd.h index df58739a54..7f8c372cff 100644 --- a/src/aircraft_cmd.h +++ b/src/aircraft_cmd.h @@ -14,6 +14,6 @@ #include "engine_type.h" #include "vehicle_type.h" -CommandCost CmdBuildAircraft(DoCommandFlag flags, TileIndex tile, const Engine *e, Vehicle **v); +CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **v); #endif /* AIRCRAFT_CMD_H */ diff --git a/src/aircraft_gui.cpp b/src/aircraft_gui.cpp index ef04b1187d..47a9c65a49 100644 --- a/src/aircraft_gui.cpp +++ b/src/aircraft_gui.cpp @@ -34,19 +34,18 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r) int y = r.top; for (const Aircraft *u = v; u != nullptr; u = u->Next()) { if (u->IsNormalAircraft()) { - SetDParam(0, PackEngineNameDParam(u->engine_type, EngineNameContext::VehicleDetails)); - SetDParam(1, u->build_year); - SetDParam(2, u->value); - if (_settings_client.gui.newgrf_developer_tools) SetDParam(3, v->index); // CM - DrawString(r.left, r.right, y, _settings_client.gui.newgrf_developer_tools ? CM_STR_VEHICLE_INFO_BUILT_VALUE_WITH_ID : STR_VEHICLE_INFO_BUILT_VALUE); + if (_settings_client.gui.newgrf_developer_tools) { + DrawString(r.left, r.right, y, GetString(CM_STR_VEHICLE_INFO_BUILT_VALUE_WITH_ID, PackEngineNameDParam(u->engine_type, EngineNameContext::VehicleDetails), u->build_year, u->value, v->index)); + } else { + DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_BUILT_VALUE, PackEngineNameDParam(u->engine_type, EngineNameContext::VehicleDetails), u->build_year, u->value)); + } y += GetCharacterHeight(FS_NORMAL); - SetDParam(0, u->cargo_type); - SetDParam(1, u->cargo_cap); - SetDParam(2, u->Next()->cargo_type); - SetDParam(3, u->Next()->cargo_cap); - SetDParam(4, GetCargoSubtypeText(u)); - DrawString(r.left, r.right, y, (u->Next()->cargo_cap != 0) ? STR_VEHICLE_INFO_CAPACITY_CAPACITY : STR_VEHICLE_INFO_CAPACITY); + if (u->Next()->cargo_cap != 0) { + DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_CAPACITY_CAPACITY, u->cargo_type, u->cargo_cap, u->Next()->cargo_type, u->Next()->cargo_cap, GetCargoSubtypeText(u))); + } else { + DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_CAPACITY, u->cargo_type, u->cargo_cap, GetCargoSubtypeText(u))); + } y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } @@ -55,10 +54,7 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r) if (cargo_count != 0) { /* Cargo names (fix pluralness) */ - SetDParam(0, u->cargo_type); - SetDParam(1, cargo_count); - SetDParam(2, u->cargo.GetFirstStation()); - DrawString(r.left, r.right, y, STR_VEHICLE_DETAILS_CARGO_FROM); + DrawString(r.left, r.right, y, GetString(STR_VEHICLE_DETAILS_CARGO_FROM, u->cargo_type, cargo_count, u->cargo.GetFirstStation())); y += GetCharacterHeight(FS_NORMAL); feeder_share += u->cargo.GetFeederShare(); } @@ -66,8 +62,7 @@ void DrawAircraftDetails(const Aircraft *v, const Rect &r) } y += WidgetDimensions::scaled.vsep_normal; - SetDParam(0, feeder_share); - DrawString(r.left, r.right, y, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE); + DrawString(r.left, r.right, y, GetString(STR_VEHICLE_INFO_FEEDER_CARGO_VALUE, feeder_share)); } @@ -96,8 +91,8 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng int heli_offs = 0; - PaletteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - seq.Draw(x, y, pal, (v->vehstatus & VS_CRASHED) != 0); + PaletteID pal = v->vehstatus.Test(VehState::Crashed) ? PALETTE_CRASH : GetVehiclePalette(v); + seq.Draw(x, y, pal, v->vehstatus.Test(VehState::Crashed)); /* Aircraft can store cargo in their shadow, show this if present. */ const Vehicle *u = v->Next(); @@ -121,6 +116,6 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng x += x_offs; y += UnScaleGUI(rect.top) - heli_offs; Rect hr = {x, y, x + width - 1, y + UnScaleGUI(rect.Height()) + heli_offs - 1}; - DrawFrameRect(hr.Expand(WidgetDimensions::scaled.bevel), COLOUR_WHITE, FR_BORDERONLY); + DrawFrameRect(hr.Expand(WidgetDimensions::scaled.bevel), COLOUR_WHITE, FrameFlag::BorderOnly); } } diff --git a/src/airport.cpp b/src/airport.cpp index a1e68b0629..1f40a2a9d6 100644 --- a/src/airport.cpp +++ b/src/airport.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "station_base.h" + #include "table/strings.h" #include "table/airport_movement.h" #include "table/airporttile_ids.h" @@ -35,7 +36,7 @@ * @param short_strip Airport has a short land/take-off strip. */ #define AIRPORT(name, num_helipads, short_strip) \ - AIRPORT_GENERIC(name, _airport_terminal_ ## name, num_helipads, AirportFTAClass::ALL | (short_strip ? AirportFTAClass::SHORT_STRIP : (AirportFTAClass::Flags)0), 0) + AIRPORT_GENERIC(name, _airport_terminal_ ## name, num_helipads, AirportFTAClass::Flags({AirportFTAClass::Flag::Airplanes, AirportFTAClass::Flag::Helicopters}) | (short_strip ? AirportFTAClass::Flags{AirportFTAClass::Flag::ShortStrip} : AirportFTAClass::Flags{}), 0) /** * Define a heliport. @@ -44,7 +45,7 @@ * @param delta_z Height of the airport above the land. */ #define HELIPORT(name, num_helipads, delta_z) \ - AIRPORT_GENERIC(name, nullptr, num_helipads, AirportFTAClass::HELICOPTERS, delta_z) + AIRPORT_GENERIC(name, nullptr, num_helipads, AirportFTAClass::Flag::Helicopters, delta_z) AIRPORT(country, 0, true) AIRPORT(city, 0, false) @@ -56,7 +57,7 @@ HELIPORT(helidepot, 1, 0) AIRPORT(intercontinental, 2, false) HELIPORT(helistation, 3, 0) HELIPORT(oilrig, 1, 54) -AIRPORT_GENERIC(dummy, nullptr, 0, AirportFTAClass::ALL, 0) +AIRPORT_GENERIC(dummy, nullptr, 0, AirportFTAClass::Flags({AirportFTAClass::Flag::Airplanes, AirportFTAClass::Flag::Helicopters}), 0) #undef HELIPORT #undef AIRPORT @@ -66,7 +67,7 @@ AIRPORT_GENERIC(dummy, nullptr, 0, AirportFTAClass::ALL, 0) static uint16_t AirportGetNofElements(const AirportFTAbuildup *apFA); -static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA); +static void AirportBuildAutomata(std::vector &layout, uint8_t nofelements, const AirportFTAbuildup *apFA); /** @@ -80,7 +81,7 @@ static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildu AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Direction rotation, uint num_tiles_x, uint num_tiles_y) { AirportMovingData amd; - amd.flag = orig->flag; + amd.flags = orig->flags; amd.direction = ChangeDir(orig->direction, (DirDiff)rotation); switch (rotation) { case DIR_N: @@ -126,20 +127,7 @@ AirportFTAClass::AirportFTAClass( delta_z(delta_z_) { /* Build the state machine itself */ - this->layout = AirportBuildAutomata(this->nofelements, apFA); -} - -AirportFTAClass::~AirportFTAClass() -{ - for (uint i = 0; i < nofelements; i++) { - AirportFTA *current = layout[i].next; - while (current != nullptr) { - AirportFTA *next = current->next; - free(current); - current = next; - } - } - free(layout); + AirportBuildAutomata(this->layout, this->nofelements, apFA); } /** @@ -162,41 +150,32 @@ static uint16_t AirportGetNofElements(const AirportFTAbuildup *apFA) return nofelements; } +AirportFTA::AirportFTA(const AirportFTAbuildup &buildup) : blocks(buildup.blocks), position(buildup.position), next_position(buildup.next), heading(buildup.heading) +{ +} + /** * Construct the FTA given a description. + * @param layout The vector to write the automata to. * @param nofelements The number of elements in the FTA. * @param apFA The description of the FTA. - * @return The FTA describing the airport. */ -static AirportFTA *AirportBuildAutomata(uint nofelements, const AirportFTAbuildup *apFA) +static void AirportBuildAutomata(std::vector &layout, uint8_t nofelements, const AirportFTAbuildup *apFA) { - AirportFTA *FAutomata = MallocT(nofelements); uint16_t internalcounter = 0; + layout.reserve(nofelements); for (uint i = 0; i < nofelements; i++) { - AirportFTA *current = &FAutomata[i]; - current->position = apFA[internalcounter].position; - current->heading = apFA[internalcounter].heading; - current->block = apFA[internalcounter].block; - current->next_position = apFA[internalcounter].next; + AirportFTA *current = &layout.emplace_back(apFA[internalcounter]); /* outgoing nodes from the same position, create linked list */ while (current->position == apFA[internalcounter + 1].position) { - AirportFTA *newNode = MallocT(1); - - newNode->position = apFA[internalcounter + 1].position; - newNode->heading = apFA[internalcounter + 1].heading; - newNode->block = apFA[internalcounter + 1].block; - newNode->next_position = apFA[internalcounter + 1].next; - /* create link */ - current->next = newNode; - current = current->next; + current->next = std::make_unique(apFA[internalcounter + 1]); + current = current->next.get(); internalcounter++; } - current->next = nullptr; internalcounter++; } - return FAutomata; } /** diff --git a/src/airport.h b/src/airport.h index d5df10e275..dd795bb943 100644 --- a/src/airport.h +++ b/src/airport.h @@ -25,7 +25,7 @@ static const uint NEW_AIRPORTTILE_OFFSET = 74; ///< offset of firs static const uint INVALID_AIRPORTTILE = NUM_AIRPORTTILES; ///< id for an invalid airport tile /** Airport types */ -enum AirportTypes { +enum AirportTypes : uint8_t { AT_SMALL = 0, ///< Small airport. AT_LARGE = 1, ///< Large airport. AT_HELIPORT = 2, ///< Heli port. @@ -44,20 +44,22 @@ enum AirportTypes { }; /** Flags for airport movement data. */ -enum AirportMovingDataFlags { - AMED_NOSPDCLAMP = 1 << 0, ///< No speed restrictions. - AMED_TAKEOFF = 1 << 1, ///< Takeoff movement. - AMED_SLOWTURN = 1 << 2, ///< Turn slowly (mostly used in the air). - AMED_LAND = 1 << 3, ///< Landing onto landing strip. - AMED_EXACTPOS = 1 << 4, ///< Go exactly to the destination coordinates. - AMED_BRAKE = 1 << 5, ///< Taxiing at the airport. - AMED_HELI_RAISE = 1 << 6, ///< Helicopter take-off. - AMED_HELI_LOWER = 1 << 7, ///< Helicopter landing. - AMED_HOLD = 1 << 8, ///< Holding pattern movement (above the airport). +enum AirportMovingDataFlag : uint8_t { + NoSpeedClamp, ///< No speed restrictions. + Takeoff, ///< Takeoff movement. + SlowTurn, ///< Turn slowly (mostly used in the air). + Land, ///< Landing onto landing strip. + ExactPosition, ///< Go exactly to the destination coordinates. + Brake, ///< Taxiing at the airport. + HeliRaise, ///< Helicopter take-off. + HeliLower, ///< Helicopter landing. + Hold, ///< Holding pattern movement (above the airport). }; +using AirportMovingDataFlags = EnumBitSet; + /** Movement States on Airports (headings target) */ -enum AirportMovementStates { +enum AirportMovementStates : uint8_t { TO_ALL = 0, ///< Go in this direction for every target. HANGAR = 1, ///< Heading for hangar. TERM1 = 2, ///< Heading for terminal 1. @@ -85,53 +87,55 @@ enum AirportMovementStates { }; /** Movement Blocks on Airports blocks (eg_airport_flags). */ -static const uint64_t - TERM1_block = 1ULL << 0, ///< Block belonging to terminal 1. - TERM2_block = 1ULL << 1, ///< Block belonging to terminal 2. - TERM3_block = 1ULL << 2, ///< Block belonging to terminal 3. - TERM4_block = 1ULL << 3, ///< Block belonging to terminal 4. - TERM5_block = 1ULL << 4, ///< Block belonging to terminal 5. - TERM6_block = 1ULL << 5, ///< Block belonging to terminal 6. - HELIPAD1_block = 1ULL << 6, ///< Block belonging to helipad 1. - HELIPAD2_block = 1ULL << 7, ///< Block belonging to helipad 2. - RUNWAY_IN_OUT_block = 1ULL << 8, - RUNWAY_IN_block = 1ULL << 8, - AIRPORT_BUSY_block = 1ULL << 8, - RUNWAY_OUT_block = 1ULL << 9, - TAXIWAY_BUSY_block = 1ULL << 10, - OUT_WAY_block = 1ULL << 11, - IN_WAY_block = 1ULL << 12, - AIRPORT_ENTRANCE_block = 1ULL << 13, - TERM_GROUP1_block = 1ULL << 14, - TERM_GROUP2_block = 1ULL << 15, - HANGAR2_AREA_block = 1ULL << 16, - TERM_GROUP2_ENTER1_block = 1ULL << 17, - TERM_GROUP2_ENTER2_block = 1ULL << 18, - TERM_GROUP2_EXIT1_block = 1ULL << 19, - TERM_GROUP2_EXIT2_block = 1ULL << 20, - PRE_HELIPAD_block = 1ULL << 21, +enum class AirportBlock : uint8_t { + Term1 = 0, ///< Block belonging to terminal 1. + Term2 = 1, ///< Block belonging to terminal 2. + Term3 = 2, ///< Block belonging to terminal 3. + Term4 = 3, ///< Block belonging to terminal 4. + Term5 = 4, ///< Block belonging to terminal 5. + Term6 = 5, ///< Block belonging to terminal 6. + Helipad1 = 6, ///< Block belonging to helipad 1. + Helipad2 = 7, ///< Block belonging to helipad 2. + RunwayInOut = 8, + RunwayIn = 8, + AirportBusy = 8, + RunwayOut = 9, + TaxiwayBusy = 10, + OutWay = 11, + InWay = 12, + AirportEntrance = 13, + TermGroup1 = 14, + TermGroup2 = 15, + Hangar2Area = 16, + TermGroup2Enter1 = 17, + TermGroup2Enter2 = 18, + TermGroup2Exit1 = 19, + TermGroup2Exit2 = 20, + PreHelipad = 21, /* blocks for new airports */ - TERM7_block = 1ULL << 22, ///< Block belonging to terminal 7. - TERM8_block = 1ULL << 23, ///< Block belonging to terminal 8. - HELIPAD3_block = 1ULL << 24, ///< Block belonging to helipad 3. - HANGAR1_AREA_block = 1ULL << 26, - OUT_WAY2_block = 1ULL << 27, - IN_WAY2_block = 1ULL << 28, - RUNWAY_IN2_block = 1ULL << 29, - RUNWAY_OUT2_block = 1ULL << 10, ///< @note re-uses #TAXIWAY_BUSY_block - HELIPAD_GROUP_block = 1ULL << 13, ///< @note re-uses #AIRPORT_ENTRANCE_block - OUT_WAY_block2 = 1ULL << 31, + Term7 = 22, ///< Block belonging to terminal 7. + Term8 = 23, ///< Block belonging to terminal 8. + Helipad3 = 24, ///< Block belonging to helipad 3. + Hangar1Area = 26, + OutWay2 = 27, + InWay2 = 28, + RunwayIn2 = 29, + RunwayOut2 = 10, ///< @note re-uses #AirportBlock::TaxiwayBusy + HelipadGroup = 13, ///< @note re-uses #AirportBlock::AirportEntrance + OutWay3 = 31, /* end of new blocks */ - NOTHING_block = 1ULL << 30, - AIRPORT_CLOSED_block = 1ULL << 63; ///< Dummy block for indicating a closed airport. + Nothing = 30, + AirportClosed = 63, ///< Dummy block for indicating a closed airport. +}; +using AirportBlocks = EnumBitSet; /** A single location on an airport where aircraft can move to. */ struct AirportMovingData { int16_t x; ///< x-coordinate of the destination. int16_t y; ///< y-coordinate of the destination. - uint16_t flag; ///< special flags when moving towards the destination. + AirportMovingDataFlags flags; ///< special flags when moving towards the destination. Direction direction; ///< Direction to turn the aircraft after reaching the destination. }; @@ -139,16 +143,27 @@ AirportMovingData RotateAirportMovingData(const AirportMovingData *orig, Directi struct AirportFTAbuildup; +/** Internal structure used in openttd - Finite sTate mAchine --> FTA */ +struct AirportFTA { + AirportFTA(const AirportFTAbuildup&); + + std::unique_ptr next; ///< possible extra movement choices from this position + AirportBlocks blocks; ///< bitmap of blocks that could be reserved + uint8_t position; ///< the position that an airplane is at + uint8_t next_position; ///< next position from this position + uint8_t heading; ///< heading (current orders), guiding an airplane to its target on an airport +}; + /** Finite sTate mAchine (FTA) of an airport. */ struct AirportFTAClass { public: /** Bitmask of airport flags. */ - enum Flags { - AIRPLANES = 0x1, ///< Can planes land on this airport type? - HELICOPTERS = 0x2, ///< Can helicopters land on this airport type? - ALL = AIRPLANES | HELICOPTERS, ///< Mask to check for both planes and helicopters. - SHORT_STRIP = 0x4, ///< This airport has a short landing strip, dangerous for fast aircraft. + enum class Flag : uint8_t { + Airplanes = 0, ///< Can planes land on this airport type? + Helicopters = 1, ///< Can helicopters land on this airport type? + ShortStrip = 2, ///< This airport has a short landing strip, dangerous for fast aircraft. }; + using Flags = EnumBitSet; AirportFTAClass( const AirportMovingData *moving_data, @@ -160,8 +175,6 @@ public: uint8_t delta_z ); - ~AirportFTAClass(); - /** * Get movement data at a position. * @param position Element number to get movement data about. @@ -174,7 +187,7 @@ public: } const AirportMovingData *moving_data; ///< Movement data. - struct AirportFTA *layout; ///< state machine for airport + std::vector layout; ///< state machine for airport const uint8_t *terminals; ///< %Array with the number of terminal groups, followed by the number of terminals in each group. const uint8_t num_helipads; ///< Number of helipads on this airport. When 0 helicopters will go to normal terminals. Flags flags; ///< Flags for this airport type. @@ -183,17 +196,6 @@ public: uint8_t delta_z; ///< Z adjustment for helicopter pads }; -DECLARE_ENUM_AS_BIT_SET(AirportFTAClass::Flags) - - -/** Internal structure used in openttd - Finite sTate mAchine --> FTA */ -struct AirportFTA { - AirportFTA *next; ///< possible extra movement choices from this position - uint64_t block; ///< 64 bit blocks (st->airport.flags), should be enough for the most complex airports - uint8_t position; ///< the position that an airplane is at - uint8_t next_position; ///< next position from this position - uint8_t heading; ///< heading (current orders), guiding an airplane to its target on an airport -}; const AirportFTAClass *GetAirport(const uint8_t airport_type); uint8_t GetVehiclePosOnBuild(TileIndex hangar_tile); diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index d4c1a41b04..d0eb0bc92f 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -21,6 +21,7 @@ #include "company_base.h" #include "station_type.h" #include "newgrf_airport.h" +#include "newgrf_badge.h" #include "newgrf_callbacks.h" #include "dropdown_type.h" #include "dropdown_func.h" @@ -37,6 +38,8 @@ #include "widgets/airport_widget.h" +#include "table/strings.h" + #include "citymania/cm_hotkeys.hpp" #include "citymania/cm_station_gui.hpp" @@ -78,7 +81,7 @@ static void PlaceAirport(TileIndex tile) auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, airport_type, layout, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, airport_type, layout, StationID::Invalid(), adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent); } @@ -89,14 +92,13 @@ static void PlaceAirport(TileIndex tile) /** Airport build toolbar window handler. */ struct BuildAirToolbarWindow : Window { - int last_user_action; // Last started user action. + int last_user_action = INVALID_WID_AT; // Last started user action. BuildAirToolbarWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc) { this->InitNested(window_number); this->OnInvalidateData(); if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); - this->last_user_action = INVALID_WID_AT; } void Close([[maybe_unused]] int data = 0) override @@ -208,20 +210,20 @@ struct BuildAirToolbarWindow : Window { static constexpr NWidgetPart _nested_air_toolbar_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetStringTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetMinimalSize(42, 22), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), EndContainer(), }; static WindowDesc _air_toolbar_desc( WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0, WC_BUILD_TOOLBAR, WC_NONE, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_air_toolbar_widgets, &BuildAirToolbarWindow::hotkeys ); @@ -242,9 +244,9 @@ Window *ShowBuildAirToolbar() } class BuildAirportWindow : public PickerWindowBase { - SpriteID preview_sprite; ///< Cached airport preview sprite. - int line_height; - Scrollbar *vscroll; + SpriteID preview_sprite{}; ///< Cached airport preview sprite. + int line_height = 0; + Scrollbar *vscroll = nullptr; /** Build a dropdown list of available airport classes */ static DropDownList BuildAirportClassDropDown() @@ -282,18 +284,18 @@ public: _selected_airport_index = Clamp(_selected_airport_index, -1, ac->GetSpecCount() - 1); /* Only when no valid airport was selected, we want to select the first airport. */ - bool selectFirstAirport = true; + bool select_first_airport = true; if (_selected_airport_index != -1) { const AirportSpec *as = ac->GetSpec(_selected_airport_index); if (as->IsAvailable()) { /* Ensure the airport layout is valid. */ _selected_airport_layout = Clamp(_selected_airport_layout, 0, static_cast(as->layouts.size() - 1)); - selectFirstAirport = false; + select_first_airport = false; this->UpdateSelectSize(); } } - if (selectFirstAirport) this->SelectFirstAvailableAirport(true); + if (select_first_airport) this->SelectFirstAvailableAirport(true); } void Close([[maybe_unused]] int data = 0) override @@ -302,28 +304,26 @@ public: this->PickerWindowBase::Close(); } - void SetStringParameters(WidgetID widget) const override + std::string GetWidgetString(WidgetID widget, StringID stringid) const override { switch (widget) { case WID_AP_CLASS_DROPDOWN: - SetDParam(0, AirportClass::Get(_selected_airport_class)->name); - break; + return GetString(AirportClass::Get(_selected_airport_class)->name); case WID_AP_LAYOUT_NUM: - SetDParam(0, STR_EMPTY); if (_selected_airport_index != -1) { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_LAYOUT_NAME); if (string != STR_UNDEFINED) { - SetDParam(0, string); + return GetString(string); } else if (as->layouts.size() > 1) { - SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME); - SetDParam(1, _selected_airport_layout + 1); + return GetString(STR_STATION_BUILD_AIRPORT_LAYOUT_NAME, _selected_airport_layout + 1); } } - break; + return {}; - default: break; + default: + return this->Window::GetWidgetString(widget, stringid); } } @@ -420,7 +420,7 @@ public: const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_ADDITIONAL_TEXT); if (string != STR_UNDEFINED) { - DrawStringMultiLine(r.left, r.right, r.top, r.bottom, string, TC_BLACK); + DrawStringMultiLine(r, string, TC_BLACK); } } break; @@ -432,7 +432,8 @@ public: this->DrawWidgets(); Rect r = this->GetWidget(WID_AP_ACCEPTANCE)->GetCurrentRect(); - int top = r.top; + const int bottom = r.bottom; + r.bottom = INT_MAX; // Allow overflow as we want to know the required height. if (_selected_airport_index != -1) { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); @@ -441,29 +442,28 @@ public: /* only show the station (airport) noise, if the noise option is activated */ if (_settings_game.economy.station_noise_level) { /* show the noise of the selected airport */ - SetDParam(0, as->noise_level); - DrawString(r.left, r.right, top, STR_STATION_BUILD_NOISE); - top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; + DrawString(r, GetString(STR_STATION_BUILD_NOISE, as->noise_level)); + r.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } if (_settings_game.economy.infrastructure_maintenance) { Money monthly = _price[PR_INFRASTRUCTURE_AIRPORT] * as->maintenance_cost >> 3; - SetDParam(0, monthly * 12); - DrawString(r.left, r.right, top, TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD : STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR); - top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; + DrawString(r, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD : STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR, monthly * 12)); + r.top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } /* strings such as 'Size' and 'Coverage Area' */ - top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal; - top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, true) + WidgetDimensions::scaled.vsep_normal; - top = DrawStationAuthorityText(r.left, r.right, top) + WidgetDimensions::scaled.vsep_normal; + r.top = DrawBadgeNameList(r, as->badges, GSF_AIRPORTS) + WidgetDimensions::scaled.vsep_normal; + r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal; + r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, true) + WidgetDimensions::scaled.vsep_normal; + r.top = DrawStationAuthorityText(r.left, r.right, r.top); } /* Resize background if the window is too small. * Never make the window smaller to avoid oscillating if the size change affects the acceptance. * (This is the case, if making the window bigger moves the mouse into the window.) */ - if (top > r.bottom) { - ResizeWindow(this, 0, top - r.bottom, false); + if (r.top > bottom) { + ResizeWindow(this, 0, r.top - bottom, false); } } @@ -487,7 +487,7 @@ public: int w = as->size_x; int h = as->size_y; Direction rotation = as->layouts[_selected_airport_layout].rotation; - if (rotation == DIR_E || rotation == DIR_W) Swap(w, h); + if (rotation == DIR_E || rotation == DIR_W) std::swap(w, h); SetTileSelectSize(w, h); this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout); @@ -595,36 +595,36 @@ public: static constexpr NWidgetPart _nested_build_airport_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetStringTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL, STR_NULL), SetFill(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_STATION_BUILD_AIRPORT_TOOLTIP), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_AIRPORT_SPRITE), SetFill(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL), SetFill(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetToolTip(STR_STATION_BUILD_AIRPORT_TOOLTIP), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_AIRPORT_SPRITE), SetFill(1, 0), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_AP_SCROLLBAR), EndContainer(), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_ORIENTATION), SetFill(1, 0), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetDataTip(AWV_DECREASE, STR_NULL), - NWidget(WWT_LABEL, COLOUR_GREY, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING1, STR_NULL), - NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetDataTip(AWV_INCREASE, STR_NULL), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(AWV_DECREASE), + NWidget(WWT_LABEL, INVALID_COLOUR, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), + NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetArrowWidgetTypeTip(AWV_INCREASE), EndContainer(), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0), - NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE), SetFill(1, 0), NWidget(NWID_HORIZONTAL), SetPIP(14, 0, 14), SetPIPRatio(1, 0, 1), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0), - SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), + SetStringTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0), - SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), + SetStringTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), EndContainer(), EndContainer(), EndContainer(), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_ACCEPTANCE), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_AP_ACCEPTANCE), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), EndContainer(), EndContainer(), }; @@ -632,7 +632,7 @@ static constexpr NWidgetPart _nested_build_airport_widgets[] = { static WindowDesc _build_airport_desc( WDP_AUTO, nullptr, 0, 0, WC_BUILD_STATION, WC_BUILD_TOOLBAR, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_build_airport_widgets ); diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index c63394a8c6..1f547b2615 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -29,7 +29,7 @@ static const uint MAX_ARTICULATED_PARTS = 100; ///< Maximum of articulated parts * @param front_type Front engine type * @param front Front engine * @param mirrored Returns whether the part shall be flipped. - * @return engine to add or INVALID_ENGINE + * @return engine to add or EngineID::Invalid() */ static EngineID GetNextArticulatedPart(uint index, EngineID front_type, Vehicle *front = nullptr, bool *mirrored = nullptr) { @@ -38,17 +38,17 @@ static EngineID GetNextArticulatedPart(uint index, EngineID front_type, Vehicle const Engine *front_engine = Engine::Get(front_type); uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ARTIC_ENGINE, index, 0, front_type, front); - if (callback == CALLBACK_FAILED) return INVALID_ENGINE; + if (callback == CALLBACK_FAILED) return EngineID::Invalid(); if (front_engine->GetGRF()->grf_version < 8) { /* 8 bits, bit 7 for mirroring */ callback = GB(callback, 0, 8); - if (callback == 0xFF) return INVALID_ENGINE; + if (callback == 0xFF) return EngineID::Invalid(); if (mirrored != nullptr) *mirrored = HasBit(callback, 7); callback = GB(callback, 0, 7); } else { /* 15 bits, bit 14 for mirroring */ - if (callback == 0x7FFF) return INVALID_ENGINE; + if (callback == 0x7FFF) return EngineID::Invalid(); if (mirrored != nullptr) *mirrored = HasBit(callback, 14); callback = GB(callback, 0, 14); } @@ -63,7 +63,7 @@ static EngineID GetNextArticulatedPart(uint index, EngineID front_type, Vehicle */ bool IsArticulatedEngine(EngineID engine_type) { - return HasBit(EngInfo(engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE); + return EngInfo(engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine); } /** @@ -74,7 +74,7 @@ bool IsArticulatedEngine(EngineID engine_type) */ uint CountArticulatedParts(EngineID engine_type, bool purchase_window) { - if (!HasBit(EngInfo(engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return 0; + if (!EngInfo(engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) return 0; /* If we can't allocate a vehicle now, we can't allocate it in the command * either, so it doesn't matter how many articulated parts there are. */ @@ -89,7 +89,7 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window) uint i; for (i = 1; i < MAX_ARTICULATED_PARTS; i++) { - if (GetNextArticulatedPart(i, engine_type, v) == INVALID_ENGINE) break; + if (GetNextArticulatedPart(i, engine_type, v) == EngineID::Invalid()) break; } delete v; @@ -103,18 +103,18 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window) * @param engine the EngineID of interest * @return cargo and capacity */ -static inline std::pair GetVehicleDefaultCapacity(EngineID engine) +static inline std::pair GetVehicleDefaultCapacity(EngineID engine) { const Engine *e = Engine::Get(engine); - CargoID cargo = e->CanCarryCargo() ? e->GetDefaultCargoType() : INVALID_CARGO; - return {cargo, IsValidCargoID(cargo) ? e->GetDisplayDefaultCapacity() : 0}; + CargoType cargo = e->CanCarryCargo() ? e->GetDefaultCargoType() : INVALID_CARGO; + return {cargo, IsValidCargoType(cargo) ? e->GetDisplayDefaultCapacity() : 0}; } /** * Returns all cargoes a vehicle can carry. * @param engine the EngineID of interest * @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask - * @return bit set of CargoIDs + * @return bit set of CargoTypes */ static inline CargoTypes GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type) { @@ -140,19 +140,19 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine) CargoArray capacity{}; const Engine *e = Engine::Get(engine); - if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoID(cargo)) { + if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoType(cargo)) { capacity[cargo] = cap; } if (!e->IsGroundVehicle()) return capacity; - if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return capacity; + if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return capacity; for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { EngineID artic_engine = GetNextArticulatedPart(i, engine); - if (artic_engine == INVALID_ENGINE) break; + if (artic_engine == EngineID::Invalid()) break; - if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoID(cargo)) { + if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoType(cargo)) { capacity[cargo] += cap; } } @@ -170,19 +170,19 @@ CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine) CargoTypes cargoes = 0; const Engine *e = Engine::Get(engine); - if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoID(cargo) && cap > 0) { + if (auto [cargo, cap] = GetVehicleDefaultCapacity(engine); IsValidCargoType(cargo) && cap > 0) { SetBit(cargoes, cargo); } if (!e->IsGroundVehicle()) return cargoes; - if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return cargoes; + if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return cargoes; for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { EngineID artic_engine = GetNextArticulatedPart(i, engine); - if (artic_engine == INVALID_ENGINE) break; + if (artic_engine == EngineID::Invalid()) break; - if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoID(cargo) && cap > 0) { + if (auto [cargo, cap] = GetVehicleDefaultCapacity(artic_engine); IsValidCargoType(cargo) && cap > 0) { SetBit(cargoes, cargo); } } @@ -202,11 +202,11 @@ bool IsArticulatedVehicleRefittable(EngineID engine) const Engine *e = Engine::Get(engine); if (!e->IsGroundVehicle()) return false; - if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return false; + if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return false; for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { EngineID artic_engine = GetNextArticulatedPart(i, engine); - if (artic_engine == INVALID_ENGINE) break; + if (artic_engine == EngineID::Invalid()) break; if (IsEngineRefittable(artic_engine)) return true; } @@ -218,8 +218,8 @@ bool IsArticulatedVehicleRefittable(EngineID engine) * Merges the refit_masks of all articulated parts. * @param engine the first part * @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask - * @param union_mask returns bit mask of CargoIDs which are a refit option for at least one articulated part - * @param intersection_mask returns bit mask of CargoIDs which are a refit option for every articulated part (with default capacity > 0) + * @param union_mask returns bit mask of CargoTypes which are a refit option for at least one articulated part + * @param intersection_mask returns bit mask of CargoTypes which are a refit option for every articulated part (with default capacity > 0) */ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask) { @@ -229,11 +229,11 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, *intersection_mask = (veh_cargoes != 0) ? veh_cargoes : ALL_CARGOTYPES; if (!e->IsGroundVehicle()) return; - if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return; + if (!e->info.callback_mask.Test(VehicleCallbackMask::ArticEngine)) return; for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { EngineID artic_engine = GetNextArticulatedPart(i, engine); - if (artic_engine == INVALID_ENGINE) break; + if (artic_engine == EngineID::Invalid()) break; veh_cargoes = GetAvailableVehicleCargoTypes(artic_engine, include_initial_cargo_type); *union_mask |= veh_cargoes; @@ -245,7 +245,7 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, * Ors the refit_masks of all articulated parts. * @param engine the first part * @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask - * @return bit mask of CargoIDs which are a refit option for at least one articulated part + * @return bit mask of CargoTypes which are a refit option for at least one articulated part */ CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type) { @@ -258,18 +258,18 @@ CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial * Get cargo mask of all cargoes carried by an articulated vehicle. * Note: Vehicles not carrying anything are ignored * @param v the first vehicle in the chain - * @param cargo_type returns the common CargoID if needed. (INVALID_CARGO if no part is carrying something or they are carrying different things) + * @param cargo_type returns the common CargoType if needed. (INVALID_CARGO if no part is carrying something or they are carrying different things) * @return cargo mask, may be 0 if the no vehicle parts have cargo capacity */ -CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoID *cargo_type) +CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type) { CargoTypes cargoes = 0; - CargoID first_cargo = INVALID_CARGO; + CargoType first_cargo = INVALID_CARGO; do { - if (IsValidCargoID(v->cargo_type) && v->GetEngine()->CanCarryCargo()) { + if (IsValidCargoType(v->cargo_type) && v->GetEngine()->CanCarryCargo()) { SetBit(cargoes, v->cargo_type); - if (!IsValidCargoID(first_cargo)) first_cargo = v->cargo_type; + if (!IsValidCargoType(first_cargo)) first_cargo = v->cargo_type; if (first_cargo != v->cargo_type) { if (cargo_type != nullptr) { *cargo_type = INVALID_CARGO; @@ -318,8 +318,8 @@ void CheckConsistencyOfArticulatedVehicle(const Vehicle *v) /* Check whether the vehicle carries more cargoes than expected */ bool carries_more = false; - for (CargoID cid : SetCargoBitIterator(real_default_cargoes)) { - if (purchase_default_capacity[cid] == 0) { + for (CargoType cargo_type : SetCargoBitIterator(real_default_cargoes)) { + if (purchase_default_capacity[cargo_type] == 0) { carries_more = true; break; } @@ -327,7 +327,7 @@ void CheckConsistencyOfArticulatedVehicle(const Vehicle *v) /* show a warning once for each GRF after each game load */ if (real_refit_union != purchase_refit_union || real_refit_intersection != purchase_refit_intersection || carries_more) { - ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GBUG_VEH_REFIT, false); + ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GRFBug::VehRefit, false); } } @@ -338,13 +338,13 @@ void CheckConsistencyOfArticulatedVehicle(const Vehicle *v) void AddArticulatedParts(Vehicle *first) { VehicleType type = first->type; - if (!HasBit(EngInfo(first->engine_type)->callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return; + if (!EngInfo(first->engine_type)->callback_mask.Test(VehicleCallbackMask::ArticEngine)) return; Vehicle *v = first; for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { bool flip_image; EngineID engine_type = GetNextArticulatedPart(i, first->engine_type, first, &flip_image); - if (engine_type == INVALID_ENGINE) return; + if (engine_type == EngineID::Invalid()) return; /* In the (very rare) case the GRF reported wrong number of articulated parts * and we run out of available vehicles, bail out. */ @@ -397,7 +397,7 @@ void AddArticulatedParts(Vehicle *first) rv->spritenum = e_artic->u.road.image_index; if (e_artic->CanCarryCargo()) { rv->cargo_type = e_artic->GetDefaultCargoType(); - assert(IsValidCargoID(rv->cargo_type)); + assert(IsValidCargoType(rv->cargo_type)); rv->cargo_cap = e_artic->u.road.capacity; // Callback 36 is called when the consist is finished } else { rv->cargo_type = front->cargo_type; // Needed for livery selection @@ -420,10 +420,11 @@ void AddArticulatedParts(Vehicle *first) v->date_of_last_service = first->date_of_last_service; v->date_of_last_service_newgrf = first->date_of_last_service_newgrf; v->build_year = first->build_year; - v->vehstatus = first->vehstatus & ~VS_STOPPED; + v->vehstatus = first->vehstatus; + v->vehstatus.Reset(VehState::Stopped); v->cargo_subtype = 0; - v->max_age = 0; + v->max_age = CalendarTime::MIN_DATE; v->engine_type = engine_type; v->value = 0; v->sprite_cache.sprite_seq.Set(SPR_IMG_QUERY); diff --git a/src/articulated_vehicles.h b/src/articulated_vehicles.h index 91195dfe8b..54d2a78f0a 100644 --- a/src/articulated_vehicles.h +++ b/src/articulated_vehicles.h @@ -19,7 +19,7 @@ CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine); void AddArticulatedParts(Vehicle *first); void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask); CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type); -CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoID *cargo_type); +CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoType *cargo_type); bool IsArticulatedVehicleRefittable(EngineID engine); bool IsArticulatedEngine(EngineID engine_type); void CheckConsistencyOfArticulatedVehicle(const Vehicle *v); diff --git a/src/autocompletion.cpp b/src/autocompletion.cpp index e88150c752..1a643f8c05 100644 --- a/src/autocompletion.cpp +++ b/src/autocompletion.cpp @@ -21,7 +21,7 @@ bool AutoCompletion::AutoComplete() { // We are pressing TAB for the first time after reset. if (this->suggestions.empty()) { - this->InitSuggestions(this->textbuf->buf); + this->InitSuggestions(this->textbuf->GetText()); if (this->suggestions.empty()) { return false; } diff --git a/src/autoreplace.cpp b/src/autoreplace.cpp index 977b5e674a..865266ecd4 100644 --- a/src/autoreplace.cpp +++ b/src/autoreplace.cpp @@ -59,13 +59,13 @@ void RemoveAllEngineReplacement(EngineRenewList *erl) * @param engine Engine type to be replaced. * @param group The group related to this replacement. * @param[out] replace_when_old Set to true if the replacement should be done when old. - * @return The engine type to replace with, or INVALID_ENGINE if no + * @return The engine type to replace with, or EngineID::Invalid() if no * replacement is in the list. */ EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old) { const EngineRenew *er = GetEngineReplacement(erl, engine, group); - if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !HasBit(Group::Get(group)->flags, GroupFlags::GF_REPLACE_PROTECTION)))) { + if (er == nullptr && (group == DEFAULT_GROUP || (Group::IsValidID(group) && !Group::Get(group)->flags.Test(GroupFlag::ReplaceProtection)))) { /* We didn't find anything useful in the vehicle's own group so we will try ALL_GROUP */ er = GetEngineReplacement(erl, engine, ALL_GROUP); } @@ -81,7 +81,7 @@ EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, *replace_when_old = er->replace_when_old; } } - return er == nullptr ? INVALID_ENGINE : er->to; + return er == nullptr ? EngineID::Invalid() : er->to; } /** @@ -94,12 +94,12 @@ EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ -CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags) +CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlags flags) { /* Check if the old vehicle is already in the list */ EngineRenew *er = GetEngineReplacement(*erl, old_engine, group); if (er != nullptr) { - if (flags & DC_EXEC) { + if (flags.Test(DoCommandFlag::Execute)) { er->to = new_engine; er->replace_when_old = replace_when_old; } @@ -108,14 +108,9 @@ CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, Engi if (!EngineRenew::CanAllocateItem()) return CMD_ERROR; - if (flags & DC_EXEC) { - er = new EngineRenew(old_engine, new_engine); - er->group_id = group; - er->replace_when_old = replace_when_old; - + if (flags.Test(DoCommandFlag::Execute)) { /* Insert before the first element */ - er->next = (EngineRenew *)(*erl); - *erl = (EngineRenewList)er; + *erl = new EngineRenew(old_engine, new_engine, group, replace_when_old, *erl); } return CommandCost(); @@ -129,14 +124,14 @@ CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, Engi * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ -CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags) +CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlags flags) { EngineRenew *er = (EngineRenew *)(*erl); EngineRenew *prev = nullptr; while (er != nullptr) { if (er->from == engine && er->group_id == group) { - if (flags & DC_EXEC) { + if (flags.Test(DoCommandFlag::Execute)) { if (prev == nullptr) { // First element /* The second becomes the new first element */ *erl = (EngineRenewList)er->next; diff --git a/src/autoreplace_base.h b/src/autoreplace_base.h index 3c9c32d893..07fc1a6a70 100644 --- a/src/autoreplace_base.h +++ b/src/autoreplace_base.h @@ -15,14 +15,14 @@ #include "engine_type.h" #include "group_type.h" -typedef uint16_t EngineRenewID; +using EngineRenewID = PoolID; /** * Memory pool for engine renew elements. DO NOT USE outside of engine.c. Is * placed here so the only exception to this rule, the saveload code, can use * it. */ -typedef Pool EngineRenewPool; +using EngineRenewPool = Pool; extern EngineRenewPool _enginerenew_pool; /** @@ -31,13 +31,15 @@ extern EngineRenewPool _enginerenew_pool; * it. */ struct EngineRenew : EngineRenewPool::PoolItem<&_enginerenew_pool> { - EngineID from; - EngineID to; - EngineRenew *next; - GroupID group_id; - bool replace_when_old; ///< Do replacement only when vehicle is old. + EngineID from = EngineID::Invalid(); + EngineID to = EngineID::Invalid(); + EngineRenew *next = nullptr; + GroupID group_id = GroupID::Invalid(); + bool replace_when_old = false; ///< Do replacement only when vehicle is old. - EngineRenew(EngineID from = INVALID_ENGINE, EngineID to = INVALID_ENGINE) : from(from), to(to) {} + EngineRenew() {} + EngineRenew(EngineID from, EngineID to, GroupID group_id, bool replace_when_old, EngineRenew *next) : + from(from), to(to), next(next), group_id(group_id), replace_when_old(replace_when_old) {} ~EngineRenew() {} }; diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 757f18d371..668360f6e7 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -71,7 +71,7 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) switch (type) { case VEH_TRAIN: { /* make sure the railtypes are compatible */ - if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false; + if (!GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes.Any(GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes)) return false; /* make sure we do not replace wagons with engines or vice versa */ if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false; @@ -80,10 +80,10 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) case VEH_ROAD: /* make sure the roadtypes are compatible */ - if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false; + if (!GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes)) return false; /* make sure that we do not replace a tram with a normal road vehicles or vice versa */ - if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false; + if (e_from->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) != e_to->info.misc_flags.Test(EngineMiscFlag::RoadIsTram)) return false; break; case VEH_AIRCRAFT: @@ -187,7 +187,7 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v; for (const Order *o : u->Orders()) { if (!o->IsRefit() || o->IsAutoRefit()) continue; - CargoID cargo_type = o->GetRefitCargo(); + CargoType cargo_type = o->GetRefitCargo(); if (!HasBit(union_refit_mask_a, cargo_type)) continue; if (!HasBit(union_refit_mask_b, cargo_type)) return false; @@ -229,14 +229,14 @@ static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID * CARGO_NO_REFIT is returned if no refit is needed * INVALID_CARGO is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible */ -static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain) +static CargoType GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain) { CargoTypes available_cargo_types, union_mask; GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types); if (union_mask == 0) return CARGO_NO_REFIT; // Don't try to refit an engine with no cargo capacity - CargoID cargo_type; + CargoType cargo_type; CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type); if (!HasAtMostOneBit(cargo_mask)) { CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type); @@ -247,7 +247,7 @@ static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool return INVALID_CARGO; // We cannot refit to mixed cargoes in an automated way } - if (!IsValidCargoID(cargo_type)) { + if (!IsValidCargoType(cargo_type)) { if (v->type != VEH_TRAIN) return CARGO_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine. if (!part_of_chain) return CARGO_NO_REFIT; @@ -276,14 +276,14 @@ static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool * @param v The vehicle to find a replacement for * @param c The vehicle's owner (it's faster to forward the pointer than refinding it) * @param always_replace Always replace, even if not old. - * @param[out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found + * @param[out] e the EngineID of the replacement. EngineID::Invalid() if no replacement is found * @return Error if the engine to build is not available */ static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e) { assert(v->type != VEH_TRAIN || !v->IsArticulatedPart()); - e = INVALID_ENGINE; + e = EngineID::Invalid(); if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) { /* we build the rear ends of multiheaded trains with the front ones */ @@ -292,10 +292,10 @@ static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool alw bool replace_when_old; e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old); - if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = INVALID_ENGINE; + if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = EngineID::Invalid(); /* Autoreplace, if engine is available */ - if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_company)) { + if (e != EngineID::Invalid() && IsEngineBuildable(e, v->type, _current_company)) { return CommandCost(); } @@ -303,7 +303,7 @@ static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool alw if (v->NeedsAutorenewing(c)) e = v->engine_type; /* Nothing to do or all is fine? */ - if (e == INVALID_ENGINE || IsEngineBuildable(e, v->type, _current_company)) return CommandCost(); + if (e == EngineID::Invalid() || IsEngineBuildable(e, v->type, _current_company)) return CommandCost(); /* The engine we need is not available. Report error to user */ return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type); @@ -318,7 +318,7 @@ static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool alw * @param flags The calling command flags. * @return cost or error */ -static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlag flags) +static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, DoCommandFlags flags) { *new_vehicle = nullptr; @@ -327,34 +327,38 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic EngineID e; CommandCost cost = GetNewEngineType(old_veh, c, true, e); if (cost.Failed()) return cost; - if (e == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered + if (e == EngineID::Invalid()) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered /* Does it need to be refitted */ - CargoID refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain); - if (!IsValidCargoID(refit_cargo)) { - if (!IsLocalCompany() || (flags & DC_EXEC) == 0) return CommandCost(); + CargoType refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain); + if (!IsValidCargoType(refit_cargo)) { + if (!IsLocalCompany() || !flags.Test(DoCommandFlag::Execute)) return CommandCost(); VehicleID old_veh_id = (old_veh->type == VEH_TRAIN) ? Train::From(old_veh)->First()->index : old_veh->index; - SetDParam(0, old_veh_id); + EncodedString headline; int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e); if (order_id != -1) { /* Orders contained a refit order that is incompatible with the new vehicle. */ - SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT); - SetDParam(2, order_id + 1); // 1-based indexing for display + headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED, + old_veh_id, + STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT, + order_id + 1); // 1-based indexing for display } else { /* Current cargo is incompatible with the new vehicle. */ - SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO); - SetDParam(2, CargoSpec::Get(old_veh->cargo_type)->name); + headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED, + old_veh_id, + STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO, + CargoSpec::Get(old_veh->cargo_type)->name); } - AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_AUTORENEW_FAILED, old_veh_id); + AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, std::move(headline), old_veh_id); return CommandCost(); } /* Build the new vehicle */ VehicleID new_veh_id; - std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command::Do(DC_EXEC | DC_AUTOREPLACE, old_veh->tile, e, true, INVALID_CARGO, INVALID_CLIENT_ID); + std::tie(cost, new_veh_id, std::ignore, std::ignore, std::ignore) = Command::Do({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, old_veh->tile, e, true, INVALID_CARGO, INVALID_CLIENT_ID); if (cost.Failed()) return cost; Vehicle *new_veh = Vehicle::Get(new_veh_id); @@ -364,13 +368,13 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic if (refit_cargo != CARGO_NO_REFIT) { uint8_t subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo); - cost.AddCost(std::get<0>(Command::Do(DC_EXEC, new_veh->index, refit_cargo, subtype, false, false, 0))); + cost.AddCost(std::get<0>(Command::Do(DoCommandFlag::Execute, new_veh->index, refit_cargo, subtype, false, false, 0))); assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace() } /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */ if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) { - Command::Do(DC_EXEC, new_veh->index, true); + Command::Do(DoCommandFlag::Execute, new_veh->index, true); } return cost; @@ -384,7 +388,7 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic */ static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_callback) { - return Command::Do(DC_EXEC | DC_AUTOREPLACE, v->index, evaluate_callback); + return Command::Do({DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, v->index, evaluate_callback); } /** @@ -395,9 +399,9 @@ static inline CommandCost DoCmdStartStopVehicle(const Vehicle *v, bool evaluate_ * @param whole_chain move all vehicles following 'v' (true), or only 'v' (false) * @return success or error */ -static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain) +static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlags flags, bool whole_chain) { - return Command::Do(flags | DC_NO_CARGO_CAP_CHECK, v->index, after != nullptr ? after->index : INVALID_VEHICLE, whole_chain); + return Command::Do(flags.Set(DoCommandFlag::NoCargoCapacityCheck), v->index, after != nullptr ? after->index : VehicleID::Invalid(), whole_chain); } /** @@ -406,20 +410,20 @@ static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, * @param new_head The new head of the completely replaced vehicle chain * @param flags the command flags to use */ -static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags) +static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlags flags) { CommandCost cost = CommandCost(); /* Share orders */ - if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command::Do(DC_EXEC, CO_SHARE, new_head->index, old_head->index)); + if (cost.Succeeded() && old_head != new_head) cost.AddCost(Command::Do(DoCommandFlag::Execute, CO_SHARE, new_head->index, old_head->index)); /* Copy group membership */ - if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command::Do(DC_EXEC, old_head->group_id, new_head->index, false, VehicleListIdentifier{}))); + if (cost.Succeeded() && old_head != new_head) cost.AddCost(std::get<0>(Command::Do(DoCommandFlag::Execute, old_head->group_id, new_head->index, false, VehicleListIdentifier{}))); /* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */ if (cost.Succeeded()) { /* Start the vehicle, might be denied by certain things */ - assert((new_head->vehstatus & VS_STOPPED) != 0); + assert(new_head->vehstatus.Test(VehState::Stopped)); cost.AddCost(DoCmdStartStopVehicle(new_head, true)); /* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */ @@ -427,7 +431,7 @@ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, } /* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */ - if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) { + if (cost.Succeeded() && old_head != new_head && flags.Test(DoCommandFlag::Execute)) { /* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */ new_head->CopyVehicleConfigAndStatistics(old_head); GroupStatistics::AddProfitLastYear(new_head); @@ -448,7 +452,7 @@ static CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, * @param nothing_to_do is set to 'false' when something was done (only valid when not failed) * @return cost or error */ -static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, bool *nothing_to_do) +static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlags flags, bool *nothing_to_do) { Train *old_v = Train::From(*single_unit); assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded()); @@ -463,9 +467,9 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b if (cost.Succeeded() && new_v != nullptr) { *nothing_to_do = false; - if ((flags & DC_EXEC) != 0) { + if (flags.Test(DoCommandFlag::Execute)) { /* Move the new vehicle behind the old */ - CmdMoveVehicle(new_v, old_v, DC_EXEC, false); + CmdMoveVehicle(new_v, old_v, DoCommandFlag::Execute, false); /* Take over cargo * Note: We do only transfer cargo from the old to the new vehicle. @@ -483,9 +487,9 @@ static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, b /* Sell the old vehicle */ cost.AddCost(Command::Do(flags, old_v->index, false, false, INVALID_CLIENT_ID)); - /* If we are not in DC_EXEC undo everything */ - if ((flags & DC_EXEC) == 0) { - Command::Do(DC_EXEC, new_v->index, false, false, INVALID_CLIENT_ID); + /* If we are not in DoCommandFlag::Execute undo everything */ + if (!flags.Test(DoCommandFlag::Execute)) { + Command::Do(DoCommandFlag::Execute, new_v->index, false, false, INVALID_CLIENT_ID); } } @@ -515,7 +519,7 @@ struct ReplaceChainItem { * @param nothing_to_do is set to 'false' when something was done (only valid when not failed) * @return cost or error */ -static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do) +static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlags flags, bool wagon_removal, bool *nothing_to_do) { Vehicle *old_head = *chain; assert(old_head->IsPrimaryVehicle()); @@ -534,10 +538,10 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon ReplaceChainItem &replacement = replacements.emplace_back(w, nullptr, 0); CommandCost ret = BuildReplacementVehicle(replacement.old_veh, &replacement.new_veh, true, flags); - cost.AddCost(ret); + replacement.cost = ret.GetCost(); + cost.AddCost(std::move(ret)); if (cost.Failed()) break; - replacement.cost = ret.GetCost(); if (replacement.new_veh != nullptr) *nothing_to_do = false; } Vehicle *new_head = replacements.front().GetVehicle(); @@ -546,7 +550,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (cost.Succeeded()) { /* Separate the head, so we can start constructing the new chain */ Train *second = Train::From(old_head)->GetNextUnit(); - if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true)); + if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true)); assert(Train::From(new_head)->GetNextUnit() == nullptr); @@ -562,14 +566,14 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue; if (it->new_veh != nullptr) { - /* Move the old engine to a separate row with DC_AUTOREPLACE. Else + /* Move the old engine to a separate row with DoCommandFlag::AutoReplace. Else * moving the wagon in front may fail later due to unitnumber limit. - * (We have to attach wagons without DC_AUTOREPLACE.) */ - CmdMoveVehicle(it->old_veh, nullptr, DC_EXEC | DC_AUTOREPLACE, false); + * (We have to attach wagons without DoCommandFlag::AutoReplace.) */ + CmdMoveVehicle(it->old_veh, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false); } if (last_engine == nullptr) last_engine = append; - cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false)); + cost.AddCost(CmdMoveVehicle(append, new_head, DoCommandFlag::Execute, false)); if (cost.Failed()) break; } if (last_engine == nullptr) last_engine = new_head; @@ -588,17 +592,17 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) { /* Insert wagon after 'last_engine' */ - CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false); + CommandCost res = CmdMoveVehicle(append, last_engine, DoCommandFlag::Execute, false); /* When we allow removal of wagons, either the move failing due * to the train becoming too long, or the train becoming longer * would move the vehicle to the empty vehicle chain. */ if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : Train::From(new_head)->gcache.cached_total_length > old_total_length)) { - CmdMoveVehicle(append, nullptr, DC_EXEC | DC_AUTOREPLACE, false); + CmdMoveVehicle(append, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false); break; } - cost.AddCost(res); + cost.AddCost(std::move(res)); if (cost.Failed()) break; } else { /* We have reached 'last_engine', continue with the next engine towards the front */ @@ -619,7 +623,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON); /* Sell wagon */ - [[maybe_unused]] CommandCost ret = Command::Do(DC_EXEC, wagon->index, false, false, INVALID_CLIENT_ID); + [[maybe_unused]] CommandCost ret = Command::Do(DoCommandFlag::Execute, wagon->index, false, false, INVALID_CLIENT_ID); assert(ret.Succeeded()); it->new_veh = nullptr; @@ -634,7 +638,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (cost.Succeeded()) { /* Success ! */ - if ((flags & DC_EXEC) != 0 && new_head != old_head) { + if (flags.Test(DoCommandFlag::Execute) && new_head != old_head) { *chain = new_head; AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index)); } @@ -646,43 +650,43 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon * Note: We cannot test 'new_vehs[i] != nullptr' as wagon removal might cause to remove both */ if (w->First() == new_head) continue; - if ((flags & DC_EXEC) != 0) TransferCargo(w, new_head, true); + if (flags.Test(DoCommandFlag::Execute)) TransferCargo(w, new_head, true); /* Sell the vehicle. - * Note: This might temporarily construct new trains, so use DC_AUTOREPLACE to prevent + * Note: This might temporarily construct new trains, so use DoCommandFlag::AutoReplace to prevent * it from failing due to engine limits. */ - cost.AddCost(Command::Do(flags | DC_AUTOREPLACE, w->index, false, false, INVALID_CLIENT_ID)); - if ((flags & DC_EXEC) != 0) { + cost.AddCost(Command::Do(DoCommandFlags{flags}.Set(DoCommandFlag::AutoReplace), w->index, false, false, INVALID_CLIENT_ID)); + if (flags.Test(DoCommandFlag::Execute)) { it->old_veh = nullptr; if (it == std::begin(replacements)) old_head = nullptr; } } - if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head); + if (flags.Test(DoCommandFlag::Execute)) CheckCargoCapacity(new_head); } - /* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles. + /* If we are not in DoCommandFlag::Execute undo everything, i.e. rearrange old vehicles. * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time. * Note: The vehicle attach callback is disabled here :) */ - if ((flags & DC_EXEC) == 0) { + if (!flags.Test(DoCommandFlag::Execute)) { /* Separate the head, so we can reattach the old vehicles */ - Train *second = Train::From(old_head)->GetNextUnit(); - if (second != nullptr) CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true); + second = Train::From(old_head)->GetNextUnit(); + if (second != nullptr) CmdMoveVehicle(second, nullptr, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, true); assert(Train::From(old_head)->GetNextUnit() == nullptr); for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) { - [[maybe_unused]] CommandCost ret = CmdMoveVehicle(it->old_veh, old_head, DC_EXEC | DC_AUTOREPLACE, false); + [[maybe_unused]] CommandCost ret = CmdMoveVehicle(it->old_veh, old_head, {DoCommandFlag::Execute, DoCommandFlag::AutoReplace}, false); assert(ret.Succeeded()); } } } /* Finally undo buying of new vehicles */ - if ((flags & DC_EXEC) == 0) { + if (!flags.Test(DoCommandFlag::Execute)) { for (auto it = std::rbegin(replacements); it != std::rend(replacements); ++it) { if (it->new_veh != nullptr) { - Command::Do(DC_EXEC, it->new_veh->index, false, false, INVALID_CLIENT_ID); + Command::Do(DoCommandFlag::Execute, it->new_veh->index, false, false, INVALID_CLIENT_ID); it->new_veh = nullptr; } } @@ -701,7 +705,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon if (cost.Succeeded()) { /* The new vehicle is constructed, now take over cargo */ - if ((flags & DC_EXEC) != 0) { + if (flags.Test(DoCommandFlag::Execute)) { TransferCargo(old_head, new_head, true); *chain = new_head; @@ -712,9 +716,9 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon cost.AddCost(Command::Do(flags, old_head->index, false, false, INVALID_CLIENT_ID)); } - /* If we are not in DC_EXEC undo everything */ - if ((flags & DC_EXEC) == 0) { - Command::Do(DC_EXEC, new_head->index, false, false, INVALID_CLIENT_ID); + /* If we are not in DoCommandFlag::Execute undo everything */ + if (!flags.Test(DoCommandFlag::Execute)) { + Command::Do(DoCommandFlag::Execute, new_head->index, false, false, INVALID_CLIENT_ID); } } } @@ -729,7 +733,7 @@ static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon * @param veh_id Index of vehicle * @return the cost of this operation or an error */ -CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) +CommandCost CmdAutoreplaceVehicle(DoCommandFlags flags, VehicleID veh_id) { Vehicle *v = Vehicle::GetIfValid(veh_id); if (v == nullptr) return CMD_ERROR; @@ -737,7 +741,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; - if (v->vehstatus & VS_CRASHED) return CMD_ERROR; + if (v->vehstatus.Test(VehState::Crashed)) return CMD_ERROR; bool free_wagon = false; if (v->type == VEH_TRAIN) { @@ -754,7 +758,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) bool wagon_removal = c->settings.renew_keep_length; const Group *g = Group::GetIfValid(v->group_id); - if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); + if (g != nullptr) wagon_removal = g->flags.Test(GroupFlag::ReplaceWagonRemoval); /* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */ Vehicle *w = v; @@ -763,7 +767,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) EngineID e; CommandCost cost = GetNewEngineType(w, c, false, e); if (cost.Failed()) return cost; - any_replacements |= (e != INVALID_ENGINE); + any_replacements |= (e != EngineID::Invalid()); w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr); } @@ -771,7 +775,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) bool nothing_to_do = true; if (any_replacements) { - bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0); + bool was_stopped = free_wagon || v->vehstatus.Test(VehState::Stopped); /* Stop the vehicle */ if (!was_stopped) cost.AddCost(DoCmdStartStopVehicle(v, true)); @@ -781,17 +785,17 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) /* We have to construct the new vehicle chain to test whether it is valid. * Vehicle construction needs random bits, so we have to save the random seeds - * to prevent desyncs and to replay newgrf callbacks during DC_EXEC */ + * to prevent desyncs and to replay newgrf callbacks during DoCommandFlag::Execute */ SavedRandomSeeds saved_seeds; SaveRandomSeeds(&saved_seeds); if (free_wagon) { - cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do)); + cost.AddCost(ReplaceFreeUnit(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), ¬hing_to_do)); } else { - cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do)); + cost.AddCost(ReplaceChain(&v, DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), wagon_removal, ¬hing_to_do)); } RestoreRandomSeeds(saved_seeds); - if (cost.Succeeded() && (flags & DC_EXEC) != 0) { + if (cost.Succeeded() && flags.Test(DoCommandFlag::Execute)) { if (free_wagon) { ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do); } else { @@ -817,7 +821,7 @@ CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id) * @param when_old replace when engine gets old? * @return the cost of this operation or an error */ -CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old) +CommandCost CmdSetAutoReplace(DoCommandFlags flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old) { Company *c = Company::GetIfValid(_current_company); if (c == nullptr) return CMD_ERROR; @@ -828,7 +832,7 @@ CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_en if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR; if (Group::IsValidID(id_g) && Group::Get(id_g)->vehicle_type != Engine::Get(old_engine_type)->type) return CMD_ERROR; - if (new_engine_type != INVALID_ENGINE) { + if (new_engine_type != EngineID::Invalid()) { if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR; if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR; @@ -837,14 +841,14 @@ CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_en cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags); } - if (flags & DC_EXEC) { + if (flags.Test(DoCommandFlag::Execute)) { GroupStatistics::UpdateAutoreplace(_current_company); if (IsLocalCompany()) SetWindowDirty(WC_REPLACE_VEHICLE, Engine::Get(old_engine_type)->type); const VehicleType vt = Engine::Get(old_engine_type)->type; - SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack()); + SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).ToWindowNumber()); } - if ((flags & DC_EXEC) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g); + if (flags.Test(DoCommandFlag::Execute) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g); return cost; } diff --git a/src/autoreplace_cmd.h b/src/autoreplace_cmd.h index c42e740c93..5f9af672d2 100644 --- a/src/autoreplace_cmd.h +++ b/src/autoreplace_cmd.h @@ -15,10 +15,10 @@ #include "engine_type.h" #include "group_type.h" -CommandCost CmdAutoreplaceVehicle(DoCommandFlag flags, VehicleID veh_id); -CommandCost CmdSetAutoReplace(DoCommandFlag flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old); +CommandCost CmdAutoreplaceVehicle(DoCommandFlags flags, VehicleID veh_id); +CommandCost CmdSetAutoReplace(DoCommandFlags flags, GroupID id_g, EngineID old_engine_type, EngineID new_engine_type, bool when_old); -DEF_CMD_TRAIT(CMD_AUTOREPLACE_VEHICLE, CmdAutoreplaceVehicle, 0, CMDT_VEHICLE_MANAGEMENT) -DEF_CMD_TRAIT(CMD_SET_AUTOREPLACE, CmdSetAutoReplace, 0, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_AUTOREPLACE_VEHICLE, CmdAutoreplaceVehicle, {}, CMDT_VEHICLE_MANAGEMENT) +DEF_CMD_TRAIT(CMD_SET_AUTOREPLACE, CmdSetAutoReplace, {}, CMDT_VEHICLE_MANAGEMENT) #endif /* AUTOREPLACE_CMD_H */ diff --git a/src/autoreplace_func.h b/src/autoreplace_func.h index 0943985e7d..3c332ed9bd 100644 --- a/src/autoreplace_func.h +++ b/src/autoreplace_func.h @@ -15,8 +15,8 @@ void RemoveAllEngineReplacement(EngineRenewList *erl); EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group, bool *replace_when_old = nullptr); -CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags); -CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlag flags); +CommandCost AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlags flags); +CommandCost RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, DoCommandFlags flags); /** * Remove all engine replacement settings for the given company. @@ -33,7 +33,7 @@ inline void RemoveAllEngineReplacementForCompany(Company *c) * @param engine Engine type. * @param group The group related to this replacement. * @param[out] replace_when_old Set to true if the replacement should be done when old. - * @return The engine type to replace with, or INVALID_ENGINE if no + * @return The engine type to replace with, or EngineID::Invalid() if no * replacement is in the list. */ inline EngineID EngineReplacementForCompany(const Company *c, EngineID engine, GroupID group, bool *replace_when_old = nullptr) @@ -50,7 +50,7 @@ inline EngineID EngineReplacementForCompany(const Company *c, EngineID engine, G */ inline bool EngineHasReplacementForCompany(const Company *c, EngineID engine, GroupID group) { - return EngineReplacementForCompany(c, engine, group) != INVALID_ENGINE; + return EngineReplacementForCompany(c, engine, group) != EngineID::Invalid(); } /** @@ -77,7 +77,7 @@ inline bool EngineHasReplacementWhenOldForCompany(const Company *c, EngineID eng * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ -inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlag flags) +inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engine, EngineID new_engine, GroupID group, bool replace_when_old, DoCommandFlags flags) { return AddEngineReplacement(&c->engine_renew_list, old_engine, new_engine, group, replace_when_old, flags); } @@ -90,7 +90,7 @@ inline CommandCost AddEngineReplacementForCompany(Company *c, EngineID old_engin * @param flags The calling command flags. * @return 0 on success, CMD_ERROR on failure. */ -inline CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID engine, GroupID group, DoCommandFlag flags) +inline CommandCost RemoveEngineReplacementForCompany(Company *c, EngineID engine, GroupID group, DoCommandFlags flags) { return RemoveEngineReplacement(&c->engine_renew_list, engine, group, flags); } diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index 31bc580f22..aba1ba6e2a 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -10,6 +10,7 @@ #include "stdafx.h" #include "command_func.h" #include "vehicle_gui.h" +#include "newgrf_badge.h" #include "newgrf_engine.h" #include "rail.h" #include "road.h" @@ -32,6 +33,8 @@ #include "widgets/autoreplace_widget.h" +#include "table/strings.h" + #include "citymania/cm_hotkeys.hpp" #include "safeguards.h" @@ -79,18 +82,20 @@ static const StringID _start_replace_dropdown[] = { * Window for the autoreplacing of vehicles. */ class ReplaceVehicleWindow : public Window { - EngineID sel_engine[2]; ///< Selected engine left and right. - GUIEngineList engines[2]; ///< Left and right list of engines. - bool replace_engines; ///< If \c true, engines are replaced, if \c false, wagons are replaced (only for trains). - bool reset_sel_engine; ///< Also reset #sel_engine while updating left and/or right and no valid engine selected. - GroupID sel_group; ///< Group selected to replace. - int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). - uint8_t sort_criteria; ///< Criteria of sorting vehicles. - bool descending_sort_order; ///< Order of sorting vehicles. - bool show_hidden_engines; ///< Whether to show the hidden engines. - RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all. - RoadType sel_roadtype; ///< Type of road selected. #INVALID_ROADTYPE to show all. - Scrollbar *vscroll[2]; + std::array sel_engine{}; ///< Selected engine left and right. + std::array engines{}; ///< Left and right list of engines. + bool replace_engines = true; ///< If \c true, engines are replaced, if \c false, wagons are replaced (only for trains). + bool reset_sel_engine = true; ///< Also reset #sel_engine while updating left and/or right and no valid engine selected. + GroupID sel_group = GroupID::Invalid(); ///< Group selected to replace. + int details_height = 0; ///< Minimal needed height of the details panels, in text lines (found so far). + VehicleType vehicle_type = VEH_INVALID; ///< Type of vehicle in this window. + uint8_t sort_criteria = 0; ///< Criteria of sorting vehicles. + bool descending_sort_order = false; ///< Order of sorting vehicles. + bool show_hidden_engines = false; ///< Whether to show the hidden engines. + RailType sel_railtype = INVALID_RAILTYPE; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all. + RoadType sel_roadtype = INVALID_ROADTYPE; ///< Type of road selected. #INVALID_ROADTYPE to show all. + std::array vscroll{}; + GUIBadgeClasses badge_classes{}; uint cm_num_hidden_engines; @@ -122,8 +127,8 @@ class ReplaceVehicleWindow : public Window { void GenerateReplaceVehList(bool draw_left) { std::vector variants; - EngineID selected_engine = INVALID_ENGINE; - VehicleType type = (VehicleType)this->window_number; + EngineID selected_engine = EngineID::Invalid(); + VehicleType type = this->window_number; uint8_t side = draw_left ? 0 : 1; GUIEngineList list; @@ -152,7 +157,7 @@ class ReplaceVehicleWindow : public Window { const uint num_engines = GetGroupNumEngines(_local_company, this->sel_group, eid); /* Skip drawing the engines we don't have any of and haven't set for replacement */ - if (num_engines == 0 && EngineReplacementForCompany(Company::Get(_local_company), eid, this->sel_group) == INVALID_ENGINE) continue; + if (num_engines == 0 && EngineReplacementForCompany(Company::Get(_local_company), eid, this->sel_group) == EngineID::Invalid()) continue; } else { if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue; if (e->IsVariantHidden(_local_company)) { @@ -161,11 +166,11 @@ class ReplaceVehicleWindow : public Window { } } - list.emplace_back(eid, e->info.variant_id, (side == 0) ? EngineDisplayFlags::None : e->display_flags, 0); + list.emplace_back(eid, e->info.variant_id, (side == 0) ? EngineDisplayFlags{} : e->display_flags, 0); if (side == 1) { EngineID parent = e->info.variant_id; - while (parent != INVALID_ENGINE) { + while (parent != EngineID::Invalid()) { variants.push_back(parent); parent = Engine::Get(parent)->info.variant_id; } @@ -178,7 +183,7 @@ class ReplaceVehicleWindow : public Window { for (const auto &variant : variants) { if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) { const Engine *e = Engine::Get(variant); - list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); + list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0); } } } @@ -208,28 +213,28 @@ class ReplaceVehicleWindow : public Window { /* We need to rebuild the left engines list */ this->GenerateReplaceVehList(true); this->vscroll[0]->SetCount(this->engines[0].size()); - if (this->reset_sel_engine && this->sel_engine[0] == INVALID_ENGINE && !this->engines[0].empty()) { + if (this->reset_sel_engine && this->sel_engine[0] == EngineID::Invalid() && !this->engines[0].empty()) { this->sel_engine[0] = this->engines[0][0].engine_id; } } if (this->engines[1].NeedRebuild() || e != this->sel_engine[0]) { /* Either we got a request to rebuild the right engines list, or the left engines list selected a different engine */ - if (this->sel_engine[0] == INVALID_ENGINE) { + if (this->sel_engine[0] == EngineID::Invalid()) { /* Always empty the right engines list when nothing is selected in the left engines list */ this->engines[1].clear(); - this->sel_engine[1] = INVALID_ENGINE; + this->sel_engine[1] = EngineID::Invalid(); this->vscroll[1]->SetCount(this->engines[1].size()); } else { - if (this->reset_sel_engine && this->sel_engine[0] != INVALID_ENGINE) { + if (this->reset_sel_engine && this->sel_engine[0] != EngineID::Invalid()) { /* Select the current replacement for sel_engine[0]. */ const Company *c = Company::Get(_local_company); this->sel_engine[1] = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group); } - /* Regenerate the list on the right. Note: This resets sel_engine[1] to INVALID_ENGINE, if it is no longer available. */ + /* Regenerate the list on the right. Note: This resets sel_engine[1] to EngineID::Invalid(), if it is no longer available. */ this->GenerateReplaceVehList(false); this->vscroll[1]->SetCount(this->engines[1].size()); - if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) { + if (this->reset_sel_engine && this->sel_engine[1] != EngineID::Invalid()) { int position = 0; for (const auto &item : this->engines[1]) { if (item.engine_id == this->sel_engine[1]) break; @@ -274,15 +279,12 @@ class ReplaceVehicleWindow : public Window { public: ReplaceVehicleWindow(WindowDesc &desc, VehicleType vehicletype, GroupID id_g) : Window(desc) { - this->sel_railtype = INVALID_RAILTYPE; - this->sel_roadtype = INVALID_ROADTYPE; - this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool) + this->vehicle_type = vehicletype; this->engines[0].ForceRebuild(); this->engines[1].ForceRebuild(); - this->reset_sel_engine = true; this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9); - this->sel_engine[0] = INVALID_ENGINE; - this->sel_engine[1] = INVALID_ENGINE; + this->sel_engine[0] = EngineID::Invalid(); + this->sel_engine[1] = EngineID::Invalid(); this->show_hidden_engines = _engine_sort_show_hidden_engines[vehicletype]; this->CreateNestedTree(); @@ -290,8 +292,7 @@ public: this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR); NWidgetCore *widget = this->GetWidget(WID_RV_SHOW_HIDDEN_ENGINES); - widget->widget_data = CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype; - widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype; + widget->SetStringTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype); widget->SetLowered(this->show_hidden_engines); this->FinishInitNested(vehicletype); @@ -301,11 +302,16 @@ public: this->sel_group = id_g; } + void OnInit() override + { + this->badge_classes = GUIBadgeClasses(static_cast(GSF_TRAINS + this->vehicle_type)); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { case WID_RV_SORT_ASCENDING_DESCENDING: { - Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); + Dimension d = GetStringBoundingBox(this->GetWidget(widget)->GetString()); d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; size = maxdim(size, d); @@ -314,7 +320,7 @@ public: case WID_RV_LEFT_MATRIX: case WID_RV_RIGHT_MATRIX: - resize.height = GetEngineListHeight((VehicleType)this->window_number); + resize.height = GetEngineListHeight(this->window_number); size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height; break; @@ -324,11 +330,10 @@ public: break; case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { - StringID str = this->GetWidget(widget)->widget_data; - SetDParam(0, STR_CONFIG_SETTING_ON); - Dimension d = GetStringBoundingBox(str); - SetDParam(0, STR_CONFIG_SETTING_OFF); - d = maxdim(d, GetStringBoundingBox(str)); + StringID str = this->GetWidget(widget)->GetString(); + StringID group_str = Group::IsValidID(this->sel_group) ? STR_GROUP_NAME : STR_GROUP_DEFAULT_TRAINS + this->window_number; + Dimension d = GetStringBoundingBox(GetString(str, group_str, this->sel_group, STR_CONFIG_SETTING_ON)); + d = maxdim(d, GetStringBoundingBox(GetString(str, group_str, this->sel_group, STR_CONFIG_SETTING_OFF))); d.width += padding.width; d.height += padding.height; size = maxdim(size, d); @@ -386,62 +391,54 @@ public: } } - void SetStringParameters(WidgetID widget) const override + std::string GetWidgetString(WidgetID widget, StringID stringid) const override { switch (widget) { case WID_RV_CAPTION: - SetDParam(0, STR_REPLACE_VEHICLE_TRAIN + this->window_number); - switch (this->sel_group) { - case ALL_GROUP: - SetDParam(1, STR_GROUP_ALL_TRAINS + this->window_number); + switch (this->sel_group.base()) { + case ALL_GROUP.base(): + return GetString(STR_REPLACE_VEHICLES_WHITE, STR_REPLACE_VEHICLE_TRAIN + this->window_number, STR_GROUP_ALL_TRAINS + this->window_number, std::monostate{}); break; - case DEFAULT_GROUP: - SetDParam(1, STR_GROUP_DEFAULT_TRAINS + this->window_number); + case DEFAULT_GROUP.base(): + return GetString(STR_REPLACE_VEHICLES_WHITE, STR_REPLACE_VEHICLE_TRAIN + this->window_number, STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}); break; default: - SetDParam(1, STR_GROUP_NAME); - SetDParam(2, sel_group); - break; + return GetString(STR_REPLACE_VEHICLES_WHITE, STR_REPLACE_VEHICLE_TRAIN + this->window_number, STR_GROUP_NAME, sel_group); } break; case WID_RV_SORT_DROPDOWN: - SetDParam(0, std::data(_engine_sort_listing[this->window_number])[this->sort_criteria]); - break; + return GetString(std::data(_engine_sort_listing[this->window_number])[this->sort_criteria]); - case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { - bool remove_wagon; - const Group *g = Group::GetIfValid(this->sel_group); - if (g != nullptr) { - remove_wagon = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL); - SetDParam(0, STR_GROUP_NAME); - SetDParam(1, sel_group); + case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: + if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) { + bool remove_wagon = g->flags.Test(GroupFlag::ReplaceWagonRemoval); + return GetString(STR_GROUP_NAME, sel_group, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); } else { const Company *c = Company::Get(_local_company); - remove_wagon = c->settings.renew_keep_length; - SetDParam(0, STR_GROUP_DEFAULT_TRAINS + this->window_number); + bool remove_wagon = c->settings.renew_keep_length; + return GetString(STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); } - SetDParam(2, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); break; - } case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: - SetDParam(0, this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS); - break; + return GetString(this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS); case WID_RV_RAIL_TYPE_DROPDOWN: - SetDParam(0, this->sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(this->sel_railtype)->strings.replace_text); - break; + return GetString(this->sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(this->sel_railtype)->strings.replace_text); case WID_RV_ROAD_TYPE_DROPDOWN: - SetDParam(0, this->sel_roadtype == INVALID_ROADTYPE ? STR_REPLACE_ALL_ROADTYPE : GetRoadTypeInfo(this->sel_roadtype)->strings.replace_text); - break; + return GetString(this->sel_roadtype == INVALID_ROADTYPE ? STR_REPLACE_ALL_ROADTYPE : GetRoadTypeInfo(this->sel_roadtype)->strings.replace_text); case WID_RV_SHOW_HIDDEN_ENGINES: SetDParam(0, this->cm_num_hidden_engines); + TODO break; + + default: + return this->Window::GetWidgetString(widget, stringid); } } @@ -454,21 +451,20 @@ public: case WID_RV_INFO_TAB: { const Company *c = Company::Get(_local_company); - StringID str; - if (this->sel_engine[0] != INVALID_ENGINE) { + std::string str; + if (this->sel_engine[0] != EngineID::Invalid()) { if (!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)) { - str = STR_REPLACE_NOT_REPLACING; + str = GetString(STR_REPLACE_NOT_REPLACING); } else { bool when_old = false; EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old); - str = when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME; - SetDParam(0, PackEngineNameDParam(e, EngineNameContext::PurchaseList)); + str = GetString(when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME, PackEngineNameDParam(e, EngineNameContext::PurchaseList)); } } else { - str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED; + str = GetString(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED); } - DrawString(r.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect), str, TC_BLACK, SA_HOR_CENTER); + DrawString(r.Shrink(WidgetDimensions::scaled.frametext, WidgetDimensions::scaled.framerect), std::move(str), TC_BLACK, SA_HOR_CENTER); break; } @@ -477,7 +473,7 @@ public: int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1; /* Do the actual drawing */ - DrawEngineList((VehicleType)this->window_number, r, this->engines[side], *this->vscroll[side], this->sel_engine[side], side == 0, this->sel_group); + DrawEngineList(this->window_number, r, this->engines[side], *this->vscroll[side], this->sel_engine[side], side == 0, this->sel_group, this->badge_classes); break; } } @@ -503,12 +499,12 @@ public: * Either engines list is empty * or The selected replacement engine has a replacement (to prevent loops). */ this->SetWidgetDisabledState(WID_RV_START_REPLACE, - this->sel_engine[0] == INVALID_ENGINE || this->sel_engine[1] == INVALID_ENGINE || EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE); + this->sel_engine[0] == EngineID::Invalid() || this->sel_engine[1] == EngineID::Invalid() || EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != EngineID::Invalid()); /* Disable the "Stop Replacing" button if: * The left engines list (existing vehicle) is empty * or The selected vehicle has no replacement set up */ - this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)); + this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == EngineID::Invalid() || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)); this->DrawWidgets(); @@ -516,7 +512,7 @@ public: int needed_height = this->details_height; /* Draw details panels. */ for (int side = 0; side < 2; side++) { - if (this->sel_engine[side] != INVALID_ENGINE) { + if (this->sel_engine[side] != EngineID::Invalid()) { /* Use default engine details without refitting */ const Engine *e = Engine::Get(this->sel_engine[side]); TestedEngineDetails ted; @@ -556,7 +552,7 @@ public: break; case WID_RV_SORT_DROPDOWN: - DisplayVehicleSortDropDown(this, static_cast(this->window_number), this->sort_criteria, WID_RV_SORT_DROPDOWN); + DisplayVehicleSortDropDown(this, this->window_number, this->sort_criteria, WID_RV_SORT_DROPDOWN); break; case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: { @@ -578,7 +574,7 @@ public: case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: { const Group *g = Group::GetIfValid(this->sel_group); if (g != nullptr) { - Command::Post(this->sel_group, GroupFlags::GF_REPLACE_WAGON_REMOVAL, !HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL), citymania::_fn_mod); + Command::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), citymania::_fn_mod); } else { // toggle renew_keep_length Command::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1); @@ -599,7 +595,7 @@ public: case WID_RV_STOP_REPLACE: { // Stop replacing EngineID veh_from = this->sel_engine[0]; - Command::Post(this->sel_group, veh_from, INVALID_ENGINE, false); + Command::Post(this->sel_group, veh_from, EngineID::Invalid(), false); break; } @@ -612,30 +608,30 @@ public: click_side = 1; } - EngineID e = INVALID_ENGINE; + EngineID e = EngineID::Invalid(); const auto it = this->vscroll[click_side]->GetScrolledItemFromWidget(this->engines[click_side], pt.y, this, widget); if (it != this->engines[click_side].end()) { const auto &item = *it; const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); - if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { + if (item.flags.Test(EngineDisplayFlag::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { /* toggle folded flag on engine */ - assert(item.variant_id != INVALID_ENGINE); + assert(item.variant_id != EngineID::Invalid()); Engine *engine = Engine::Get(item.variant_id); - engine->display_flags ^= EngineDisplayFlags::IsFolded; + engine->display_flags.Flip(EngineDisplayFlag::IsFolded); - InvalidateWindowData(WC_REPLACE_VEHICLE, (VehicleType)this->window_number, 0); // Update the autoreplace window + InvalidateWindowData(WC_REPLACE_VEHICLE, this->window_number, 0); // Update the autoreplace window InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well return; } - if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) e = item.engine_id; + if (!item.flags.Test(EngineDisplayFlag::Shaded)) e = item.engine_id; } /* If Ctrl is pressed on the left side and we don't have any engines of the selected type, stop autoreplacing. * This is most common when we have finished autoreplacing the engine and want to remove it from the list. */ - if (click_side == 0 && citymania::_fn_mod && e != INVALID_ENGINE && + if (click_side == 0 && citymania::_fn_mod && e != EngineID::Invalid() && (GetGroupNumEngines(_local_company, sel_group, e) == 0 || GetGroupNumEngines(_local_company, ALL_GROUP, e) == 0)) { EngineID veh_from = e; - Command::Post(this->sel_group, veh_from, INVALID_ENGINE, false); + Command::Post(this->sel_group, veh_from, EngineID::Invalid(), false); break; } @@ -698,10 +694,9 @@ public: if (widget != WID_RV_TRAIN_WAGONREMOVE_TOGGLE) return false; if (Group::IsValidID(this->sel_group)) { - SetDParam(0, STR_REPLACE_REMOVE_WAGON_HELP); - GuiShowTooltips(this, STR_REPLACE_REMOVE_WAGON_GROUP_HELP, close_cond, 1); + GuiShowTooltips(this, GetEncodedString(STR_REPLACE_REMOVE_WAGON_GROUP_HELP, STR_REPLACE_REMOVE_WAGON_TOOLTIP), close_cond); } else { - GuiShowTooltips(this, STR_REPLACE_REMOVE_WAGON_HELP, close_cond); + GuiShowTooltips(this, GetEncodedString(STR_REPLACE_REMOVE_WAGON_TOOLTIP), close_cond); } return true; } @@ -731,56 +726,56 @@ public: static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(STR_JUST_STRING, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetDataTip(STR_JUST_STRING, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetToolTip(STR_REPLACE_RAILTYPE_TOOLTIP), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetToolTip(STR_REPLACE_ENGINE_WAGON_SELECT_TOOLTIP), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetStringTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_LEFT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_RIGHT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(NWID_VERTICAL), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetStringTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_TOOLTIP), SetFill(1, 0), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON), - NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), + NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetStringTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_START_BUTTON_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetToolTip(STR_REPLACE_REPLACE_INFO_TAB_TOOLTIP), SetResize(1, 0), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetStringTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_STOP_BUTTON_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; @@ -788,57 +783,57 @@ static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = { static WindowDesc _replace_rail_vehicle_desc( WDP_AUTO, "replace_vehicle_train", 500, 140, WC_REPLACE_VEHICLE, WC_NONE, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_replace_rail_vehicle_widgets ); static constexpr NWidgetPart _nested_replace_road_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(NWID_VERTICAL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(STR_JUST_STRING, STR_REPLACE_HELP_ROADTYPE), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetToolTip(STR_REPLACE_ROADTYPE_TOOLTIP), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetStringTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_LEFT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_RIGHT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON), - NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), + NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetStringTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_START_BUTTON_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetToolTip(STR_REPLACE_REPLACE_INFO_TAB_TOOLTIP), SetResize(1, 0), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetStringTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_STOP_BUTTON_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; @@ -846,53 +841,53 @@ static constexpr NWidgetPart _nested_replace_road_vehicle_widgets[] = { static WindowDesc _replace_road_vehicle_desc( WDP_AUTO, "replace_vehicle_road", 500, 140, WC_REPLACE_VEHICLE, WC_NONE, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_replace_road_vehicle_widgets ); static constexpr NWidgetPart _nested_replace_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetMinimalSize(433, 14), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetMinimalSize(433, 14), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical()), SetResize(1, 0), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetToolTip(STR_TOOLTIP_SORT_CRITERIA), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetStringTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), EndContainer(), EndContainer(), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_LEFT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_RIGHT_ARRAY_TOOLTIP), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON), - NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON), + NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetStringTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_START_BUTTON_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetToolTip(STR_REPLACE_REPLACE_INFO_TAB_TOOLTIP), SetResize(1, 0), EndContainer(), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetStringTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_STOP_BUTTON_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; @@ -900,7 +895,7 @@ static constexpr NWidgetPart _nested_replace_vehicle_widgets[] = { static WindowDesc _replace_vehicle_desc( WDP_AUTO, "replace_vehicle", 456, 118, WC_REPLACE_VEHICLE, WC_NONE, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_replace_vehicle_widgets ); diff --git a/src/base_consist.cpp b/src/base_consist.cpp index 0a30893be2..3374619ca7 100644 --- a/src/base_consist.cpp +++ b/src/base_consist.cpp @@ -34,13 +34,13 @@ void BaseConsist::CopyConsistPropertiesFrom(const BaseConsist *src) this->cur_real_order_index = src->cur_real_order_index; this->cur_implicit_order_index = src->cur_implicit_order_index; - if (HasBit(src->vehicle_flags, VF_TIMETABLE_STARTED)) SetBit(this->vehicle_flags, VF_TIMETABLE_STARTED); - if (HasBit(src->vehicle_flags, VF_AUTOFILL_TIMETABLE)) SetBit(this->vehicle_flags, VF_AUTOFILL_TIMETABLE); - if (HasBit(src->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) SetBit(this->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME); - if (HasBit(src->vehicle_flags, VF_SERVINT_IS_PERCENT) != HasBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT)) { - ToggleBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT); + if (src->vehicle_flags.Test(VehicleFlag::TimetableStarted)) this->vehicle_flags.Set(VehicleFlag::TimetableStarted); + if (src->vehicle_flags.Test(VehicleFlag::AutofillTimetable)) this->vehicle_flags.Set(VehicleFlag::AutofillTimetable); + if (src->vehicle_flags.Test(VehicleFlag::AutofillPreserveWaitTime)) this->vehicle_flags.Set(VehicleFlag::AutofillPreserveWaitTime); + if (src->vehicle_flags.Test(VehicleFlag::ServiceIntervalIsPercent) != this->vehicle_flags.Test(VehicleFlag::ServiceIntervalIsPercent)) { + this->vehicle_flags.Flip(VehicleFlag::ServiceIntervalIsPercent); } - if (HasBit(src->vehicle_flags, VF_SERVINT_IS_CUSTOM)) SetBit(this->vehicle_flags, VF_SERVINT_IS_CUSTOM); + if (src->vehicle_flags.Test(VehicleFlag::ServiceIntervalIsCustom)) this->vehicle_flags.Set(VehicleFlag::ServiceIntervalIsCustom); } /** diff --git a/src/base_consist.h b/src/base_consist.h index 632e77c630..0c488a10bd 100644 --- a/src/base_consist.h +++ b/src/base_consist.h @@ -10,28 +10,44 @@ #ifndef BASE_CONSIST_H #define BASE_CONSIST_H +#include "core/enum_type.hpp" #include "order_type.h" #include "timer/timer_game_tick.h" +/** Bit numbers in #Vehicle::vehicle_flags. */ +enum class VehicleFlag : uint8_t { + LoadingFinished = 0, ///< Vehicle has finished loading. + CargoUnloading = 1, ///< Vehicle is unloading cargo. + BuiltAsPrototype = 2, ///< Vehicle is a prototype (accepted as exclusive preview). + TimetableStarted = 3, ///< Whether the vehicle has started running on the timetable yet. + AutofillTimetable = 4, ///< Whether the vehicle should fill in the timetable automatically. + AutofillPreserveWaitTime = 5, ///< Whether non-destructive auto-fill should preserve waiting times + StopLoading = 6, ///< Don't load anymore during the next load cycle. + PathfinderLost = 7, ///< Vehicle's pathfinder is lost. + ServiceIntervalIsCustom = 8, ///< Service interval is custom. + ServiceIntervalIsPercent = 9, ///< Service interval is percent. +}; +using VehicleFlags = EnumBitSet; + /** Various front vehicle properties that are preserved when autoreplacing, using order-backup or switching front engines within a consist. */ struct BaseConsist { - std::string name; ///< Name of vehicle + std::string name{}; ///< Name of vehicle /* Used for timetabling. */ - TimerGameTick::Ticks current_order_time; ///< How many ticks have passed since this order started. - TimerGameTick::Ticks lateness_counter; ///< How many ticks late (or early if negative) this vehicle is. - TimerGameTick::TickCounter timetable_start; ///< At what tick of TimerGameTick::counter the vehicle should start its timetable. + TimerGameTick::Ticks current_order_time{}; ///< How many ticks have passed since this order started. + TimerGameTick::Ticks lateness_counter{}; ///< How many ticks late (or early if negative) this vehicle is. + TimerGameTick::TickCounter timetable_start{}; ///< At what tick of TimerGameTick::counter the vehicle should start its timetable. - TimerGameTick::TickCounter depot_unbunching_last_departure; ///< When the vehicle last left its unbunching depot. - TimerGameTick::TickCounter depot_unbunching_next_departure; ///< When the vehicle will next try to leave its unbunching depot. + TimerGameTick::TickCounter depot_unbunching_last_departure{}; ///< When the vehicle last left its unbunching depot. + TimerGameTick::TickCounter depot_unbunching_next_departure{}; ///< When the vehicle will next try to leave its unbunching depot. TimerGameTick::Ticks round_trip_time; ///< How many ticks for a single circumnavigation of the orders. - uint16_t service_interval; ///< The interval for (automatic) servicing; either in days or %. + uint16_t service_interval = 0; ///< The interval for (automatic) servicing; either in days or %. - VehicleOrderID cur_real_order_index;///< The index to the current real (non-implicit) order - VehicleOrderID cur_implicit_order_index;///< The index to the current implicit order + VehicleOrderID cur_real_order_index = 0; ///< The index to the current real (non-implicit) order + VehicleOrderID cur_implicit_order_index = 0; ///< The index to the current implicit order - uint16_t vehicle_flags; ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum) + VehicleFlags vehicle_flags{}; ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum) virtual ~BaseConsist() = default; diff --git a/src/base_media_base.h b/src/base_media_base.h index 0164a1176a..2c645ccfae 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -11,7 +11,6 @@ #define BASE_MEDIA_BASE_H #include "fileio_func.h" -#include "gfx_type.h" #include "textfile_type.h" #include "textfile_gui.h" #include "3rdparty/md5/md5.h" @@ -24,7 +23,7 @@ struct ContentInfo; /** Structure holding filename and MD5 information about a single file */ struct MD5File { /** The result of a checksum check */ - enum ChecksumResult { + enum ChecksumResult : uint8_t { CR_UNKNOWN, ///< The file has not been checked yet CR_MATCH, ///< The file did exist and the md5 checksum did match CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match @@ -39,37 +38,38 @@ struct MD5File { ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const; }; +/** Defines the traits of a BaseSet type. */ +template struct BaseSetTraits; + /** * Information about a single base set. * @tparam T the real class we're going to be - * @tparam Tnum_files the number of files in the set - * @tparam Tsearch_in_tars whether to search in the tars or not */ -template +template struct BaseSet { typedef std::unordered_map TranslatedStrings; /** Number of files in this set */ - static const size_t NUM_FILES = Tnum_files; + static constexpr size_t NUM_FILES = BaseSetTraits::num_files; /** Whether to search in the tars or not. */ - static const bool SEARCH_IN_TARS = Tsearch_in_tars; + static constexpr bool SEARCH_IN_TARS = BaseSetTraits::search_in_tars; - /** Internal names of the files in this set. */ - static const char * const *file_names; + /** BaseSet type name. */ + static constexpr std::string_view SET_TYPE = BaseSetTraits::set_type; std::string name; ///< The name of the base set std::string url; ///< URL for information about the base set TranslatedStrings description; ///< Description of the base set - uint32_t shortname; ///< Four letter short variant of the name - uint32_t version; ///< The version of this base set - bool fallback; ///< This set is a fallback set, i.e. it should be used only as last resort + uint32_t shortname = 0; ///< Four letter short variant of the name + uint32_t version = 0; ///< The version of this base set + bool fallback = false; ///< This set is a fallback set, i.e. it should be used only as last resort - MD5File files[NUM_FILES]; ///< All files part of this set - uint found_files; ///< Number of the files that could be found - uint valid_files; ///< Number of the files that could be found and are valid + std::array::NUM_FILES> files{}; ///< All files part of this set + uint found_files = 0; ///< Number of the files that could be found + uint valid_files = 0; ///< Number of the files that could be found and are valid - T *next; ///< The next base set in this list + T *next = nullptr; ///< The next base set in this list /** Free everything we allocated */ ~BaseSet() @@ -83,7 +83,7 @@ struct BaseSet { */ int GetNumMissing() const { - return Tnum_files - this->found_files; + return BaseSet::NUM_FILES - this->found_files; } /** @@ -93,7 +93,7 @@ struct BaseSet { */ int GetNumInvalid() const { - return Tnum_files - this->valid_files; + return BaseSet::NUM_FILES - this->valid_files; } bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename, bool allow_empty_filename = true); @@ -122,21 +122,6 @@ struct BaseSet { return this->description.at(std::string{}); } - /** - * Get string to use when listing this set in the settings window. - * If there are no invalid files, then this is just the set name, - * otherwise a string is formatted including the number of invalid files. - * @return the string to display. - */ - std::string GetListLabel() const - { - if (this->GetNumInvalid() == 0) return this->name; - - SetDParamStr(0, this->name); - SetDParam(1, this->GetNumInvalid()); - return GetString(STR_BASESET_STATUS); - } - /** * Calculate and check the MD5 hash of the supplied file. * @param file The file get the hash of. @@ -166,6 +151,12 @@ struct BaseSet { } return std::nullopt; } + + /** + * Get the internal names of the files in this set. + * @return the internal filenames + */ + static std::span GetFilenames(); }; /** @@ -175,9 +166,9 @@ struct BaseSet { template class BaseMedia : FileScanner { protected: - static Tbase_set *available_sets; ///< All available sets - static Tbase_set *duplicate_sets; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded. - static const Tbase_set *used_set; ///< The currently used set + static inline Tbase_set *available_sets = nullptr; ///< All available sets + static inline Tbase_set *duplicate_sets = nullptr; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded. + static inline const Tbase_set *used_set = nullptr; ///< The currently used set bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override; @@ -220,13 +211,9 @@ public: * @param md5sum whether to check the MD5 checksum * @return true iff we have an set matching. */ - static bool HasSet(const ContentInfo *ci, bool md5sum); + static bool HasSet(const ContentInfo &ci, bool md5sum); }; -template /* static */ const Tbase_set *BaseMedia::used_set; -template /* static */ Tbase_set *BaseMedia::available_sets; -template /* static */ Tbase_set *BaseMedia::duplicate_sets; - /** * Check whether there's a base set matching some information. * @param ci The content info to compare it to. @@ -235,120 +222,6 @@ template /* static */ Tbase_set *BaseMedia::duplica * @return The filename of the first file of the base set, or \c nullptr if there is no match. */ template -const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s); - -/** Types of graphics in the base graphics set */ -enum GraphicsFileType { - GFT_BASE, ///< Base sprites for all climates - GFT_LOGOS, ///< Logos, landscape icons and original terrain generator sprites - GFT_ARCTIC, ///< Landscape replacement sprites for arctic - GFT_TROPICAL, ///< Landscape replacement sprites for tropical - GFT_TOYLAND, ///< Landscape replacement sprites for toyland - GFT_EXTRA, ///< Extra sprites that were not part of the original sprites - MAX_GFT, ///< We are looking for this amount of GRFs -}; - -/** Blitter type for base graphics sets. */ -enum BlitterType { - BLT_8BPP, ///< Base set has 8 bpp sprites only. - BLT_32BPP, ///< Base set has both 8 bpp and 32 bpp sprites. -}; - -struct GRFConfig; - -/** All data of a graphics set. */ -struct GraphicsSet : BaseSet { -private: - mutable std::unique_ptr extra_cfg; ///< Parameters for extra GRF -public: - PaletteType palette; ///< Palette of this graphics set - BlitterType blitter; ///< Blitter of this graphics set - - GraphicsSet(); - ~GraphicsSet(); - - bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename); - GRFConfig *GetExtraConfig() const { return this->extra_cfg.get(); } - GRFConfig &GetOrCreateExtraConfig() const; - bool IsConfigurable() const; - void CopyCompatibleConfig(const GraphicsSet &src); - - static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir); -}; - -/** All data/functions related with replacing the base graphics. */ -class BaseGraphics : public BaseMedia { -public: - /** Values loaded from config file. */ - struct Ini { - std::string name; - uint32_t shortname; ///< unique key for base set - uint32_t extra_version; ///< version of the extra GRF - std::vector extra_params; ///< parameters for the extra GRF - }; - static inline Ini ini_data; - -}; - -/** All data of a sounds set. */ -struct SoundsSet : BaseSet { -}; - -/** All data/functions related with replacing the base sounds */ -class BaseSounds : public BaseMedia { -public: - /** The set as saved in the config file. */ - static inline std::string ini_set; - -}; - -/** Maximum number of songs in the 'class' playlists. */ -static const uint NUM_SONGS_CLASS = 10; -/** Number of classes for songs */ -static const uint NUM_SONG_CLASSES = 3; -/** Maximum number of songs in the full playlist; theme song + the classes */ -static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS; - -/** Maximum number of songs in the (custom) playlist */ -static const uint NUM_SONGS_PLAYLIST = 32; - -/* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */ -std::optional GetMusicCatEntryName(const std::string &filename, size_t entrynum); -std::optional> GetMusicCatEntryData(const std::string &filename, size_t entrynum); - -enum MusicTrackType { - MTT_STANDARDMIDI, ///< Standard MIDI file - MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file) -}; - -/** Metadata about a music track. */ -struct MusicSongInfo { - std::string songname; ///< name of song displayed in UI - uint8_t tracknr; ///< track number of song displayed in UI - std::string filename; ///< file on disk containing song (when used in MusicSet class) - MusicTrackType filetype; ///< decoder required for song file - int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI - bool loop; ///< song should play in a tight loop if possible, never ending - int override_start; ///< MIDI ticks to skip over in beginning - int override_end; ///< MIDI tick to end the song at (0 if no override) -}; - -/** All data of a music set. */ -struct MusicSet : BaseSet { - /** Data about individual songs in set. */ - MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]; - /** Number of valid songs in set. */ - uint8_t num_available; - - bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename); -}; - -/** All data/functions related with replacing the base music */ -class BaseMusic : public BaseMedia { -public: - /** The set as saved in the config file. */ - static inline std::string ini_set; - -}; +const char *TryGetBaseSetFile(const ContentInfo &ci, bool md5sum, const Tbase_set *s); #endif /* BASE_MEDIA_BASE_H */ diff --git a/src/base_media_func.h b/src/base_media_func.h index 2d68caaa6b..2e0d644ff9 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -7,7 +7,6 @@ /** * @file base_media_func.h Generic function implementations for base data (graphics, sounds). - * @note You should _never_ include this file due to the SET_TYPE define. */ #include "base_media_base.h" @@ -25,7 +24,7 @@ extern void CheckExternalFiles(); #define fetch_metadata(name) \ item = metadata->GetItem(name); \ if (item == nullptr || !item->value.has_value() || item->value->empty()) { \ - Debug(grf, 0, "Base " SET_TYPE "set detail loading: {} field missing.", name); \ + Debug(grf, 0, "Base {}set detail loading: {} field missing.", BaseSet::SET_TYPE, name); \ Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename); \ return false; \ } @@ -38,12 +37,12 @@ extern void CheckExternalFiles(); * @param allow_empty_filename empty filenames are valid * @return true if loading was successful. */ -template -bool BaseSet::FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename, bool allow_empty_filename) +template +bool BaseSet::FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename, bool allow_empty_filename) { const IniGroup *metadata = ini.GetGroup("metadata"); if (metadata == nullptr) { - Debug(grf, 0, "Base " SET_TYPE "set detail loading: metadata missing."); + Debug(grf, 0, "Base {}set detail loading: metadata missing.", BaseSet::SET_TYPE); Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename); return false; } @@ -80,12 +79,14 @@ bool BaseSet::FillSetDetails(const IniFile &ini, const IniGroup *files = ini.GetGroup("files"); const IniGroup *md5s = ini.GetGroup("md5s"); const IniGroup *origin = ini.GetGroup("origin"); - for (uint i = 0; i < Tnum_files; i++) { + auto file_names = BaseSet::GetFilenames(); + + for (uint i = 0; i < BaseSet::NUM_FILES; i++) { MD5File *file = &this->files[i]; /* Find the filename first. */ - item = files != nullptr ? files->GetItem(BaseSet::file_names[i]) : nullptr; + item = files != nullptr ? files->GetItem(file_names[i]) : nullptr; if (item == nullptr || (!item->value.has_value() && !allow_empty_filename)) { - Debug(grf, 0, "No " SET_TYPE " file for: {} (in {})", BaseSet::file_names[i], full_filename); + Debug(grf, 0, "No {} file for: {} (in {})", BaseSet::SET_TYPE, file_names[i], full_filename); return false; } @@ -164,7 +165,7 @@ template bool BaseMedia::AddFile(const std::string &filename, size_t basepath_length, const std::string &) { bool ret = false; - Debug(grf, 1, "Checking {} for base " SET_TYPE " set", filename); + Debug(grf, 1, "Checking {} for base {} set", filename, BaseSet::SET_TYPE); Tbase_set *set = new Tbase_set(); IniFile ini{}; @@ -190,7 +191,8 @@ bool BaseMedia::AddFile(const std::string &filename, size_t basepath_ /* The more complete set takes precedence over the version number. */ if ((duplicate->valid_files == set->valid_files && duplicate->version >= set->version) || duplicate->valid_files > set->valid_files) { - Debug(grf, 1, "Not adding {} ({}) as base " SET_TYPE " set (duplicate, {})", set->name, set->version, + Debug(grf, 1, "Not adding {} ({}) as base {} set (duplicate, {})", set->name, set->version, + BaseSet::SET_TYPE, duplicate->valid_files > set->valid_files ? "less valid files" : "lower version"); set->next = BaseMedia::duplicate_sets; BaseMedia::duplicate_sets = set; @@ -209,7 +211,8 @@ bool BaseMedia::AddFile(const std::string &filename, size_t basepath_ * version number until a new game is started which isn't a big problem */ if (BaseMedia::used_set == duplicate) BaseMedia::used_set = set; - Debug(grf, 1, "Removing {} ({}) as base " SET_TYPE " set (duplicate, {})", duplicate->name, duplicate->version, + Debug(grf, 1, "Removing {} ({}) as base {} set (duplicate, {})", duplicate->name, duplicate->version, + BaseSet::SET_TYPE, duplicate->valid_files < set->valid_files ? "less valid files" : "lower version"); duplicate->next = BaseMedia::duplicate_sets; BaseMedia::duplicate_sets = duplicate; @@ -223,7 +226,7 @@ bool BaseMedia::AddFile(const std::string &filename, size_t basepath_ ret = true; } if (ret) { - Debug(grf, 1, "Adding {} ({}) as base " SET_TYPE " set", set->name, set->version); + Debug(grf, 1, "Adding {} ({}) as base {} set", set->name, set->version, BaseSet::SET_TYPE); } } else { delete set; @@ -296,7 +299,7 @@ template template /* static */ void BaseMedia::GetSetsList(std::back_insert_iterator &output_iterator) { - fmt::format_to(output_iterator, "List of " SET_TYPE " sets:\n"); + fmt::format_to(output_iterator, "List of {} sets:\n", BaseSet::SET_TYPE); for (const Tbase_set *s = BaseMedia::available_sets; s != nullptr; s = s->next) { fmt::format_to(output_iterator, "{:>18}: {}", s->name, s->GetDescription({})); int invalid = s->GetNumInvalid(); @@ -316,25 +319,25 @@ template #include "network/core/tcp_content_type.h" -template const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s) +template const char *TryGetBaseSetFile(const ContentInfo &ci, bool md5sum, const Tbase_set *s) { for (; s != nullptr; s = s->next) { if (s->GetNumMissing() != 0) continue; - if (s->shortname != ci->unique_id) continue; + if (s->shortname != ci.unique_id) continue; if (!md5sum) return s->files[0].filename.c_str(); MD5Hash md5; for (const auto &file : s->files) { md5 ^= file.hash; } - if (md5 == ci->md5sum) return s->files[0].filename.c_str(); + if (md5 == ci.md5sum) return s->files[0].filename.c_str(); } return nullptr; } template -/* static */ bool BaseMedia::HasSet(const ContentInfo *ci, bool md5sum) +/* static */ bool BaseMedia::HasSet(const ContentInfo &ci, bool md5sum) { return (TryGetBaseSetFile(ci, md5sum, BaseMedia::available_sets) != nullptr) || (TryGetBaseSetFile(ci, md5sum, BaseMedia::duplicate_sets) != nullptr); @@ -383,7 +386,7 @@ template if (index == 0) return s; index--; } - FatalError("Base" SET_TYPE "::GetSet(): index {} out of range", index); + FatalError("Base{}::GetSet(): index {} out of range", BaseSet::SET_TYPE, index); } /** @@ -405,25 +408,3 @@ template { return BaseMedia::available_sets; } - -/** - * Force instantiation of methods so we don't get linker errors. - * @param repl_type the type of the BaseMedia to instantiate - * @param set_type the type of the BaseSet to instantiate - */ -#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type) \ - template const char *repl_type::GetExtension(); \ - template bool repl_type::AddFile(const std::string &filename, size_t pathlength, const std::string &tar_filename); \ - template bool repl_type::HasSet(const struct ContentInfo *ci, bool md5sum); \ - template bool repl_type::SetSet(const set_type *set); \ - template bool repl_type::SetSetByName(const std::string &name); \ - template bool repl_type::SetSetByShortname(uint32_t shortname); \ - template void repl_type::GetSetsList(std::back_insert_iterator &output_iterator); \ - template int repl_type::GetNumSets(); \ - template int repl_type::GetIndexOfUsedSet(); \ - template const set_type *repl_type::GetSet(int index); \ - template const set_type *repl_type::GetUsedSet(); \ - template bool repl_type::DetermineBestSet(); \ - template set_type *repl_type::GetAvailableSets(); \ - template const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const set_type *s); - diff --git a/src/base_media_graphics.h b/src/base_media_graphics.h new file mode 100644 index 0000000000..7340cc1ee9 --- /dev/null +++ b/src/base_media_graphics.h @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +/** @file base_media_graphics.h Generic functions for replacing base graphics data. */ + +#ifndef BASE_MEDIA_GRAPHICS_H +#define BASE_MEDIA_GRAPHICS_H + +#include "base_media_base.h" + +/** Types of graphics in the base graphics set */ +enum GraphicsFileType : uint8_t { + GFT_BASE, ///< Base sprites for all climates + GFT_LOGOS, ///< Logos, landscape icons and original terrain generator sprites + GFT_ARCTIC, ///< Landscape replacement sprites for arctic + GFT_TROPICAL, ///< Landscape replacement sprites for tropical + GFT_TOYLAND, ///< Landscape replacement sprites for toyland + GFT_EXTRA, ///< Extra sprites that were not part of the original sprites + MAX_GFT, ///< We are looking for this amount of GRFs +}; + +/** Blitter type for base graphics sets. */ +enum BlitterType : uint8_t { + BLT_8BPP, ///< Base set has 8 bpp sprites only. + BLT_32BPP, ///< Base set has both 8 bpp and 32 bpp sprites. +}; + +struct GRFConfig; + +template <> struct BaseSetTraits { + static constexpr size_t num_files = MAX_GFT; + static constexpr bool search_in_tars = true; + static constexpr std::string_view set_type = "graphics"; +}; + +/** All data of a graphics set. */ +struct GraphicsSet : BaseSet { +private: + mutable std::unique_ptr extra_cfg; ///< Parameters for extra GRF +public: + PaletteType palette{}; ///< Palette of this graphics set + BlitterType blitter{}; ///< Blitter of this graphics set + + GraphicsSet(); + ~GraphicsSet(); + + bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename); + GRFConfig *GetExtraConfig() const { return this->extra_cfg.get(); } + GRFConfig &GetOrCreateExtraConfig() const; + bool IsConfigurable() const; + void CopyCompatibleConfig(const GraphicsSet &src); + + static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir); +}; + +/** All data/functions related with replacing the base graphics. */ +class BaseGraphics : public BaseMedia { +public: + /** Values loaded from config file. */ + struct Ini { + std::string name; + uint32_t shortname; ///< unique key for base set + uint32_t extra_version; ///< version of the extra GRF + std::vector extra_params; ///< parameters for the extra GRF + }; + static inline Ini ini_data; +}; + +#endif /* BASE_MEDIA_BASE_H */ diff --git a/src/base_media_music.h b/src/base_media_music.h new file mode 100644 index 0000000000..86b3e8cad7 --- /dev/null +++ b/src/base_media_music.h @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +/** @file base_media_music.h Generic functions for replacing base music data. */ + +#ifndef BASE_MEDIA_MUSIC_H +#define BASE_MEDIA_MUSIC_H + +#include "base_media_base.h" + +/** Maximum number of songs in the 'class' playlists. */ +static const uint NUM_SONGS_CLASS = 10; +/** Number of classes for songs */ +static const uint NUM_SONG_CLASSES = 3; +/** Maximum number of songs in the full playlist; theme song + the classes */ +static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS; + +/** Maximum number of songs in the (custom) playlist */ +static const uint NUM_SONGS_PLAYLIST = 32; + +/* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */ +std::optional GetMusicCatEntryName(const std::string &filename, size_t entrynum); +std::optional> GetMusicCatEntryData(const std::string &filename, size_t entrynum); + +enum MusicTrackType : uint8_t { + MTT_STANDARDMIDI, ///< Standard MIDI file + MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file) +}; + +/** Metadata about a music track. */ +struct MusicSongInfo { + std::string songname; ///< name of song displayed in UI + uint8_t tracknr; ///< track number of song displayed in UI + std::string filename; ///< file on disk containing song (when used in MusicSet class) + MusicTrackType filetype; ///< decoder required for song file + int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI + bool loop; ///< song should play in a tight loop if possible, never ending + int override_start; ///< MIDI ticks to skip over in beginning + int override_end; ///< MIDI tick to end the song at (0 if no override) +}; + +template <> struct BaseSetTraits { + static constexpr size_t num_files = NUM_SONGS_AVAILABLE; + static constexpr bool search_in_tars = false; + static constexpr std::string_view set_type = "music"; +}; + +/** All data of a music set. */ +struct MusicSet : BaseSet { + /** Data about individual songs in set. */ + MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]; + /** Number of valid songs in set. */ + uint8_t num_available = 0; + + bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename); +}; + +/** All data/functions related with replacing the base music */ +class BaseMusic : public BaseMedia { +public: + /** The set as saved in the config file. */ + static inline std::string ini_set; +}; + +#endif /* BASE_MEDIA_MUSIC_H */ diff --git a/src/base_media_sounds.h b/src/base_media_sounds.h new file mode 100644 index 0000000000..d7a5cd0ffa --- /dev/null +++ b/src/base_media_sounds.h @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +/** @file base_media_sounds.h Generic functions for replacing base sounds data. */ + +#ifndef BASE_MEDIA_SOUNDS_H +#define BASE_MEDIA_SOUNDS_H + +#include "base_media_base.h" + +template <> struct BaseSetTraits { + static constexpr size_t num_files = 1; + static constexpr bool search_in_tars = true; + static constexpr std::string_view set_type = "sounds"; +}; + +/** All data of a sounds set. */ +struct SoundsSet : BaseSet {}; + +/** All data/functions related with replacing the base sounds */ +class BaseSounds : public BaseMedia { +public: + /** The set as saved in the config file. */ + static inline std::string ini_set; +}; + +#endif /* BASE_MEDIA_SOUNDS_H */ diff --git a/src/base_station_base.h b/src/base_station_base.h index 72a5bb85ce..dd6d3a5976 100644 --- a/src/base_station_base.h +++ b/src/base_station_base.h @@ -16,26 +16,25 @@ #include "station_map.h" #include "timer/timer_game_calendar.h" -typedef Pool StationPool; +typedef Pool StationPool; extern StationPool _station_pool; template struct SpecMapping { - const T *spec; ///< Custom spec. - uint32_t grfid; ///< GRF ID of this custom spec. - uint16_t localidx; ///< Local ID within GRF of this custom spec. + const T *spec = nullptr; ///< Custom spec. + uint32_t grfid = 0; ///< GRF ID of this custom spec. + uint16_t localidx = 0; ///< Local ID within GRF of this custom spec. }; struct RoadStopTileData { - TileIndex tile; - uint8_t random_bits; - uint8_t animation_frame; + TileIndex tile = INVALID_TILE; + uint8_t random_bits = 0; + uint8_t animation_frame = 0; }; /** StationRect - used to track station spread out rectangle - cheaper than scanning whole map */ struct StationRect : public Rect { - enum StationRectMode - { + enum StationRectMode : uint8_t { ADD_TEST = 0, ADD_TRY, ADD_FORCE @@ -57,44 +56,40 @@ struct StationRect : public Rect { /** Base class for all station-ish types */ struct BaseStation : StationPool::PoolItem<&_station_pool> { - TileIndex xy; ///< Base tile of the station - TrackedViewportSign sign; ///< NOSAVE: Dimensions of sign - uint8_t delete_ctr; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted. + TileIndex xy = INVALID_TILE; ///< Base tile of the station + TrackedViewportSign sign{}; ///< NOSAVE: Dimensions of sign + uint8_t delete_ctr = 0; ///< Delete counter. If greater than 0 then it is decremented until it reaches 0; the waypoint is then is deleted. - std::string name; ///< Custom name - StringID string_id; ///< Default name (town area) of station + std::string name{}; ///< Custom name + StringID string_id = INVALID_STRING_ID; ///< Default name (town area) of station mutable std::string cached_name; ///< NOSAVE: Cache of the resolved name of the station, if not using a custom name - Town *town; ///< The town this station is associated with - Owner owner; ///< The owner of this station - StationFacility facilities; ///< The facilities that this station has + Town *town = nullptr; ///< The town this station is associated with + Owner owner = INVALID_OWNER; ///< The owner of this station + StationFacilities facilities{}; ///< The facilities that this station has - std::vector> speclist; ///< List of rail station specs of this station. - std::vector> roadstop_speclist; ///< List of road stop specs of this station + std::vector> speclist{}; ///< List of rail station specs of this station. + std::vector> roadstop_speclist{}; ///< List of road stop specs of this station - TimerGameCalendar::Date build_date; ///< Date of construction + TimerGameCalendar::Date build_date{}; ///< Date of construction - uint16_t random_bits; ///< Random bits assigned to this station - uint8_t waiting_triggers; ///< Waiting triggers (NewGRF) for this station - uint8_t cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. - uint8_t cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen. - CargoTypes cached_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask - CargoTypes cached_roadstop_cargo_triggers; ///< NOSAVE: Combined cargo trigger bitmask for road stops + uint16_t random_bits = 0; ///< Random bits assigned to this station + uint8_t waiting_triggers = 0; ///< Waiting triggers (NewGRF) for this station + uint8_t cached_anim_triggers = 0; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. + uint8_t cached_roadstop_anim_triggers = 0; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen. + CargoTypes cached_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask + CargoTypes cached_roadstop_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask for road stops - TileArea train_station; ///< Tile area the train 'station' part covers - StationRect rect; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions + TileArea train_station{INVALID_TILE, 0, 0}; ///< Tile area the train 'station' part covers + StationRect rect{}; ///< NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions - std::vector custom_roadstop_tile_data; ///< List of custom road stop tile data + std::vector custom_roadstop_tile_data{}; ///< List of custom road stop tile data /** * Initialize the base station. * @param tile The location of the station sign */ - BaseStation(TileIndex tile) : - xy(tile), - train_station(INVALID_TILE, 0, 0) - { - } + BaseStation(TileIndex tile) : xy(tile) {} virtual ~BaseStation(); @@ -176,7 +171,7 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> { */ inline bool IsInUse() const { - return (this->facilities & ~FACIL_WAYPOINT) != 0; + return this->facilities.Any({StationFacility::Train, StationFacility::TruckStop, StationFacility::BusStop, StationFacility::Airport, StationFacility::Dock}); } inline uint8_t GetRoadStopRandomBits(TileIndex tile) const @@ -215,7 +210,7 @@ private: */ template struct SpecializedStation : public BaseStation { - static const StationFacility EXPECTED_FACIL = Tis_waypoint ? FACIL_WAYPOINT : FACIL_NONE; ///< Specialized type + static constexpr StationFacilities EXPECTED_FACIL = Tis_waypoint ? StationFacility::Waypoint : StationFacilities{}; ///< Specialized type /** * Set station type correctly @@ -234,7 +229,7 @@ struct SpecializedStation : public BaseStation { */ static inline bool IsExpected(const BaseStation *st) { - return (st->facilities & FACIL_WAYPOINT) == EXPECTED_FACIL; + return st->facilities.Test(StationFacility::Waypoint) == Tis_waypoint; } /** @@ -242,7 +237,7 @@ struct SpecializedStation : public BaseStation { * @param index tested index * @return is this index valid index of T? */ - static inline bool IsValidID(size_t index) + static inline bool IsValidID(auto index) { return BaseStation::IsValidID(index) && IsExpected(BaseStation::Get(index)); } @@ -251,7 +246,7 @@ struct SpecializedStation : public BaseStation { * Gets station with given index * @return pointer to station with given index casted to T * */ - static inline T *Get(size_t index) + static inline T *Get(auto index) { return (T *)BaseStation::Get(index); } @@ -260,7 +255,7 @@ struct SpecializedStation : public BaseStation { * Returns station if the index is a valid index for this station type * @return pointer to station with given index if it's a station of this type */ - static inline T *GetIfValid(size_t index) + static inline T *GetIfValid(auto index) { return IsValidID(index) ? Get(index) : nullptr; } diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 8a46bdcc18..02c502c739 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -22,11 +22,6 @@ /** Instantiation of the 32bpp with animation blitter factory. */ static FBlitter_32bppAnim iFBlitter_32bppAnim; -Blitter_32bppAnim::~Blitter_32bppAnim() -{ - free(this->anim_alloc); -} - template inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom) { @@ -105,7 +100,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel draw:; switch (mode) { - case CM_BM_TINT_REMAP: + case BlitterMode::CMTintRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -142,7 +137,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_COLOUR_REMAP: + case BlitterMode::ColourRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -153,7 +148,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } else { uint r = remap[GB(m, 0, 8)]; *anim = r | (m & 0xFF00); - if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); + if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); } anim++; dst++; @@ -169,7 +164,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } else { uint r = remap[GB(m, 0, 8)]; *anim = 0; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); } anim++; dst++; @@ -179,7 +174,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_CRASH_REMAP: + case BlitterMode::CrashRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -190,7 +185,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } else { uint r = remap[GB(m, 0, 8)]; *anim = r | (m & 0xFF00); - if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); + if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); } anim++; dst++; @@ -209,7 +204,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } else { uint r = remap[GB(m, 0, 8)]; *anim = 0; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); } anim++; dst++; @@ -220,7 +215,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: do { *dst++ = Colour(0, 0, 0); *anim++ = 0; @@ -229,7 +224,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } while (--n != 0); break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent */ src_n += n; if (src_px->a == 255) { @@ -251,7 +246,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ src_n += n; if (src_px->a != 0) { @@ -276,7 +271,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel uint m = GB(*src_n, 0, 8); /* Above PALETTE_ANIM_START is palette animation */ *anim++ = *src_n; - *dst++ = (m >= PALETTE_ANIM_START) ? this->AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)) : src_px->data; + *dst++ = (m >= PALETTE_ANIM_START) ? AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)) : src_px->data; src_px++; src_n++; } while (--n != 0); @@ -285,7 +280,7 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel uint m = GB(*src_n, 0, 8); *anim++ = 0; if (m >= PALETTE_ANIM_START) { - *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)), src_px->a, *dst); + *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(m), GB(*src_n, 8, 8)), src_px->a, *dst); } else { *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst); } @@ -315,13 +310,13 @@ void Blitter_32bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL switch (mode) { default: NOT_REACHED(); - case BM_NORMAL: Draw (bp, zoom); return; - case BM_COLOUR_REMAP: Draw(bp, zoom); return; - case CM_BM_TINT_REMAP: Draw(bp, zoom); return; - case BM_TRANSPARENT: Draw (bp, zoom); return; - case BM_TRANSPARENT_REMAP: Draw(bp, zoom); return; - case BM_CRASH_REMAP: Draw (bp, zoom); return; - case BM_BLACK_REMAP: Draw (bp, zoom); return; + case BlitterMode::Normal: Draw(bp, zoom); return; + case BlitterMode::ColourRemap: Draw(bp, zoom); return; + case BlitterMode::CMTintRemap: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, zoom); return; + case BlitterMode::TransparentRemap: Draw(bp, zoom); return; + case BlitterMode::CrashRemap: Draw(bp, zoom); return; + case BlitterMode::BlackRemap: Draw(bp, zoom); return; } } @@ -453,7 +448,7 @@ void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width, uint colour = GB(*anim_pal, 0, 8); if (colour >= PALETTE_ANIM_START) { /* Update this pixel */ - *dst_pal = this->AdjustBrightness(LookupColourInPalette(colour), GB(*anim_pal, 8, 8)); + *dst_pal = AdjustBrightness(LookupColourInPalette(colour), GB(*anim_pal, 8, 8)); } dst_pal++; anim_pal++; @@ -562,7 +557,7 @@ void Blitter_32bppAnim::PaletteAnimate(const Palette &palette) uint8_t colour = GB(value, 0, 8); if (colour >= PALETTE_ANIM_START) { /* Update this pixel */ - *dst = this->AdjustBrightness(LookupColourInPalette(colour), GB(value, 8, 8)); + *dst = AdjustBrightness(LookupColourInPalette(colour), GB(value, 8, 8)); } dst++; anim++; @@ -577,7 +572,7 @@ void Blitter_32bppAnim::PaletteAnimate(const Palette &palette) Blitter::PaletteAnimation Blitter_32bppAnim::UsePaletteAnimation() { - return Blitter::PALETTE_ANIMATION_BLITTER; + return Blitter::PaletteAnimation::Blitter; } void Blitter_32bppAnim::PostResize() @@ -585,13 +580,12 @@ void Blitter_32bppAnim::PostResize() if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height || _screen.pitch != this->anim_buf_pitch) { /* The size of the screen changed; we can assume we can wipe all data from our buffer */ - free(this->anim_alloc); this->anim_buf_width = _screen.width; this->anim_buf_height = _screen.height; this->anim_buf_pitch = (_screen.width + 7) & ~7; - this->anim_alloc = CallocT(this->anim_buf_pitch * this->anim_buf_height + 8); + this->anim_alloc = std::make_unique(this->anim_buf_pitch * this->anim_buf_height + 8); /* align buffer to next 16 byte boundary */ - this->anim_buf = reinterpret_cast((reinterpret_cast(this->anim_alloc) + 0xF) & (~0xF)); + this->anim_buf = reinterpret_cast((reinterpret_cast(this->anim_alloc.get()) + 0xF) & (~0xF)); } } diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index 975a9bfd62..acc911ffe0 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -16,7 +16,7 @@ class Blitter_32bppAnim : public Blitter_32bppOptimized { protected: uint16_t *anim_buf; ///< In this buffer we keep track of the 8bpp indexes so we can do palette animation - void *anim_alloc; ///< The raw allocated buffer, not necessarily aligned correctly + std::unique_ptr anim_alloc; ///< The raw allocated buffer, not necessarily aligned correctly int anim_buf_width; ///< The width of the animation buffer. int anim_buf_height; ///< The height of the animation buffer. int anim_buf_pitch; ///< The pitch of the animation buffer (width rounded up to 16 byte boundary). @@ -25,7 +25,6 @@ protected: public: Blitter_32bppAnim() : anim_buf(nullptr), - anim_alloc(nullptr), anim_buf_width(0), anim_buf_height(0), anim_buf_pitch(0) @@ -33,8 +32,6 @@ public: this->palette = _cur_palette; } - ~Blitter_32bppAnim(); - void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void SetPixel(void *video, int x, int y, uint8_t colour) override; @@ -74,7 +71,7 @@ public: class FBlitter_32bppAnim : public BlitterFactory { public: FBlitter_32bppAnim() : BlitterFactory("32bpp-anim", "32bpp Animation Blitter (palette animation)") {} - Blitter *CreateInstance() override { return new Blitter_32bppAnim(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_32BPP_ANIM_HPP */ diff --git a/src/blitter/32bpp_anim_sse2.cpp b/src/blitter/32bpp_anim_sse2.cpp index b166037ddd..14e9856a53 100644 --- a/src/blitter/32bpp_anim_sse2.cpp +++ b/src/blitter/32bpp_anim_sse2.cpp @@ -40,7 +40,7 @@ void Blitter_32bppSSE2_Anim::PaletteAnimate(const Palette &palette) const int screen_pitch = _screen.pitch; const int anim_pitch = this->anim_buf_pitch; __m128i anim_cmp = _mm_set1_epi16(PALETTE_ANIM_START - 1); - __m128i brightness_cmp = _mm_set1_epi16(Blitter_32bppBase::DEFAULT_BRIGHTNESS); + __m128i brightness_cmp = _mm_set1_epi16(DEFAULT_BRIGHTNESS); __m128i colour_mask = _mm_set1_epi16(0xFF); for (int y = this->anim_buf_height; y != 0 ; y--) { Colour *next_dst_ln = dst + screen_pitch; diff --git a/src/blitter/32bpp_anim_sse2.hpp b/src/blitter/32bpp_anim_sse2.hpp index 56262e387f..623bf2bd5d 100644 --- a/src/blitter/32bpp_anim_sse2.hpp +++ b/src/blitter/32bpp_anim_sse2.hpp @@ -38,7 +38,7 @@ public: class FBlitter_32bppSSE2_Anim : public BlitterFactory { public: FBlitter_32bppSSE2_Anim() : BlitterFactory("32bpp-sse2-anim", "32bpp partially SSE2 Animation Blitter (palette animation)", HasCPUIDFlag(1, 3, 26)) {} - Blitter *CreateInstance() override { return new Blitter_32bppSSE2_Anim(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* WITH_SSE */ diff --git a/src/blitter/32bpp_anim_sse4.cpp b/src/blitter/32bpp_anim_sse4.cpp index b4c005f09a..2dad7f5753 100644 --- a/src/blitter/32bpp_anim_sse4.cpp +++ b/src/blitter/32bpp_anim_sse4.cpp @@ -61,7 +61,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom for (int y = bp->height; y != 0; y--) { Colour *dst = dst_line; const Colour *src = src_rgba_line + META_LENGTH; - if (mode != BM_TRANSPARENT) src_mv = src_mv_line; + if (mode != BlitterMode::Transparent) src_mv = src_mv_line; uint16_t *anim = anim_line; if (read_mode == RM_WITH_MARGIN) { @@ -69,7 +69,7 @@ inline void Blitter_32bppSSE4_Anim::Draw(const BlitterParams *bp, ZoomLevel zoom anim += src_rgba_line[0].data; src += src_rgba_line[0].data; dst += src_rgba_line[0].data; - if (mode != BM_TRANSPARENT) src_mv += src_rgba_line[0].data; + if (mode != BlitterMode::Transparent) src_mv += src_rgba_line[0].data; const int width_diff = si->sprite_width - bp->width; effective_width = bp->width - (int) src_rgba_line[0].data; const int delta_diff = (int) src_rgba_line[1].data - width_diff; @@ -180,7 +180,7 @@ bmno_full_transparency: } break; - case BM_COLOUR_REMAP: + case BlitterMode::ColourRemap: for (uint x = (uint) effective_width / 2; x != 0; x--) { uint32_t mvX2 = *((uint32_t *) const_cast(src_mv)); __m128i srcABCD = _mm_loadl_epi64((const __m128i*) src); @@ -299,7 +299,7 @@ bmcr_alpha_blend_single: } break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent. */ for (uint x = (uint) bp->width / 2; x > 0; x--) { __m128i srcABCD = _mm_loadl_epi64((const __m128i*) src); @@ -320,7 +320,7 @@ bmcr_alpha_blend_single: } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ for (uint x = (uint) bp->width; x > 0; x--) { if (src->a != 0) { @@ -352,7 +352,7 @@ bmcr_alpha_blend_single: } break; - case BM_CRASH_REMAP: + case BlitterMode::CrashRemap: for (uint x = (uint) bp->width; x > 0; x--) { if (src_mv->m == 0) { if (src->a != 0) { @@ -362,7 +362,7 @@ bmcr_alpha_blend_single: } } else { uint r = remap[src_mv->m]; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst); } src_mv++; dst++; @@ -371,7 +371,7 @@ bmcr_alpha_blend_single: } break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: for (uint x = (uint) bp->width; x > 0; x--) { if (src->a != 0) { *dst = Colour(0, 0, 0); @@ -386,7 +386,7 @@ bmcr_alpha_blend_single: } next_line: - if (mode != BM_TRANSPARENT && mode != BM_TRANSPARENT_REMAP) src_mv_line += si->sprite_width; + if (mode != BlitterMode::Transparent && mode != BlitterMode::TransparentRemap) src_mv_line += si->sprite_width; src_rgba_line = (const Colour*) ((const uint8_t*) src_rgba_line + si->sprite_line_size); dst_line += bp->pitch; anim_line += this->anim_buf_pitch; @@ -416,51 +416,51 @@ bm_normal: if (bp->skip_left != 0 || bp->width <= MARGIN_NORMAL_THRESHOLD) { const BlockType bt_last = (BlockType) (bp->width & 1); if (bt_last == BT_EVEN) { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } else { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } } else { #ifdef POINTER_IS_64BIT - if (sprite_flags & SF_TRANSLUCENT) { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::Translucent)) { + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } else { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } #else - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); #endif } break; } - case BM_COLOUR_REMAP: - if (sprite_flags & SF_NO_REMAP) goto bm_normal; + case BlitterMode::ColourRemap: + if (sprite_flags.Test(SpriteFlag::NoRemap)) goto bm_normal; if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } else { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags.Test(SpriteFlag::NoAnim)) Draw(bp, zoom); + else Draw(bp, zoom); } break; - case CM_BM_TINT_REMAP: + case BlitterMode::CMTintRemap: if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); + else Draw(bp, zoom); } else { - if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); - else Draw(bp, zoom); + if (sprite_flags & SF_NO_ANIM) Draw(bp, zoom); + else Draw(bp, zoom); } break; - case BM_TRANSPARENT: Draw(bp, zoom); return; - case BM_TRANSPARENT_REMAP: Draw(bp, zoom); return; - case BM_CRASH_REMAP: Draw(bp, zoom); return; - case BM_BLACK_REMAP: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, zoom); return; + case BlitterMode::TransparentRemap: Draw(bp, zoom); return; + case BlitterMode::CrashRemap: Draw(bp, zoom); return; + case BlitterMode::BlackRemap: Draw(bp, zoom); return; } } diff --git a/src/blitter/32bpp_anim_sse4.hpp b/src/blitter/32bpp_anim_sse4.hpp index 8217e7d805..cad4295a9b 100644 --- a/src/blitter/32bpp_anim_sse4.hpp +++ b/src/blitter/32bpp_anim_sse4.hpp @@ -50,7 +50,7 @@ public: class FBlitter_32bppSSE4_Anim: public BlitterFactory { public: FBlitter_32bppSSE4_Anim() : BlitterFactory("32bpp-sse4-anim", "32bpp SSE4 Blitter (palette animation)", HasCPUIDFlag(1, 2, 19)) {} - Blitter *CreateInstance() override { return static_cast(new Blitter_32bppSSE4_Anim()); } + std::unique_ptr CreateInstance() override { return std::unique_ptr(static_cast(new Blitter_32bppSSE4_Anim())); } }; #endif /* WITH_SSE */ diff --git a/src/blitter/32bpp_base.cpp b/src/blitter/32bpp_base.cpp index eef00b404d..58acafc35a 100644 --- a/src/blitter/32bpp_base.cpp +++ b/src/blitter/32bpp_base.cpp @@ -150,37 +150,7 @@ void Blitter_32bppBase::PaletteAnimate(const Palette &) /* By default, 32bpp doesn't have palette animation */ } -Colour Blitter_32bppBase::ReallyAdjustBrightness(Colour colour, uint8_t brightness) -{ - assert(DEFAULT_BRIGHTNESS == 1 << 7); - - uint64_t combined = (((uint64_t) colour.r) << 32) | (((uint64_t) colour.g) << 16) | ((uint64_t) colour.b); - combined *= brightness; - - uint16_t r = GB(combined, 39, 9); - uint16_t g = GB(combined, 23, 9); - uint16_t b = GB(combined, 7, 9); - - if ((combined & 0x800080008000L) == 0L) { - return Colour(r, g, b, colour.a); - } - - uint16_t ob = 0; - /* Sum overbright */ - if (r > 255) ob += r - 255; - if (g > 255) ob += g - 255; - if (b > 255) ob += b - 255; - - /* Reduce overbright strength */ - ob /= 2; - return Colour( - r >= 255 ? 255 : std::min(r + ob * (255 - r) / 256, 255), - g >= 255 ? 255 : std::min(g + ob * (255 - g) / 256, 255), - b >= 255 ? 255 : std::min(b + ob * (255 - b) / 256, 255), - colour.a); -} - Blitter::PaletteAnimation Blitter_32bppBase::UsePaletteAnimation() { - return Blitter::PALETTE_ANIMATION_NONE; + return Blitter::PaletteAnimation::None; } diff --git a/src/blitter/32bpp_base.hpp b/src/blitter/32bpp_base.hpp index f5d8a30c4e..c3378b47c3 100644 --- a/src/blitter/32bpp_base.hpp +++ b/src/blitter/32bpp_base.hpp @@ -11,9 +11,8 @@ #define BLITTER_32BPP_BASE_HPP #include "base.hpp" -#include "../core/bitmath_func.hpp" -#include "../core/math_func.hpp" #include "../gfx_func.h" +#include "../palette_func.h" /** Base for all 32bpp blitters. */ class Blitter_32bppBase : public Blitter { @@ -153,28 +152,6 @@ public: return Colour(grey, grey, grey); } - - static const int DEFAULT_BRIGHTNESS = 128; - - static Colour ReallyAdjustBrightness(Colour colour, uint8_t brightness); - - static inline Colour AdjustBrightness(Colour colour, uint8_t brightness) - { - /* Shortcut for normal brightness */ - if (brightness == DEFAULT_BRIGHTNESS) return colour; - - return ReallyAdjustBrightness(colour, brightness); - } - - static inline uint8_t GetColourBrightness(Colour colour) - { - uint8_t rgb_max = std::max(colour.r, std::max(colour.g, colour.b)); - - /* Black pixel (8bpp or old 32bpp image), so use default value */ - if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS; - - return rgb_max; - } }; #endif /* BLITTER_32BPP_BASE_HPP */ diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index a23624ab2c..76330db6b8 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -113,7 +113,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL draw:; switch (mode) { - case CM_BM_TINT_REMAP: + case BlitterMode::CMTintRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -144,7 +144,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } break; - case BM_COLOUR_REMAP: + case BlitterMode::ColourRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -153,7 +153,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL *dst = src_px->data; } else { uint r = remap[GB(m, 0, 8)]; - if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); + if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); } dst++; src_px++; @@ -166,7 +166,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst); } else { uint r = remap[GB(m, 0, 8)]; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); } dst++; src_px++; @@ -175,7 +175,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } break; - case BM_CRASH_REMAP: + case BlitterMode::CrashRemap: if (src_px->a == 255) { do { uint m = *src_n; @@ -184,7 +184,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL *dst = ComposeColourRGBA(g, g, g, src_px->a, *dst); } else { uint r = remap[GB(m, 0, 8)]; - if (r != 0) *dst = this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); + if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)); } dst++; src_px++; @@ -200,7 +200,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } } else { uint r = remap[GB(m, 0, 8)]; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst); } dst++; src_px++; @@ -209,7 +209,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: do { *dst = Colour(0, 0, 0); dst++; @@ -218,7 +218,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } while (--n != 0); break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent */ src_n += n; if (src_px->a == 255) { @@ -236,7 +236,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ src_n += n; if (src_px->a != 0) { @@ -257,7 +257,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL do { if (Tpal_to_rgb && *src_n != 0) { /* Convert the mapping channel to a RGB value */ - *dst = this->AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8)).data; + *dst = AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8)).data; } else { *dst = src_px->data; } @@ -269,7 +269,7 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL do { if (Tpal_to_rgb && *src_n != 0) { /* Convert the mapping channel to a RGB value */ - Colour colour = this->AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8)); + Colour colour = AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8)); *dst = ComposeColourRGBANoCheck(colour.r, colour.g, colour.b, src_px->a, *dst); } else { *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst); @@ -294,13 +294,13 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, { switch (mode) { default: NOT_REACHED(); - case BM_NORMAL: Draw(bp, zoom); return; - case BM_COLOUR_REMAP: Draw(bp, zoom); return; - case CM_BM_TINT_REMAP: Draw(bp, zoom); return; - case BM_TRANSPARENT: Draw(bp, zoom); return; - case BM_TRANSPARENT_REMAP: Draw(bp, zoom); return; - case BM_CRASH_REMAP: Draw(bp, zoom); return; - case BM_BLACK_REMAP: Draw(bp, zoom); return; + case BlitterMode::Normal: Draw(bp, zoom); return; + case BlitterMode::ColourRemap: Draw(bp, zoom); return; + case BlitterMode::CMTintRemap: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, zoom); return; + case BlitterMode::TransparentRemap: Draw(bp, zoom); return; + case BlitterMode::CrashRemap: Draw(bp, zoom); return; + case BlitterMode::BlackRemap: Draw(bp, zoom); return; } } @@ -324,7 +324,7 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const /* streams of pixels (a, r, g, b channels) * * stored in separated stream so data are always aligned on 4B boundary */ - Colour *dst_px_orig[ZOOM_LVL_END]; + std::array, ZOOM_LVL_END> dst_px_orig; /* interleaved stream of 'm' channel and 'n' channel * 'n' is number of following pixels with the same alpha channel class @@ -332,7 +332,7 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const * * it has to be stored in one stream so fewer registers are used - * x86 has problems with register allocation even with this solution */ - uint16_t *dst_n_orig[ZOOM_LVL_END]; + std::array, ZOOM_LVL_END> dst_n_orig; /* lengths of streams */ uint32_t lengths[ZOOM_LVL_END][2]; @@ -354,11 +354,11 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const uint size = src_orig->height * src_orig->width; - dst_px_orig[z] = CallocT(size + src_orig->height * 2); - dst_n_orig[z] = CallocT(size * 2 + src_orig->height * 4 * 2); + dst_px_orig[z] = std::make_unique(size + src_orig->height * 2); + dst_n_orig[z] = std::make_unique(size * 2 + src_orig->height * 4 * 2); - uint32_t *dst_px_ln = (uint32_t *)dst_px_orig[z]; - uint32_t *dst_n_ln = (uint32_t *)dst_n_orig[z]; + uint32_t *dst_px_ln = reinterpret_cast(dst_px_orig[z].get()); + uint32_t *dst_n_ln = reinterpret_cast(dst_n_orig[z].get()); const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data; @@ -400,7 +400,7 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const if (Tpal_to_rgb) { /* Pre-convert the mapping channel to a RGB value */ - Colour colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max); + Colour colour = AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max); dst_px->r = colour.r; dst_px->g = colour.g; dst_px->b = colour.b; @@ -439,8 +439,8 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const dst_n_ln = (uint32_t *)dst_n; } - lengths[z][0] = (uint8_t *)dst_px_ln - (uint8_t *)dst_px_orig[z]; // all are aligned to 4B boundary - lengths[z][1] = (uint8_t *)dst_n_ln - (uint8_t *)dst_n_orig[z]; + lengths[z][0] = reinterpret_cast(dst_px_ln) - reinterpret_cast(dst_px_orig[z].get()); // all are aligned to 4B boundary + lengths[z][1] = reinterpret_cast(dst_n_ln) - reinterpret_cast(dst_n_orig[z].get()); } uint len = 0; // total length of data @@ -462,11 +462,8 @@ template Sprite *Blitter_32bppOptimized::EncodeInternal(const dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1]; dst->offset[z][1] = lengths[z][0] + dst->offset[z][0]; - memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]); - memcpy(dst->data + dst->offset[z][1], dst_n_orig[z], lengths[z][1]); - - free(dst_px_orig[z]); - free(dst_n_orig[z]); + memcpy(dst->data + dst->offset[z][0], dst_px_orig[z].get(), lengths[z][0]); + memcpy(dst->data + dst->offset[z][1], dst_n_orig[z].get(), lengths[z][1]); } return dest_sprite; diff --git a/src/blitter/32bpp_optimized.hpp b/src/blitter/32bpp_optimized.hpp index 862c1d9e7e..ccd7d9ac2e 100644 --- a/src/blitter/32bpp_optimized.hpp +++ b/src/blitter/32bpp_optimized.hpp @@ -37,7 +37,7 @@ protected: class FBlitter_32bppOptimized : public BlitterFactory { public: FBlitter_32bppOptimized() : BlitterFactory("32bpp-optimized", "32bpp Optimized Blitter (no palette animation)") {} - Blitter *CreateInstance() override { return new Blitter_32bppOptimized(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_32BPP_OPTIMIZED_HPP */ diff --git a/src/blitter/32bpp_simple.cpp b/src/blitter/32bpp_simple.cpp index 99140096c2..3a0bcee4c6 100644 --- a/src/blitter/32bpp_simple.cpp +++ b/src/blitter/32bpp_simple.cpp @@ -39,7 +39,7 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo for (int x = 0; x < bp->width; x++) { switch (mode) { - case CM_BM_TINT_REMAP: + case BlitterMode::CMTintRemap: /* In case the m-channel is zero, do not remap this pixel in any way */ if (src->m == 0) { if (src->a != 0) *dst = citymania::Remap32RGBA(src->r, src->g, src->b, src->a, *dst, bp->remap); @@ -48,40 +48,40 @@ void Blitter_32bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoo } break; - case BM_COLOUR_REMAP: + case BlitterMode::ColourRemap: /* In case the m-channel is zero, do not remap this pixel in any way */ if (src->m == 0) { if (src->a != 0) *dst = ComposeColourRGBA(src->r, src->g, src->b, src->a, *dst); } else { - if (bp->remap[src->m] != 0) *dst = ComposeColourPA(this->AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); + if (bp->remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); } break; - case BM_CRASH_REMAP: + case BlitterMode::CrashRemap: if (src->m == 0) { if (src->a != 0) { uint8_t g = MakeDark(src->r, src->g, src->b); *dst = ComposeColourRGBA(g, g, g, src->a, *dst); } } else { - if (bp->remap[src->m] != 0) *dst = ComposeColourPA(this->AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); + if (bp->remap[src->m] != 0) *dst = ComposeColourPA(AdjustBrightness(this->LookupColourInPalette(bp->remap[src->m]), src->v), src->a, *dst); } break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: if (src->a != 0) { *dst = Colour(0, 0, 0); } break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent */ if (src->a != 0) { *dst = MakeTransparent(*dst, 192); } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ if (src->a != 0) { *dst = this->LookupColourInPalette(bp->remap[GetNearestColourIndex(*dst)]); @@ -156,7 +156,7 @@ Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite dst[i].v = rgb_max; /* Pre-convert the mapping channel to a RGB value */ - Colour colour = this->AdjustBrightness(this->LookupColourInPalette(src->m), dst[i].v); + Colour colour = AdjustBrightness(this->LookupColourInPalette(src->m), dst[i].v); dst[i].r = colour.r; dst[i].g = colour.g; dst[i].b = colour.b; diff --git a/src/blitter/32bpp_simple.hpp b/src/blitter/32bpp_simple.hpp index 902af2905a..cbecf3c44b 100644 --- a/src/blitter/32bpp_simple.hpp +++ b/src/blitter/32bpp_simple.hpp @@ -35,7 +35,7 @@ public: class FBlitter_32bppSimple : public BlitterFactory { public: FBlitter_32bppSimple() : BlitterFactory("32bpp-simple", "32bpp Simple Blitter (no palette animation)") {} - Blitter *CreateInstance() override { return new Blitter_32bppSimple(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_32BPP_SIMPLE_HPP */ diff --git a/src/blitter/32bpp_sse2.cpp b/src/blitter/32bpp_sse2.cpp index 2b4505253b..c0ccb269c3 100644 --- a/src/blitter/32bpp_sse2.cpp +++ b/src/blitter/32bpp_sse2.cpp @@ -35,8 +35,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri } /* Calculate sizes and allocate. */ - SpriteData sd; - memset(&sd, 0, sizeof(sd)); + SpriteData sd{}; uint all_sprites_size = 0; for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { const SpriteLoader::Sprite *src_sprite = &sprite[z]; @@ -81,7 +80,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri /* Get brightest value (or default brightness if it's a black pixel). */ const uint8_t rgb_max = std::max({src->r, src->g, src->b}); - dst_mv->v = (rgb_max == 0) ? Blitter_32bppBase::DEFAULT_BRIGHTNESS : rgb_max; + dst_mv->v = (rgb_max == 0) ? DEFAULT_BRIGHTNESS : rgb_max; /* Pre-convert the mapping channel to a RGB value. */ const Colour colour = AdjustBrightneSSE(Blitter_32bppBase::LookupColourInPalette(src->m), dst_mv->v); @@ -92,7 +91,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri dst_rgba->r = src->r; dst_rgba->g = src->g; dst_rgba->b = src->b; - dst_mv->v = Blitter_32bppBase::DEFAULT_BRIGHTNESS; + dst_mv->v = DEFAULT_BRIGHTNESS; } } else { dst_rgba->data = 0; @@ -129,10 +128,10 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri } /* Store sprite flags. */ - sd.flags = SF_NONE; - if (has_translucency) sd.flags |= SF_TRANSLUCENT; - if (!has_remap) sd.flags |= SF_NO_REMAP; - if (!has_anim) sd.flags |= SF_NO_ANIM; + sd.flags = {}; + if (has_translucency) sd.flags.Set(SpriteFlag::Translucent); + if (!has_remap) sd.flags.Set(SpriteFlag::NoRemap); + if (!has_anim) sd.flags.Set(SpriteFlag::NoAnim); memcpy(dst_sprite->data, &sd, sizeof(SpriteData)); return dst_sprite; diff --git a/src/blitter/32bpp_sse2.hpp b/src/blitter/32bpp_sse2.hpp index ebbb31e059..4d2c1f10bc 100644 --- a/src/blitter/32bpp_sse2.hpp +++ b/src/blitter/32bpp_sse2.hpp @@ -38,14 +38,14 @@ public: static_assert(sizeof(MapValue) == 2); /** Helper for creating specialised functions for specific optimisations. */ - enum ReadMode { + enum ReadMode : uint8_t { RM_WITH_SKIP, ///< Use normal code for skipping empty pixels. RM_WITH_MARGIN, ///< Use cached number of empty pixels at begin and end of line to reduce work. RM_NONE, ///< No specialisation. }; /** Helper for creating specialised functions for the case where the sprite width is odd or even. */ - enum BlockType { + enum BlockType : uint8_t { BT_EVEN, ///< An even number of pixels in the width; no need for a special case for the last pixel. BT_ODD, ///< An odd number of pixels in the width; special case for the last pixel. BT_NONE, ///< No specialisation for either case. @@ -56,31 +56,30 @@ public: * - calculations (alpha blending), * - heavy branching (remap lookups and animation buffer handling). */ - enum SpriteFlags { - SF_NONE = 0, - SF_TRANSLUCENT = 1 << 1, ///< The sprite has at least 1 translucent pixel. - SF_NO_REMAP = 1 << 2, ///< The sprite has no remappable colour pixel. - SF_NO_ANIM = 1 << 3, ///< The sprite has no palette animated pixel. + enum class SpriteFlag : uint8_t { + Translucent, ///< The sprite has at least 1 translucent pixel. + NoRemap, ///< The sprite has no remappable colour pixel. + NoAnim, ///< The sprite has no palette animated pixel. }; + using SpriteFlags = EnumBitSet; + /** Data stored about a (single) sprite. */ struct SpriteInfo { - uint32_t sprite_offset; ///< The offset to the sprite data. - uint32_t mv_offset; ///< The offset to the map value data. - uint16_t sprite_line_size; ///< The size of a single line (pitch). - uint16_t sprite_width; ///< The width of the sprite. + uint32_t sprite_offset = 0; ///< The offset to the sprite data. + uint32_t mv_offset = 0; ///< The offset to the map value data. + uint16_t sprite_line_size = 0; ///< The size of a single line (pitch). + uint16_t sprite_width = 0; ///< The width of the sprite. }; struct SpriteData { - SpriteFlags flags; - SpriteInfo infos[ZOOM_LVL_END]; + SpriteFlags flags{}; + std::array infos{}; uint8_t data[]; ///< Data, all zoomlevels. }; Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); }; -DECLARE_ENUM_AS_BIT_SET(Blitter_32bppSSE_Base::SpriteFlags); - /** The SSE2 32 bpp blitter (without palette animation). */ class Blitter_32bppSSE2 : public Blitter_32bppSimple, public Blitter_32bppSSE_Base { public: @@ -99,7 +98,7 @@ public: class FBlitter_32bppSSE2 : public BlitterFactory { public: FBlitter_32bppSSE2() : BlitterFactory("32bpp-sse2", "32bpp SSE2 Blitter (no palette animation)", HasCPUIDFlag(1, 3, 26)) {} - Blitter *CreateInstance() override { return new Blitter_32bppSSE2(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* WITH_SSE */ diff --git a/src/blitter/32bpp_sse4.hpp b/src/blitter/32bpp_sse4.hpp index 7d8798c697..a46e624b02 100644 --- a/src/blitter/32bpp_sse4.hpp +++ b/src/blitter/32bpp_sse4.hpp @@ -39,7 +39,7 @@ public: class FBlitter_32bppSSE4: public BlitterFactory { public: FBlitter_32bppSSE4() : BlitterFactory("32bpp-sse4", "32bpp SSE4 Blitter (no palette animation)", HasCPUIDFlag(1, 2, 19)) {} - Blitter *CreateInstance() override { return new Blitter_32bppSSE4(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* WITH_SSE */ diff --git a/src/blitter/32bpp_sse_func.hpp b/src/blitter/32bpp_sse_func.hpp index a681f552f5..d8c456295d 100644 --- a/src/blitter/32bpp_sse_func.hpp +++ b/src/blitter/32bpp_sse_func.hpp @@ -125,7 +125,7 @@ INTERNAL_LINKAGE Colour ReallyAdjustBrightness(Colour colour, uint8_t brightness uint64_t c16 = colour.b | (uint64_t) colour.g << 16 | (uint64_t) colour.r << 32; c16 *= brightness; uint64_t c16_ob = c16; // Helps out of order execution. - c16 /= Blitter_32bppBase::DEFAULT_BRIGHTNESS; + c16 /= DEFAULT_BRIGHTNESS; c16 &= 0x01FF01FF01FFULL; /* Sum overbright (maximum for each rgb is 508, 9 bits, -255 is changed in -256 so we just have to take the 8 lower bits into account). */ @@ -157,7 +157,7 @@ IGNORE_UNINITIALIZED_WARNING_STOP INTERNAL_LINKAGE inline Colour AdjustBrightneSSE(Colour colour, uint8_t brightness) { /* Shortcut for normal brightness. */ - if (brightness == Blitter_32bppBase::DEFAULT_BRIGHTNESS) return colour; + if (brightness == DEFAULT_BRIGHTNESS) return colour; return ReallyAdjustBrightness(colour, brightness); } @@ -173,7 +173,7 @@ INTERNAL_LINKAGE inline __m128i AdjustBrightnessOfTwoPixels([[maybe_unused]] __m * OK, not a 1 but DEFAULT_BRIGHTNESS to compensate the div. */ brightness &= 0xFF00FF00; - brightness += Blitter_32bppBase::DEFAULT_BRIGHTNESS; + brightness += DEFAULT_BRIGHTNESS; __m128i colAB = _mm_unpacklo_epi8(from, _mm_setzero_si128()); __m128i briAB = _mm_cvtsi32_si128(brightness); @@ -261,13 +261,13 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel for (int y = bp->height; y != 0; y--) { Colour *dst = dst_line; const Colour *src = src_rgba_line + META_LENGTH; - if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv = src_mv_line; + if (mode == BlitterMode::ColourRemap || mode == BlitterMode::CrashRemap) src_mv = src_mv_line; if (read_mode == RM_WITH_MARGIN) { assert(bt_last == BT_NONE); // or you must ensure block type is preserved src += src_rgba_line[0].data; dst += src_rgba_line[0].data; - if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv += src_rgba_line[0].data; + if (mode == BlitterMode::ColourRemap || mode == BlitterMode::CrashRemap) src_mv += src_rgba_line[0].data; const int width_diff = si->sprite_width - bp->width; effective_width = bp->width - (int) src_rgba_line[0].data; const int delta_diff = (int) src_rgba_line[1].data - width_diff; @@ -302,7 +302,7 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_COLOUR_REMAP: + case BlitterMode::ColourRemap: #if (SSE_VERSION >= 3) for (uint x = (uint) effective_width / 2; x > 0; x--) { __m128i srcABCD = _mm_loadl_epi64((const __m128i*) src); @@ -384,7 +384,7 @@ bmcr_alpha_blend_single: } break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent. */ for (uint x = (uint) bp->width / 2; x > 0; x--) { __m128i srcABCD = _mm_loadl_epi64((const __m128i*) src); @@ -401,7 +401,7 @@ bmcr_alpha_blend_single: } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ for (uint x = (uint) bp->width; x > 0; x--) { if (src->a != 0) { @@ -413,7 +413,7 @@ bmcr_alpha_blend_single: } break; - case CM_BM_TINT_REMAP: + case BlitterMode::CMTintRemap: for (uint x = (uint) bp->width; x > 0; x--) { if (src_mv->m == 0) { if (src->a != 0) { @@ -429,7 +429,7 @@ bmcr_alpha_blend_single: } break; - case BM_CRASH_REMAP: + case BlitterMode::CrashRemap: for (uint x = (uint) bp->width; x > 0; x--) { if (src_mv->m == 0) { if (src->a != 0) { @@ -438,7 +438,7 @@ bmcr_alpha_blend_single: } } else { uint r = remap[src_mv->m]; - if (r != 0) *dst = ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst); + if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), src_mv->v), src->a, *dst); } src_mv++; dst++; @@ -446,7 +446,7 @@ bmcr_alpha_blend_single: } break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: for (uint x = (uint) bp->width; x > 0; x--) { if (src->a != 0) { *dst = Colour(0, 0, 0); @@ -459,7 +459,7 @@ bmcr_alpha_blend_single: } next_line: - if (mode == BM_COLOUR_REMAP || mode == BM_CRASH_REMAP) src_mv_line += si->sprite_width; + if (mode == BlitterMode::ColourRemap || mode == BlitterMode::CrashRemap) src_mv_line += si->sprite_width; src_rgba_line = (const Colour*) ((const uint8_t*) src_rgba_line + si->sprite_line_size); dst_line += bp->pitch; } @@ -487,36 +487,36 @@ void Blitter_32bppSSE4::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL bm_normal: const BlockType bt_last = (BlockType) (bp->width & 1); switch (bt_last) { - default: Draw(bp, zoom); return; - case BT_ODD: Draw(bp, zoom); return; + default: Draw(bp, zoom); return; + case BT_ODD: Draw(bp, zoom); return; } } else { - if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags & SF_TRANSLUCENT) { - Draw(bp, zoom); + if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags.Test(SpriteFlag::Translucent)) { + Draw(bp, zoom); } else { - Draw(bp, zoom); + Draw(bp, zoom); } return; } break; } - case BM_COLOUR_REMAP: - if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags & SF_NO_REMAP) goto bm_normal; + case BlitterMode::ColourRemap: + if (((const Blitter_32bppSSE_Base::SpriteData *) bp->sprite)->flags.Test(SpriteFlag::NoRemap)) goto bm_normal; if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { - Draw(bp, zoom); return; + Draw(bp, zoom); return; } else { - Draw(bp, zoom); return; + Draw(bp, zoom); return; } - case CM_BM_TINT_REMAP: + case BlitterMode::Transparent: Draw(bp, zoom); return; + case BlitterMode::TransparentRemap: Draw(bp, zoom); return; + case BlitterMode::CrashRemap: Draw(bp, zoom); return; + case BlitterMode::BlackRemap: Draw(bp, zoom); return; + case BlitterMode::CMTintRemap: if (bp->skip_left != 0 || bp->width <= MARGIN_REMAP_THRESHOLD) { Draw(bp, zoom); return; } else { Draw(bp, zoom); return; } - case BM_TRANSPARENT: Draw(bp, zoom); return; - case BM_TRANSPARENT_REMAP: Draw(bp, zoom); return; - case BM_CRASH_REMAP: Draw(bp, zoom); return; - case BM_BLACK_REMAP: Draw(bp, zoom); return; } } #endif /* FULL_ANIMATION */ diff --git a/src/blitter/32bpp_sse_type.h b/src/blitter/32bpp_sse_type.h index 57de1d6d58..c9a05e5c08 100644 --- a/src/blitter/32bpp_sse_type.h +++ b/src/blitter/32bpp_sse_type.h @@ -28,8 +28,8 @@ #endif #define META_LENGTH 2 ///< Number of uint32_t inserted before each line of pixels in a sprite. -#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_8X ? 8 : 4) ///< Minimum width to use margins with BM_NORMAL. -#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BM_COLOUR_REMAP. +#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_8X ? 8 : 4) ///< Minimum width to use margins with BlitterMode::Normal. +#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BlitterMode::ColourRemap. #undef ALIGN diff --git a/src/blitter/32bpp_ssse3.hpp b/src/blitter/32bpp_ssse3.hpp index 4ee475d45f..323b77d825 100644 --- a/src/blitter/32bpp_ssse3.hpp +++ b/src/blitter/32bpp_ssse3.hpp @@ -39,7 +39,7 @@ public: class FBlitter_32bppSSSE3: public BlitterFactory { public: FBlitter_32bppSSSE3() : BlitterFactory("32bpp-ssse3", "32bpp SSSE3 Blitter (no palette animation)", HasCPUIDFlag(1, 2, 9)) {} - Blitter *CreateInstance() override { return new Blitter_32bppSSSE3(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* WITH_SSE */ diff --git a/src/blitter/40bpp_anim.cpp b/src/blitter/40bpp_anim.cpp index 7a4edf038f..c5c68208a1 100644 --- a/src/blitter/40bpp_anim.cpp +++ b/src/blitter/40bpp_anim.cpp @@ -115,7 +115,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel assert(VideoDriver::GetInstance()->GetAnimBuffer() != nullptr); uint8_t *anim = VideoDriver::GetInstance()->GetAnimBuffer() + ((uint32_t *)bp->dst - (uint32_t *)_screen.dst_ptr) + bp->top * bp->pitch + bp->left; - /* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */ + /* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */ const uint8_t *remap = bp->remap; for (int y = 0; y < bp->height; y++) { @@ -183,18 +183,18 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel draw:; switch (mode) { - case BM_COLOUR_REMAP: - case CM_BM_TINT_REMAP: - case BM_CRASH_REMAP: + case BlitterMode::ColourRemap: + case BlitterMode::CMTintRemap; + case BlitterMode::CrashRemap: if (src_px->a == 255) { do { uint8_t m = GB(*src_n, 0, 8); /* In case the m-channel is zero, only apply the crash remap by darkening the RGB colour. */ if (m == 0) { switch (mode) { - case BM_COLOUR_REMAP: *dst = *src_px; break; - case CM_BM_TINT_REMAP: *dst = citymania::Remap32RGB(src_px->r, src_px->g, src_px->b, remap); break; - case BM_CRASH_REMAP: *dst = this->MakeDark(*src_px); break; + case BlitterMode::ColourRemap: *dst = *src_px; break; + case BlitterMode::CMTintRemap: *dst = citymania::Remap32RGB(src_px->r, src_px->g, src_px->b, remap); break; + case BlitterMode::CrashRemap: *dst = this->MakeDark(*src_px); break; default: NOT_REACHED(); } *anim = 0; @@ -216,9 +216,9 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel Colour b = this->RealizeBlendedColour(*anim, *dst); if (m == 0) { switch (mode) { - case BM_COLOUR_REMAP: *dst = this->ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, b); break; - case CM_BM_TINT_REMAP: *dst = citymania::Remap32RGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, b, remap); break; - case BM_CRASH_REMAP: *dst = this->ComposeColourPANoCheck(this->MakeDark(*src_px), src_px->a, b); break; + case BlitterMode::ColourRemap: *dst = this->ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, b); break; + case BlitterMode::CMTintRemap: *dst = citymania::Remap32RGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, b, remap); break; + case BlitterMode::CrashRemap: *dst = this->ComposeColourPANoCheck(this->MakeDark(*src_px), src_px->a, b); break; default: NOT_REACHED(); } *anim = 0; @@ -237,7 +237,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: do { *anim++ = 0; *dst++ = _black_colour; @@ -246,7 +246,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } while (--n != 0); break; - case BM_TRANSPARENT: + case BlitterMode::Transparent: /* Make the current colour a bit more black, so it looks like this image is transparent */ src_n += n; if (src_px->a == 255) { @@ -255,7 +255,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel /* If the anim buffer contains a color value, the image composition will * only look at the RGB brightness value. As such, we can simply darken the * RGB value to darken the anim color. */ - Colour b = *anim != 0 ? Colour(this->GetColourBrightness(*dst), 0, 0) : *dst; + Colour b = *anim != 0 ? Colour(GetColourBrightness(*dst), 0, 0) : *dst; *dst = this->MakeTransparent(b, 3, 4); anim++; dst++; @@ -272,7 +272,7 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel } break; - case BM_TRANSPARENT_REMAP: + case BlitterMode::TransparentRemap: /* Apply custom transparency remap. */ src_n += n; if (src_px->a != 0) { @@ -351,13 +351,13 @@ void Blitter_40bppAnim::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomL switch (mode) { default: NOT_REACHED(); - case BM_NORMAL: Draw (bp, zoom); return; - case BM_COLOUR_REMAP: Draw(bp, zoom); return; - case CM_BM_TINT_REMAP: Draw(bp, zoom); return; - case BM_TRANSPARENT: Draw (bp, zoom); return; - case BM_TRANSPARENT_REMAP: Draw(bp, zoom); return; - case BM_CRASH_REMAP: Draw (bp, zoom); return; - case BM_BLACK_REMAP: Draw (bp, zoom); return; + case BlitterMode::Normal: Draw(bp, zoom); return; + case BlitterMode::ColourRemap: Draw(bp, zoom); return; + case BlitterMode::CMTintRemap: Draw(bp, zoom); return; + case BlitterMode::Transparent: Draw(bp, zoom); return; + case BlitterMode::TransparentRemap: Draw(bp, zoom); return; + case BlitterMode::CrashRemap: Draw(bp, zoom); return; + case BlitterMode::BlackRemap: Draw(bp, zoom); return; } } @@ -378,7 +378,7 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height, * RGB value to darken the anim color. */ do { for (int i = 0; i != width; i++) { - Colour b = *anim != 0 ? Colour(this->GetColourBrightness(*udst), 0, 0) : *udst; + Colour b = *anim != 0 ? Colour(GetColourBrightness(*udst), 0, 0) : *udst; *udst = MakeTransparent(b, 154); udst++; anim++; @@ -541,7 +541,7 @@ size_t Blitter_40bppAnim::BufferSize(uint width, uint height) Blitter::PaletteAnimation Blitter_40bppAnim::UsePaletteAnimation() { - return Blitter::PALETTE_ANIMATION_VIDEO_BACKEND; + return Blitter::PaletteAnimation::VideoBackend; } bool Blitter_40bppAnim::NeedsAnimationBuffer() diff --git a/src/blitter/40bpp_anim.hpp b/src/blitter/40bpp_anim.hpp index 0174735c3a..385a4fe555 100644 --- a/src/blitter/40bpp_anim.hpp +++ b/src/blitter/40bpp_anim.hpp @@ -54,7 +54,7 @@ protected: public: FBlitter_40bppAnim() : BlitterFactory("40bpp-anim", "40bpp Animation Blitter (OpenGL)") {} - Blitter *CreateInstance() override { return new Blitter_40bppAnim(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_40BPP_OPTIMIZED_HPP */ diff --git a/src/blitter/8bpp_base.cpp b/src/blitter/8bpp_base.cpp index a972a27d3d..a936138a1a 100644 --- a/src/blitter/8bpp_base.cpp +++ b/src/blitter/8bpp_base.cpp @@ -156,5 +156,5 @@ void Blitter_8bppBase::PaletteAnimate(const Palette &) Blitter::PaletteAnimation Blitter_8bppBase::UsePaletteAnimation() { - return Blitter::PALETTE_ANIMATION_VIDEO_BACKEND; + return Blitter::PaletteAnimation::VideoBackend; } diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 216505495d..b646dc6f89 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -84,9 +84,9 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z width -= pixels; switch (mode) { - case CM_BM_TINT_REMAP: - case BM_COLOUR_REMAP: - case BM_CRASH_REMAP: { + case BlitterMode::ColourRemap: + case BlitterMode::CMTintRemap: + case BlitterMode::CrashRemap: { const uint8_t *remap = bp->remap; do { uint m = remap[*src]; @@ -96,13 +96,13 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z break; } - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: MemSetT(dst, 0, pixels); dst += pixels; break; - case BM_TRANSPARENT: - case BM_TRANSPARENT_REMAP: { + case BlitterMode::Transparent: + case BlitterMode::TransparentRemap: { const uint8_t *remap = bp->remap; src += pixels; do { @@ -149,8 +149,7 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri * memory around as this function is called quite often * and the memory usage is quite low. */ static ReusableBuffer temp_buffer; - SpriteData *temp_dst = (SpriteData *)temp_buffer.Allocate(memory); - memset(temp_dst, 0, sizeof(*temp_dst)); + SpriteData *temp_dst = reinterpret_cast(temp_buffer.ZeroAllocate(memory)); uint8_t *dst = temp_dst->data; /* Make the sprites per zoom-level */ diff --git a/src/blitter/8bpp_optimized.hpp b/src/blitter/8bpp_optimized.hpp index c3c8e381d0..8452111d73 100644 --- a/src/blitter/8bpp_optimized.hpp +++ b/src/blitter/8bpp_optimized.hpp @@ -32,7 +32,7 @@ public: class FBlitter_8bppOptimized : public BlitterFactory { public: FBlitter_8bppOptimized() : BlitterFactory("8bpp-optimized", "8bpp Optimized Blitter (compression + all-ZoomLevel cache)") {} - Blitter *CreateInstance() override { return new Blitter_8bppOptimized(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_8BPP_OPTIMIZED_HPP */ diff --git a/src/blitter/8bpp_simple.cpp b/src/blitter/8bpp_simple.cpp index 3956de7eeb..03541826f2 100644 --- a/src/blitter/8bpp_simple.cpp +++ b/src/blitter/8bpp_simple.cpp @@ -36,18 +36,18 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom uint colour = 0; switch (mode) { - case CM_BM_TINT_REMAP: - case BM_COLOUR_REMAP: - case BM_CRASH_REMAP: + case BlitterMode::ColourRemap: + case BlitterMode::CMTintRemap: + case BlitterMode::CrashRemap: colour = bp->remap[*src]; break; - case BM_TRANSPARENT: - case BM_TRANSPARENT_REMAP: + case BlitterMode::Transparent: + case BlitterMode::TransparentRemap: if (*src != 0) colour = bp->remap[*dst]; break; - case BM_BLACK_REMAP: + case BlitterMode::BlackRemap: if (*src != 0) *dst = 0; break; diff --git a/src/blitter/8bpp_simple.hpp b/src/blitter/8bpp_simple.hpp index 12e7f0fd8e..77dcf16a84 100644 --- a/src/blitter/8bpp_simple.hpp +++ b/src/blitter/8bpp_simple.hpp @@ -26,7 +26,7 @@ public: class FBlitter_8bppSimple : public BlitterFactory { public: FBlitter_8bppSimple() : BlitterFactory("8bpp-simple", "8bpp Simple Blitter (relative slow, but never wrong)") {} - Blitter *CreateInstance() override { return new Blitter_8bppSimple(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_8BPP_SIMPLE_HPP */ diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index 827e719115..13943980e3 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -14,14 +14,14 @@ #include "../spriteloader/spriteloader.hpp" /** The modes of blitting we can do. */ -enum BlitterMode { - BM_NORMAL, ///< Perform the simple blitting. - BM_COLOUR_REMAP, ///< Perform a colour remapping. - BM_TRANSPARENT, ///< Perform transparency darkening remapping. - BM_TRANSPARENT_REMAP, ///< Perform transparency colour remapping. - BM_CRASH_REMAP, ///< Perform a crash remapping. - BM_BLACK_REMAP, ///< Perform remapping to a completely blackened sprite - CM_BM_TINT_REMAP, ///< Perform tinting. +enum class BlitterMode : uint8_t { + Normal, ///< Perform the simple blitting. + ColourRemap, ///< Perform a colour remapping. + Transparent, ///< Perform transparency darkening remapping. + TransparentRemap, ///< Perform transparency colour remapping. + CrashRemap, ///< Perform a crash remapping. + BlackRemap, ///< Perform remapping to a completely blackened sprite + CMTintRemap, ///< Perform tinting. }; /** @@ -48,10 +48,10 @@ public: }; /** Types of palette animation. */ - enum PaletteAnimation { - PALETTE_ANIMATION_NONE, ///< No palette animation - PALETTE_ANIMATION_VIDEO_BACKEND, ///< Palette animation should be done by video backend (8bpp only!) - PALETTE_ANIMATION_BLITTER, ///< The blitter takes care of the palette animation + enum class PaletteAnimation : uint8_t { + None, ///< No palette animation + VideoBackend, ///< Palette animation should be done by video backend (8bpp only!) + Blitter, ///< The blitter takes care of the palette animation }; /** diff --git a/src/blitter/factory.hpp b/src/blitter/factory.hpp index 5071a168ae..0dceea90f2 100644 --- a/src/blitter/factory.hpp +++ b/src/blitter/factory.hpp @@ -39,10 +39,10 @@ private: * Get the currently active blitter. * @return The currently active blitter. */ - static Blitter **GetActiveBlitter() + static std::unique_ptr &GetActiveBlitter() { - static Blitter *s_blitter = nullptr; - return &s_blitter; + static std::unique_ptr s_blitter = nullptr; + return s_blitter; } protected: @@ -98,12 +98,10 @@ public: BlitterFactory *b = GetBlitterFactory(name); if (b == nullptr) return nullptr; - Blitter *newb = b->CreateInstance(); - delete *GetActiveBlitter(); - *GetActiveBlitter() = newb; + GetActiveBlitter() = b->CreateInstance(); - Debug(driver, 1, "Successfully {} blitter '{}'", name.empty() ? "probed" : "loaded", newb->GetName()); - return newb; + Debug(driver, 1, "Successfully {} blitter '{}'", name.empty() ? "probed" : "loaded", GetCurrentBlitter()->GetName()); + return GetCurrentBlitter(); } /** @@ -137,7 +135,7 @@ public: */ static Blitter *GetCurrentBlitter() { - return *GetActiveBlitter(); + return GetActiveBlitter().get(); } /** @@ -175,7 +173,7 @@ public: /** * Create an instance of this Blitter-class. */ - virtual Blitter *CreateInstance() = 0; + virtual std::unique_ptr CreateInstance() = 0; }; extern std::string _ini_blitter; diff --git a/src/blitter/null.hpp b/src/blitter/null.hpp index a0ecfbe8db..15dc722929 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -29,7 +29,7 @@ public: void ScrollBuffer(void *, int &, int &, int &, int &, int, int) override {}; size_t BufferSize(uint, uint) override { return 0; }; void PaletteAnimate(const Palette &) override { }; - Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PALETTE_ANIMATION_NONE; }; + Blitter::PaletteAnimation UsePaletteAnimation() override { return Blitter::PaletteAnimation::None; }; std::string_view GetName() override { return "null"; } }; @@ -38,7 +38,7 @@ public: class FBlitter_Null : public BlitterFactory { public: FBlitter_Null() : BlitterFactory("null", "Null Blitter (does nothing)") {} - Blitter *CreateInstance() override { return new Blitter_Null(); } + std::unique_ptr CreateInstance() override { return std::make_unique(); } }; #endif /* BLITTER_NULL_HPP */ diff --git a/src/bootstrap_gui.cpp b/src/bootstrap_gui.cpp index a9abfb9303..44bce54ca8 100644 --- a/src/bootstrap_gui.cpp +++ b/src/bootstrap_gui.cpp @@ -9,6 +9,7 @@ #include "stdafx.h" #include "base_media_base.h" +#include "base_media_graphics.h" #include "blitter/factory.hpp" #include "error_func.h" @@ -43,7 +44,7 @@ static constexpr NWidgetPart _background_widgets[] = { static WindowDesc _background_desc( WDP_MANUAL, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, - WDF_NO_CLOSE, + WindowDefaultFlag::NoClose, _background_widgets ); @@ -53,7 +54,7 @@ public: BootstrapBackground() : Window(_background_desc) { this->InitNested(0); - CLRBITS(this->flags, WF_WHITE_BORDER); + this->flags.Reset(WindowFlag::WhiteBorder); ResizeWindow(this, _screen.width, _screen.height); } @@ -67,11 +68,11 @@ public: /** Nested widgets for the error window. */ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = { NWidget(NWID_HORIZONTAL), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_BEM_CAPTION), SetDataTip(STR_MISSING_GRAPHICS_ERROR_TITLE, STR_NULL), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_BEM_CAPTION), SetStringTip(STR_MISSING_GRAPHICS_ERROR_TITLE), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_BEM_MESSAGE), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BEM_QUIT), SetDataTip(STR_MISSING_GRAPHICS_ERROR_QUIT, STR_NULL), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BEM_QUIT), SetStringTip(STR_MISSING_GRAPHICS_ERROR_QUIT), SetFill(1, 0), EndContainer(), }; @@ -79,7 +80,7 @@ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = { static WindowDesc _bootstrap_errmsg_desc( WDP_CENTER, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, - WDF_MODAL | WDF_NO_CLOSE, + {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, _nested_bootstrap_errmsg_widgets ); @@ -123,7 +124,7 @@ public: /** Nested widgets for the download window. */ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[] = { - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_PANEL, COLOUR_GREY), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), SetPadding(WidgetDimensions::unscaled.modalpopup), NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_BAR), SetFill(1, 0), @@ -136,7 +137,7 @@ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[] static WindowDesc _bootstrap_download_status_window_desc( WDP_CENTER, nullptr, 0, 0, WC_NETWORK_STATUS_WINDOW, WC_NONE, - WDF_MODAL | WDF_NO_CLOSE, + {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, _nested_bootstrap_download_status_window_widgets ); @@ -175,12 +176,12 @@ public: /** The widgets for the query. It has no close box as that sprite does not exist yet. */ static constexpr NWidgetPart _bootstrap_query_widgets[] = { NWidget(NWID_HORIZONTAL), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MISSING_GRAPHICS_SET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_MISSING_GRAPHICS_SET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_BAFD_QUESTION), EndContainer(), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_YES), SetDataTip(STR_MISSING_GRAPHICS_YES_DOWNLOAD, STR_NULL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_NO), SetDataTip(STR_MISSING_GRAPHICS_NO_QUIT, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_YES), SetStringTip(STR_MISSING_GRAPHICS_YES_DOWNLOAD), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_NO), SetStringTip(STR_MISSING_GRAPHICS_NO_QUIT), EndContainer(), }; @@ -188,13 +189,13 @@ static constexpr NWidgetPart _bootstrap_query_widgets[] = { static WindowDesc _bootstrap_query_desc( WDP_CENTER, nullptr, 0, 0, WC_CONFIRM_POPUP_QUERY, WC_NONE, - WDF_NO_CLOSE, + WindowDefaultFlag::NoClose, _bootstrap_query_widgets ); /** The window for the query. It can't use the generic query window as that uses sprites that don't exist yet. */ class BootstrapAskForDownloadWindow : public Window, ContentCallback { - Dimension button_size; ///< The dimension of the button + Dimension button_size{}; ///< The dimension of the button public: /** Start listening to the content client events. */ @@ -272,10 +273,10 @@ public: _network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); } - void OnReceiveContentInfo(const ContentInfo *ci) override + void OnReceiveContentInfo(const ContentInfo &ci) override { /* And once the meta data is received, start downloading it. */ - _network_content_client.Select(ci->id); + _network_content_client.Select(ci.id); new BootstrapContentDownloadStatusWindow(); this->Close(); } @@ -319,19 +320,19 @@ public: _network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); } - void OnReceiveContentInfo(const ContentInfo *ci) override + void OnReceiveContentInfo(const ContentInfo &ci) override { if (this->downloading) return; /* And once the metadata is received, start downloading it. */ - _network_content_client.Select(ci->id); + _network_content_client.Select(ci.id); _network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes); this->downloading = true; EM_ASM({ if (window["openttd_bootstrap"]) openttd_bootstrap($0, $1); }, this->downloaded_bytes, this->total_bytes); } - void OnDownloadProgress(const ContentInfo *, int bytes) override + void OnDownloadProgress(const ContentInfo &, int bytes) override { /* A negative value means we are resetting; for example, when retrying or using a fallback. */ if (bytes < 0) { @@ -406,7 +407,7 @@ bool HandleBootstrap() if (_exit_game) return false; /* Try to probe the graphics. Should work this time. */ - if (!BaseGraphics::SetSet({})) goto failure; + if (!BaseGraphics::SetSet(nullptr)) goto failure; /* Finally we can continue heading for the menu. */ _game_mode = GM_MENU; diff --git a/src/bridge.h b/src/bridge.h index 4f849baa52..c9e7200698 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -19,7 +19,7 @@ * which is used to determine the proper sprite table to use * while drawing a given bridge part. */ -enum BridgePieces { +enum BridgePieces : uint8_t { BRIDGE_PIECE_NORTH = 0, BRIDGE_PIECE_SOUTH, BRIDGE_PIECE_INNER_NORTH, @@ -30,7 +30,7 @@ enum BridgePieces { NUM_BRIDGE_PIECES, }; -DECLARE_POSTFIX_INCREMENT(BridgePieces) +DECLARE_INCREMENT_DECREMENT_OPERATORS(BridgePieces) static const uint MAX_BRIDGES = 13; ///< Maximal number of available bridge specs. constexpr uint SPRITES_PER_BRIDGE_PIECE = 32; ///< Number of sprites there are per bridge piece. @@ -50,7 +50,7 @@ struct BridgeSpec { PaletteID pal; ///< the palette which is used in the GUI StringID material; ///< the string that contains the bridge description StringID transport_name[2]; ///< description of the bridge, when built for road or rail - PalSpriteID **sprite_table; ///< table of sprites for drawing the bridge + std::vector> sprite_table; ///< table of sprites for drawing the bridge uint8_t flags; ///< bit 0 set: disable drawing of far pillars. }; @@ -72,7 +72,7 @@ inline const BridgeSpec *GetBridgeSpec(BridgeType i) void DrawBridgeMiddle(const TileInfo *ti); -CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlag flags = DC_NONE); +CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlags flags = {}); int CalcBridgeLenCostFactor(int x); void ResetBridges(); diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index 156b318d8b..b746e8a2bd 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -88,13 +88,13 @@ private: static const std::initializer_list sorter_funcs; /* Internal variables */ - TileIndex start_tile; - TileIndex end_tile; - TransportType transport_type; - uint8_t road_rail_type; - GUIBridgeList bridges; - int icon_width; ///< Scaled width of the the bridge icon sprite. - Scrollbar *vscroll; + TileIndex start_tile = INVALID_TILE; + TileIndex end_tile = INVALID_TILE; + TransportType transport_type = INVALID_TRANSPORT; + uint8_t road_rail_type = 0; + GUIBridgeList bridges{}; + int icon_width = 0; ///< Scaled width of the the bridge icon sprite. + Scrollbar *vscroll = nullptr; /** Sort the bridges by their index */ static bool BridgeIndexSorter(const BuildBridgeData &a, const BuildBridgeData &b) @@ -131,7 +131,7 @@ private: this->bridges.Sort(); /* Display the current sort variant */ - this->GetWidget(WID_BBS_DROPDOWN_CRITERIA)->widget_data = BuildBridgeWindow::sorter_names[this->bridges.SortType()]; + this->GetWidget(WID_BBS_DROPDOWN_CRITERIA)->SetString(BuildBridgeWindow::sorter_names[this->bridges.SortType()]); /* Set the modified widgets dirty */ this->SetWidgetDirty(WID_BBS_DROPDOWN_CRITERIA); @@ -143,16 +143,19 @@ private: * @param bridge_data the bridge to get the StringID of. * @return the StringID. */ - StringID GetBridgeSelectString(const BuildBridgeData &bridge_data) const + std::string GetBridgeSelectString(const BuildBridgeData &bridge_data) const { - SetDParam(0, bridge_data.spec->material); - SetDParam(1, PackVelocity(bridge_data.spec->speed, static_cast(this->transport_type))); - SetDParam(2, bridge_data.cost); /* If the bridge has no meaningful speed limit, don't display it. */ if (bridge_data.spec->speed == UINT16_MAX) { - return _game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_INFO_NAME : STR_SELECT_BRIDGE_INFO_NAME_COST; + return _game_mode == GM_EDITOR + ? GetString(STR_SELECT_BRIDGE_INFO_NAME, bridge_data.spec->material) + : GetString(STR_SELECT_BRIDGE_INFO_NAME_COST, bridge_data.spec->material, bridge_data.cost); } - return _game_mode == GM_EDITOR ? STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED : STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED_COST; + + uint64_t packed_velocity = PackVelocity(bridge_data.spec->speed, static_cast(this->transport_type)); + return _game_mode == GM_EDITOR + ? GetString(STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED, bridge_data.spec->material, packed_velocity) + : GetString(STR_SELECT_BRIDGE_INFO_NAME_MAX_SPEED_COST, bridge_data.spec->material, packed_velocity, bridge_data.cost); } public: @@ -166,7 +169,7 @@ public: this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_BBS_SCROLLBAR); /* Change the data, or the caption of the gui. Set it to road or rail, accordingly. */ - this->GetWidget(WID_BBS_CAPTION)->widget_data = (transport_type == TRANSPORT_ROAD) ? STR_SELECT_ROAD_BRIDGE_CAPTION : STR_SELECT_RAIL_BRIDGE_CAPTION; + this->GetWidget(WID_BBS_CAPTION)->SetString((transport_type == TRANSPORT_ROAD) ? STR_SELECT_ROAD_BRIDGE_CAPTION : STR_SELECT_RAIL_BRIDGE_CAPTION); this->FinishInitNested(transport_type); // Initializes 'this->icon_width'. this->parent = FindWindowById(WC_BUILD_TOOLBAR, transport_type); @@ -187,7 +190,7 @@ public: { switch (widget) { case WID_BBS_DROPDOWN_ORDER: { - Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); + Dimension d = GetStringBoundingBox(this->GetWidget(widget)->GetString()); d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; size = maxdim(size, d); @@ -315,7 +318,7 @@ static constexpr NWidgetPart _nested_build_bridge_widgets[] = { /* Header */ NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BBS_CAPTION), SetDataTip(STR_SELECT_RAIL_BRIDGE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BBS_CAPTION), SetStringTip(STR_SELECT_RAIL_BRIDGE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), EndContainer(), @@ -323,8 +326,8 @@ static constexpr NWidgetPart _nested_build_bridge_widgets[] = { NWidget(NWID_VERTICAL), /* Sort order + criteria buttons */ NWidget(NWID_HORIZONTAL), - NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_BBS_DROPDOWN_ORDER), SetFill(1, 0), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), - NWidget(WWT_DROPDOWN, COLOUR_DARK_GREEN, WID_BBS_DROPDOWN_CRITERIA), SetFill(1, 0), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_BBS_DROPDOWN_ORDER), SetFill(1, 0), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), + NWidget(WWT_DROPDOWN, COLOUR_DARK_GREEN, WID_BBS_DROPDOWN_CRITERIA), SetFill(1, 0), SetToolTip(STR_TOOLTIP_SORT_CRITERIA), EndContainer(), /* Matrix. */ NWidget(WWT_MATRIX, COLOUR_DARK_GREEN, WID_BBS_BRIDGE_LIST), SetFill(1, 0), SetResize(0, 22), SetMatrixDataTip(1, 0, STR_SELECT_BRIDGE_SELECTION_TOOLTIP), SetScrollbar(WID_BBS_SCROLLBAR), @@ -342,7 +345,7 @@ static constexpr NWidgetPart _nested_build_bridge_widgets[] = { static WindowDesc _build_bridge_desc( WDP_AUTO, "build_bridge", 200, 114, WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_build_bridge_widgets ); @@ -382,7 +385,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo /* only query bridge building possibility once, result is the same for all bridges! * returns CMD_ERROR on failure, and price on success */ StringID errmsg = INVALID_STRING_ID; - CommandCost ret = Command::Do(CommandFlagsToDCFlags(GetCommandFlags()) | DC_QUERY_COST, end, start, transport_type, 0, road_rail_type); + CommandCost ret = Command::Do(CommandFlagsToDCFlags(GetCommandFlags()) | DoCommandFlag::QueryCost, end, start, transport_type, 0, road_rail_type); GUIBridgeList bl; if (ret.Failed()) { @@ -427,7 +430,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo item.index = brd_type; item.spec = GetBridgeSpec(brd_type); /* Add to terraforming & bulldozing costs the cost of the - * bridge itself (not computed with DC_QUERY_COST) */ + * bridge itself (not computed with DoCommandFlag::QueryCost) */ item.cost = ret.GetCost() + (((int64_t)tot_bridgedata_len * _price[PR_BUILD_BRIDGE] * item.spec->price) >> 8) + infra_cost; any_available = true; } @@ -442,6 +445,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo if (!bl.empty()) { new BuildBridgeWindow(_build_bridge_desc, start, end, transport_type, road_rail_type, std::move(bl)); } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, errmsg, WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE), GetEncodedString(errmsg), + WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); } } diff --git a/src/bridge_map.h b/src/bridge_map.h index ea14d41039..5ced7b5d2f 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -132,11 +132,11 @@ inline void MakeBridgeRamp(Tile t, Owner o, BridgeType bridgetype, DiagDirection SetDockingTile(t, false); t.m2() = 0; t.m3() = 0; - t.m4() = INVALID_ROADTYPE; + t.m4() = 0; t.m5() = 1 << 7 | tt << 2 | d; SB(t.m6(), 2, 4, bridgetype); t.m7() = 0; - t.m8() = INVALID_ROADTYPE << 6; + t.m8() = 0; } /** diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 608f7dd26b..f5583f986b 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -18,6 +18,7 @@ #include "command_func.h" #include "company_func.h" #include "vehicle_gui.h" +#include "newgrf_badge.h" #include "newgrf_engine.h" #include "newgrf_text.h" #include "group.h" @@ -61,27 +62,27 @@ uint GetEngineListHeight(VehicleType type) static constexpr NWidgetPart _nested_build_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), SetTextStyle(TC_WHITE), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_BV_CAPTION), SetTextStyle(TC_WHITE), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SORT_ASCENDING_DESCENDING), SetStringTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_SORT_CRITERIA), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDDEN_ENGINES), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), - NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), EndContainer(), EndContainer(), /* Vehicle list. */ NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0, STR_NULL), SetScrollbar(WID_BV_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_BV_LIST), SetResize(1, 1), SetFill(1, 0), SetMatrixDataTip(1, 0), SetScrollbar(WID_BV_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_BV_SCROLLBAR), EndContainer(), /* Panel with details. */ @@ -91,7 +92,7 @@ static constexpr NWidgetPart _nested_build_vehicle_widgets[] = { NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BV_BUILD_SEL), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_BUILD), SetResize(1, 0), SetFill(1, 0), EndContainer(), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDE), SetResize(1, 0), SetFill(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BV_RENAME), SetResize(1, 0), SetFill(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), @@ -102,7 +103,7 @@ bool _engine_sort_direction; ///< \c false = descending, \c true = ascending. uint8_t _engine_sort_last_criteria[] = {0, 0, 0, 0}; ///< Last set sort criteria, for each vehicle type. bool _engine_sort_last_order[] = {false, false, false, false}; ///< Last set direction of the sort order, for each vehicle type. bool _engine_sort_show_hidden_engines[] = {false, false, false, false}; ///< Last set 'show hidden engines' setting for each vehicle type. -static CargoID _engine_sort_last_cargo_criteria[] = {CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY}; ///< Last set filter criteria, for each vehicle type. +static CargoType _engine_sort_last_cargo_criteria[] = {CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY, CargoFilterCriteria::CF_ANY}; ///< Last set filter criteria, for each vehicle type. /** * Determines order of engines by engineID @@ -135,7 +136,7 @@ static bool EngineIntroDateSorter(const GUIEngineListItem &a, const GUIEngineLis } /* cached values for EngineNameSorter to spare many GetString() calls */ -static EngineID _last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE }; +static EngineID _last_engine[2] = { EngineID::Invalid(), EngineID::Invalid() }; /** * Determines order of engines by name @@ -149,14 +150,12 @@ static bool EngineNameSorter(const GUIEngineListItem &a, const GUIEngineListItem if (a.engine_id != _last_engine[0]) { _last_engine[0] = a.engine_id; - SetDParam(0, PackEngineNameDParam(a.engine_id, EngineNameContext::PurchaseList)); - last_name[0] = GetString(STR_ENGINE_NAME); + last_name[0] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(a.engine_id, EngineNameContext::PurchaseList)); } if (b.engine_id != _last_engine[1]) { _last_engine[1] = b.engine_id; - SetDParam(0, PackEngineNameDParam(b.engine_id, EngineNameContext::PurchaseList)); - last_name[1] = GetString(STR_ENGINE_NAME); + last_name[1] = GetString(STR_ENGINE_NAME, PackEngineNameDParam(b.engine_id, EngineNameContext::PurchaseList)); } int r = StrNaturalCompare(last_name[0], last_name[1]); // Sort by name (natural sorting). @@ -537,15 +536,15 @@ const std::initializer_list _engine_sort_listing[] = {{ }}; /** Filters vehicles by cargo and engine (in case of rail vehicle). */ -static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoID cid) +static bool CargoAndEngineFilter(const GUIEngineListItem *item, const CargoType cargo_type) { - if (cid == CargoFilterCriteria::CF_ANY) { + if (cargo_type == CargoFilterCriteria::CF_ANY) { return true; - } else if (cid == CargoFilterCriteria::CF_ENGINES) { + } else if (cargo_type == CargoFilterCriteria::CF_ENGINES) { return Engine::Get(item->engine_id)->GetPower() != 0; } else { CargoTypes refit_mask = GetUnionOfArticulatedRefitMasks(item->engine_id, true) & _standard_cargo_mask; - return (cid == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid)); + return (cargo_type == CargoFilterCriteria::CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cargo_type)); } } @@ -556,12 +555,12 @@ static GUIEngineList::FilterFunction * const _engine_filter_funcs[] = { static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype) { uint weight = 0; - for (CargoID c = 0; c < NUM_CARGO; c++) { - if (cap[c] != 0) { + for (CargoType cargo = 0; cargo < NUM_CARGO; ++cargo) { + if (cap[cargo] != 0) { if (vtype == VEH_TRAIN) { - weight += CargoSpec::Get(c)->WeightOfNUnitsInTrain(cap[c]); + weight += CargoSpec::Get(cargo)->WeightOfNUnitsInTrain(cap[cargo]); } else { - weight += CargoSpec::Get(c)->WeightOfNUnits(cap[c]); + weight += CargoSpec::Get(cargo)->WeightOfNUnits(cap[cargo]); } } } @@ -571,13 +570,10 @@ static uint GetCargoWeight(const CargoArray &cap, VehicleType vtype) static int DrawCargoCapacityInfo(int left, int right, int y, TestedEngineDetails &te, bool refittable) { for (const CargoSpec *cs : _sorted_cargo_specs) { - CargoID cid = cs->Index(); - if (te.all_capacities[cid] == 0) continue; + CargoType cargo_type = cs->Index(); + if (te.all_capacities[cargo_type] == 0) continue; - SetDParam(0, cid); - SetDParam(1, te.all_capacities[cid]); - SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY); - DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, cargo_type, te.all_capacities[cargo_type], refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY)); y += GetCharacterHeight(FS_NORMAL); } @@ -591,36 +587,30 @@ static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine /* Purchase cost */ if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost)); } else { - SetDParam(0, e->GetCost()); - DrawString(left, right, y, STR_PURCHASE_INFO_COST); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost())); } y += GetCharacterHeight(FS_NORMAL); /* Wagon weight - (including cargo) */ uint weight = e->GetDisplayWeight(); - SetDParam(0, weight); - SetDParam(1, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight); - DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT); + DrawString(left, right, y, + GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_TRAIN) + weight)); y += GetCharacterHeight(FS_NORMAL); /* Wagon speed limit, displayed if above zero */ if (_settings_game.vehicle.wagon_speed_limits) { uint max_speed = e->GetDisplayMaxSpeed(); if (max_speed > 0) { - SetDParam(0, PackVelocity(max_speed, e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED, PackVelocity(max_speed, e->type))); y += GetCharacterHeight(FS_NORMAL); } } /* Running cost */ if (rvi->running_cost_class != INVALID_PRICE) { - SetDParam(0, e->GetRunningCost()); - DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR); + DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost())); y += GetCharacterHeight(FS_NORMAL); } @@ -634,42 +624,31 @@ static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engin /* Purchase Cost - Engine weight */ if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - SetDParam(2, e->GetDisplayWeight()); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_WEIGHT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_WEIGHT, e->GetCost() + te.cost, te.cost, e->GetDisplayWeight())); } else { - SetDParam(0, e->GetCost()); - SetDParam(1, e->GetDisplayWeight()); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_WEIGHT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_WEIGHT, e->GetCost(), e->GetDisplayWeight())); } y += GetCharacterHeight(FS_NORMAL); /* Max speed - Engine power */ - SetDParam(0, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - SetDParam(1, e->GetPower()); - DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower())); y += GetCharacterHeight(FS_NORMAL); /* Max tractive effort - not applicable if old acceleration or maglev */ if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && GetRailTypeInfo(rvi->railtype)->acceleration_type != 2) { - SetDParam(0, e->GetDisplayMaxTractiveEffort()); - DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort())); y += GetCharacterHeight(FS_NORMAL); } /* Running cost */ if (rvi->running_cost_class != INVALID_PRICE) { - SetDParam(0, e->GetRunningCost()); - DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR); + DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost())); y += GetCharacterHeight(FS_NORMAL); } /* Powered wagons power - Powered wagons extra weight */ if (rvi->pow_wag_power != 0) { - SetDParam(0, rvi->pow_wag_power); - SetDParam(1, rvi->pow_wag_weight); - DrawString(left, right, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, rvi->pow_wag_power, rvi->pow_wag_weight)); y += GetCharacterHeight(FS_NORMAL); } @@ -684,50 +663,36 @@ static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_n if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) { /* Purchase Cost */ if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost)); } else { - SetDParam(0, e->GetCost()); - DrawString(left, right, y, STR_PURCHASE_INFO_COST); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost())); } y += GetCharacterHeight(FS_NORMAL); /* Road vehicle weight - (including cargo) */ int16_t weight = e->GetDisplayWeight(); - SetDParam(0, weight); - SetDParam(1, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight); - DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_WEIGHT_CWEIGHT, weight, GetCargoWeight(te.all_capacities, VEH_ROAD) + weight)); y += GetCharacterHeight(FS_NORMAL); /* Max speed - Engine power */ - SetDParam(0, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - SetDParam(1, e->GetPower()); - DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_POWER, PackVelocity(e->GetDisplayMaxSpeed(), e->type), e->GetPower())); y += GetCharacterHeight(FS_NORMAL); /* Max tractive effort */ - SetDParam(0, e->GetDisplayMaxTractiveEffort()); - DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_MAX_TE, e->GetDisplayMaxTractiveEffort())); y += GetCharacterHeight(FS_NORMAL); } else { /* Purchase cost - Max speed */ if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - SetDParam(2, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type))); } else { - SetDParam(0, e->GetCost()); - SetDParam(1, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type))); } y += GetCharacterHeight(FS_NORMAL); } /* Running cost */ - SetDParam(0, e->GetRunningCost()); - DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR); + DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost())); y += GetCharacterHeight(FS_NORMAL); return y; @@ -745,46 +710,32 @@ static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_numb if (ocean_speed == canal_speed) { if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - SetDParam(2, PackVelocity(ocean_speed, e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(ocean_speed, e->type))); } else { - SetDParam(0, e->GetCost()); - SetDParam(1, PackVelocity(ocean_speed, e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(ocean_speed, e->type))); } y += GetCharacterHeight(FS_NORMAL); } else { if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT, e->GetCost() + te.cost, te.cost)); } else { - SetDParam(0, e->GetCost()); - DrawString(left, right, y, STR_PURCHASE_INFO_COST); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST, e->GetCost())); } y += GetCharacterHeight(FS_NORMAL); - SetDParam(0, PackVelocity(ocean_speed, e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_OCEAN); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_OCEAN, PackVelocity(ocean_speed, e->type))); y += GetCharacterHeight(FS_NORMAL); - SetDParam(0, PackVelocity(canal_speed, e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_CANAL); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_SPEED_CANAL, PackVelocity(canal_speed, e->type))); y += GetCharacterHeight(FS_NORMAL); } /* Cargo type + capacity */ - SetDParam(0, te.cargo); - SetDParam(1, te.capacity); - SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY); - DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY)); y += GetCharacterHeight(FS_NORMAL); /* Running cost */ - SetDParam(0, e->GetRunningCost()); - DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR); + DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost())); y += GetCharacterHeight(FS_NORMAL); return y; @@ -805,49 +756,34 @@ static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_ /* Purchase cost - Max speed */ if (te.cost != 0) { - SetDParam(0, e->GetCost() + te.cost); - SetDParam(1, te.cost); - SetDParam(2, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_REFIT_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_REFIT_SPEED, e->GetCost() + te.cost, te.cost, PackVelocity(e->GetDisplayMaxSpeed(), e->type))); } else { - SetDParam(0, e->GetCost()); - SetDParam(1, PackVelocity(e->GetDisplayMaxSpeed(), e->type)); - DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_COST_SPEED, e->GetCost(), PackVelocity(e->GetDisplayMaxSpeed(), e->type))); } y += GetCharacterHeight(FS_NORMAL); /* Cargo capacity */ if (te.mail_capacity > 0) { - SetDParam(0, te.cargo); - SetDParam(1, te.capacity); - SetDParam(2, GetCargoIDByLabel(CT_MAIL)); - SetDParam(3, te.mail_capacity); - DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, te.cargo, te.capacity, GetCargoTypeByLabel(CT_MAIL), te.mail_capacity)); } else { /* Note, if the default capacity is selected by the refit capacity * callback, then the capacity shown is likely to be incorrect. */ - SetDParam(0, te.cargo); - SetDParam(1, te.capacity); - SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY); - DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, te.cargo, te.capacity, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY)); } y += GetCharacterHeight(FS_NORMAL); /* Running cost */ - SetDParam(0, e->GetRunningCost()); - DrawString(left, right, y, TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR); + DrawString(left, right, y, GetString(TimerGameEconomy::UsingWallclockUnits() ? STR_PURCHASE_INFO_RUNNINGCOST_PERIOD : STR_PURCHASE_INFO_RUNNINGCOST_YEAR, e->GetRunningCost())); y += GetCharacterHeight(FS_NORMAL); /* Aircraft type */ - SetDParam(0, e->GetAircraftTypeText()); - DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_TYPE); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_TYPE, e->GetAircraftTypeText())); y += GetCharacterHeight(FS_NORMAL); /* Aircraft range, if available. */ uint16_t range = e->GetRange(); if (range != 0) { - SetDParam(0, range); - DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_RANGE); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_AIRCRAFT_RANGE, range)); y += GetCharacterHeight(FS_NORMAL); } @@ -871,10 +807,7 @@ static std::optional GetNewGRFAdditionalText(EngineID engine) return std::nullopt; } - StartTextRefStackUsage(grffile, 6); - std::string result = GetString(GetGRFStringID(grffile->grfid, 0xD000 + callback)); - StopTextRefStackUsage(); - return result; + return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); } /** @@ -902,8 +835,8 @@ void TestedEngineDetails::FillDefaultCapacities(const Engine *e) } else { this->capacity = e->GetDisplayDefaultCapacity(&this->mail_capacity); this->all_capacities[this->cargo] = this->capacity; - if (IsValidCargoID(GetCargoIDByLabel(CT_MAIL))) { - this->all_capacities[GetCargoIDByLabel(CT_MAIL)] = this->mail_capacity; + if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) { + this->all_capacities[GetCargoTypeByLabel(CT_MAIL)] = this->mail_capacity; } else { this->mail_capacity = 0; } @@ -961,9 +894,7 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, int new_y = DrawCargoCapacityInfo(left, right, y, te, refittable); if (new_y == y) { - SetDParam(0, INVALID_CARGO); - SetDParam(2, STR_EMPTY); - DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_CAPACITY, INVALID_CARGO, 0, STR_EMPTY)); y += GetCharacterHeight(FS_NORMAL); } else { y = new_y; @@ -973,19 +904,18 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, /* Draw details that apply to all types except rail wagons. */ if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { /* Design date - Life length */ - SetDParam(0, ymd.year); - SetDParam(1, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays())); - DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays()))); y += GetCharacterHeight(FS_NORMAL); /* Reliability */ - SetDParam(0, ToPercent16(e->reliability)); - DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY); + DrawString(left, right, y, GetString(STR_PURCHASE_INFO_RELIABILITY, ToPercent16(e->reliability))); y += GetCharacterHeight(FS_NORMAL); } if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number); + y = DrawBadgeNameList({left, y, right, INT16_MAX}, e->badges, static_cast(GSF_TRAINS + e->type)); + /* Additional text from NewGRF */ y = ShowAdditionalText(left, right, y, engine_number); @@ -1000,6 +930,11 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, return y; } +static void DrawEngineBadgeColumn(const Rect &r, int column_group, const GUIBadgeClasses &badge_classes, const Engine *e, PaletteID remap) +{ + DrawBadgeColumn(r, column_group, badge_classes, e->badges, static_cast(GSF_TRAINS + e->type), e->info.base_intro, remap); +} + /** * Engine drawing loop * @param type Type of vehicle (VEH_*) @@ -1010,9 +945,9 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, * @param show_count Whether to show the amount of engines or not * @param selected_group the group to list the engines of */ -void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group) +void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group, const GUIBadgeClasses &badge_classes) { - static const int sprite_y_offsets[] = { -1, -1, -2, -2 }; + static const std::array sprite_y_offsets = { 0, 0, -1, -1 }; auto [first, last] = sb.GetVisibleRangeIterators(eng_list); @@ -1024,7 +959,9 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width); int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL); - Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix); + auto badge_column_widths = badge_classes.GetColumnWidths(); + + Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero); int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2; Dimension replace_icon = {0, 0}; @@ -1038,72 +975,99 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li biggest_num_engines = std::max(biggest_num_engines, num_engines); } - SetDParam(0, biggest_num_engines); - count_width = GetStringBoundingBox(STR_JUST_COMMA, FS_SMALL).width; + count_width = GetStringBoundingBox(GetString(STR_JUST_COMMA, biggest_num_engines), FS_SMALL).width; } - Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position - Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position - Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position - if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl); - - int normal_text_y_offset = (ir.Height() - GetCharacterHeight(FS_NORMAL)) / 2; - int small_text_y_offset = ir.Height() - GetCharacterHeight(FS_SMALL); - int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2; + const int text_row_height = ir.Shrink(WidgetDimensions::scaled.matrix).Height(); + const int normal_text_y_offset = (text_row_height - GetCharacterHeight(FS_NORMAL)) / 2; + const int small_text_y_offset = text_row_height - GetCharacterHeight(FS_SMALL); const int offset = (rtl ? -circle_width : circle_width) / 2; const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent; - int y = ir.top; for (auto it = first; it != last; ++it) { const auto &item = *it; + const Engine *e = Engine::Get(item.engine_id); + uint indent = item.indent * WidgetDimensions::scaled.hsep_indent; - bool has_variants = HasFlag(item.flags, EngineDisplayFlags::HasVariants); - bool is_folded = HasFlag(item.flags, EngineDisplayFlags::IsFolded); - bool shaded = HasFlag(item.flags, EngineDisplayFlags::Shaded); + bool has_variants = item.flags.Test(EngineDisplayFlag::HasVariants); + bool is_folded = item.flags.Test(EngineDisplayFlag::IsFolded); + bool shaded = item.flags.Test(EngineDisplayFlag::Shaded); + + Rect textr = ir.Shrink(WidgetDimensions::scaled.matrix); + Rect tr = ir.Indent(indent, rtl); if (item.indent > 0) { /* Draw tree continuation lines. */ int tx = (rtl ? ir.right : ir.left) + offset; - int ty = y - WidgetDimensions::scaled.matrix.top; for (uint lvl = 1; lvl <= item.indent; ++lvl) { - if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ty, tx, ty + step_size - 1, linecolour, WidgetDimensions::scaled.fullbevel.top); + if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ir.top, tx, ir.bottom, linecolour, WidgetDimensions::scaled.fullbevel.top); if (lvl < item.indent) tx += level_width; } /* Draw our node in the tree. */ - int ycentre = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2 - 1; - if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ty, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top); + int ycentre = CenterBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top); + if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top); GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top); } + if (has_variants) { + Rect fr = tr.WithWidth(circle_width, rtl); + DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, textr.top, fr.right, textr.bottom}, SA_CENTER); + } + + tr = tr.Indent(circle_width + WidgetDimensions::scaled.hsep_normal, rtl); + /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */ const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id); + const PaletteID pal = (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company); - const Engine *e = Engine::Get(item.engine_id); - bool hidden = HasBit(e->company_hidden, _local_company); + if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) { + Rect br = tr.WithWidth(badge_column_widths[0], rtl); + DrawEngineBadgeColumn(br, 0, badge_classes, e, pal); + tr = tr.Indent(badge_column_widths[0], rtl); + } + + int sprite_x = tr.WithWidth(sprite_width, rtl).left + sprite_left; + DrawVehicleEngine(r.left, r.right, sprite_x, tr.top + sprite_y_offset, item.engine_id, pal, EIT_PURCHASE); + + tr = tr.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); + + if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) { + Rect br = tr.WithWidth(badge_column_widths[1], rtl); + DrawEngineBadgeColumn(br, 1, badge_classes, e, pal); + tr = tr.Indent(badge_column_widths[1], rtl); + } + + if (show_count) { + /* Rect for replace-protection icon. */ + Rect rr = tr.WithWidth(replace_icon.width, !rtl); + tr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_normal, !rtl); + /* Rect for engine type count text. */ + Rect cr = tr.WithWidth(count_width, !rtl); + tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal, !rtl); + + DrawString(cr.left, cr.right, textr.top + small_text_y_offset, GetString(STR_JUST_COMMA, num_engines), TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL); + + if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) { + DrawSpriteIgnorePadding(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr, SA_CENTER); + } + } + + if (badge_column_widths.size() >= 3 && badge_column_widths[2] > 0) { + Rect br = tr.WithWidth(badge_column_widths[2], !rtl).Indent(WidgetDimensions::scaled.hsep_wide, rtl); + DrawEngineBadgeColumn(br, 2, badge_classes, e, pal); + tr = tr.Indent(badge_column_widths[2], !rtl); + } + + bool hidden = e->company_hidden.Test(_local_company); StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME; TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK); - if (show_count) { - /* relies on show_count to find 'Vehicle in use' panel of autoreplace window */ - SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::AutoreplaceVehicleInUse, item.indent)); - } else { - SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::PurchaseList, item.indent)); - } - Rect itr = tr.Indent(indent, rtl); - DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc); - int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left; - DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE); - if (show_count) { - SetDParam(0, num_engines); - DrawString(cr.left, cr.right, y + small_text_y_offset, STR_JUST_COMMA, TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL); - if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset); - } - if (has_variants) { - Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl); - DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, SA_CENTER); - } - y += step_size; + /* If the count is visible then this is part of in-use autoreplace list. */ + auto engine_name = PackEngineNameDParam(item.engine_id, show_count ? EngineNameContext::AutoreplaceVehicleInUse : EngineNameContext::PurchaseList, item.indent); + DrawString(tr.left, tr.right, textr.top + normal_text_y_offset,GetString(str, engine_name), tc); + + ir = ir.Translate(0, step_size); } } @@ -1144,14 +1108,14 @@ void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, Engi const Engine *e = Engine::Get(item.engine_id); EngineDisplayFlags flags = item.flags; - if (e->display_last_variant != INVALID_ENGINE) flags &= ~EngineDisplayFlags::Shaded; - dst.emplace_back(e->display_last_variant == INVALID_ENGINE ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent); + if (e->display_last_variant != EngineID::Invalid()) flags.Reset(EngineDisplayFlag::Shaded); + dst.emplace_back(e->display_last_variant == EngineID::Invalid() ? item.engine_id : e->display_last_variant, item.engine_id, flags, indent); /* Add variants if not folded */ - if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && !HasFlag(item.flags, EngineDisplayFlags::IsFolded)) { + if (item.flags.Test(EngineDisplayFlag::HasVariants) && !item.flags.Test(EngineDisplayFlag::IsFolded)) { /* Add this engine again as a child */ - if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) { - dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags::None, indent + 1); + if (!item.flags.Test(EngineDisplayFlag::Shaded)) { + dst.emplace_back(item.engine_id, item.engine_id, EngineDisplayFlags{}, indent + 1); } GUIEngineListAddChildren(dst, src, item.engine_id, indent + 1); } @@ -1169,46 +1133,46 @@ void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, Engi } /** Enum referring to the Hotkeys in the build vehicle window */ -enum BuildVehicleHotkeys { +enum BuildVehicleHotkeys : int32_t { BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string }; /** GUI for building vehicles. */ struct BuildVehicleWindow : Window { - VehicleType vehicle_type; ///< Type of vehicles shown in the window. + VehicleType vehicle_type = VEH_INVALID; ///< Type of vehicles shown in the window. union { RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. - } filter; ///< Filter to apply. - bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction - uint8_t sort_criteria; ///< Current sort criterium. - bool show_hidden_engines; ///< State of the 'show hidden engines' button. - bool listview_mode; ///< If set, only display the available vehicles and do not show a 'build' button. - EngineID sel_engine; ///< Currently selected engine, or #INVALID_ENGINE - EngineID rename_engine; ///< Engine being renamed. - GUIEngineList eng_list; - CargoID cargo_filter_criteria; ///< Selected cargo filter - int details_height; ///< Minimal needed height of the details panels, in text lines (found so far). - Scrollbar *vscroll; - TestedEngineDetails te; ///< Tested cost and capacity after refit. + } filter{}; ///< Filter to apply. + bool descending_sort_order = false; ///< Sort direction, @see _engine_sort_direction + uint8_t sort_criteria = 0; ///< Current sort criterium. + bool show_hidden_engines = false; ///< State of the 'show hidden engines' button. + bool listview_mode = false; ///< If set, only display the available vehicles and do not show a 'build' button. + EngineID sel_engine = EngineID::Invalid(); ///< Currently selected engine, or #EngineID::Invalid() + EngineID rename_engine = EngineID::Invalid(); ///< Engine being renamed. + GUIEngineList eng_list{}; + CargoType cargo_filter_criteria{}; ///< Selected cargo filter + int details_height = 0; ///< Minimal needed height of the details panels, in text lines (found so far). + Scrollbar *vscroll = nullptr; + TestedEngineDetails te{}; ///< Tested cost and capacity after refit. + GUIBadgeClasses badge_classes{}; + + StringFilter string_filter{}; ///< Filter for vehicle name + QueryString vehicle_editbox; ///< Filter editbox - StringFilter string_filter; ///< Filter for vehicle name - QueryString vehicle_editbox; ///< Filter editbox uint cm_num_hidden_engines; void SetBuyVehicleText() { NWidgetCore *widget = this->GetWidget(WID_BV_BUILD); - bool refit = this->sel_engine != INVALID_ENGINE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY && this->cargo_filter_criteria != CargoFilterCriteria::CF_NONE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ENGINES; + bool refit = this->sel_engine != EngineID::Invalid() && this->cargo_filter_criteria != CargoFilterCriteria::CF_ANY && this->cargo_filter_criteria != CargoFilterCriteria::CF_NONE && this->cargo_filter_criteria != CargoFilterCriteria::CF_ENGINES; if (refit) refit = Engine::Get(this->sel_engine)->GetDefaultCargoType() != this->cargo_filter_criteria; if (refit) { - widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type; - widget->tool_tip = STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP + this->vehicle_type; + widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_TOOLTIP + this->vehicle_type); } else { - widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type; - widget->tool_tip = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + this->vehicle_type; + widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type, STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + this->vehicle_type); } } @@ -1218,8 +1182,6 @@ struct BuildVehicleWindow : Window { this->listview_mode = tile == INVALID_TILE; this->window_number = this->listview_mode ? (int)type : tile.base(); - this->sel_engine = INVALID_ENGINE; - this->sort_criteria = _engine_sort_last_criteria[type]; this->descending_sort_order = _engine_sort_last_order[type]; this->show_hidden_engines = _engine_sort_show_hidden_engines[type]; @@ -1238,18 +1200,16 @@ struct BuildVehicleWindow : Window { if (this->listview_mode) this->GetWidget(WID_BV_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE); NWidgetCore *widget = this->GetWidget(WID_BV_LIST); - widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type; + widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type); widget = this->GetWidget(WID_BV_SHOW_HIDE); - widget->tool_tip = STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP + type; + widget->SetToolTip(STR_BUY_VEHICLE_TRAIN_HIDE_SHOW_TOGGLE_TOOLTIP + type); widget = this->GetWidget(WID_BV_RENAME); - widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type; - widget->tool_tip = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type; + widget->SetStringTip(STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type, STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type); widget = this->GetWidget(WID_BV_SHOW_HIDDEN_ENGINES); - widget->widget_data = CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type; - widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type; + widget->SetStringTip(CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + type, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + type); widget->SetLowered(this->show_hidden_engines); this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9); @@ -1269,8 +1229,8 @@ struct BuildVehicleWindow : Window { // CM moved up this->GenerateBuildList(); // generate the list, since we need it in the next line /* Select the first unshaded engine in the list as default when opening the window */ - EngineID engine = INVALID_ENGINE; - auto it = std::ranges::find_if(this->eng_list, [&](GUIEngineListItem &item) { return !HasFlag(item.flags, EngineDisplayFlags::Shaded); }); + EngineID engine = EngineID::Invalid(); + auto it = std::ranges::find_if(this->eng_list, [](const GUIEngineListItem &item) { return !item.flags.Test(EngineDisplayFlag::Shaded); }); if (it != this->eng_list.end()) engine = it->engine_id; this->SelectEngine(engine); } @@ -1305,13 +1265,13 @@ struct BuildVehicleWindow : Window { } } - StringID GetCargoFilterLabel(CargoID cid) const + StringID GetCargoFilterLabel(CargoType cargo_type) const { - switch (cid) { + switch (cargo_type) { case CargoFilterCriteria::CF_ANY: return STR_PURCHASE_INFO_ALL_TYPES; case CargoFilterCriteria::CF_ENGINES: return STR_PURCHASE_INFO_ENGINES_ONLY; case CargoFilterCriteria::CF_NONE: return STR_PURCHASE_INFO_NONE; - default: return CargoSpec::Get(cid)->name; + default: return CargoSpec::Get(cargo_type)->name; } } @@ -1328,24 +1288,24 @@ struct BuildVehicleWindow : Window { void SelectEngine(EngineID engine) { - CargoID cargo = this->cargo_filter_criteria; + CargoType cargo = this->cargo_filter_criteria; if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; this->sel_engine = engine; this->SetBuyVehicleText(); - if (this->sel_engine == INVALID_ENGINE) return; + if (this->sel_engine == EngineID::Invalid()) return; const Engine *e = Engine::Get(this->sel_engine); if (!this->listview_mode) { /* Query for cost and refitted capacity */ - auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID); + auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command::Do(DoCommandFlag::QueryCost, TileIndex(this->window_number), this->sel_engine, true, cargo, INVALID_CLIENT_ID); if (ret.Succeeded()) { this->te.cost = ret.GetCost() - e->GetCost(); this->te.capacity = refit_capacity; this->te.mail_capacity = refit_mail; - this->te.cargo = !IsValidCargoID(cargo) ? e->GetDefaultCargoType() : cargo; + this->te.cargo = !IsValidCargoType(cargo) ? e->GetDefaultCargoType() : cargo; this->te.all_capacities = cargo_capacities; return; } @@ -1355,20 +1315,21 @@ struct BuildVehicleWindow : Window { this->te.cost = 0; this->te.FillDefaultCapacities(e); } - - // CM calls this in constructor - // void OnInit() override - // { - // this->SetCargoFilterArray(); - // } + + void OnInit() override + { + this->badge_classes = GUIBadgeClasses(static_cast(GSF_TRAINS + this->vehicle_type)); + // CM calls this in constructor + // this->SetCargoFilterArray(); + } /** Filter the engine list against the currently selected cargo filter */ EngineID FilterEngineList() { this->eng_list.Filter(this->cargo_filter_criteria); if (0 == this->eng_list.size()) { // no engine passed through the filter, invalidate the previously selected engine - // CM this->SelectEngine(INVALID_ENGINE); - return INVALID_ENGINE; + // CM this->SelectEngine(EngineID::Invalid()); + return EngineID::Invalid(); } else if (std::ranges::find(this->eng_list, this->sel_engine, &GUIEngineListItem::engine_id) == this->eng_list.end()) { // previously selected engine didn't pass the filter, select the first engine of the list // CM this->SelectEngine(this->eng_list[0].engine_id); return this->eng_list[0].engine_id; @@ -1379,7 +1340,7 @@ struct BuildVehicleWindow : Window { /** Filter a single engine */ bool FilterSingleEngine(EngineID eid) { - GUIEngineListItem item = {eid, eid, EngineDisplayFlags::None, 0}; + GUIEngineListItem item = {eid, eid, EngineDisplayFlags{}, 0}; return CargoAndEngineFilter(&item, this->cargo_filter_criteria); } @@ -1391,8 +1352,7 @@ struct BuildVehicleWindow : Window { /* Filter engine name */ this->string_filter.ResetState(); - SetDParam(0, PackEngineNameDParam(e->index, EngineNameContext::PurchaseList)); - this->string_filter.AddLine(GetString(STR_ENGINE_NAME)); + this->string_filter.AddLine(GetString(STR_ENGINE_NAME, PackEngineNameDParam(e->index, EngineNameContext::PurchaseList))); /* Filter NewGRF extra text */ auto text = GetNewGRFAdditionalText(e->index); @@ -1405,15 +1365,17 @@ struct BuildVehicleWindow : Window { EngineID GenerateBuildTrainList(GUIEngineList &list) { std::vector variants; - EngineID sel_id = INVALID_ENGINE; + EngineID sel_id = EngineID::Invalid(); size_t num_engines = 0; this->cm_num_hidden_engines = 0; list.clear(); + BadgeTextFilter btf(this->string_filter, GSF_TRAINS); + /* Make list of all available train engines and wagons. * Also check to see if the previously selected engine is still available, - * and if not, reset selection to INVALID_ENGINE. This could be the case + * and if not, reset selection to EngineID::Invalid(). This could be the case * when engines become obsolete and are removed */ for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { // CM for num_hidden / if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; @@ -1427,7 +1389,7 @@ struct BuildVehicleWindow : Window { if (!FilterSingleEngine(eid)) continue; /* Filter by name or NewGRF extra text */ - if (!FilterByText(e)) continue; + if (!FilterByText(e) && !btf.Filter(e->badges)) continue; /* CityMania code start */ /* Note: needs to be the last check to calculate the number correctly */ @@ -1443,7 +1405,7 @@ struct BuildVehicleWindow : Window { /* Add all parent variants of this engine to the variant list */ EngineID parent = e->info.variant_id; - while (parent != INVALID_ENGINE) { + while (parent != EngineID::Invalid()) { variants.push_back(parent); parent = Engine::Get(parent)->info.variant_id; } @@ -1455,7 +1417,7 @@ struct BuildVehicleWindow : Window { for (const auto &variant : variants) { if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) { const Engine *e = Engine::Get(variant); - list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); + list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0); if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++; } } @@ -1463,7 +1425,7 @@ struct BuildVehicleWindow : Window { // CM for num_hidden / this->SelectEngine(sel_id); /* invalidate cached values for name sorter - engine names could change */ - _last_engine[0] = _last_engine[1] = INVALID_ENGINE; + _last_engine[0] = _last_engine[1] = EngineID::Invalid(); /* make engines first, and then wagons, sorted by selected sort_criteria */ _engine_sort_direction = false; @@ -1482,11 +1444,13 @@ struct BuildVehicleWindow : Window { /* Figure out what road vehicle EngineIDs to put in the list */ void GenerateBuildRoadVehList() { - // CM num_hidden / EngineID sel_id = INVALID_ENGINE; + // CM num_hidden / EngineID sel_id = EngineID::Invalid(); this->cm_num_hidden_engines = 0; this->eng_list.clear(); + BadgeTextFilter btf(this->string_filter, GSF_ROADVEHICLES); + for (const Engine *e : Engine::IterateType(VEH_ROAD)) { // CM num_hidden / if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; EngineID eid = e->index; @@ -1494,7 +1458,7 @@ struct BuildVehicleWindow : Window { if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue; /* Filter by name or NewGRF extra text */ - if (!FilterByText(e)) continue; + if (!FilterByText(e) && !btf.Filter(e->badges)) continue; /* CityMania code start */ // this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0); @@ -1513,17 +1477,20 @@ struct BuildVehicleWindow : Window { /* Figure out what ship EngineIDs to put in the list */ void GenerateBuildShipList() { - // CM EngineID sel_id = INVALID_ENGINE; + // CM num_hidden / EngineID sel_id = EngineID::Invalid(); this->cm_num_hidden_engines = 0; + this->eng_list.clear(); + BadgeTextFilter btf(this->string_filter, GSF_SHIPS); + for (const Engine *e : Engine::IterateType(VEH_SHIP)) { if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue; /* Filter by name or NewGRF extra text */ - if (!FilterByText(e)) continue; + if (!FilterByText(e) && !btf.Filter(e->badges)) continue; /* CityMania code start */ // this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0); @@ -1542,16 +1509,18 @@ struct BuildVehicleWindow : Window { /* Figure out what aircraft EngineIDs to put in the list */ void GenerateBuildAircraftList() { - // CM num_hidden / EngineID sel_id = INVALID_ENGINE; + // CM num_hidden / EngineID sel_id = EngineID::Invalid(); this->cm_num_hidden_engines = 0; this->eng_list.clear(); - const Station *st = this->listview_mode ? nullptr : Station::GetByTile(this->window_number); + const Station *st = this->listview_mode ? nullptr : Station::GetByTile(TileIndex(this->window_number)); + + BadgeTextFilter btf(this->string_filter, GSF_AIRCRAFT); /* Make list of all available planes. * Also check to see if the previously selected plane is still available, - * and if not, reset selection to INVALID_ENGINE. This could be the case + * and if not, reset selection to EngineID::Invalid(). This could be the case * when planes become obsolete and are removed */ for (const Engine *e : Engine::IterateType(VEH_AIRCRAFT)) { if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; @@ -1561,7 +1530,7 @@ struct BuildVehicleWindow : Window { if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue; /* Filter by name or NewGRF extra text */ - if (!FilterByText(e)) continue; + if (!FilterByText(e) && !btf.Filter(e->badges)) continue; /* CityMania code start */ // this->eng_list.emplace_back(eid, e->info.variant_id, e->display_flags, 0); @@ -1613,7 +1582,7 @@ struct BuildVehicleWindow : Window { std::vector variants; for (const auto &item : this->eng_list) { EngineID parent = item.variant_id; - while (parent != INVALID_ENGINE) { + while (parent != EngineID::Invalid()) { variants.push_back(parent); parent = Engine::Get(parent)->info.variant_id; } @@ -1622,7 +1591,7 @@ struct BuildVehicleWindow : Window { for (const auto &variant : variants) { if (std::ranges::find(this->eng_list, variant, &GUIEngineListItem::engine_id) == this->eng_list.end()) { const Engine *e = Engine::Get(variant); - this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlags::Shaded, 0); + this->eng_list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0); } } @@ -1630,7 +1599,7 @@ struct BuildVehicleWindow : Window { EngList_Sort(this->eng_list, _engine_sort_functions[this->vehicle_type][this->sort_criteria]); this->eng_list.swap(list); - GUIEngineListAddChildren(this->eng_list, list, INVALID_ENGINE, 0); + GUIEngineListAddChildren(this->eng_list, list, EngineID::Invalid(), 0); this->eng_list.RebuildDone(); return sel_id; // CM @@ -1663,20 +1632,20 @@ struct BuildVehicleWindow : Window { void BuildVehicle() { EngineID sel_eng = this->sel_engine; - if (sel_eng == INVALID_ENGINE) return; + if (sel_eng == EngineID::Invalid()) return; - CargoID cargo = this->cargo_filter_criteria; + CargoType cargo = this->cargo_filter_criteria; if (cargo == CargoFilterCriteria::CF_ANY || cargo == CargoFilterCriteria::CF_ENGINES || cargo == CargoFilterCriteria::CF_NONE) cargo = INVALID_CARGO; if (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildWagon, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID); } else { - Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, this->window_number, sel_eng, true, cargo, INVALID_CLIENT_ID); + Command::Post(GetCmdBuildVehMsg(this->vehicle_type), CcBuildPrimaryVehicle, TileIndex(this->window_number), sel_eng, true, cargo, INVALID_CLIENT_ID); } /* Update last used variant in hierarchy and refresh if necessary. */ bool refresh = false; EngineID parent = sel_eng; - while (parent != INVALID_ENGINE) { + while (parent != EngineID::Invalid()) { Engine *e = Engine::Get(parent); refresh |= (e->display_last_variant != sel_eng); e->display_last_variant = sel_eng; @@ -1708,22 +1677,22 @@ struct BuildVehicleWindow : Window { break; case WID_BV_LIST: { - EngineID e = INVALID_ENGINE; + EngineID e = EngineID::Invalid(); const auto it = this->vscroll->GetScrolledItemFromWidget(this->eng_list, pt.y, this, WID_BV_LIST); if (it != this->eng_list.end()) { const auto &item = *it; const Rect r = this->GetWidget(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.matrix).WithWidth(WidgetDimensions::scaled.hsep_indent * (item.indent + 1), _current_text_dir == TD_RTL); - if (HasFlag(item.flags, EngineDisplayFlags::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { + if (item.flags.Test(EngineDisplayFlag::HasVariants) && IsInsideMM(r.left, r.right, pt.x)) { /* toggle folded flag on engine */ - assert(item.variant_id != INVALID_ENGINE); + assert(item.variant_id != EngineID::Invalid()); Engine *engine = Engine::Get(item.variant_id); - engine->display_flags ^= EngineDisplayFlags::IsFolded; + engine->display_flags.Flip(EngineDisplayFlag::IsFolded); InvalidateWindowData(WC_REPLACE_VEHICLE, this->vehicle_type, 0); // Update the autoreplace window InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well return; } - if (!HasFlag(item.flags, EngineDisplayFlags::Shaded)) e = item.engine_id; + if (!item.flags.Test(EngineDisplayFlag::Shaded)) e = item.engine_id; } this->SelectEngine(e); this->SetDirty(); @@ -1744,7 +1713,7 @@ struct BuildVehicleWindow : Window { break; case WID_BV_SHOW_HIDE: { - const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine); + const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine); if (e != nullptr) { Command::Post(this->sel_engine, !e->IsHidden(_current_company)); } @@ -1757,10 +1726,9 @@ struct BuildVehicleWindow : Window { case WID_BV_RENAME: { EngineID sel_eng = this->sel_engine; - if (sel_eng != INVALID_ENGINE) { + if (sel_eng != EngineID::Invalid()) { this->rename_engine = sel_eng; - SetDParam(0, PackEngineNameDParam(sel_eng, EngineNameContext::Generic)); - ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); + ShowQueryString(GetString(STR_ENGINE_NAME, PackEngineNameDParam(sel_eng, EngineNameContext::Generic)), STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, {QueryStringFlag::EnableDefault, QueryStringFlag::LengthIsInChars}); } break; } @@ -1785,42 +1753,42 @@ struct BuildVehicleWindow : Window { this->eng_list.ForceRebuild(); } - void SetStringParameters(WidgetID widget) const override + std::string GetWidgetString(WidgetID widget, StringID stringid) const override { switch (widget) { case WID_BV_CAPTION: if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) { const RailTypeInfo *rti = GetRailTypeInfo(this->filter.railtype); - SetDParam(0, rti->strings.build_caption); - } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) { - const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype); - SetDParam(0, rti->strings.build_caption); - } else { - SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type); + return GetString(rti->strings.build_caption); } - break; + if (this->vehicle_type == VEH_ROAD && !this->listview_mode) { + const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype); + return GetString(rti->strings.build_caption); + } + return GetString((this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type); case WID_BV_SORT_DROPDOWN: - SetDParam(0, std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]); - break; + return GetString(std::data(_engine_sort_listing[this->vehicle_type])[this->sort_criteria]); case WID_BV_CARGO_FILTER_DROPDOWN: - SetDParam(0, this->GetCargoFilterLabel(this->cargo_filter_criteria)); - break; + return GetString(this->GetCargoFilterLabel(this->cargo_filter_criteria)); case WID_BV_SHOW_HIDE: { - const Engine *e = (this->sel_engine == INVALID_ENGINE) ? nullptr : Engine::Get(this->sel_engine); + const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine); if (e != nullptr && e->IsHidden(_local_company)) { - SetDParam(0, STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type); - } else { - SetDParam(0, STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type); + return GetString(STR_BUY_VEHICLE_TRAIN_SHOW_TOGGLE_BUTTON + this->vehicle_type); } - break; + return GetString(STR_BUY_VEHICLE_TRAIN_HIDE_TOGGLE_BUTTON + this->vehicle_type); } case WID_BV_SHOW_HIDDEN_ENGINES: + TODO + return GetString() SetDParam(0, this->cm_num_hidden_engines); break; + + default: + return this->Window::GetWidgetString(widget, stringid); } } @@ -1830,7 +1798,7 @@ struct BuildVehicleWindow : Window { case WID_BV_LIST: resize.height = GetEngineListHeight(this->vehicle_type); size.height = 3 * resize.height; - size.width = std::max(size.width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width; + size.width = std::max(size.width, this->badge_classes.GetTotalColumnsWidth() + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width; break; case WID_BV_PANEL: @@ -1838,7 +1806,7 @@ struct BuildVehicleWindow : Window { break; case WID_BV_SORT_ASCENDING_DESCENDING: { - Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); + Dimension d = GetStringBoundingBox(this->GetWidget(widget)->GetString()); d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; size = maxdim(size, d); @@ -1876,7 +1844,8 @@ struct BuildVehicleWindow : Window { *this->vscroll, this->sel_engine, false, - DEFAULT_GROUP + DEFAULT_GROUP, + this->badge_classes ); break; @@ -1903,17 +1872,17 @@ struct BuildVehicleWindow : Window { this->eng_list.size()); /* CityMania code end */ - this->SetWidgetsDisabledState(this->sel_engine == INVALID_ENGINE, WID_BV_SHOW_HIDE, WID_BV_BUILD); + this->SetWidgetsDisabledState(this->sel_engine == EngineID::Invalid(), WID_BV_SHOW_HIDE, WID_BV_BUILD); /* Disable renaming engines in network games if you are not the server. */ - this->SetWidgetDisabledState(WID_BV_RENAME, this->sel_engine == INVALID_ENGINE || (_networking && !_network_server)); + this->SetWidgetDisabledState(WID_BV_RENAME, this->sel_engine == EngineID::Invalid() || (_networking && !_network_server)); this->DrawWidgets(); if (!this->IsShaded()) { int needed_height = this->details_height; /* Draw details panels. */ - if (this->sel_engine != INVALID_ENGINE) { + if (this->sel_engine != EngineID::Invalid()) { const Rect r = this->GetWidget(WID_BV_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect); int text_end = DrawVehiclePurchaseInfo(r.left, r.right, r.top, this->sel_engine, this->te); needed_height = std::max(needed_height, (text_end - r.top) / GetCharacterHeight(FS_NORMAL)); @@ -1967,7 +1936,7 @@ struct BuildVehicleWindow : Window { void OnEditboxChanged(WidgetID wid) override { if (wid == WID_BV_FILTER) { - this->string_filter.SetFilterTerm(this->vehicle_editbox.text.buf); + this->string_filter.SetFilterTerm(this->vehicle_editbox.text.GetText()); this->InvalidateData(); } } @@ -2002,7 +1971,7 @@ struct BuildVehicleWindow : Window { static WindowDesc _build_vehicle_desc( WDP_AUTO, "build_vehicle", 240, 268, WC_BUILD_VEHICLE, WC_NONE, - WDF_CONSTRUCTION, + WindowDefaultFlag::Construction, _nested_build_vehicle_widgets, &BuildVehicleWindow::hotkeys ); diff --git a/src/cachecheck.cpp b/src/cachecheck.cpp index a68fa7871c..8f39f8d5ed 100644 --- a/src/cachecheck.cpp +++ b/src/cachecheck.cpp @@ -85,7 +85,7 @@ void CheckCaches() std::vector tra_cache; for (Vehicle *v : Vehicle::Iterate()) { - if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue; + if (v != v->First() || v->vehstatus.Test(VehState::Crashed) || !v->IsPrimaryVehicle()) continue; for (const Vehicle *u = v; u != nullptr; u = u->Next()) { FillNewGRFVehicleCache(u); @@ -170,11 +170,14 @@ void CheckCaches() for (Station *st : Station::Iterate()) { for (GoodsEntry &ge : st->goods) { - [[maybe_unused]] const auto a = ge.cargo.PeriodsInTransit(); - [[maybe_unused]] const auto b = ge.cargo.TotalCount(); - ge.cargo.InvalidateCache(); - assert(a == ge.cargo.PeriodsInTransit()); - assert(b == ge.cargo.TotalCount()); + if (!ge.HasData()) continue; + + StationCargoList &cargo_list = ge.GetData().cargo; + [[maybe_unused]] const auto a = cargo_list.PeriodsInTransit(); + [[maybe_unused]] const auto b = cargo_list.TotalCount(); + cargo_list.InvalidateCache(); + assert(a == cargo_list.PeriodsInTransit()); + assert(b == cargo_list.TotalCount()); } /* Check docking tiles */ diff --git a/src/cargo_type.h b/src/cargo_type.h index 7dc765b752..7bc0552f98 100644 --- a/src/cargo_type.h +++ b/src/cargo_type.h @@ -12,6 +12,7 @@ #include "core/enum_type.hpp" #include "core/strong_typedef_type.hpp" +#include "core/convertible_through_base.hpp" /** Globally unique label of a cargo type. */ using CargoLabel = StrongType::Typedef; @@ -19,7 +20,7 @@ using CargoLabel = StrongType::Typedef { } }; - -/** Types of cargo source and destination */ -enum class SourceType : uint8_t { - Industry, ///< Source/destination is an industry - Town, ///< Source/destination is a town - Headquarters, ///< Source/destination are company headquarters -}; - -typedef uint16_t SourceID; ///< Contains either industry ID, town ID or company ID (or INVALID_SOURCE) -static const SourceID INVALID_SOURCE = 0xFFFF; ///< Invalid/unknown index of source - #endif /* CARGO_TYPE_H */ diff --git a/src/cargoaction.cpp b/src/cargoaction.cpp index e6d32aac89..b5ff2273d9 100644 --- a/src/cargoaction.cpp +++ b/src/cargoaction.cpp @@ -20,7 +20,7 @@ * @return Either new packet if splitting was necessary or the given one * otherwise. */ -template +template CargoPacket *CargoMovement::Preprocess(CargoPacket *cp) { if (this->max_move < cp->Count()) { @@ -38,7 +38,7 @@ CargoPacket *CargoMovement::Preprocess(CargoPacket *cp) * @param cp Packet to be removed completely or partially. * @return Amount of cargo to be removed. */ -template +template uint CargoRemoval::Preprocess(CargoPacket *cp) { if (this->max_move >= cp->Count()) { @@ -57,7 +57,7 @@ uint CargoRemoval::Preprocess(CargoPacket *cp) * @param remove Amount of cargo to be removed. * @return True if the packet was deleted, False if it was reduced. */ -template +template bool CargoRemoval::Postprocess(CargoPacket *cp, uint remove) { if (remove == cp->Count()) { @@ -75,7 +75,7 @@ bool CargoRemoval::Postprocess(CargoPacket *cp, uint remove) * @return True if the packet was completely delivered, false if only part of * it was. */ -template<> +template <> bool CargoRemoval::operator()(CargoPacket *cp) { uint remove = this->Preprocess(cp); @@ -89,7 +89,7 @@ bool CargoRemoval::operator()(CargoPacket *cp) * @return True if the packet was completely delivered, false if only part of * it was. */ -template<> +template <> bool CargoRemoval::operator()(CargoPacket *cp) { uint remove = this->Preprocess(cp); diff --git a/src/cargoaction.h b/src/cargoaction.h index c9bb7351b3..3a886381c3 100644 --- a/src/cargoaction.h +++ b/src/cargoaction.h @@ -16,7 +16,7 @@ * Abstract action of removing cargo from a vehicle or a station. * @tparam Tsource CargoList subclass to remove cargo from. */ -template +template class CargoRemoval { protected: Tsource *source; ///< Source of the cargo. @@ -40,9 +40,9 @@ class CargoDelivery : public CargoRemoval { protected: TileIndex current_tile; ///< Current tile cargo delivery is happening. CargoPayment *payment; ///< Payment object where payments will be registered. - CargoID cargo; ///< The cargo type of the cargo. + CargoType cargo; ///< The cargo type of the cargo. public: - CargoDelivery(VehicleCargoList *source, uint max_move, CargoID cargo, CargoPayment *payment, TileIndex current_tile) : + CargoDelivery(VehicleCargoList *source, uint max_move, CargoType cargo, CargoPayment *payment, TileIndex current_tile) : CargoRemoval(source, max_move), current_tile(current_tile), payment(payment), cargo(cargo) {} bool operator()(CargoPacket *cp); }; @@ -52,7 +52,7 @@ public: * @tparam Tsource CargoList subclass to remove cargo from. * @tparam Tdest CargoList subclass to add cargo to. */ -template +template class CargoMovement { protected: Tsource *source; ///< Source of the cargo. @@ -117,7 +117,7 @@ public: }; /** Action of rerouting cargo between different cargo lists and/or next hops. */ -template +template class CargoReroute : public CargoMovement { protected: StationID avoid; diff --git a/src/cargomonitor.cpp b/src/cargomonitor.cpp index ab8dc4a06f..046e0b2e0b 100644 --- a/src/cargomonitor.cpp +++ b/src/cargomonitor.cpp @@ -110,26 +110,25 @@ int32_t GetPickupAmount(CargoMonitorID monitor, bool keep_monitoring) * @param cargo_type type of cargo. * @param company company delivering the cargo. * @param amount Amount of cargo delivered. - * @param src_type type of \a src. - * @param src index of source. + * @param src source of cargo. * @param st station where the cargo is delivered to. * @param dest industry index where the cargo is delivered to. */ -void AddCargoDelivery(CargoID cargo_type, CompanyID company, uint32_t amount, SourceType src_type, SourceID src, const Station *st, IndustryID dest) +void AddCargoDelivery(CargoType cargo_type, CompanyID company, uint32_t amount, Source src, const Station *st, IndustryID dest) { if (amount == 0) return; - if (src != INVALID_SOURCE) { + if (src.IsValid()) { /* Handle pickup update. */ - switch (src_type) { + switch (src.type) { case SourceType::Industry: { - CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, src); + CargoMonitorID num = EncodeCargoIndustryMonitor(company, cargo_type, src.ToIndustryID()); CargoMonitorMap::iterator iter = _cargo_pickups.find(num); if (iter != _cargo_pickups.end()) iter->second += amount; break; } case SourceType::Town: { - CargoMonitorID num = EncodeCargoTownMonitor(company, cargo_type, src); + CargoMonitorID num = EncodeCargoTownMonitor(company, cargo_type, src.ToTownID()); CargoMonitorMap::iterator iter = _cargo_pickups.find(num); if (iter != _cargo_pickups.end()) iter->second += amount; break; diff --git a/src/cargomonitor.h b/src/cargomonitor.h index 7360fa2c66..8d015f024e 100644 --- a/src/cargomonitor.h +++ b/src/cargomonitor.h @@ -55,16 +55,16 @@ static_assert(MAX_COMPANIES <= (1 << CCB_COMPANY_LENGTH)); * @param ind %Industry providing or accepting the cargo. * @return The encoded cargo/company/industry number. */ -inline CargoMonitorID EncodeCargoIndustryMonitor(CompanyID company, CargoID ctype, IndustryID ind) +inline CargoMonitorID EncodeCargoIndustryMonitor(CompanyID company, CargoType ctype, IndustryID ind) { assert(ctype < (1 << CCB_CARGO_TYPE_LENGTH)); assert(company < (1 << CCB_COMPANY_LENGTH)); uint32_t ret = 0; - SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, ind); + SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, ind.base()); SetBit(ret, CCB_IS_INDUSTRY_BIT); SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype); - SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company); + SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base()); return ret; } @@ -75,15 +75,15 @@ inline CargoMonitorID EncodeCargoIndustryMonitor(CompanyID company, CargoID ctyp * @param town %Town providing or accepting the cargo. * @return The encoded cargo/company/town number. */ -inline CargoMonitorID EncodeCargoTownMonitor(CompanyID company, CargoID ctype, TownID town) +inline CargoMonitorID EncodeCargoTownMonitor(CompanyID company, CargoType ctype, TownID town) { assert(ctype < (1 << CCB_CARGO_TYPE_LENGTH)); assert(company < (1 << CCB_COMPANY_LENGTH)); uint32_t ret = 0; - SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, town); + SB(ret, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH, town.base()); SB(ret, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH, ctype); - SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company); + SB(ret, CCB_COMPANY_START, CCB_COMPANY_LENGTH, company.base()); return ret; } @@ -102,7 +102,7 @@ inline CompanyID DecodeMonitorCompany(CargoMonitorID num) * @param num Cargo monitoring number to decode. * @return The extracted cargo type. */ -inline CargoID DecodeMonitorCargoType(CargoMonitorID num) +inline CargoType DecodeMonitorCargoType(CargoMonitorID num) { return GB(num, CCB_CARGO_TYPE_START, CCB_CARGO_TYPE_LENGTH); } @@ -120,29 +120,29 @@ inline bool MonitorMonitorsIndustry(CargoMonitorID num) /** * Extract the industry number from the cargo monitor. * @param num Cargo monitoring number to decode. - * @return The extracted industry id, or #INVALID_INDUSTRY if the number does not monitor an industry. + * @return The extracted industry id, or #IndustryID::Invalid() if the number does not monitor an industry. */ inline IndustryID DecodeMonitorIndustry(CargoMonitorID num) { - if (!MonitorMonitorsIndustry(num)) return INVALID_INDUSTRY; - return GB(num, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH); + if (!MonitorMonitorsIndustry(num)) return IndustryID::Invalid(); + return static_cast(GB(num, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH)); } /** * Extract the town number from the cargo monitor. * @param num Cargo monitoring number to decode. - * @return The extracted town id, or #INVALID_TOWN if the number does not monitor a town. + * @return The extracted town id, or #TownID::Invalid() if the number does not monitor a town. */ inline TownID DecodeMonitorTown(CargoMonitorID num) { - if (MonitorMonitorsIndustry(num)) return INVALID_TOWN; - return GB(num, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH); + if (MonitorMonitorsIndustry(num)) return TownID::Invalid(); + return static_cast(GB(num, CCB_TOWN_IND_NUMBER_START, CCB_TOWN_IND_NUMBER_LENGTH)); } void ClearCargoPickupMonitoring(CompanyID company = INVALID_OWNER); void ClearCargoDeliveryMonitoring(CompanyID company = INVALID_OWNER); int32_t GetDeliveryAmount(CargoMonitorID monitor, bool keep_monitoring); int32_t GetPickupAmount(CargoMonitorID monitor, bool keep_monitoring); -void AddCargoDelivery(CargoID cargo_type, CompanyID company, uint32_t amount, SourceType src_type, SourceID src, const Station *st, IndustryID dest = INVALID_INDUSTRY); +void AddCargoDelivery(CargoType cargo_type, CompanyID company, uint32_t amount, Source src, const Station *st, IndustryID dest = IndustryID::Invalid()); #endif /* CARGOMONITOR_H */ diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 02145491fd..5dfe57c873 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -26,8 +26,6 @@ INSTANTIATE_POOL_METHODS(CargoPacket) */ CargoPacket::CargoPacket() { - this->source_type = SourceType::Industry; - this->source_id = INVALID_SOURCE; } /** @@ -35,14 +33,12 @@ CargoPacket::CargoPacket() * * @param first_station Source station of the packet. * @param count Number of cargo entities to put in this packet. - * @param source_type 'Type' of source the packet comes from (for subsidies). - * @param source_id Actual source of the packet (for subsidies). + * @param source Source of the packet (for subsidies). * @pre count != 0 */ -CargoPacket::CargoPacket(StationID first_station,uint16_t count, SourceType source_type, SourceID source_id) : +CargoPacket::CargoPacket(StationID first_station,uint16_t count, Source source) : count(count), - source_id(source_id), - source_type(source_type), + source(source), first_station(first_station) { assert(count != 0); @@ -80,8 +76,7 @@ CargoPacket::CargoPacket(uint16_t count, Money feeder_share, CargoPacket &origin feeder_share(feeder_share), source_xy(original.source_xy), travelled(original.travelled), - source_id(original.source_id), - source_type(original.source_type), + source(original.source), #ifdef WITH_ASSERT in_vehicle(original.in_vehicle), #endif /* WITH_ASSERT */ @@ -134,21 +129,21 @@ void CargoPacket::Reduce(uint count) * @param src_type Type of source. * @param src Index of source. */ -/* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src) +/* static */ void CargoPacket::InvalidateAllFrom(Source src) { for (CargoPacket *cp : CargoPacket::Iterate()) { - if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE; + if (cp->source == src) cp->source.MakeInvalid(); } } /** - * Invalidates (sets source to INVALID_STATION) all cargo packets from given station. + * Invalidates (sets source to StationID::Invalid()) all cargo packets from given station. * @param sid Station that gets removed. */ /* static */ void CargoPacket::InvalidateAllFrom(StationID sid) { for (CargoPacket *cp : CargoPacket::Iterate()) { - if (cp->first_station == sid) cp->first_station = INVALID_STATION; + if (cp->first_station == sid) cp->first_station = StationID::Invalid(); } } @@ -291,7 +286,7 @@ void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action) * will be kept and the loop will be aborted. * @param action Action instance to be applied. */ -template +template void VehicleCargoList::ShiftCargo(Taction action) { Iterator it(this->packets.begin()); @@ -313,7 +308,7 @@ void VehicleCargoList::ShiftCargo(Taction action) * will be kept and the loop will be aborted. * @param action Action instance to be applied. */ -template +template void VehicleCargoList::PopCargo(Taction action) { if (this->packets.empty()) return; @@ -411,11 +406,11 @@ void VehicleCargoList::AgeCargo() /* static */ VehicleCargoList::MoveToAction VehicleCargoList::ChooseAction(const CargoPacket *cp, StationID cargo_next, StationID current_station, bool accepted, StationIDStack next_station) { - if (cargo_next == INVALID_STATION) { + if (cargo_next == StationID::Invalid()) { return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP; } else if (cargo_next == current_station) { return MTA_DELIVER; - } else if (next_station.Contains(cargo_next)) { + } else if (next_station.Contains(cargo_next.base())) { return MTA_KEEP; } else { return MTA_TRANSFER; @@ -437,7 +432,7 @@ void VehicleCargoList::AgeCargo() * @param current_tile Current tile the cargo handling is happening on. * return If any cargo will be unloaded. */ -bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoID cargo, CargoPayment *payment, TileIndex current_tile) +bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoType cargo, CargoPayment *payment, TileIndex current_tile) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); @@ -446,6 +441,9 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID Iterator it = this->packets.begin(); uint sum = 0; + static const FlowStatMap EMPTY_FLOW_STAT_MAP = {}; + const FlowStatMap &flows = ge->HasData() ? ge->GetData().flows : EMPTY_FLOW_STAT_MAP; + bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0; bool force_unload = (order_flags & OUFB_UNLOAD) != 0; bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0; @@ -454,7 +452,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID CargoPacket *cp = *it; this->packets.erase(it++); - StationID cargo_next = INVALID_STATION; + StationID cargo_next = StationID::Invalid(); MoveToAction action = MTA_LOAD; if (force_keep) { action = MTA_KEEP; @@ -464,18 +462,18 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID action = MTA_TRANSFER; /* We cannot send the cargo to any of the possible next hops and * also not to the current station. */ - FlowStatMap::const_iterator flow_it(ge->flows.find(cp->first_station)); - if (flow_it == ge->flows.end()) { - cargo_next = INVALID_STATION; + FlowStatMap::const_iterator flow_it(flows.find(cp->first_station)); + if (flow_it == flows.end()) { + cargo_next = StationID::Invalid(); } else { FlowStat new_shares = flow_it->second; new_shares.ChangeShare(current_station, INT_MIN); StationIDStack excluded = next_station; while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) { - new_shares.ChangeShare(excluded.Pop(), INT_MIN); + new_shares.ChangeShare(StationID{excluded.Pop()}, INT_MIN); } if (new_shares.GetShares()->empty()) { - cargo_next = INVALID_STATION; + cargo_next = StationID::Invalid(); } else { cargo_next = new_shares.GetVia(); } @@ -483,13 +481,13 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID } else { /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ - if (cp->first_station == INVALID_STATION && !ge->flows.empty()) { - cp->first_station = ge->flows.begin()->first; + if (cp->first_station == StationID::Invalid() && !flows.empty()) { + cp->first_station = flows.begin()->first; } bool restricted = false; - FlowStatMap::const_iterator flow_it(ge->flows.find(cp->first_station)); - if (flow_it == ge->flows.end()) { - cargo_next = INVALID_STATION; + FlowStatMap::const_iterator flow_it(flows.find(cp->first_station)); + if (flow_it == flows.end()) { + cargo_next = StationID::Invalid(); } else { cargo_next = flow_it->second.GetViaWithRestricted(restricted); } @@ -547,7 +545,7 @@ void VehicleCargoList::InvalidateCache() * @param max_move Maximum amount of cargo to reassign. * @return Amount of cargo actually reassigned. */ -template +template uint VehicleCargoList::Reassign(uint max_move) { static_assert(Tfrom != MTA_TRANSFER && Tto != MTA_TRANSFER); @@ -564,7 +562,7 @@ uint VehicleCargoList::Reassign(uint max_move) * @param max_move Maximum amount of cargo to reassign. * @return Amount of cargo actually reassigned. */ -template<> +template <> uint VehicleCargoList::Reassign(uint max_move) { max_move = std::min(this->action_counts[MTA_DELIVER], max_move); @@ -579,7 +577,7 @@ uint VehicleCargoList::ReassignCount(); this->packets.insert(it, cp_split); } - cp->next_hop = INVALID_STATION; + cp->next_hop = StationID::Invalid(); } this->action_counts[MTA_DELIVER] -= max_move; @@ -625,7 +623,7 @@ uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest) * @param current_tile Current tile the cargo handling is happening on. * @return Amount of cargo actually unloaded. */ -uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoID cargo, CargoPayment *payment, TileIndex current_tile) +uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoType cargo, CargoPayment *payment, TileIndex current_tile) { uint moved = 0; if (this->action_counts[MTA_TRANSFER] > 0) { @@ -736,7 +734,7 @@ bool StationCargoList::ShiftCargo(Taction &action, StationID next) * will be kept and the loop will be aborted. * @param action Action instance to be applied. * @param next Next hop the cargo wants to visit. - * @param include_invalid If cargo from the INVALID_STATION list should be + * @param include_invalid If cargo from the StationID::Invalid() list should be * used if necessary. * @return Amount of cargo actually moved. */ @@ -745,11 +743,11 @@ uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool incl { uint max_move = action.MaxMove(); while (!next.IsEmpty()) { - this->ShiftCargo(action, next.Pop()); + this->ShiftCargo(action, StationID{next.Pop()}); if (action.MaxMove() == 0) break; } if (include_invalid && action.MaxMove() > 0) { - this->ShiftCargo(action, INVALID_STATION); + this->ShiftCargo(action, StationID::Invalid()); } return max_move - action.MaxMove(); } @@ -855,7 +853,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, StationIDStac */ uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge) { - return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false); + return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid.base(), false); } /* diff --git a/src/cargopacket.h b/src/cargopacket.h index b97a521a10..f5017e2846 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -15,16 +15,17 @@ #include "station_type.h" #include "order_type.h" #include "cargo_type.h" +#include "source_type.h" #include "vehicle_type.h" #include "core/multimap.hpp" #include "saveload/saveload.h" /** Unique identifier for a single cargo packet. */ -typedef uint32_t CargoPacketID; +using CargoPacketID = PoolID; struct CargoPacket; /** Type of the pool for cargo packets for a little over 16 million packets. */ -typedef Pool CargoPacketPool; +using CargoPacketPool = Pool; /** The actual pool with cargo packets. */ extern CargoPacketPool _cargopacket_pool; @@ -53,15 +54,14 @@ private: TileIndex source_xy = INVALID_TILE; ///< The origin of the cargo. Vector travelled{0, 0}; ///< If cargo is in station: the vector from the unload tile to the source tile. If in vehicle: an intermediate value. - SourceID source_id = INVALID_SOURCE; ///< Index of industry/town/HQ, INVALID_SOURCE if unknown/invalid. - SourceType source_type = SourceType::Industry; ///< Type of \c source_id. + Source source{Source::Invalid, SourceType::Industry}; ///< Source of the cargo #ifdef WITH_ASSERT bool in_vehicle = false; ///< NOSAVE: Whether this cargo is in a vehicle or not. #endif /* WITH_ASSERT */ - StationID first_station = INVALID_STATION; ///< The station where the cargo came from first. - StationID next_hop = INVALID_STATION; ///< Station where the cargo wants to go next. + StationID first_station = StationID::Invalid(); ///< The station where the cargo came from first. + StationID next_hop = StationID::Invalid(); ///< Station where the cargo wants to go next. /** The CargoList caches, thus needs to know about it. */ template friend class CargoList; @@ -74,7 +74,7 @@ public: static const uint16_t MAX_COUNT = UINT16_MAX; CargoPacket(); - CargoPacket(StationID first_station, uint16_t count, SourceType source_type, SourceID source_id); + CargoPacket(StationID first_station, uint16_t count, Source source); CargoPacket(uint16_t count, uint16_t periods_in_transit, StationID first_station, TileIndex source_xy, Money feeder_share); CargoPacket(uint16_t count, Money feeder_share, CargoPacket &original); @@ -184,7 +184,7 @@ public: /** * Gets the number of cargo aging periods this cargo has been in transit. * By default a period is 2.5 days (CARGO_AGING_TICKS = 185 ticks), however - * vehicle NewGRFs can overide the length of the cargo aging period. The + * vehicle NewGRFs can override the length of the cargo aging period. The * value is capped at UINT16_MAX. * @return Length this cargo has been in transit. */ @@ -194,21 +194,12 @@ public: } /** - * Gets the type of the cargo's source. industry, town or head quarter. - * @return Source type. + * Gets the source of the packet for subsidy purposes. + * @return The source. */ - inline SourceType GetSourceType() const + inline Source GetSource() const { - return this->source_type; - } - - /** - * Gets the ID of the cargo's source. An IndustryID, TownID or CompanyID. - * @return Source ID. - */ - inline SourceID GetSourceID() const - { - return this->source_id; + return this->source; } /** @@ -270,7 +261,7 @@ public: return this->next_hop; } - static void InvalidateAllFrom(SourceType src_type, SourceID src); + static void InvalidateAllFrom(Source src); static void InvalidateAllFrom(StationID sid); static void AfterLoad(); }; @@ -292,7 +283,7 @@ public: typedef typename Tcont::const_reverse_iterator ConstReverseIterator; /** Kind of actions that could be done with packets on move. */ - enum MoveToAction { + enum MoveToAction : uint8_t { MTA_BEGIN = 0, MTA_TRANSFER = 0, ///< Transfer the cargo to the station. MTA_DELIVER, ///< Deliver the cargo to some town or industry. @@ -303,10 +294,10 @@ public: }; protected: - uint count; ///< Cache for the number of cargo entities. - uint64_t cargo_periods_in_transit; ///< Cache for the sum of number of cargo aging periods in transit of each entity; comparable to man-hours. + uint count = 0; ///< Cache for the number of cargo entities. + uint64_t cargo_periods_in_transit = 0; ///< Cache for the sum of number of cargo aging periods in transit of each entity; comparable to man-hours. - Tcont packets; ///< The cargo packets in this list. + Tcont packets{}; ///< The cargo packets in this list. void AddToCache(const CargoPacket *cp); @@ -356,10 +347,10 @@ protected: Money feeder_share; ///< Cache for the feeder share. uint action_counts[NUM_MOVE_TO_ACTION]; ///< Counts of cargo to be transferred, delivered, kept and loaded. - template + template void ShiftCargo(Taction action); - template + template void PopCargo(Taction action); /** @@ -393,7 +384,7 @@ public: friend class CargoShift; friend class CargoTransfer; friend class CargoDelivery; - template + template friend class CargoRemoval; friend class CargoReturn; friend class VehicleCargoReroute; @@ -404,7 +395,7 @@ public: */ inline StationID GetFirstStation() const { - return this->count == 0 ? INVALID_STATION : this->packets.front()->first_station; + return this->count == 0 ? StationID::Invalid() : this->packets.front()->first_station; } /** @@ -478,7 +469,7 @@ public: void InvalidateCache(); - bool Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoID cargo, CargoPayment *payment, TileIndex current_tile); + bool Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8_t order_flags, const GoodsEntry *ge, CargoType cargo, CargoPayment *payment, TileIndex current_tile); /** * Marks all cargo in the vehicle as to be kept. This is mostly useful for @@ -495,10 +486,10 @@ public: * amount of cargo to be moved. Second parameter is destination (if * applicable), return value is amount of cargo actually moved. */ - template + template uint Reassign(uint max_move); uint Return(uint max_move, StationCargoList *dest, StationID next_station, TileIndex current_tile); - uint Unload(uint max_move, StationCargoList *dest, CargoID cargo, CargoPayment *payment, TileIndex current_tile); + uint Unload(uint max_move, StationCargoList *dest, CargoType cargo, CargoPayment *payment, TileIndex current_tile); uint Shift(uint max_move, VehicleCargoList *dest); uint Truncate(uint max_move = UINT_MAX); uint Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge); @@ -514,9 +505,8 @@ public: { return cp1->source_xy == cp2->source_xy && cp1->periods_in_transit == cp2->periods_in_transit && - cp1->source_type == cp2->source_type && cp1->first_station == cp2->first_station && - cp1->source_id == cp2->source_id; + cp1->source == cp2->source; } }; @@ -541,18 +531,18 @@ public: friend class CargoLoad; friend class CargoTransfer; - template + template friend class CargoRemoval; friend class CargoReservation; friend class CargoReturn; friend class StationCargoReroute; - static void InvalidateAllFrom(SourceType src_type, SourceID src); + static void InvalidateAllFrom(Source src); - template + template bool ShiftCargo(Taction &action, StationID next); - template + template uint ShiftCargo(Taction action, StationIDStack next, bool include_invalid); void Append(CargoPacket *cp, StationID next); @@ -565,10 +555,10 @@ public: inline bool HasCargoFor(StationIDStack next) const { while (!next.IsEmpty()) { - if (this->packets.find(next.Pop()) != this->packets.end()) return true; + if (this->packets.find(StationID{next.Pop()}) != this->packets.end()) return true; } - /* Packets for INVALID_STATION can go anywhere. */ - return this->packets.find(INVALID_STATION) != this->packets.end(); + /* Packets for StationID::Invalid() can go anywhere. */ + return this->packets.find(StationID::Invalid()) != this->packets.end(); } /** @@ -577,7 +567,7 @@ public: */ inline StationID GetFirstStation() const { - return this->count == 0 ? INVALID_STATION : this->packets.begin()->second.front()->first_station; + return this->count == 0 ? StationID::Invalid() : this->packets.begin()->second.front()->first_station; } /** @@ -629,9 +619,8 @@ public: { return cp1->source_xy == cp2->source_xy && cp1->periods_in_transit == cp2->periods_in_transit && - cp1->source_type == cp2->source_type && cp1->first_station == cp2->first_station && - cp1->source_id == cp2->source_id; + cp1->source == cp2->source; } }; diff --git a/src/cargotype.cpp b/src/cargotype.cpp index 454895c9b7..f65e6f9186 100644 --- a/src/cargotype.cpp +++ b/src/cargotype.cpp @@ -19,8 +19,6 @@ #include "table/strings.h" #include "table/cargo_const.h" -#include - #include "safeguards.h" CargoSpec CargoSpec::array[NUM_CARGO]; @@ -44,9 +42,9 @@ CargoTypes _standard_cargo_mask; static std::vector _default_cargo_labels; /** - * Default cargo translation for upto version 7 NewGRFs. + * Default cargo translation for up to version 7 NewGRFs. * This maps the original 12 cargo slots to their original label. If a climate dependent cargo is not present it will - * map to CT_INVALID. For default cargoes this ends up as a 1:1 mapping via climate slot -> label -> cargo ID. + * map to CT_INVALID. For default cargoes this ends up as a 1:1 mapping via climate slot -> label -> cargo type. */ static std::array _climate_dependent_cargo_labels; @@ -61,9 +59,9 @@ static std::array _climate_independent_cargo_labels; * Set up the default cargo types for the given landscape type. * @param l Landscape */ -void SetupCargoForClimate(LandscapeID l) +void SetupCargoForClimate(LandscapeType l) { - assert(l < lengthof(_default_climate_cargo)); + assert(to_underlying(l) < std::size(_default_climate_cargo)); _cargo_mask = 0; _default_cargo_labels.clear(); @@ -72,7 +70,7 @@ void SetupCargoForClimate(LandscapeID l) /* Copy from default cargo by label or index. */ auto insert = std::begin(CargoSpec::array); - for (const auto &cl : _default_climate_cargo[l]) { + for (const auto &cl : _default_climate_cargo[to_underlying(l)]) { struct visitor { const CargoSpec &operator()(const int &index) @@ -136,8 +134,8 @@ void BuildCargoLabelMap() CargoSpec::label_map.clear(); for (const CargoSpec &cs : CargoSpec::array) { /* During initialization, CargoSpec can be marked valid before the label has been set. */ - if (!cs.IsValid() || cs.label == CargoLabel{0} || cs.label == CT_INVALID) continue; - /* Label already exists, don't addd again. */ + if (!cs.IsValid() || cs.label == CargoLabel{} || cs.label == CT_INVALID) continue; + /* Label already exists, don't add again. */ if (CargoSpec::label_map.count(cs.label) != 0) continue; CargoSpec::label_map.emplace(cs.label, cs.Index()); @@ -146,12 +144,12 @@ void BuildCargoLabelMap() /** * Test if a cargo is a default cargo type. - * @param cid Cargo ID. + * @param cargo_type Cargo type. * @returns true iff the cargo type is a default cargo type. */ -bool IsDefaultCargo(CargoID cid) +bool IsDefaultCargo(CargoType cargo_type) { - auto cs = CargoSpec::Get(cid); + auto cs = CargoSpec::Get(cargo_type); if (!cs->IsValid()) return false; CargoLabel label = cs->label; @@ -188,7 +186,7 @@ SpriteID CargoSpec::GetCargoIcon() const return sprite; } -std::array _sorted_cargo_types; ///< Sort order of cargoes by cargo ID. +std::array _sorted_cargo_types; ///< Sort order of cargoes by cargo type. std::vector _sorted_cargo_specs; ///< Cargo specifications sorted alphabetically by name. std::span _sorted_standard_cargo_specs; ///< Standard cargo specifications sorted alphabetically by name. @@ -207,11 +205,11 @@ static bool CargoSpecNameSorter(const CargoSpec * const &a, const CargoSpec * co /** Sort cargo specifications by their cargo class. */ static bool CargoSpecClassSorter(const CargoSpec * const &a, const CargoSpec * const &b) { - int res = (b->classes & CC_PASSENGERS) - (a->classes & CC_PASSENGERS); + int res = b->classes.Test(CargoClass::Passengers) - a->classes.Test(CargoClass::Passengers); if (res == 0) { - res = (b->classes & CC_MAIL) - (a->classes & CC_MAIL); + res = b->classes.Test(CargoClass::Mail) - a->classes.Test(CargoClass::Mail); if (res == 0) { - res = (a->classes & CC_SPECIAL) - (b->classes & CC_SPECIAL); + res = a->classes.Test(CargoClass::Special) - b->classes.Test(CargoClass::Special); if (res == 0) { return CargoSpecNameSorter(a, b); } @@ -245,7 +243,7 @@ void InitializeSortedCargoSpecs() for (const auto &cargo : _sorted_cargo_specs) { assert(cargo->town_production_effect != INVALID_TPE); CargoSpec::town_production_cargoes[cargo->town_production_effect].push_back(cargo); - if (cargo->classes & CC_SPECIAL) break; + if (cargo->classes.Test(CargoClass::Special)) break; nb_standard_cargo++; SetBit(_standard_cargo_mask, cargo->Index()); } @@ -276,17 +274,15 @@ std::optional BuildCargoAcceptanceString(const CargoArray &acceptan bool found = false; for (const CargoSpec *cs : _sorted_cargo_specs) { - CargoID cid = cs->Index(); - if (acceptance[cid] > 0) { + CargoType cargo_type = cs->Index(); + if (acceptance[cargo_type] > 0) { /* Add a comma between each item. */ if (found) line << list_separator; found = true; /* If the accepted value is less than 8, show it in 1/8:ths */ - if (acceptance[cid] < 8) { - SetDParam(0, acceptance[cid]); - SetDParam(1, cs->name); - line << GetString(STR_LAND_AREA_INFORMATION_CARGO_EIGHTS); + if (acceptance[cargo_type] < 8) { + line << GetString(STR_LAND_AREA_INFORMATION_CARGO_EIGHTS, acceptance[cargo_type], cs->name); } else { line << GetString(cs->name); } diff --git a/src/cargotype.h b/src/cargotype.h index 335d531798..3ca15bfcae 100644 --- a/src/cargotype.h +++ b/src/cargotype.h @@ -13,6 +13,7 @@ #include "economy_type.h" #include "cargo_type.h" #include "gfx_type.h" +#include "newgrf_callbacks.h" #include "strings_type.h" #include "landscape_type.h" #include "core/bitmath_func.hpp" @@ -45,28 +46,25 @@ enum TownProductionEffect : uint8_t { }; /** Cargo classes. */ -enum CargoClass : uint16_t { - CC_NOAVAILABLE = 0, ///< No cargo class has been specified - CC_PASSENGERS = 1 << 0, ///< Passengers - CC_MAIL = 1 << 1, ///< Mail - CC_EXPRESS = 1 << 2, ///< Express cargo (Goods, Food, Candy, but also possible for passengers) - CC_ARMOURED = 1 << 3, ///< Armoured cargo (Valuables, Gold, Diamonds) - CC_BULK = 1 << 4, ///< Bulk cargo (Coal, Grain etc., Ores, Fruit) - CC_PIECE_GOODS = 1 << 5, ///< Piece goods (Livestock, Wood, Steel, Paper) - CC_LIQUID = 1 << 6, ///< Liquids (Oil, Water, Rubber) - CC_REFRIGERATED = 1 << 7, ///< Refrigerated cargo (Food, Fruit) - CC_HAZARDOUS = 1 << 8, ///< Hazardous cargo (Nuclear Fuel, Explosives, etc.) - CC_COVERED = 1 << 9, ///< Covered/Sheltered Freight (Transportation in Box Vans, Silo Wagons, etc.) - CC_OVERSIZED = 1 << 10, ///< Oversized (stake/flatbed wagon) - CC_POWDERIZED = 1 << 11, ///< Powderized, moist protected (powder/silo wagon) - CC_NOT_POURABLE = 1 << 12, ///< Not Pourable (open wagon, but not hopper wagon) - CC_POTABLE = 1 << 13, ///< Potable / food / clean. - CC_NON_POTABLE = 1 << 14, ///< Non-potable / non-food / dirty. - CC_SPECIAL = 1 << 15, ///< Special bit used for livery refit tricks instead of normal cargoes. +enum class CargoClass : uint8_t { + Passengers = 0, ///< Passengers + Mail = 1, ///< Mail + Express = 2, ///< Express cargo (Goods, Food, Candy, but also possible for passengers) + Armoured = 3, ///< Armoured cargo (Valuables, Gold, Diamonds) + Bulk = 4, ///< Bulk cargo (Coal, Grain etc., Ores, Fruit) + PieceGoods = 5, ///< Piece goods (Livestock, Wood, Steel, Paper) + Liquid = 6, ///< Liquids (Oil, Water, Rubber) + Refrigerated = 7, ///< Refrigerated cargo (Food, Fruit) + Hazardous = 8, ///< Hazardous cargo (Nuclear Fuel, Explosives, etc.) + Covered = 9, ///< Covered/Sheltered Freight (Transportation in Box Vans, Silo Wagons, etc.) + Oversized = 10, ///< Oversized (stake/flatbed wagon) + Powderized = 11, ///< Powderized, moist protected (powder/silo wagon) + NotPourable = 12, ///< Not Pourable (open wagon, but not hopper wagon) + Potable = 13, ///< Potable / food / clean. + NonPotable = 14, ///< Non-potable / non-food / dirty. + Special = 15, ///< Special bit used for livery refit tricks instead of normal cargoes. }; - -/** Bitmask of cargo classes. */ -using CargoClasses = uint16_t; +using CargoClasses = EnumBitSet; static const uint8_t INVALID_CARGO_BITNUM = 0xFF; ///< Constant representing invalid cargo @@ -87,8 +85,8 @@ struct CargoSpec { bool is_freight; ///< Cargo type is considered to be freight (affects train freight multiplier). TownAcceptanceEffect town_acceptance_effect; ///< The effect that delivering this cargo type has on towns. Also affects destination of subsidies. TownProductionEffect town_production_effect = INVALID_TPE; ///< The effect on town cargo production. - uint16_t town_production_multiplier = TOWN_PRODUCTION_DIVISOR; ///< Town production multipler, if commanded by TownProductionEffect. - uint8_t callback_mask; ///< Bitmask of cargo callbacks that have to be called + uint16_t town_production_multiplier = TOWN_PRODUCTION_DIVISOR; ///< Town production multiplier, if commanded by TownProductionEffect. + CargoCallbackMasks callback_mask; ///< Bitmask of cargo callbacks that have to be called StringID name; ///< Name of this type of cargo. StringID name_single; ///< Name of a single entity of this type of cargo. @@ -107,7 +105,7 @@ struct CargoSpec { * Determines index of this cargospec * @return index (in the CargoSpec::array array) */ - inline CargoID Index() const + inline CargoType Index() const { return this - CargoSpec::array; } @@ -132,9 +130,9 @@ struct CargoSpec { } /** - * Retrieve cargo details for the given cargo ID + * Retrieve cargo details for the given cargo type * @param index ID of cargo - * @pre index is a valid cargo ID + * @pre index is a valid cargo type */ static inline CargoSpec *Get(size_t index) { @@ -167,7 +165,6 @@ struct CargoSpec { }; bool operator==(const Iterator &other) const { return this->index == other.index; } - bool operator!=(const Iterator &other) const { return !(*this == other); } CargoSpec * operator*() const { return CargoSpec::Get(this->index); } Iterator & operator++() { this->index++; this->ValidateIndex(); return *this; } @@ -199,24 +196,24 @@ struct CargoSpec { private: static CargoSpec array[NUM_CARGO]; ///< Array holding all CargoSpecs - static inline std::map label_map{}; ///< Translation map from CargoLabel to Cargo ID. + static inline std::map label_map{}; ///< Translation map from CargoLabel to Cargo type. - friend void SetupCargoForClimate(LandscapeID l); + friend void SetupCargoForClimate(LandscapeType l); friend void BuildCargoLabelMap(); - friend inline CargoID GetCargoIDByLabel(CargoLabel ct); + friend inline CargoType GetCargoTypeByLabel(CargoLabel ct); friend void FinaliseCargoArray(); }; extern CargoTypes _cargo_mask; extern CargoTypes _standard_cargo_mask; -void SetupCargoForClimate(LandscapeID l); -bool IsDefaultCargo(CargoID cid); +void SetupCargoForClimate(LandscapeType l); +bool IsDefaultCargo(CargoType cargo_type); void BuildCargoLabelMap(); std::optional BuildCargoAcceptanceString(const CargoArray &acceptance, StringID label); -inline CargoID GetCargoIDByLabel(CargoLabel label) +inline CargoType GetCargoTypeByLabel(CargoLabel label) { auto found = CargoSpec::label_map.find(label); if (found != std::end(CargoSpec::label_map)) return found->second; @@ -232,20 +229,20 @@ extern std::span _sorted_standard_cargo_specs; /** * Does cargo \a c have cargo class \a cc? - * @param c Cargo type. + * @param cargo Cargo type. * @param cc Cargo class. * @return The type fits in the class. */ -inline bool IsCargoInClass(CargoID c, CargoClass cc) +inline bool IsCargoInClass(CargoType cargo, CargoClasses cc) { - return (CargoSpec::Get(c)->classes & cc) != 0; + return CargoSpec::Get(cargo)->classes.Any(cc); } -using SetCargoBitIterator = SetBitIterator; +using SetCargoBitIterator = SetBitIterator; -/** Comparator to sort CargoID by according to desired order. */ -struct CargoIDComparator { - bool operator() (const CargoID &lhs, const CargoID &rhs) const { return _sorted_cargo_types[lhs] < _sorted_cargo_types[rhs]; } +/** Comparator to sort CargoType by according to desired order. */ +struct CargoTypeComparator { + bool operator() (const CargoType &lhs, const CargoType &rhs) const { return _sorted_cargo_types[lhs] < _sorted_cargo_types[rhs]; } }; #endif /* CARGOTYPE_H */ diff --git a/src/cheat_func.h b/src/cheat_func.h index 5c205b04bd..8585a589e2 100644 --- a/src/cheat_func.h +++ b/src/cheat_func.h @@ -12,8 +12,6 @@ #include "cheat_type.h" -extern Cheats _cheats; - void ShowCheatWindow(); diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index d226591add..b2994d5eee 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -12,6 +12,7 @@ #include "cheat_type.h" #include "company_base.h" #include "company_func.h" +#include "currency.h" #include "saveload/saveload.h" #include "vehicle_base.h" #include "textbuf_gui.h" @@ -29,6 +30,8 @@ #include "error.h" #include "misc_cmd.h" #include "core/geometry_func.hpp" +#include "settings_type.h" +#include "settings_internal.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" #include "timer/timer_game_economy.h" @@ -36,6 +39,7 @@ #include "widgets/cheat_widget.h" #include "table/sprites.h" +#include "table/strings.h" #include "safeguards.h" @@ -72,12 +76,12 @@ static int32_t ClickChangeCompanyCheat(int32_t new_value, int32_t change_directi while ((uint)new_value < Company::GetPoolSize()) { if (Company::IsValidID((CompanyID)new_value)) { SetLocalCompany((CompanyID)new_value); - return _local_company; + return _local_company.base(); } new_value += change_direction; } - return _local_company; + return _local_company.base(); } /** @@ -113,7 +117,7 @@ static int32_t ClickChangeDateCheat(int32_t new_value, int32_t) /* If not using wallclock units, we keep economy date in sync with calendar date and must change it also. */ if (!TimerGameEconomy::UsingWallclockUnits()) { /* Keep economy and calendar dates synced. */ - TimerGameEconomy::Date new_economy_date = new_calendar_date.base(); + TimerGameEconomy::Date new_economy_date{new_calendar_date.base()}; /* Shift cached dates before we change the date. */ for (auto v : Vehicle::Iterate()) v->ShiftDates(new_economy_date - TimerGameEconomy::date); @@ -148,7 +152,7 @@ static int32_t ClickChangeMaxHlCheat(int32_t new_value, int32_t) * If yes, disallow the change. */ for (const auto t : Map::Iterate()) { if ((int32_t)TileHeight(t) > new_value) { - ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN), {}, WL_ERROR); /* Return old, unchanged value */ return _settings_game.construction.map_height_limit; } @@ -165,7 +169,7 @@ static int32_t ClickChangeMaxHlCheat(int32_t new_value, int32_t) } /** Available cheats. */ -enum CheatNumbers { +enum CheatNumbers : uint8_t { CHT_MONEY, ///< Change amount of money. CHT_CHANGE_COMPANY, ///< Switch company. CHT_EXTRA_DYNAMITE, ///< Dynamite anything. @@ -217,22 +221,33 @@ static_assert(CHT_NUM_CHEATS == lengthof(_cheats_ui)); static constexpr NWidgetPart _nested_cheat_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), - NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CHEATS, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_GREY), SetStringTip(STR_CHEATS, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_C_PANEL), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.framerect), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_PANEL), + NWidget(WWT_EMPTY, INVALID_COLOUR, WID_C_SETTINGS), + EndContainer(), + EndContainer(), }; /** GUI for the cheats. */ struct CheatWindow : Window { - int clicked; - int clicked_widget; - uint line_height; - Dimension icon; ///< Dimension of company icon sprite + int clicked = 0; + int clicked_cheat = 0; + uint line_height = 0; + Dimension icon{}; ///< Dimension of company icon sprite + + std::vector sandbox_settings{}; + const SettingDesc *clicked_setting = nullptr; + const SettingDesc *last_clicked_setting = nullptr; + const SettingDesc *valuewindow_entry = nullptr; CheatWindow(WindowDesc &desc) : Window(desc) { + this->sandbox_settings = GetFilteredSettingCollection([](const SettingDesc &sd) { return sd.flags.Test(SettingFlag::Sandbox); }); this->InitNested(); } @@ -243,9 +258,15 @@ struct CheatWindow : Window { void DrawWidget(const Rect &r, WidgetID widget) const override { - if (widget != WID_C_PANEL) return; + switch (widget) { + case WID_C_PANEL: DrawCheatWidget(r); break; + case WID_C_SETTINGS: DrawSettingsWidget(r); break; + } + } - const Rect ir = r.Shrink(WidgetDimensions::scaled.framerect); + void DrawCheatWidget(const Rect &r) const + { + const Rect ir = r; int y = ir.top; bool rtl = _current_text_dir == TD_RTL; @@ -260,76 +281,124 @@ struct CheatWindow : Window { for (int i = 0; i != lengthof(_cheats_ui); i++) { const CheatEntry *ce = &_cheats_ui[i]; + std::string str; switch (ce->type) { case SLE_BOOL: { bool on = (*(bool*)ce->variable); DrawBoolButton(button_left, y + button_y_offset, on, true); - SetDParam(0, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); + str = GetString(ce->str, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); break; } default: { - int32_t val = (int32_t)ReadValue(ce->variable, ce->type); + int32_t val = static_cast(ReadValue(ce->variable, ce->type)); /* Draw [<][>] boxes for settings of an integer-type */ DrawArrowButtons(button_left, y + button_y_offset, COLOUR_YELLOW, clicked - (i * 2), true, true); switch (ce->str) { /* Display date for change date cheat */ - case STR_CHEAT_CHANGE_DATE: SetDParam(0, TimerGameCalendar::date); break; + case STR_CHEAT_CHANGE_DATE: + str = GetString(ce->str, TimerGameCalendar::date); + break; /* Draw coloured flag for change company cheat */ case STR_CHEAT_CHANGE_COMPANY: { - SetDParam(0, val + 1); - uint offset = WidgetDimensions::scaled.hsep_indent + GetStringBoundingBox(ce->str).width; + str = GetString(ce->str, val + 1); + uint offset = WidgetDimensions::scaled.hsep_indent + GetStringBoundingBox(str).width; DrawCompanyIcon(_local_company, rtl ? text_right - offset - WidgetDimensions::scaled.hsep_indent : text_left + offset, y + icon_y_offset); break; } - default: SetDParam(0, val); + default: + str = GetString(ce->str, val); + break; } break; } } - DrawString(text_left, text_right, y + text_y_offset, ce->str); + DrawString(text_left, text_right, y + text_y_offset, str); y += this->line_height; } } + void DrawSettingsWidget(const Rect &r) const + { + Rect ir = r.WithHeight(this->line_height); + + for (const auto &desc : this->sandbox_settings) { + DrawSetting(ir, desc); + ir = ir.Translate(0, this->line_height); + } + } + + void DrawSetting(const Rect r, const SettingDesc *desc) const + { + const IntSettingDesc *sd = desc->AsIntSetting(); + int state = this->clicked_setting == sd ? this->clicked : 0; + + bool rtl = _current_text_dir == TD_RTL; + + Rect buttons = r.WithWidth(SETTING_BUTTON_WIDTH, rtl); + Rect text = r.Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide, rtl); + buttons.top += (r.Height() - SETTING_BUTTON_HEIGHT) / 2; + text.top += (r.Height() - GetCharacterHeight(FS_NORMAL)) / 2; + + /* We do not allow changes of some items when we are a client in a network game */ + bool editable = sd->IsEditable(); + + auto [min_val, max_val] = sd->GetRange(); + int32_t value = sd->Read(&GetGameSettings()); + if (sd->IsBoolSetting()) { + /* Draw checkbox for boolean-value either on/off */ + DrawBoolButton(buttons.left, buttons.top, value != 0, editable); + } else if (sd->flags.Test(SettingFlag::GuiDropdown)) { + /* Draw [v] button for settings of an enum-type */ + DrawDropDownButton(buttons.left, buttons.top, COLOUR_YELLOW, state != 0, editable); + } else { + /* Draw [<][>] boxes for settings of an integer-type */ + DrawArrowButtons(buttons.left, buttons.top, COLOUR_YELLOW, state, + editable && value != (sd->flags.Test(SettingFlag::GuiZeroIsSpecial) ? 0 : min_val), editable && static_cast(value) != max_val); + } + auto [param1, param2] = sd->GetValueParams(value); + DrawString(text.left, text.right, text.top, GetString(sd->GetTitle(), STR_CONFIG_SETTING_VALUE, param1, param2), TC_LIGHT_BLUE); + } + void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { - if (widget != WID_C_PANEL) return; + switch (widget) { + case WID_C_PANEL: UpdateCheatPanelSize(size); break; + case WID_C_SETTINGS: UpdateSettingsPanelSize(size); break; + } + } + void UpdateCheatPanelSize(Dimension &size) + { uint width = 0; for (const auto &ce : _cheats_ui) { switch (ce.type) { case SLE_BOOL: - SetDParam(0, STR_CONFIG_SETTING_ON); - width = std::max(width, GetStringBoundingBox(ce.str).width); - SetDParam(0, STR_CONFIG_SETTING_OFF); - width = std::max(width, GetStringBoundingBox(ce.str).width); + width = std::max(width, GetStringBoundingBox(GetString(ce.str, STR_CONFIG_SETTING_ON)).width); + width = std::max(width, GetStringBoundingBox(GetString(ce.str, STR_CONFIG_SETTING_OFF)).width); break; default: switch (ce.str) { /* Display date for change date cheat */ case STR_CHEAT_CHANGE_DATE: - SetDParam(0, TimerGameCalendar::ConvertYMDToDate(CalendarTime::MAX_YEAR, 11, 31)); - width = std::max(width, GetStringBoundingBox(ce.str).width); + width = std::max(width, GetStringBoundingBox(GetString(ce.str, TimerGameCalendar::ConvertYMDToDate(CalendarTime::MAX_YEAR, 11, 31))).width); break; /* Draw coloured flag for change company cheat */ case STR_CHEAT_CHANGE_COMPANY: - SetDParamMaxValue(0, MAX_COMPANIES); - width = std::max(width, GetStringBoundingBox(ce.str).width + WidgetDimensions::scaled.hsep_wide); + width = std::max(width, GetStringBoundingBox(GetString(ce.str, MAX_COMPANIES)).width + WidgetDimensions::scaled.hsep_wide); break; default: - SetDParam(0, INT64_MAX); - width = std::max(width, GetStringBoundingBox(ce.str).width); + width = std::max(width, GetStringBoundingBox(GetString(ce.str, INT64_MAX)).width); break; } break; @@ -340,13 +409,33 @@ struct CheatWindow : Window { this->line_height = std::max(this->line_height, GetCharacterHeight(FS_NORMAL)) + WidgetDimensions::scaled.framerect.Vertical(); size.width = width + WidgetDimensions::scaled.hsep_wide * 2 + SETTING_BUTTON_WIDTH; - size.height = WidgetDimensions::scaled.framerect.Vertical() + this->line_height * lengthof(_cheats_ui); + size.height = this->line_height * lengthof(_cheats_ui); + } + + void UpdateSettingsPanelSize(Dimension &size) + { + uint width = 0; + for (const auto &desc : this->sandbox_settings) { + const IntSettingDesc *sd = desc->AsIntSetting(); + + auto [param1, param2] = sd->GetValueParams(sd->GetDefaultValue()); + width = std::max(width, GetStringBoundingBox(GetString(sd->GetTitle(), STR_CONFIG_SETTING_VALUE, param1, param2)).width); + } + + size.width = width + WidgetDimensions::scaled.hsep_wide * 2 + SETTING_BUTTON_WIDTH; + size.height = this->line_height * static_cast(std::size(this->sandbox_settings)); } void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { - if (widget != WID_C_PANEL) return; + switch (widget) { + case WID_C_PANEL: CheatPanelClick(pt); break; + case WID_C_SETTINGS: SettingsPanelClick(pt); break; + } + } + void CheatPanelClick(Point pt) + { Rect r = this->GetWidget(WID_C_PANEL)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect); uint btn = (pt.y - r.top) / this->line_height; int x = pt.x - r.left; @@ -356,25 +445,24 @@ struct CheatWindow : Window { if (btn >= lengthof(_cheats_ui)) return; const CheatEntry *ce = &_cheats_ui[btn]; - int value = (int32_t)ReadValue(ce->variable, ce->type); + int value = static_cast(ReadValue(ce->variable, ce->type)); int oldvalue = value; if (btn == CHT_CHANGE_DATE && x >= SETTING_BUTTON_WIDTH) { /* Click at the date text directly. */ - clicked_widget = CHT_CHANGE_DATE; - SetDParam(0, value); - ShowQueryString(STR_JUST_INT, STR_CHEAT_CHANGE_DATE_QUERY_CAPT, 8, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); + clicked_cheat = CHT_CHANGE_DATE; + ShowQueryString(GetString(STR_JUST_INT, value), STR_CHEAT_CHANGE_DATE_QUERY_CAPT, 8, this, CS_NUMERAL, QueryStringFlag::AcceptUnchanged); return; } else if (btn == CHT_EDIT_MAX_HL && x >= SETTING_BUTTON_WIDTH) { - clicked_widget = CHT_EDIT_MAX_HL; - SetDParam(0, value); - ShowQueryString(STR_JUST_INT, STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT, 8, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); + clicked_cheat = CHT_EDIT_MAX_HL; + ShowQueryString(GetString(STR_JUST_INT, value), STR_CHEAT_EDIT_MAX_HL_QUERY_CAPT, 8, this, CS_NUMERAL, QueryStringFlag::AcceptUnchanged); return; } /* Not clicking a button? */ if (!IsInsideMM(x, 0, SETTING_BUTTON_WIDTH)) return; + this->clicked_setting = nullptr; *ce->been_used = true; switch (ce->type) { @@ -392,15 +480,119 @@ struct CheatWindow : Window { break; } - if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64_t)value); + if (value != oldvalue) WriteValue(ce->variable, ce->type, static_cast(value)); this->SetTimeout(); this->SetDirty(); } + void SettingsPanelClick(Point pt) + { + int row = this->GetRowFromWidget(pt.y, WID_C_SETTINGS, WidgetDimensions::scaled.framerect.top, this->line_height); + if (row == INT_MAX) return; + + const SettingDesc *desc = this->sandbox_settings[row]; + const IntSettingDesc *sd = desc->AsIntSetting(); + + if (!sd->IsEditable()) return; + + Rect r = this->GetWidget(WID_C_SETTINGS)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect); + int x = pt.x - r.left; + bool rtl = _current_text_dir == TD_RTL; + if (rtl) x = r.Width() - 1 - x; + + if (x < SETTING_BUTTON_WIDTH) { + ChangeSettingValue(sd, x); + } else { + /* Only open editbox if clicked for the second time, and only for types where it is sensible for. */ + if (this->last_clicked_setting == sd && !sd->IsBoolSetting() && !sd->flags.Test(SettingFlag::GuiDropdown)) { + int64_t value64 = sd->Read(&GetGameSettings()); + + /* Show the correct currency-translated value */ + if (sd->flags.Test(SettingFlag::GuiCurrency)) value64 *= GetCurrency().rate; + + CharSetFilter charset_filter = CS_NUMERAL; //default, only numeric input allowed + if (sd->min < 0) charset_filter = CS_NUMERAL_SIGNED; // special case, also allow '-' sign for negative input + + this->valuewindow_entry = sd; + + /* Limit string length to 14 so that MAX_INT32 * max currency rate doesn't exceed MAX_INT64. */ + ShowQueryString(GetString(STR_JUST_INT, value64), STR_CONFIG_SETTING_QUERY_CAPTION, 15, this, charset_filter, QueryStringFlag::EnableDefault); + } + + this->clicked_setting = sd; + } + } + + void ChangeSettingValue(const IntSettingDesc *sd, int x) + { + int32_t value = sd->Read(&GetGameSettings()); + int32_t oldvalue = value; + if (sd->IsBoolSetting()) { + value ^= 1; + } else { + /* don't allow too fast scrolling */ + if (this->flags.Test(WindowFlag::Timeout) && this->timeout_timer > 1) { + _left_button_clicked = false; + return; + } + + /* Add a dynamic step-size to the scroller. In a maximum of + * 50-steps you should be able to get from min to max, + * unless specified otherwise in the 'interval' variable + * of the current setting. */ + uint32_t step = (sd->interval == 0) ? ((sd->max - sd->min) / 50) : sd->interval; + if (step == 0) step = 1; + + /* Increase or decrease the value and clamp it to extremes */ + if (x >= SETTING_BUTTON_WIDTH / 2) { + value += step; + if (sd->min < 0) { + assert(static_cast(sd->max) >= 0); + if (value > static_cast(sd->max)) value = static_cast(sd->max); + } else { + if (static_cast(value) > sd->max) value = static_cast(sd->max); + } + if (value < sd->min) value = sd->min; // skip between "disabled" and minimum + } else { + value -= step; + if (value < sd->min) value = sd->flags.Test(SettingFlag::GuiZeroIsSpecial) ? 0 : sd->min; + } + + /* Set up scroller timeout for numeric values */ + if (value != oldvalue) { + this->last_clicked_setting = nullptr; + this->clicked_setting = sd; + this->clicked = (x >= SETTING_BUTTON_WIDTH / 2) != (_current_text_dir == TD_RTL) ? 2 : 1; + this->SetTimeout(); + _left_button_clicked = false; + } + } + + if (value != oldvalue) { + SetSettingValue(sd, value); + this->SetDirty(); + } + } + + bool OnTooltip([[maybe_unused]] Point pt, WidgetID widget, TooltipCloseCondition close_cond) override + { + if (widget != WID_C_SETTINGS) return false; + + int row = GetRowFromWidget(pt.y, widget, WidgetDimensions::scaled.framerect.top, this->line_height); + if (row == INT_MAX) return false; + + const SettingDesc *desc = this->sandbox_settings[row]; + const IntSettingDesc *sd = desc->AsIntSetting(); + GuiShowTooltips(this, GetEncodedString(sd->GetHelp()), close_cond); + + return true; + } + void OnTimeout() override { + this->clicked_setting = nullptr; this->clicked = 0; this->SetDirty(); } @@ -410,13 +602,33 @@ struct CheatWindow : Window { /* Was 'cancel' pressed or nothing entered? */ if (!str.has_value() || str->empty()) return; - const CheatEntry *ce = &_cheats_ui[clicked_widget]; - int oldvalue = (int32_t)ReadValue(ce->variable, ce->type); - int value = atoi(str->c_str()); - *ce->been_used = true; - value = ce->proc(value, value - oldvalue); + if (this->valuewindow_entry != nullptr) { + const IntSettingDesc *sd = this->valuewindow_entry->AsIntSetting(); - if (value != oldvalue) WriteValue(ce->variable, ce->type, (int64_t)value); + int32_t value; + if (!str->empty()) { + long long llvalue = atoll(str->c_str()); + + /* Save the correct currency-translated value */ + if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue /= GetCurrency().rate; + + value = ClampTo(llvalue); + } else { + value = sd->GetDefaultValue(); + } + + SetSettingValue(sd, value); + } else { + const CheatEntry *ce = &_cheats_ui[clicked_cheat]; + int oldvalue = static_cast(ReadValue(ce->variable, ce->type)); + int value = atoi(str->c_str()); + *ce->been_used = true; + value = ce->proc(value, value - oldvalue); + + if (value != oldvalue) WriteValue(ce->variable, ce->type, static_cast(value)); + } + + this->valuewindow_entry = nullptr; this->SetDirty(); } @@ -429,7 +641,7 @@ struct CheatWindow : Window { static WindowDesc _cheats_desc( WDP_AUTO, "cheats", 0, 0, WC_CHEATS, WC_NONE, - 0, + {}, _nested_cheat_widgets ); diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index c40904e0fd..04f52cfdee 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -23,9 +23,9 @@ #include "safeguards.h" -static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlag flags) +static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlags flags) { - static const Price clear_price_table[] = { + static constexpr Price clear_price_table[] = { PR_CLEAR_GRASS, PR_CLEAR_ROUGH, PR_CLEAR_ROCKS, @@ -35,11 +35,17 @@ static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlag flags) }; CommandCost price(EXPENSES_CONSTRUCTION); - if (!IsClearGround(tile, CLEAR_GRASS) || GetClearDensity(tile) != 0) { - price.AddCost(_price[clear_price_table[GetClearGround(tile)]]); + ClearGround ground = GetClearGround(tile); + uint8_t density = GetClearDensity(tile); + if (IsSnowTile(tile)) { + price.AddCost(_price[clear_price_table[ground]]); + /* Add a little more for removing snow. */ + price.AddCost(std::abs(_price[PR_CLEAR_ROUGH] - _price[PR_CLEAR_GRASS])); + } else if (ground != CLEAR_GRASS || density != 0) { + price.AddCost(_price[clear_price_table[ground]]); } - if (flags & DC_EXEC) DoClearSquare(tile); + if (flags.Test(DoCommandFlag::Execute)) DoClearSquare(tile); return price; } @@ -100,7 +106,10 @@ static void DrawClearLandFence(const TileInfo *ti) static void DrawTile_Clear(TileInfo *ti) { - switch (GetClearGround(ti->tile)) { + ClearGround real_ground = GetClearGround(ti->tile); + ClearGround ground = IsSnowTile(ti->tile) ? CLEAR_SNOW : real_ground; + + switch (ground) { case CLEAR_GRASS: DrawClearLandTile(ti, GetClearDensity(ti->tile)); break; @@ -110,7 +119,7 @@ static void DrawTile_Clear(TileInfo *ti) break; case CLEAR_ROCKS: - DrawGroundSprite((HasGrfMiscBit(GMB_SECOND_ROCKY_TILE_SET) && (TileHash(ti->x, ti->y) & 1) ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + DrawGroundSprite((HasGrfMiscBit(GrfMiscBit::SecondRockyTileSet) && (TileHash(ti->x, ti->y) & 1) ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break; case CLEAR_FIELDS: @@ -118,7 +127,17 @@ static void DrawTile_Clear(TileInfo *ti) DrawClearLandFence(ti); break; - case CLEAR_SNOW: + case CLEAR_SNOW: { + uint8_t density = GetClearDensity(ti->tile); + DrawGroundSprite(_clear_land_sprites_snow_desert[density] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + if (real_ground == CLEAR_ROCKS) { + /* There 4 levels of snowy overlay rocks, each with 19 sprites. */ + ++density; + DrawGroundSprite(SPR_OVERLAY_ROCKS_BASE + (density * 19) + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + } + break; + } + case CLEAR_DESERT: DrawGroundSprite(_clear_land_sprites_snow_desert[GetClearDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break; @@ -165,7 +184,8 @@ static void TileLoopClearAlps(TileIndex tile) /* Below the snow line, do nothing if no snow. */ /* At or above the snow line, make snow tile if needed. */ if (k >= 0) { - MakeSnow(tile); + /* Snow density is started at 0 so that it can gradually reach the required density. */ + MakeSnow(tile, 0); MarkTileDirtyByTile(tile); } return; @@ -231,10 +251,13 @@ static void TileLoop_Clear(TileIndex tile) AmbientSoundEffect(tile); switch (_settings_game.game_creation.landscape) { - case LT_TROPIC: TileLoopClearDesert(tile); break; - case LT_ARCTIC: TileLoopClearAlps(tile); break; + case LandscapeType::Tropic: TileLoopClearDesert(tile); break; + case LandscapeType::Arctic: TileLoopClearAlps(tile); break; + default: break; } + if (IsSnowTile(tile)) return; + switch (GetClearGround(tile)) { case CLEAR_GRASS: if (GetClearDensity(tile) == 3) return; @@ -264,7 +287,7 @@ static void TileLoop_Clear(TileIndex tile) SetClearCounter(tile, 0); } - if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) { + if (GetIndustryIndexOfField(tile) == IndustryID::Invalid() && GetFieldType(tile) >= 7) { /* This farmfield is no longer farmfield, so make it grass again */ MakeClear(tile, CLEAR_GRASS, 2); } else { @@ -327,23 +350,25 @@ static TrackStatus GetTileTrackStatus_Clear(TileIndex, TransportType, uint, Diag return 0; } -static const StringID _clear_land_str[] = { - STR_LAI_CLEAR_DESCRIPTION_GRASS, - STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND, - STR_LAI_CLEAR_DESCRIPTION_ROCKS, - STR_LAI_CLEAR_DESCRIPTION_FIELDS, - STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND, - STR_LAI_CLEAR_DESCRIPTION_DESERT -}; - -static void GetTileDesc_Clear(TileIndex tile, TileDesc *td) +static void GetTileDesc_Clear(TileIndex tile, TileDesc &td) { - if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) { - td->str = STR_LAI_CLEAR_DESCRIPTION_BARE_LAND; + /* Each pair holds a normal and a snowy ClearGround description. */ + static constexpr std::pair clear_land_str[] = { + {STR_LAI_CLEAR_DESCRIPTION_GRASS, STR_LAI_CLEAR_DESCRIPTION_SNOWY_GRASS}, + {STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND, STR_LAI_CLEAR_DESCRIPTION_SNOWY_ROUGH_LAND}, + {STR_LAI_CLEAR_DESCRIPTION_ROCKS, STR_LAI_CLEAR_DESCRIPTION_SNOWY_ROCKS}, + {STR_LAI_CLEAR_DESCRIPTION_FIELDS, STR_EMPTY}, + {STR_EMPTY, STR_EMPTY}, // CLEAR_SNOW does not appear in the map. + {STR_LAI_CLEAR_DESCRIPTION_DESERT, STR_EMPTY}, + }; + + if (!IsSnowTile(tile) && IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) { + td.str = STR_LAI_CLEAR_DESCRIPTION_BARE_LAND; } else { - td->str = _clear_land_str[GetClearGround(tile)]; + const auto &[name, snowy_name] = clear_land_str[GetClearGround(tile)]; + td.str = IsSnowTile(tile) ? snowy_name : name; } - td->owner[0] = GetTileOwner(tile); + td.owner[0] = GetTileOwner(tile); } static void ChangeTileOwner_Clear(TileIndex, Owner, Owner) @@ -351,7 +376,7 @@ static void ChangeTileOwner_Clear(TileIndex, Owner, Owner) return; } -static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlag flags, int, Slope) +static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlags flags, int, Slope) { return Command::Do(flags, tile); } diff --git a/src/clear_map.h b/src/clear_map.h index 64eb640d12..a1a58612ce 100644 --- a/src/clear_map.h +++ b/src/clear_map.h @@ -16,12 +16,12 @@ /** * Ground types. Valid densities in comments after the enum. */ -enum ClearGround { +enum ClearGround : uint8_t { CLEAR_GRASS = 0, ///< 0-3 CLEAR_ROUGH = 1, ///< 3 CLEAR_ROCKS = 2, ///< 3 CLEAR_FIELDS = 3, ///< 3 - CLEAR_SNOW = 4, ///< 0-3 + CLEAR_SNOW = 4, ///< 0-3 (Not stored in map.) CLEAR_DESERT = 5, ///< 1,3 }; @@ -38,18 +38,6 @@ inline bool IsSnowTile(Tile t) return HasBit(t.m3(), 4); } -/** - * Get the type of clear tile but never return CLEAR_SNOW. - * @param t the tile to get the clear ground type of - * @pre IsTileType(t, MP_CLEAR) - * @return the ground type - */ -inline ClearGround GetRawClearGround(Tile t) -{ - assert(IsTileType(t, MP_CLEAR)); - return (ClearGround)GB(t.m5(), 2, 3); -} - /** * Get the type of clear tile. * @param t the tile to get the clear ground type of @@ -58,8 +46,8 @@ inline ClearGround GetRawClearGround(Tile t) */ inline ClearGround GetClearGround(Tile t) { - if (IsSnowTile(t)) return CLEAR_SNOW; - return GetRawClearGround(t); + assert(IsTileType(t, MP_CLEAR)); + return static_cast(GB(t.m5(), 2, 3)); } /** @@ -207,7 +195,7 @@ inline IndustryID GetIndustryIndexOfField(Tile t) inline void SetIndustryIndexOfField(Tile t, IndustryID i) { assert(GetClearGround(t) == CLEAR_FIELDS); - t.m2() = i; + t.m2() = i.base(); } @@ -282,7 +270,7 @@ inline void MakeField(Tile t, uint field_type, IndustryID industry) SetTileType(t, MP_CLEAR); t.m1() = 0; SetTileOwner(t, OWNER_NONE); - t.m2() = industry; + t.m2() = industry.base(); t.m3() = field_type; t.m4() = 0 << 5 | 0 << 2; SetClearGroundDensity(t, CLEAR_FIELDS, 3); @@ -295,13 +283,13 @@ inline void MakeField(Tile t, uint field_type, IndustryID industry) * Make a snow tile. * @param t the tile to make snowy * @param density The density of snowiness. - * @pre GetClearGround(t) != CLEAR_SNOW + * @pre !IsSnowTile(t) */ inline void MakeSnow(Tile t, uint density = 0) { - assert(GetClearGround(t) != CLEAR_SNOW); + assert(!IsSnowTile(t)); SetBit(t.m3(), 4); - if (GetRawClearGround(t) == CLEAR_FIELDS) { + if (GetClearGround(t) == CLEAR_FIELDS) { SetClearGroundDensity(t, CLEAR_GRASS, density); } else { SetClearDensity(t, density); @@ -311,11 +299,11 @@ inline void MakeSnow(Tile t, uint density = 0) /** * Clear the snow from a tile and return it to its previous type. * @param t the tile to clear of snow - * @pre GetClearGround(t) == CLEAR_SNOW + * @pre IsSnowTile(t) */ inline void ClearSnow(Tile t) { - assert(GetClearGround(t) == CLEAR_SNOW); + assert(IsSnowTile(t)); ClrBit(t.m3(), 4); SetClearDensity(t, 3); } diff --git a/src/command.cpp b/src/command.cpp index 964ccd8a0f..03cdb96bc4 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -77,7 +77,7 @@ int RecursiveCommandCounter::_counter = 0; * Define a command with the flags which belongs to it. * * This struct connects a command handler function with the flags created with - * the #CMD_AUTO, #CMD_OFFLINE and #CMD_SERVER values. + * the #CommandFlag::Auto, #CommandFlag::Offline and #CommandFlag::Server values. */ struct CommandInfo { const char *name; ///< A human readable name for the procedure @@ -88,7 +88,7 @@ struct CommandInfo { template inline constexpr CommandInfo CommandFromTrait() noexcept { return { T::name, T::flags, T::type }; }; -template +template inline constexpr auto MakeCommandsFromTraits(std::integer_sequence) noexcept { return std::array{{ CommandFromTrait(i)>>()... }}; } @@ -185,17 +185,17 @@ void CommandHelperBase::InternalDoBefore(bool top_level, bool test) * @param top_level Top level of command execution, i.e. command from a command. * @param test Test run of command? */ -void CommandHelperBase::InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test) +void CommandHelperBase::InternalDoAfter(CommandCost &res, DoCommandFlags flags, bool top_level, bool test) { if (test) { SetTownRatingTestMode(false); - if (res.Succeeded() && top_level && !(flags & DC_QUERY_COST) && !(flags & DC_BANKRUPT)) { + if (res.Succeeded() && top_level && !flags.Test(DoCommandFlag::QueryCost) && !flags.Test(DoCommandFlag::Bankrupt)) { CheckCompanyHasMoney(res); // CheckCompanyHasMoney() modifies 'res' to an error if it fails. } } else { /* If top-level, subtract the money. */ - if (res.Succeeded() && top_level && !(flags & DC_BANKRUPT)) { + if (res.Succeeded() && top_level && !flags.Test(DoCommandFlag::Bankrupt)) { SubtractMoneyFromCompany(res); } } @@ -217,14 +217,15 @@ std::tuple CommandHelperBase::InternalPostBefore(Commands cmd, * However, in case of incoming network commands, * map generation or the pause button we do want * to execute. */ - bool estimate_only = citymania::_estimate_mod && IsLocalCompany() && !_generating_world && !network_command && !(flags & CMD_NO_EST) && !citymania::_no_estimate_command; + bool estimate_only = citymania::_estimate_mod && IsLocalCompany() && !_generating_world && !network_command && !flags.Test(CommandFlag::NoEst) && !citymania::_no_estimate_command; /* We're only sending the command, so don't do * fancy things for 'success'. */ bool only_sending = _networking && !network_command; - if (_pause_mode != PM_UNPAUSED && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) { - ShowErrorMessage(err_message, STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); + if (_pause_mode.Any() && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) { + ShowErrorMessage(GetEncodedString(err_message), GetEncodedString(STR_ERROR_NOT_ALLOWED_WHILE_PAUSED), + WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); return { true, estimate_only, only_sending }; } else { return { false, estimate_only, only_sending }; @@ -240,7 +241,7 @@ std::tuple CommandHelperBase::InternalPostBefore(Commands cmd, * @param err_message Message prefix to show on error. * @param my_cmd Is the command from this client? */ -void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd) +void CommandHelperBase::InternalPostResult(CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd) { int x = TileX(tile) * TILE_SIZE; int y = TileY(tile) * TILE_SIZE; @@ -250,7 +251,7 @@ void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex til if (res.Failed()) { /* Only show the error when it's for us. */ if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) { - ShowErrorMessage(err_message, x, y, res); + ShowErrorMessage(GetEncodedString(err_message), x, y, res); } } else if (estimate_only) { ShowEstimatedCostOrIncome(res.GetCost(), x, y); @@ -273,7 +274,7 @@ void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex til /** Helper to make a desync log for a command. */ void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message, const CommandDataBuffer &args, bool failed) { - Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {} ({})", failed ? "cmdf" : "cmd", (uint32_t)TimerGameEconomy::date.base(), TimerGameEconomy::date_fract, (int)_current_company, cmd, err_message, FormatArrayAsHex(args), GetCommandName(cmd)); + Debug(desync, 1, "{}: {:08x}; {:02x}; {:02x}; {:08x}; {:08x}; {} ({})", failed ? "cmdf" : "cmd", (uint32_t)TimerGameEconomy::date.base(), TimerGameEconomy::date_fract, _current_company, cmd, err_message, FormatArrayAsHex(args), GetCommandName(cmd)); } /** @@ -285,12 +286,12 @@ void CommandHelperBase::LogCommandExecution(Commands cmd, StringID err_message, bool CommandHelperBase::InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex, Backup &cur_company) { /* Always execute server and spectator commands as spectator */ - bool exec_as_spectator = (cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0; + bool exec_as_spectator = cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server}); /* If the company isn't valid it may only do server command or start a new company! * The server will ditch any server commands a client sends to it, so effectively * this guards the server from executing functions for an invalid company. */ - if (_game_mode == GM_NORMAL && !exec_as_spectator && !Company::IsValidID(_current_company) && !(_current_company == OWNER_DEITY && (cmd_flags & CMD_DEITY) != 0)) { + if (_game_mode == GM_NORMAL && !exec_as_spectator && !Company::IsValidID(_current_company) && !(_current_company == OWNER_DEITY && cmd_flags.Test(CommandFlag::Deity))) { return false; } @@ -318,14 +319,14 @@ std::tuple CommandHelperBase::InternalExecuteValidateTestAndPr SetTownRatingTestMode(false); /* Make sure we're not messing things up here. */ - assert((cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0 ? _current_company == COMPANY_SPECTATOR : cur_company.Verify()); + assert(cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server}) ? _current_company == COMPANY_SPECTATOR : cur_company.Verify()); /* If the command fails, we're doing an estimate * or the player does not have enough money * (unless it's a command where the test and * execution phase might return different costs) * we bail out here. */ - bool test_and_exec_can_differ = (cmd_flags & CMD_NO_TEST) != 0; + bool test_and_exec_can_differ = cmd_flags.Test(CommandFlag::NoTest); if (res.Failed() || estimate_only || (!test_and_exec_can_differ && !CheckCompanyHasMoney(res))) { return { true, !_networking || _generating_world || network_command, false }; } @@ -364,7 +365,7 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman _current_company = _local_company; } else { /* Make sure nothing bad happened, like changing the current company. */ - assert((cmd_flags & (CMD_SPECTATOR | CMD_SERVER)) != 0 ? _current_company == COMPANY_SPECTATOR : cur_company.Verify()); + assert(cmd_flags.Any({CommandFlag::Spectator, CommandFlag::Server}) ? _current_company == COMPANY_SPECTATOR : cur_company.Verify()); cur_company.Restore(); } @@ -372,7 +373,7 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman * return of the command. Otherwise we can check whether the * test and execution have yielded the same result, * i.e. cost and error state are the same. */ - bool test_and_exec_can_differ = (cmd_flags & CMD_NO_TEST) != 0; + bool test_and_exec_can_differ = cmd_flags.Test(CommandFlag::NoTest); if (!test_and_exec_can_differ) { assert(res_test.GetCost() == res_exec.GetCost() && res_test.Failed() == res_exec.Failed()); // sanity check } else if (res_exec.Failed()) { @@ -385,8 +386,7 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman /* It could happen we removed rail, thus gained money, and deleted something else. * So make sure the signal buffer is empty even in this case */ UpdateSignalsInBuffer(); - SetDParam(0, extra_cash); - return CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + return CommandCostWithParam(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, extra_cash); } /* update last build coordinate of company. */ @@ -400,7 +400,7 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman SubtractMoneyFromCompany(res_exec); /* Record if there was a command issues during pause; ignore pause/other setting related changes. */ - if (_pause_mode != PM_UNPAUSED && _command_proc_table[cmd].type != CMDT_SERVER_SETTING) _pause_mode |= PM_COMMAND_DURING_PAUSE; + if (_pause_mode.Any() && _command_proc_table[cmd].type != CMDT_SERVER_SETTING) _pause_mode.Set(PauseMode::CommandDuringPause); /* update signals if needed */ UpdateSignalsInBuffer(); @@ -414,35 +414,27 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman * Also takes a possible error message when it is set. * @param ret The command to add the cost of. */ -void CommandCost::AddCost(const CommandCost &ret) +void CommandCost::AddCost(CommandCost &&ret) { this->AddCost(ret.cost); if (this->success && !ret.success) { this->message = ret.message; + this->encoded_message = std::move(ret.encoded_message); this->success = false; } } /** - * Values to put on the #TextRefStack for the error message. - * There is only one static instance of the array, just like there is only one - * instance of normal DParams. + * Return an error status, with string and parameter. + * @param str StringID of error. + * @param value Single parameter for error. + * @returns CommandCost representing the error. */ -uint32_t CommandCost::textref_stack[16]; - -/** - * Activate usage of the NewGRF #TextRefStack for the error message. - * @param grffile NewGRF that provides the #TextRefStack - * @param num_registers number of entries to copy from the temporary NewGRF registers - */ -void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers) +CommandCost CommandCostWithParam(StringID str, uint64_t value) { - extern TemporaryStorageArray _temp_store; - - assert(num_registers < lengthof(textref_stack)); - this->textref_stack_grffile = grffile; - this->textref_stack_size = num_registers; - for (uint i = 0; i < num_registers; i++) { - textref_stack[i] = _temp_store.GetValue(0x100 + i); + CommandCost error = CommandCost(str); + if (IsLocalCompany()) { + error.SetEncodedMessage(GetEncodedString(str, value)); } + return error; } diff --git a/src/command_func.h b/src/command_func.h index 6a7f544586..007b55beef 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -54,12 +54,12 @@ constexpr CommandFlags GetCommandFlags() * @param cmd_flags Flags from GetCommandFlags * @return flags for DoCommand */ -static constexpr inline DoCommandFlag CommandFlagsToDCFlags(CommandFlags cmd_flags) +static constexpr inline DoCommandFlags CommandFlagsToDCFlags(CommandFlags cmd_flags) { - DoCommandFlag flags = DC_NONE; - if (cmd_flags & CMD_NO_WATER) flags |= DC_NO_WATER; - if (cmd_flags & CMD_AUTO) flags |= DC_AUTO; - if (cmd_flags & CMD_ALL_TILES) flags |= DC_ALL_TILES; + DoCommandFlags flags = {}; + if (cmd_flags.Test(CommandFlag::NoWater)) flags.Set(DoCommandFlag::NoWater); + if (cmd_flags.Test(CommandFlag::Auto)) flags.Set(DoCommandFlag::Auto); + if (cmd_flags.Test(CommandFlag::AllTiles)) flags.Set(DoCommandFlag::AllTiles); return flags; } @@ -89,14 +89,14 @@ private: # define SILENCE_GCC_FUNCTION_POINTER_CAST #endif -template struct CommandHelper; +template struct CommandHelper; class CommandHelperBase { protected: static void InternalDoBefore(bool top_level, bool test); - static void InternalDoAfter(CommandCost &res, DoCommandFlag flags, bool top_level, bool test); + static void InternalDoAfter(CommandCost &res, DoCommandFlags flags, bool top_level, bool test); static std::tuple InternalPostBefore(Commands cmd, CommandFlags flags, TileIndex tile, StringID err_message, bool network_command); - static void InternalPostResult(const CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd); + static void InternalPostResult(CommandCost &res, TileIndex tile, bool estimate_only, bool only_sending, StringID err_message, bool my_cmd); static bool InternalExecutePrepTest(CommandFlags cmd_flags, TileIndex tile, Backup &cur_company); static std::tuple InternalExecuteValidateTestAndPrepExec(CommandCost &res, CommandFlags cmd_flags, bool estimate_only, bool network_command, Backup &cur_company); static CommandCost InternalExecuteProcessResult(Commands cmd, CommandFlags cmd_flags, const CommandCost &res_test, const CommandCost &res_exec, Money extra_cash, TileIndex tile, Backup &cur_company); @@ -111,7 +111,7 @@ protected: * @tparam Targs The command parameter types. */ template -struct CommandHelper : protected CommandHelperBase { +struct CommandHelper : protected CommandHelperBase { private: /** Extract the \c CommandCost from a command proc result. */ static inline CommandCost &ExtractCommandCost(Tret &ret) @@ -145,23 +145,23 @@ public: * @see CommandProc * @return the cost */ - static Tret Do(DoCommandFlag flags, Targs... args) + static Tret Do(DoCommandFlags flags, Targs... args) { if constexpr (std::is_same_v>>) { /* Do not even think about executing out-of-bounds tile-commands. */ TileIndex tile = std::get<0>(std::make_tuple(args...)); - if (tile != 0 && (tile >= Map::Size() || (!IsValidTile(tile) && (flags & DC_ALL_TILES) == 0))) return MakeResult(CMD_ERROR); + if (tile != 0 && (tile >= Map::Size() || (!IsValidTile(tile) && !flags.Test(DoCommandFlag::AllTiles)))) return MakeResult(CMD_ERROR); } RecursiveCommandCounter counter{}; /* Only execute the test call if it's toplevel, or we're not execing. */ - if (counter.IsTopLevel() || !(flags & DC_EXEC)) { + if (counter.IsTopLevel() || !flags.Test(DoCommandFlag::Execute)) { InternalDoBefore(counter.IsTopLevel(), true); - Tret res = CommandTraits::proc(flags & ~DC_EXEC, args...); + Tret res = CommandTraits::proc(DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), args...); InternalDoAfter(ExtractCommandCost(res), flags, counter.IsTopLevel(), true); // Can modify res. - if (ExtractCommandCost(res).Failed() || !(flags & DC_EXEC)) return res; + if (ExtractCommandCost(res).Failed() || !flags.Test(DoCommandFlag::Execute)) return res; } /* Execute the command here. All cost-relevant functions set the expenses type @@ -264,7 +264,7 @@ protected: } /** Set all invalid ClientID's to the proper value. */ - template + template static inline void SetClientIds(Ttuple &values, std::index_sequence) { ((SetClientIdHelper(std::get(values))), ...); @@ -293,13 +293,13 @@ protected: static bool InternalPost(StringID err_message, Tcallback *callback, bool my_cmd, bool network_command, TileIndex tile, std::tuple args) { /* Do not even think about executing out-of-bounds tile-commands. */ - if (tile != 0 && (tile >= Map::Size() || (!IsValidTile(tile) && (GetCommandFlags() & CMD_ALL_TILES) == 0))) return false; + if (tile != 0 && (tile >= Map::Size() || (!IsValidTile(tile) && !GetCommandFlags().Test(CommandFlag::AllTiles)))) return false; auto [err, estimate_only, only_sending] = InternalPostBefore(Tcmd, GetCommandFlags(), tile, err_message, network_command); if (err) return false; /* Only set client IDs when the command does not come from the network. */ - if (!network_command && GetCommandFlags() & CMD_CLIENT_ID) SetClientIds(args, std::index_sequence_for{}); + if (!network_command && GetCommandFlags().Test(CommandFlag::ClientID)) SetClientIds(args, std::index_sequence_for{}); Tret res = Execute(err_message, reinterpret_cast(reinterpret_cast(callback)), my_cmd, estimate_only, network_command, tile, args); InternalPostResult(ExtractCommandCost(res), tile, estimate_only, only_sending, err_message, my_cmd); @@ -345,13 +345,13 @@ protected: } /** Check if all ClientID arguments are set to valid values. */ - template + template static inline bool AllClientIdsSet(Ttuple &values, std::index_sequence) { return (ClientIdIsSet(std::get(values)) && ...); } - template + template static inline Money ExtractAdditionalMoney([[maybe_unused]] Ttuple &values) { if constexpr (std::is_same_v, Money>) { @@ -370,7 +370,7 @@ protected: /* Command flags are used internally */ constexpr CommandFlags cmd_flags = GetCommandFlags(); - if constexpr ((cmd_flags & CMD_CLIENT_ID) != 0) { + if constexpr (cmd_flags.Test(CommandFlag::ClientID)) { /* Make sure arguments are properly set to a ClientID also when processing external commands. */ assert(AllClientIdsSet(args, std::index_sequence_for{})); } @@ -382,7 +382,7 @@ protected: } /* Test the command. */ - DoCommandFlag flags = CommandFlagsToDCFlags(cmd_flags); + DoCommandFlags flags = CommandFlagsToDCFlags(cmd_flags); Tret res = std::apply(CommandTraits::proc, std::tuple_cat(std::make_tuple(flags), args)); auto [exit_test, desync_log, send_net] = InternalExecuteValidateTestAndPrepExec(ExtractCommandCost(res), cmd_flags, estimate_only, network_command, cur_company); @@ -409,7 +409,7 @@ protected: // Debug(misc, 0, "EXEC {}/{} {} {}({}) seed={} company={} tile={}", _date, _date_fract, _frame_counter, GetCommandName(Tcmd), Tcmd, _random.state[0] & 0xFF, (int)_current_company, tile); /* Actually try and execute the command. */ - Tret res2 = std::apply(CommandTraits::proc, std::tuple_cat(std::make_tuple(flags | DC_EXEC), args)); + Tret res2 = std::apply(CommandTraits::proc, std::tuple_cat(std::make_tuple(flags | DoCommandFlag::Execute), args)); /* Convention: If the second result element is of type Money, * this is the additional cash required for the command. */ @@ -435,7 +435,7 @@ protected: * @tparam Targs The command parameter types. */ template -struct CommandHelper : CommandHelper +struct CommandHelper : CommandHelper { /* Do not allow Post without explicit location. */ static inline bool Post(StringID err_message, Targs... args) = delete; @@ -478,7 +478,7 @@ struct CommandHelper : CommandHel template static inline bool Post(StringID err_message, Tcallback *callback, TileIndex location, Targs... args) { - return CommandHelper::InternalPost(err_message, callback, true, false, location, std::forward_as_tuple(args...)); + return CommandHelper::InternalPost(err_message, callback, true, false, location, std::forward_as_tuple(args...)); } }; @@ -487,6 +487,6 @@ struct CommandHelper : CommandHel #endif template -using Command = CommandHelper::ProcType, (GetCommandFlags() & CMD_LOCATION) == 0>; +using Command = CommandHelper::ProcType, !GetCommandFlags().Test(CommandFlag::Location)>; #endif /* COMMAND_FUNC_H */ diff --git a/src/command_type.h b/src/command_type.h index e1eba7793a..de960e3e54 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -10,6 +10,7 @@ #ifndef COMMAND_TYPE_H #define COMMAND_TYPE_H +#include "company_type.h" #include "economy_type.h" #include "strings_type.h" #include "tile_type.h" @@ -27,11 +28,9 @@ class CommandCost { StringID message; ///< Warning message for when success is unset ExpensesType expense_type; ///< the type of expence as shown on the finances view bool success; ///< Whether the command went fine up to this moment - const GRFFile *textref_stack_grffile = nullptr; ///< NewGRF providing the #TextRefStack content. - uint textref_stack_size = 0; ///< Number of uint32_t values to put on the #TextRefStack for the error message. + Owner owner = CompanyID::Invalid(); ///< Originator owner of error. StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset - - static uint32_t textref_stack[16]; + EncodedString encoded_message{}; ///< Encoded error message, used if the error message includes parameters. public: citymania::ext::CommandCost cm; @@ -59,6 +58,42 @@ public: */ CommandCost(ExpensesType ex_t, const Money &cst) : cost(cst), message(INVALID_STRING_ID), expense_type(ex_t), success(true) {} + /** + * Set the 'owner' (the originator) of this error message. This is used to show a company owner's face if you + * attempt an action on something owned by other company. + */ + inline void SetErrorOwner(Owner owner) + { + this->owner = owner; + } + + /** + * Set the encoded message string. If set, this is used by the error message window instead of the error StringID, + * to allow more information to be displayed to the local player. + * @note Do not set an encoded message if the error is not for the local player, as it will never be seen. + * @param message EncodedString message to set. + */ + void SetEncodedMessage(EncodedString &&message) + { + this->encoded_message = std::move(message); + } + + /** + * Get the last encoded error message. + * @returns Reference to the encoded message. + */ + EncodedString &GetEncodedMessage() + { + return this->encoded_message; + } + + /** + * Get the originator owner for this error. + */ + inline CompanyID GetErrorOwner() const + { + return this->owner; + } /** * Adds the given cost to the cost of the command. @@ -69,7 +104,7 @@ public: this->cost += cost; } - void AddCost(const CommandCost &cmd_cost); + void AddCost(CommandCost &&cmd_cost); /** * Multiplies the cost of the command by the given factor. @@ -102,41 +137,12 @@ public: * Makes this #CommandCost behave like an error command. * @param message The error message. */ - void MakeError(StringID message, StringID extra_message = INVALID_STRING_ID) + void MakeError(StringID message) { assert(message != INVALID_STRING_ID); this->success = false; this->message = message; - this->extra_message = extra_message; - } - - void UseTextRefStack(const GRFFile *grffile, uint num_registers); - - /** - * Returns the NewGRF providing the #TextRefStack of the error message. - * @return the NewGRF. - */ - const GRFFile *GetTextRefStackGRF() const - { - return this->textref_stack_grffile; - } - - /** - * Returns the number of uint32_t values for the #TextRefStack of the error message. - * @return number of uint32_t values. - */ - uint GetTextRefStackSize() const - { - return this->textref_stack_size; - } - - /** - * Returns a pointer to the values for the #TextRefStack of the error message. - * @return uint32_t values for the #TextRefStack - */ - const uint32_t *GetTextRefStack() const - { - return textref_stack; + this->extra_message = INVALID_STRING_ID; } /** @@ -178,6 +184,9 @@ public: } }; +CommandCost CommandCostWithParam(StringID str, uint64_t value); +CommandCost CommandCostWithParam(StringID str, ConvertibleThroughBase auto value) { return CommandCostWithParam(str, value.base()); } + /** * List of commands. * @@ -188,7 +197,7 @@ public: * * @see _command_proc_table */ -enum Commands : uint16_t { +enum Commands : uint8_t { CMD_BUILD_RAILROAD_TRACK, ///< build a rail track CMD_REMOVE_RAILROAD_TRACK, ///< remove a rail track CMD_BUILD_SINGLE_RAIL, ///< build a single rail track @@ -375,46 +384,44 @@ enum Commands : uint16_t { * * This enums defines some flags which can be used for the commands. */ -enum DoCommandFlag { - DC_NONE = 0x000, ///< no flag is set - DC_EXEC = 0x001, ///< execute the given command - DC_AUTO = 0x002, ///< don't allow building on structures - DC_QUERY_COST = 0x004, ///< query cost only, don't build. - DC_NO_WATER = 0x008, ///< don't allow building on water - // 0x010 is unused - DC_NO_TEST_TOWN_RATING = 0x020, ///< town rating does not disallow you from building - DC_BANKRUPT = 0x040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases - DC_AUTOREPLACE = 0x080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) - DC_NO_CARGO_CAP_CHECK = 0x100, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo - DC_ALL_TILES = 0x200, ///< allow this command also on MP_VOID tiles - DC_NO_MODIFY_TOWN_RATING = 0x400, ///< do not change town rating - DC_FORCE_CLEAR_TILE = 0x800, ///< do not only remove the object on the tile, but also clear any water left on it +enum DoCommandFlag : uint8_t { + Execute, ///< execute the given command + Auto, ///< don't allow building on structures + QueryCost, ///< query cost only, don't build. + NoWater, ///< don't allow building on water + NoTestTownRating, ///< town rating does not disallow you from building + Bankrupt, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases + AutoReplace, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) + NoCargoCapacityCheck, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo + AllTiles, ///< allow this command also on MP_VOID tiles + NoModifyTownRating, ///< do not change town rating + ForceClearTile, ///< do not only remove the object on the tile, but also clear any water left on it }; -DECLARE_ENUM_AS_BIT_SET(DoCommandFlag) +using DoCommandFlags = EnumBitSet; /** * Command flags for the command table _command_proc_table. * * This enumeration defines flags for the _command_proc_table. */ -enum CommandFlags { - CMD_SERVER = 0x001, ///< the command can only be initiated by the server - CMD_SPECTATOR = 0x002, ///< the command may be initiated by a spectator - CMD_OFFLINE = 0x004, ///< the command cannot be executed in a multiplayer game; single-player only - CMD_AUTO = 0x008, ///< set the DC_AUTO flag on this command - CMD_ALL_TILES = 0x010, ///< allow this command also on MP_VOID tiles - CMD_NO_TEST = 0x020, ///< the command's output may differ between test and execute due to town rating changes etc. - CMD_NO_WATER = 0x040, ///< set the DC_NO_WATER flag on this command - CMD_CLIENT_ID = 0x080, ///< set p2 with the ClientID of the sending client. - CMD_DEITY = 0x100, ///< the command may be executed by COMPANY_DEITY - CMD_STR_CTRL = 0x200, ///< the command's string may contain control strings - CMD_NO_EST = 0x400, ///< the command is never estimated. - CMD_LOCATION = 0x800, ///< the command has implicit location argument. +enum class CommandFlag : uint8_t { + Server, ///< the command can only be initiated by the server + Spectator, ///< the command may be initiated by a spectator + Offline, ///< the command cannot be executed in a multiplayer game; single-player only + Auto, ///< set the DoCommandFlag::Auto flag on this command + AllTiles, ///< allow this command also on MP_VOID tiles + NoTest, ///< the command's output may differ between test and execute due to town rating changes etc. + NoWater, ///< set the DoCommandFlag::NoWater flag on this command + ClientID, ///< set p2 with the ClientID of the sending client. + Deity, ///< the command may be executed by COMPANY_DEITY + StrCtrl, ///< the command's string may contain control strings + NoEst, ///< the command is never estimated. + Location, ///< the command has implicit location argument. }; -DECLARE_ENUM_AS_BIT_SET(CommandFlags) +using CommandFlags = EnumBitSet; /** Types of commands we have. */ -enum CommandType { +enum CommandType : uint8_t { CMDT_LANDSCAPE_CONSTRUCTION, ///< Construction and destruction of objects on the map. CMDT_VEHICLE_CONSTRUCTION, ///< Construction, modification (incl. refit) and destruction of vehicles. CMDT_MONEY_MANAGEMENT, ///< Management of money, i.e. loans. @@ -429,7 +436,7 @@ enum CommandType { }; /** Different command pause levels. */ -enum CommandPauseLevel { +enum CommandPauseLevel : uint8_t { CMDPL_NO_ACTIONS, ///< No user actions may be executed. CMDPL_NO_CONSTRUCTION, ///< No construction actions may be executed. CMDPL_NO_LANDSCAPING, ///< No landscaping actions may be executed. @@ -439,14 +446,14 @@ enum CommandPauseLevel { template struct CommandFunctionTraitHelper; template -struct CommandFunctionTraitHelper { +struct CommandFunctionTraitHelper { using Args = std::tuple...>; using RetTypes = void; using CbArgs = Args; using CbProcType = void(*)(Commands, const CommandCost &); }; template