diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 139b0654d3..7c0cc1b846 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,10 @@ on: - '*LICENSE' pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + env: SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" @@ -22,13 +26,13 @@ jobs: matrix: include: - name: GCC x86_64 - runner: ubuntu-latest + runner: ubuntu-24.04 preset: gcc artifact_arch: x86_64 - # - name: GCC aarch64 - # runner: ubuntu-24.04-arm - # preset: gcc - # artifact_arch: aarch64 + - name: GCC aarch64 + runner: ubuntu-24.04-arm + preset: gcc + artifact_arch: aarch64 # - name: Clang x86_64 # runner: ubuntu-latest # preset: clang @@ -221,12 +225,12 @@ jobs: msvc_arch: amd64 vcpkg_arch: x64 artifact_arch: x86_64 - # - name: MSVC arm64 - # runner: windows-11-arm - # preset: arm64-msvc - # msvc_arch: arm64 - # vcpkg_arch: arm64 - # artifact_arch: arm64 + - name: MSVC arm64 + runner: windows-latest + preset: arm64-msvc + msvc_arch: amd64_arm64 + vcpkg_arch: arm64 + artifact_arch: arm64 # - name: Clang x86_64 # runner: windows-latest # preset: clang @@ -255,7 +259,7 @@ jobs: - name: Install dependencies run: | choco install ninja - vcpkg install freetype:${{matrix.vcpkg_arch}}-windows-static zstd:${{matrix.vcpkg_arch}}-windows-static + vcpkg install freetype:${{matrix.vcpkg_arch}}-windows zstd:${{matrix.vcpkg_arch}}-windows - name: Configure CMake run: cmake --preset x-windows-ci-${{matrix.preset}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b9b08912b..a6df18fc96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ if (DUSK_VERSION_OVERRIDE) set(DUSK_WC_DESCRIBE "${DUSK_VERSION_OVERRIDE}") set(DUSK_VERSION_STRING "0.0.0.0") set(DUSK_SHORT_VERSION_STRING "0.0.0") + set(DUSK_VERSION_CODE "1") set(DUSK_WC_REVISION "") set(DUSK_WC_BRANCH "") set(DUSK_WC_DATE "") @@ -61,6 +62,9 @@ endif () if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)([-+].*)?$") set(DUSK_SHORT_VERSION_STRING "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}") + set(_ver_major ${CMAKE_MATCH_1}) + set(_ver_minor ${CMAKE_MATCH_2}) + set(_ver_patch ${CMAKE_MATCH_3}) set(DUSK_VERSION_TWEAK "0") if (DUSK_WC_DESCRIBE MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+)(-dirty)?$") set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}") @@ -68,10 +72,19 @@ if (DUSK_WC_DESCRIBE MATCHES "^v([0-9]+)\\.([0-9]+)\\.([0-9]+)([-+].*)?$") set(DUSK_VERSION_TWEAK "${CMAKE_MATCH_1}") endif () set(DUSK_VERSION_STRING "${DUSK_SHORT_VERSION_STRING}.${DUSK_VERSION_TWEAK}") + if(DUSK_VERSION_TWEAK GREATER 999) + set(_tweak 999) + else() + set(_tweak ${DUSK_VERSION_TWEAK}) + endif() + # encoding: major*1e7 + minor*1e5 + patch*1e3 + tweak; collision-free for major<210, minor<100, patch<100, tweak<=999 + math(EXPR DUSK_VERSION_CODE + "${_ver_major} * 10000000 + ${_ver_minor} * 100000 + ${_ver_patch} * 1000 + ${_tweak}") else () set(DUSK_WC_DESCRIBE "UNKNOWN-VERSION") set(DUSK_VERSION_STRING "0.0.0.0") set(DUSK_SHORT_VERSION_STRING "0.0.0") + set(DUSK_VERSION_CODE "1") endif () endif () @@ -79,6 +92,7 @@ endif () # Add version information to CI environment variables if(DEFINED ENV{GITHUB_ENV}) file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION=${DUSK_WC_DESCRIBE}\n") + file(APPEND "$ENV{GITHUB_ENV}" "DUSK_VERSION_CODE=${DUSK_VERSION_CODE}\n") endif() message(STATUS "Dusklight version set to ${DUSK_WC_DESCRIBE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") @@ -86,6 +100,8 @@ project(dusklight LANGUAGES C CXX VERSION ${DUSK_VERSION_STRING}) if (APPLE) enable_language(OBJC OBJCXX) endif () +# Adjust CMAKE_SYSTEM_PROCESSOR on Windows to match compiler target +include(cmake/WindowsTargetProcessor.cmake) if (APPLE AND NOT TVOS AND CMAKE_SYSTEM_NAME STREQUAL tvOS) # ios.toolchain.cmake hack for SDL set(TVOS ON) @@ -160,6 +176,7 @@ if (DUSK_MOVIE_SUPPORT) endif () set(_jpeg_cmake_args -DCMAKE_INSTALL_PREFIX=${_jpeg_install_dir} + -DCMAKE_PROJECT_INCLUDE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/WindowsTargetProcessor.cmake -DENABLE_SHARED=OFF -DWITH_TURBOJPEG=ON -DWITH_JAVA=OFF @@ -357,6 +374,10 @@ endif () if (WIN32) list(APPEND GAME_LIBS Ws2_32) + if (CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND GAME_LIBS dbghelp) + list(APPEND GAME_COMPILE_DEFS DUSK_CRASH_DBGHELP=1) + endif () endif () set(DUSK_HTTP_BACKEND_SOURCE src/dusk/http/no_backend.cpp) @@ -493,6 +514,10 @@ if (ANDROID) target_link_options(dusklight PRIVATE "-Wl,-u,SDL_main") endif () +if (CMAKE_SYSTEM_NAME STREQUAL Linux) + target_link_options(dusklight PRIVATE "-Wl,--build-id=sha1") +endif () + if (NOT APPLE) add_custom_command(TARGET dusklight POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory diff --git a/CMakePresets.json b/CMakePresets.json index 0201c61ed6..748c4df396 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,7 +33,11 @@ "value": true }, "DUSK_SENTRY_DSN": "$env{SENTRY_DSN}", - "DUSK_SENTRY_ENVIRONMENT": "production" + "DUSK_SENTRY_ENVIRONMENT": "production", + "Rust_RUSTUP_INSTALL_MISSING_TARGET": { + "type": "BOOL", + "value": true + } } }, { @@ -322,7 +326,9 @@ "BUILD_SHARED_LIBS": { "type": "BOOL", "value": false - } + }, + "AURORA_SDL3_VERSION": "3.4.8", + "AURORA_SDL3_REF": "refs/tags/release-3.4.8" } }, { @@ -430,7 +436,6 @@ "x-macos-ci" ], "cacheVariables": { - "AURORA_DAWN_PROVIDER": "vendor", "CMAKE_OSX_ARCHITECTURES": "x86_64", "Rust_CARGO_TARGET": "x86_64-apple-darwin" } @@ -490,7 +495,10 @@ "inherits": [ "x-windows-ci", "windows-arm64-msvc" - ] + ], + "cacheVariables": { + "VCPKG_TARGET_TRIPLET": "arm64-windows" + } } ], "buildPresets": [ diff --git a/README.md b/README.md index 106d2c0a0e..5a9701bede 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,11 @@ It aims to be as accurate as possible to the original while also providing new o > [!IMPORTANT] > At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them. -### 1. Verify your dump +### 1. Dump your game -First, make sure your dump of the game is clean and supported by Dusklight. You can do this by checking the SHA-1 hash of your dump against this list of supported versions: +You must dump your own copy of the game, please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to a `.rvz` to save space. -| Version | SHA-1 hash | -|--------------| ------------------------------------------ | -| GameCube USA | `75edd3ddff41f125d1b4ce1a40378f1b565519e7` | -| GameCube EUR | `2601822a488eeb86fb89db16ca8f29c2c953e1ca` | - -*Support for other versions of the game is planned in the future. +Currently, only the GameCube USA and EUR releases are supported. Support for other versions of the game is planned in the future. ### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases) diff --git a/ci/build-appimage.sh b/ci/build-appimage.sh index 6653876f71..e12f1163b9 100755 --- a/ci/build-appimage.sh +++ b/ci/build-appimage.sh @@ -6,6 +6,7 @@ fi build_dir="$PWD/build" linuxdeploy="$build_dir/linuxdeploy-$(uname -m).AppImage" +lib_dir="/usr/lib/$(uname -m)-linux-gnu" # Get linuxdeploy mkdir -p "$build_dir" @@ -19,8 +20,8 @@ for install_path in build/install/*; do cp -r "$install_path" build/appdir/usr/bin done cp -r platforms/freedesktop/{16x16,32x32,48x48,64x64,128x128,256x256,512x512,1024x1024} build/appdir/usr/share/icons/hicolor -cp platforms/freedesktop/dusklight.desktop build/appdir/usr/share/applications +cp platforms/freedesktop/dev.twilitrealm.dusk.desktop build/appdir/usr/share/applications cd build/install VERSION="$DUSK_VERSION" NO_STRIP=1 "$linuxdeploy" \ - -l /usr/lib/x86_64-linux-gnu/libusb-1.0.so --appdir "$build_dir/appdir" --output appimage + -l "$lib_dir/libusb-1.0.so" --appdir "$build_dir/appdir" --output appimage diff --git a/cmake/WindowsTargetProcessor.cmake b/cmake/WindowsTargetProcessor.cmake new file mode 100644 index 0000000000..0fba82add5 --- /dev/null +++ b/cmake/WindowsTargetProcessor.cmake @@ -0,0 +1,9 @@ +if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + if (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64" OR CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64EC") + set(CMAKE_SYSTEM_PROCESSOR "ARM64") + elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "x64") + set(CMAKE_SYSTEM_PROCESSOR "AMD64") + elseif (CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "X86") + set(CMAKE_SYSTEM_PROCESSOR "X86") + endif () +endif () diff --git a/extern/aurora b/extern/aurora index 5c5873e53d..f00825d9f5 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 5c5873e53ddec4cbfe5b992b472358fb5dab2631 +Subproject commit f00825d9f50abc0fcc355cc06a10012ad3f8b2ed diff --git a/files.cmake b/files.cmake index f87136348e..d586c22551 100644 --- a/files.cmake +++ b/files.cmake @@ -1432,6 +1432,7 @@ set(DUSK_FILES src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp src/dusk/config.cpp + src/dusk/crash_handler.cpp src/dusk/crash_reporting.cpp src/dusk/data.cpp src/dusk/data.hpp @@ -1451,6 +1452,7 @@ set(DUSK_FILES src/dusk/logging.cpp src/dusk/settings.cpp src/dusk/speedrun.cpp + src/dusk/string.cpp src/dusk/stubs.cpp src/dusk/update_check.cpp src/dusk/update_check.hpp diff --git a/flake.lock b/flake.lock index 8ec14d2852..08dc0f458c 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1775710090, - "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", + "lastModified": 1778869304, + "narHash": "sha256-30sZNZoA1cqF5JNO9fVX+wgiQYjB7HJqqJ4ztCDeBZE=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4c1018dae018162ec878d42fec712642d214fdfa", + "rev": "d233902339c02a9c334e7e593de68855ad26c4cb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 29c99b10a5..0a2156a4d5 100644 --- a/flake.nix +++ b/flake.nix @@ -1,219 +1,374 @@ { - inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - outputs = { self, nixpkgs }: + description = "Dusklight — native PC port of the Twilight Princess decompilation"; + + inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + + outputs = + { self, nixpkgs }: let + inherit (nixpkgs) lib; + supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; - forAllSystems = nixpkgs.lib.genAttrs supportedSystems; - pkgsFor = system: import nixpkgs { inherit system; }; + forAllSystems = lib.genAttrs supportedSystems; - # Dependencies that are not packaged in nixpkgs (used by the Linux package build): - buildSources = pkgs: { - dawn-src = pkgs.fetchzip { - url = "https://github.com/encounter/dawn-build/releases/download/v20260423.175430/dawn-linux-x86_64.tar.gz"; + dawnVersion = "v20260423.175430"; + nodVersion = "v2.0.0-alpha.8"; + versionSuffix = "nix-" + (self.shortRev or self.dirtyShortRev or "dirty"); + + dawnInfo = { + "x86_64-linux" = { + triple = "linux-x86_64"; hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs="; - stripRoot = false; }; - nod-src = pkgs.fetchzip { - url = "https://github.com/encounter/nod/releases/download/v2.0.0-alpha.8/libnod-linux-x86_64.tar.gz"; - hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; - stripRoot = false; + "aarch64-linux" = { + triple = "linux-aarch64"; + hash = "sha256-34yyFpfqBZUwoFXQ41F0AwAU78FaNihOSY0oriwn6B0="; }; - # The version of imgui on nixpkgs does not map cleanly. - imgui-src = pkgs.fetchFromGitHub { - owner = "ocornut"; - repo = "imgui"; - rev = "v1.91.9b-docking"; - hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ="; + "aarch64-darwin" = { + triple = "darwin-arm64"; + hash = "sha256-eQnzrBp6gjiBek1VYQ9A5W13ClYWrDDKjIqv/7eNTR4="; }; - sqlite-src = pkgs.fetchzip { - url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip"; - hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM="; - }; - rmlui-src = pkgs.fetchzip { - url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz"; - hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg="; + "x86_64-darwin" = { + triple = "darwin-x86_64"; + hash = "sha256-QGWiGdxiI9kci3NPXH6QFFirxn16851zB/w3jqhIBJ4="; }; }; - # Dusklight Actual (Linux x86_64 only — relies on prebuilt dawn/nod binaries) - mkDusklight = pkgs: - let srcs = buildSources pkgs; - versionSuffix = if self ? shortRev && self.shortRev != null - then "nix-${self.shortRev}" - else "nix-dirty"; - in - pkgs.stdenv.mkDerivation { - name = "dusklight"; - src = ./.; - postUnpack = '' - sed -i '/add_subdirectory(tests)/d' $sourceRoot/extern/aurora/CMakeLists.txt - ''; - # Remove last line to re-enable tests - cmakeFlags = [ - "-DDUSK_VERSION_OVERRIDE=${versionSuffix}" - "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" - "-DFETCHCONTENT_SOURCE_DIR_CXXOPTS=${pkgs.cxxopts.src}" - "-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}" - "-DFETCHCONTENT_SOURCE_DIR_DAWN_PREBUILT=${srcs.dawn-src}" - "-DFETCHCONTENT_SOURCE_DIR_XXHASH=${pkgs.xxHash.src}" - "-DFETCHCONTENT_SOURCE_DIR_FMT=${pkgs.fmt.src}" - "-DFETCHCONTENT_SOURCE_DIR_TRACY=${pkgs.tracy.src}" - "-DAURORA_SDL3_PROVIDER=system" - "-DFETCHCONTENT_SOURCE_DIR_NOD_PREBUILT=${srcs.nod-src}" - "-DAURORA_NOD_PROVIDER=package" - "-DFETCHCONTENT_SOURCE_DIR_FREETYPE=${pkgs.freetype.src}" - "-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}" - "-DFETCHCONTENT_SOURCE_DIR_SQLITE3=${srcs.sqlite-src}" - "-DFETCHCONTENT_SOURCE_DIR_IMGUI=${srcs.imgui-src}" - "-DFETCHCONTENT_SOURCE_DIR_RMLUI=${srcs.rmlui-src}" - "-DCMAKE_CROSSCOMPILING=ON" # Tests are not working as I didn't want to work through getting google's test suite working as well. This is the only guard I could find to disable it. - ]; - installPhase = '' - mkdir -p $out/bin - cp dusklight $out/bin/dusklight - cp -r ./res $out/bin/res + nodPrebuiltInfo = { + "x86_64-linux" = { + triple = "linux-x86_64"; + hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; + }; + "aarch64-darwin" = { + triple = "macos-arm64"; + hash = "sha256-UPy1ywCcv0K6VJOU3uUelJuUdBh3UNaPRlyP5LOBeDw="; + }; + }; - mkdir -p $out/share/applications - cp $src/platforms/freedesktop/dusklight.desktop $out/share/applications/dusklight.desktop + perSystem = + system: + let + pkgs = import nixpkgs { inherit system; }; + inherit (pkgs.stdenv.hostPlatform) isDarwin; + hasNodPrebuilt = nodPrebuiltInfo ? ${system}; - for size in 16 32 48 64 128 256 512 1024; do - install -Dm644 $src/platforms/freedesktop/''${size}x''${size}/apps/dusklight.png \ - $out/share/icons/hicolor/''${size}x''${size}/apps/dusklight.png - done - ''; - nativeBuildInputs = [ + aurora = pkgs.fetchFromGitHub { + owner = "encounter"; + repo = "aurora"; + rev = "10006618ee493f248b8597e4dfa1d2871d76a1d9"; + hash = "sha256-lY2xuVyB7aPJ9+2wwLRB3F5U/BuPSxdSpegdG+qNd9o="; + }; + + dawn = pkgs.fetchzip { + url = "https://github.com/encounter/dawn-build/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz"; + hash = dawnInfo.${system}.hash; + stripRoot = false; + }; + + corrosion = pkgs.fetchFromGitHub { + owner = "corrosion-rs"; + repo = "corrosion"; + rev = "v0.6.1"; + hash = "sha256-ppuDNObfKhneD9AlnPAvyCRHKW3BidXKglD1j/LE9CM="; + }; + + nodFromSource = pkgs.stdenv.mkDerivation (finalAttrs: { + pname = "nod"; + version = nodVersion; + src = pkgs.fetchFromGitHub { + owner = "encounter"; + repo = "nod"; + rev = nodVersion; + hash = "sha256-+zrtVzjo0+X/6uMcNUn1+FaSR+jOhrcQSDNBFjw0NDs="; + }; + cargoDeps = pkgs.rustPlatform.importCargoLock { + lockFile = "${finalAttrs.src}/Cargo.lock"; + }; + postPatch = '' + substituteInPlace CMakeLists.txt \ + --replace-warn "add_subdirectory(nod-ffi/examples)" "" + ''; + nativeBuildInputs = [ + pkgs.cmake + pkgs.ninja + pkgs.rustPlatform.cargoSetupHook + pkgs.cargo + pkgs.rustc + ]; + CARGO_NET_OFFLINE = "true"; + cmakeFlags = [ + "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" + "-DFETCHCONTENT_SOURCE_DIR_CORROSION=${corrosion}" + "-DNOD_ENABLE_INSTALL=ON" + "-DBUILD_SHARED_LIBS=OFF" + ]; + doCheck = false; + }); + + nod = + if hasNodPrebuilt then + pkgs.fetchzip { + url = "https://github.com/encounter/nod/releases/download/${nodVersion}/libnod-${ + nodPrebuiltInfo.${system}.triple + }.tar.gz"; + hash = nodPrebuiltInfo.${system}.hash; + stripRoot = false; + } + else + nodFromSource; + + fetchContentDirs = { + DAWN_PREBUILT = dawn; + NOD_PREBUILT = nod; + CXXOPTS = pkgs.cxxopts.src; + JSON = pkgs.nlohmann_json.src; + XXHASH = pkgs.xxHash.src; + ZSTD = pkgs.zstd.src; + FMT = pkgs.fetchzip { + url = "https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz"; + hash = "sha256-sUbxlYi/Aupaox3JjWFqXIjcaQa0LFjclQAOleT+FRA="; + }; + TRACY = pkgs.fetchzip { + url = "https://github.com/wolfpld/tracy/archive/a64b9a20294d59421a2f57aeca3c6383d8c48169.tar.gz"; + hash = "sha256-hbNGOsGeyGSvCJ2No8RkwOib1lX2on3vNZSzyVkZdXw="; + }; + IMGUI = pkgs.fetchFromGitHub { + owner = "ocornut"; + repo = "imgui"; + rev = "v1.91.9b-docking"; + hash = "sha256-mQOJ6jCN+7VopgZ61yzaCnt4R1QLrW7+47xxMhFRHLQ="; + }; + SQLITE3 = pkgs.fetchzip { + url = "https://sqlite.org/2026/sqlite-amalgamation-3510300.zip"; + hash = "sha256-pNMR8zxaaqfAzQ0AQBOXMct4usdjey1Q0Gnitg06UhM="; + }; + RMLUI = pkgs.fetchzip { + url = "https://github.com/mikke89/RmlUi/archive/f9b8c9e2935d5df2c7dff2c190d3968e99b0c3dc.tar.gz"; + hash = "sha256-g4O/JZUrrcseOz8o2QJRt+2CeuiLnVeuDJc906xvuIg="; + }; + }; + + dusklight = pkgs.stdenv.mkDerivation { + pname = "dusklight"; + version = versionSuffix; + src = ./.; + + postUnpack = '' + chmod -R u+w "$sourceRoot" + rm -rf "$sourceRoot/extern/aurora" + mkdir -p "$sourceRoot/extern" + cp -r ${aurora} "$sourceRoot/extern/aurora" + chmod -R u+w "$sourceRoot/extern/aurora" + substituteInPlace "$sourceRoot/extern/aurora/CMakeLists.txt" \ + --replace-warn "add_subdirectory(tests)" "" + ''; + + nativeBuildInputs = [ + pkgs.cmake + pkgs.ninja + pkgs.pkg-config + pkgs.python3 + pkgs.python3Packages.markupsafe + ] + ++ lib.optionals (!isDarwin) [ pkgs.autoPatchelfHook ]; + + buildInputs = [ + pkgs.sdl3 + pkgs.freetype + pkgs.zstd + pkgs.cxxopts + pkgs.nlohmann_json + pkgs.xxHash + pkgs.abseil-cpp + pkgs.zlib + pkgs.libpng + pkgs.libjpeg_turbo + pkgs.curl + pkgs.openssl + ] + ++ lib.optionals isDarwin [ + pkgs.apple-sdk_15 + pkgs.libiconv + ] + ++ lib.optionals (!isDarwin) [ + pkgs.libGL + pkgs.libGLU + pkgs.libglvnd + pkgs.vulkan-loader + pkgs.libX11 + pkgs.libxcb + pkgs.libXcursor + pkgs.libxi + pkgs.libxrandr + pkgs.libxscrnsaver + pkgs.libxtst + pkgs.libxinerama + pkgs.libxkbcommon + pkgs.wayland + pkgs.libdecor + pkgs.alsa-lib + pkgs.libpulseaudio + pkgs.pipewire + pkgs.dbus + pkgs.udev + pkgs.libusb1 + pkgs.libunwind + pkgs.gtk3 + ]; + + cmakeBuildType = "RelWithDebInfo"; + ninjaFlags = [ "dusklight" ]; + + cmakeFlags = [ + "-DDUSK_VERSION_OVERRIDE=${versionSuffix}" + "-DFETCHCONTENT_FULLY_DISCONNECTED=ON" + "-DAURORA_DAWN_PROVIDER=package" + "-DAURORA_DAWN_LINKAGE=static" + "-DAURORA_NOD_PROVIDER=package" + "-DAURORA_NOD_LINKAGE=static" + "-DAURORA_SDL3_PROVIDER=system" + ] + ++ lib.mapAttrsToList (key: src: "-DFETCHCONTENT_SOURCE_DIR_${key}=${src}") fetchContentDirs; + + installPhase = + if isDarwin then + '' + runHook preInstall + mkdir -p "$out/Applications" + cp -r Dusklight.app "$out/Applications/Dusklight.app" + runHook postInstall + '' + else + '' + runHook preInstall + install -Dm755 dusklight "$out/bin/dusklight" + cp -r "$src/res" "$out/bin/res" + install -Dm644 "$src/platforms/freedesktop/dev.twilitrealm.dusk.desktop" \ + "$out/share/applications/dev.twilitrealm.dusk.desktop" + for size in 16 32 48 64 128 256 512 1024; do + install -Dm644 "$src/platforms/freedesktop/''${size}x''${size}/apps/dev.twilitrealm.dusk.png" \ + "$out/share/icons/hicolor/''${size}x''${size}/apps/dev.twilitrealm.dusk.png" + done + runHook postInstall + ''; + + dontStrip = true; + + meta = { + description = "Dusklight — native PC port of the Twilight Princess decompilation"; + homepage = "https://github.com/zeldaret/tp"; + platforms = supportedSystems; + mainProgram = "dusklight"; + }; + }; + + # Tooling common to every supported host (Linux and macOS). + commonDevTools = [ pkgs.cmake + pkgs.ninja pkgs.pkg-config - pkgs.wayland + pkgs.git + pkgs.python3 + pkgs.python3Packages.markupsafe + pkgs.rustc + pkgs.cargo + pkgs.sccache ]; - buildInputs = [ - pkgs.libGL - pkgs.libX11 - pkgs.libXcursor - pkgs.libxi - pkgs.libxcb - pkgs.libxrandr - pkgs.libxscrnsaver - pkgs.libxtst - pkgs.libjpeg8 - pkgs.libxkbcommon - pkgs.libglvnd + + # Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml + # so the cmake presets resolve the same set of headers as CI. + linuxDevDeps = [ + # Compilers / linkers + pkgs.clang + pkgs.lld + # C/C++ utilities + pkgs.curl + pkgs.openssl + pkgs.zlib + pkgs.libpng + pkgs.libjpeg_turbo + pkgs.freetype + pkgs.zstd + pkgs.fmt + pkgs.tracy pkgs.cxxopts pkgs.abseil-cpp pkgs.sdl3 - pkgs.fmt - pkgs.tracy - pkgs.freetype - pkgs.zstd + pkgs.ncurses + pkgs.libunwind + pkgs.libusb1 + pkgs.fuse + # Wayland / display server + pkgs.wayland + pkgs.wayland-protocols + pkgs.libxkbcommon + pkgs.libdecor + # OpenGL / Vulkan + pkgs.libGL + pkgs.libGLU + pkgs.libglvnd + pkgs.vulkan-headers + pkgs.vulkan-loader + # X11 + pkgs.libX11 + pkgs.libxcb + pkgs.libXcursor + pkgs.libxi + pkgs.libxrandr + pkgs.libxscrnsaver + pkgs.libxtst + pkgs.libxinerama + # Audio + pkgs.alsa-lib + pkgs.libpulseaudio + pkgs.pipewire + # System integration + pkgs.dbus + pkgs.udev + pkgs.gtk3 ]; + + # On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up + # Apple Clang and the Xcode SDK directly, matching the macOS CI workflow. + darwinShell = pkgs.mkShellNoCC { + packages = commonDevTools; + shellHook = '' + echo "Dusklight dev shell (macOS)" + echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK." + echo "Configure: cmake --preset macos-default-relwithdebinfo" + echo "Build: cmake --build --preset macos-default-relwithdebinfo" + ''; + }; + + linuxShell = pkgs.mkShell { + packages = commonDevTools ++ linuxDevDeps; + shellHook = '' + echo "Dusklight dev shell (Linux)" + echo "Configure: cmake --preset linux-default-relwithdebinfo" + echo " cmake --preset linux-clang-relwithdebinfo" + echo "Build: cmake --build --preset " + ''; + }; + in + { + packages = { + default = dusklight; + dusklight = dusklight; + } + // lib.optionalAttrs (!hasNodPrebuilt) { nod = nodFromSource; }; + + devShells.default = if isDarwin then darwinShell else linuxShell; }; - # Tooling common to every supported host (Linux and macOS). - commonDevTools = pkgs: [ - pkgs.cmake - pkgs.ninja - pkgs.pkg-config - pkgs.git - pkgs.python3 - pkgs.python3Packages.markupsafe - pkgs.rustc - pkgs.cargo - pkgs.sccache - ]; - - # Linux-only system libraries — mirrors the apt deps from .github/workflows/build.yml - # so the cmake presets resolve the same set of headers as CI. - linuxDevDeps = pkgs: [ - # Compilers / linkers - pkgs.clang - pkgs.lld - # C/C++ utilities - pkgs.curl - pkgs.openssl - pkgs.zlib - pkgs.libpng - pkgs.libjpeg_turbo - pkgs.freetype - pkgs.zstd - pkgs.fmt - pkgs.tracy - pkgs.cxxopts - pkgs.abseil-cpp - pkgs.sdl3 - pkgs.ncurses - pkgs.libunwind - pkgs.libusb1 - pkgs.fuse - # Wayland / display server - pkgs.wayland - pkgs.wayland-protocols - pkgs.libxkbcommon - pkgs.libdecor - # OpenGL / Vulkan - pkgs.libGL - pkgs.libGLU - pkgs.libglvnd - pkgs.vulkan-headers - pkgs.vulkan-loader - # X11 - pkgs.libX11 - pkgs.libxcb - pkgs.libXcursor - pkgs.libxi - pkgs.libxrandr - pkgs.libxscrnsaver - pkgs.libxtst - pkgs.libxinerama - # Audio - pkgs.alsa-lib - pkgs.libpulseaudio - pkgs.pipewire - # System integration - pkgs.dbus - pkgs.udev - pkgs.gtk3 - ]; - - # On macOS we deliberately avoid pulling Nix's cc-wrapper so CMake picks up - # Apple Clang and the Xcode SDK directly, matching the macOS CI workflow. - mkDarwinShell = pkgs: - pkgs.mkShellNoCC { - packages = commonDevTools pkgs; - shellHook = '' - echo "Dusklight dev shell (macOS)" - echo "Requires Xcode Command Line Tools for Apple Clang and the macOS SDK." - echo "Configure: cmake --preset macos-default-relwithdebinfo" - echo "Build: cmake --build --preset macos-default-relwithdebinfo" - ''; - }; - - mkLinuxShell = pkgs: - pkgs.mkShell { - packages = (commonDevTools pkgs) ++ (linuxDevDeps pkgs); - shellHook = '' - echo "Dusklight dev shell (Linux)" - echo "Configure: cmake --preset linux-default-relwithdebinfo" - echo " cmake --preset linux-clang-relwithdebinfo" - echo "Build: cmake --build --preset " - ''; - }; - - mkDevShell = pkgs: - if pkgs.stdenv.isDarwin - then mkDarwinShell pkgs - else mkLinuxShell pkgs; - in { - packages.x86_64-linux.default = mkDusklight (pkgsFor "x86_64-linux"); - - devShells = forAllSystems (system: { - default = mkDevShell (pkgsFor system); - }); + systems = forAllSystems perSystem; + in + { + packages = lib.mapAttrs (_: s: s.packages) systems; + devShells = lib.mapAttrs (_: s: s.devShells) systems; }; } diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index 107afc1ed3..8f6a40ae13 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4564,6 +4564,7 @@ public: cXyz mIBChainInterpCurrHandRoot; bool mIBChainInterpPrevValid; bool mIBChainInterpCurrValid; + bool mIsRollstab = false; #endif }; // Size: 0x385C diff --git a/include/d/actor/d_a_movie_player.h b/include/d/actor/d_a_movie_player.h index 0e666ae470..c382e0d1f7 100644 --- a/include/d/actor/d_a_movie_player.h +++ b/include/d/actor/d_a_movie_player.h @@ -5,6 +5,7 @@ #include #else #include +#include #endif #include "f_op/f_op_actor.h" #include "d/d_drawlist.h" @@ -125,6 +126,7 @@ struct daMP_THPPlayer { /* 0x0D4 */ s32 curCount; #if TARGET_PC /* 0x0D8 */ std::atomic videoDecodeCount; + std::chrono::steady_clock::time_point thpPlaybackClock; #else /* 0x0D8 */ s32 videoDecodeCount; #endif diff --git a/include/d/actor/d_a_player.h b/include/d/actor/d_a_player.h index 41a11ff662..698e3b03cd 100644 --- a/include/d/actor/d_a_player.h +++ b/include/d/actor/d_a_player.h @@ -77,7 +77,7 @@ private: #define PLAYER_CREATE_ANM_HEAP_F(heap, type, fmt, ...) \ { \ char pcah_name_buf[32]; \ - sprintf(pcah_name_buf, fmt, ##__VA_ARGS__); \ + snprintf(pcah_name_buf, sizeof(pcah_name_buf), fmt, ##__VA_ARGS__); \ (heap).createHeap(type, pcah_name_buf); \ \ } diff --git a/include/d/d_camera.h b/include/d/d_camera.h index c481262a46..b694933461 100644 --- a/include/d/d_camera.h +++ b/include/d/d_camera.h @@ -444,8 +444,7 @@ public: /* 0x1C */ f32 mFovy; /* 0x20 */ f32 mBank; /* 0x24 */ fopAc_ac_c* mRelActor; - /* 0x28 */ char mRelUseMask; - /* 0x29 */ char field_0x29; + /* 0x28 */ char mRelUseMask[4]; /* 0x2C */ int mTimer; /* 0x30 */ bool field_0x30; /* 0x34 */ cXyz mBasePos; @@ -526,7 +525,7 @@ public: /* 0x3C */ fopAc_ac_c* field_0x3c; /* 0x40 */ fopAc_ac_c* field_0x40; /* 0x44 */ fpc_ProcID field_0x44; - /* 0x48 */ char field_0x48; + /* 0x48 */ char field_0x48[4]; /* 0x4C */ int field_0x4c; }; @@ -541,11 +540,7 @@ public: /* 0x3C */ f32 field_0x3c; /* 0x40 */ fopAc_ac_c* mRelActor; /* 0x44 */ fpc_ProcID mRelActorID; - /* 0x48 */ char mRelUseMask; - /* 0x49 */ char field_0x49; - /* 0x4A */ char field_0x4a; - /* 0x4B */ char field_0x4b; - /* 0x4C */ u8 field_0x4c[4]; + /* 0x48 */ char mRelUseMask[8]; /* 0x50 */ int mTimer; /* 0x54 */ int mTransType; /* 0x58 */ f32 mCushion; @@ -696,8 +691,7 @@ public: /* 0x24 */ f32* field_0x24; /* 0x28 */ f32 field_0x28; /* 0x2C */ fopAc_ac_c* mRelActor; - /* 0x30 */ char mRelUseMask; - /* 0x31 */ char field_0x31; + /* 0x30 */ char mRelUseMask[4]; /* 0x34 */ int mTimer; /* 0x38 */ int field_0x38; /* 0x3C */ int mChoice; @@ -780,8 +774,7 @@ public: /* 0xAC */ f32 field_0xac; /* 0xB0 */ fopAc_ac_c* mRelActor; /* 0xB4 */ fpc_ProcID mRelActorID; - /* 0xB8 */ char mRelUseMask; - /* 0xB9 */ char field_0xb9; + /* 0xB8 */ char mRelUseMask[4]; /* 0xBC */ f32 mCushion; /* 0xC0 */ u32 field_0xc0[6]; }; @@ -917,7 +910,12 @@ public: char* getEvStringPntData(char*, char*); char* getEvStringPntData(char*); bool getEvXyzData(cXyz*, char*, cXyz); +#if TARGET_PC + template + bool getEvStringData(char (&)[N], char*, char*); +#else bool getEvStringData(char*, char*, char*); +#endif fopAc_ac_c* getEvActor(char*); fopAc_ac_c* getEvActor(char*, char*); bool pauseEvCamera(); diff --git a/include/d/d_com_inf_game.h b/include/d/d_com_inf_game.h index d81fc5bc1e..2e644b2d3e 100644 --- a/include/d/d_com_inf_game.h +++ b/include/d/d_com_inf_game.h @@ -1851,7 +1851,7 @@ inline u16 dComIfGs_getDeathCount() { } #endif -inline char* dComIfGs_getPlayerName() { +inline TEXT_SPAN dComIfGs_getPlayerName() { return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getPlayerName(); } @@ -1859,7 +1859,7 @@ inline void dComIfGs_setPlayerName(const char* i_name) { g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().setPlayerName(i_name); } -inline char* dComIfGs_getHorseName() { +inline TEXT_SPAN dComIfGs_getHorseName() { return g_dComIfG_gameInfo.info.getPlayer().getPlayerInfo().getHorseName(); } diff --git a/include/d/d_file_sel_info.h b/include/d/d_file_sel_info.h index 73641bc770..973349c469 100644 --- a/include/d/d_file_sel_info.h +++ b/include/d/d_file_sel_info.h @@ -46,10 +46,10 @@ private: /* 0x22 */ u8 field_0x22; /* 0x24 */ CPaneMgrAlpha* mDatBase; /* 0x28 */ CPaneMgrAlpha* mNoDatBase; - /* 0x2C */ char* mPlayerName; - /* 0x30 */ char* mSaveDate; - /* 0x34 */ char* mPlayTime; - /* 0x38 */ char* mSaveStatus; + /* 0x2C */ TEXT_SPAN mPlayerName; + /* 0x30 */ TEXT_SPAN mSaveDate; + /* 0x34 */ TEXT_SPAN mPlayTime; + /* 0x38 */ TEXT_SPAN mSaveStatus; }; typedef void (dFile_info_c::*warningFunc)(void); diff --git a/include/d/d_file_select.h b/include/d/d_file_select.h index cf081a3b10..d219a5a11e 100644 --- a/include/d/d_file_select.h +++ b/include/d/d_file_select.h @@ -11,7 +11,42 @@ class dFile_info_c; class J2DPicture; +#if TARGET_PC +static bool cachedPanes = false; +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mSelDtPanes[] = { + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('gray_n'), 0.0f, false}, + {MULTI_CHAR('b_base'), 0.0f, false}, + {MULTI_CHAR('b_base1'), 0.0f, false}, +}; + +static PaneCache fileSelPanes[] = { + {MULTI_CHAR('w_uzu00'), 0.0f, false}, + {MULTI_CHAR('w_uzu01'), 0.0f, false}, + {MULTI_CHAR('w_uzu02'), 0.0f, false}, + {MULTI_CHAR('w_uzu03'), 0.0f, false}, + {MULTI_CHAR('w_uzu04'), 0.0f, false}, + {MULTI_CHAR('w_uzu05'), 0.0f, false}, + {MULTI_CHAR('w_uzu06'), 0.0f, false}, + {MULTI_CHAR('w_uzu07'), 0.0f, false}, + {MULTI_CHAR('w_uzu08'), 0.0f, false}, + {MULTI_CHAR('w_uzu09'), 0.0f, false}, +}; +#endif class dDlst_FileSel_c : public dDlst_base_c { public: void draw(); @@ -530,7 +565,7 @@ public: /* 0x0130 */ int field_0x0130; /* 0x0134 */ int field_0x0134; /* 0x0138 */ CPaneMgrAlpha* mErrorMsgTxtPane[2]; - /* 0x0140 */ char* mErrorMsgStringPtr[2]; + /* 0x0140 */ TEXT_SPAN mErrorMsgStringPtr[2]; /* 0x0148 */ u8 mErrorTxtDispIdx; /* 0x0149 */ u8 field_0x0149; /* 0x014A */ bool field_0x014a; @@ -574,7 +609,7 @@ public: /* 0x020A */ u8 mFadeTimer; /* 0x020B */ u8 field_0x020b; /* 0x020C */ CPaneMgrAlpha* mHeaderTxtPane[2]; - /* 0x0214 */ char* mHeaderStringPtr[2]; + /* 0x0214 */ TEXT_SPAN mHeaderStringPtr[2]; /* 0x021C */ u8 mHeaderTxtDispIdx; /* 0x021D */ u8 field_0x021d; /* 0x021E */ u8 field_0x021e; @@ -591,7 +626,7 @@ public: /* 0x024B */ u8 field_0x024b; /* 0x024C */ u8 field_0x024c; /* 0x024B */ u8 field_0x024d[3]; - /* 0x0250 */ char* mModoruStringPtr; + /* 0x0250 */ TEXT_SPAN mModoruStringPtr; /* 0x0254 */ STControl* stick; /* 0x0258 */ u8 mIsDataNew[3]; /* 0x025B */ u8 mIsNoData[3]; diff --git a/include/d/d_menu_collect.h b/include/d/d_menu_collect.h index 55d5373c71..d259027a65 100644 --- a/include/d/d_menu_collect.h +++ b/include/d/d_menu_collect.h @@ -15,6 +15,49 @@ class dMenu_Fishing_c; class dMenu_Skill_c; class dMenu_Insect_c; class dSelect_cursor_c; +#if TARGET_PC +static bool cachedPanes = false; + +struct PaneCache { + u64 tag; + f32 origTransX; + f32 origTransY; + bool cached; +}; + +static PaneCache mpScreenPanes[] = { + {MULTI_CHAR('sa_tex_n'), 0.0f, false}, + {MULTI_CHAR('op_tex_n'), 0.0f, false}, + {MULTI_CHAR('heart_n'), 0.0f, false}, + {MULTI_CHAR('wolf_n'), 0.0f, false}, + {MULTI_CHAR('item_0_n'), 0.0f, false}, + {MULTI_CHAR('item_1_n'), 0.0f, false}, + {MULTI_CHAR('item_2_n'), 0.0f, false}, + {MULTI_CHAR('fish_3_n'), 0.0f, false}, + {MULTI_CHAR('lett_4_n'), 0.0f, false}, + {MULTI_CHAR('maki_5_n'), 0.0f, false}, + {MULTI_CHAR('fuku_n0'), 0.0f, false}, + {MULTI_CHAR('fuku_n1'), 0.0f, false}, + {MULTI_CHAR('fuku_n2'), 0.0f, false}, + {MULTI_CHAR('tate_n0'), 0.0f, false}, + {MULTI_CHAR('tate_n1'), 0.0f, false}, + {MULTI_CHAR('ken_n0'), 0.0f, false}, + {MULTI_CHAR('ken_n1'), 0.0f, false}, + {MULTI_CHAR('kabu_6n'), 0.0f, false}, + {MULTI_CHAR('t_t00'), 0.0f, false}, + {MULTI_CHAR('f_t00'), 0.0f, false}, + {MULTI_CHAR('itemn_n'), 0.0f, false}, + {MULTI_CHAR('infotxtn'), 0.0f, false}, + {MULTI_CHAR('sa_op_n'), 0.0f, false}, + {MULTI_CHAR('title_n'), 0.0f, false}, + {MULTI_CHAR('menu_n'), 0.0f, false}, + {MULTI_CHAR('w_er_n'), 0.0f, false}, + {MULTI_CHAR('center_n'), 0.0f, false}, + {MULTI_CHAR('info_n'), 0.0f, false}, + {MULTI_CHAR('lavel_n'), 0.0f, false}, + {MULTI_CHAR('modelbgn'), 0.0f, false}, +}; +#endif class dMenu_Collect2D_c; class dMenu_Collect2DTop_c : public dDlst_base_c { diff --git a/include/d/d_menu_dmap.h b/include/d/d_menu_dmap.h index 81baea1446..7740927f4e 100644 --- a/include/d/d_menu_dmap.h +++ b/include/d/d_menu_dmap.h @@ -183,6 +183,11 @@ public: /* 0xDD8 */ u8 field_0xdd8; /* 0xDD9 */ u8 field_0xdd9; /* 0xDDA */ u8 field_0xdda; + +#if TARGET_PC + J2DTextBox* mpPoeCountPane; + J2DPicture* mpPoeCountIcon; +#endif }; class dMenu_Dmap_c { diff --git a/include/d/d_menu_fmap2D.h b/include/d/d_menu_fmap2D.h index 7df78bb6d0..3c37c3da74 100644 --- a/include/d/d_menu_fmap2D.h +++ b/include/d/d_menu_fmap2D.h @@ -421,6 +421,12 @@ public: /* 0xC2 */ u8 mAlphaButtonZ; /* 0xC3 */ u8 mAlphaAnalogStick; /* 0xC4 */ u8 mAlphaDpad; + +#if TARGET_PC + J2DTextBox* mpPoeCountPane; + J2DPicture* mpPoeCountIcon; + u8 mSelectRegionNo; +#endif }; #endif /* D_MENU_D_MENU_FMAP2D_H */ diff --git a/include/d/d_menu_map_common.h b/include/d/d_menu_map_common.h index de50d775ca..55de3d0f6c 100644 --- a/include/d/d_menu_map_common.h +++ b/include/d/d_menu_map_common.h @@ -76,6 +76,11 @@ public: } #endif +#if TARGET_PC + static void getDmapPoeCount(const std::string& stageName, int& nowCount, int& totalCount); + static void getFmapPoeCount(const int regionNo, int& nowCount, int& totalCount); +#endif + struct Stage_c { // Incomplete class diff --git a/include/d/d_menu_save.h b/include/d/d_menu_save.h index 4a70d1d045..116795f7be 100644 --- a/include/d/d_menu_save.h +++ b/include/d/d_menu_save.h @@ -313,7 +313,7 @@ private: /* 0x00B8 */ int field_0xb8; /* 0x00BC */ int field_0xbc; /* 0x00C0 */ CPaneMgrAlpha* mpErrTxtPane[2]; - /* 0x00C8 */ char* mpErrTxt[2]; + /* 0x00C8 */ TEXT_SPAN mpErrTxt[2]; /* 0x00D0 */ u8 mErrTxtType; /* 0x00D1 */ u8 mErrTxtAnmComplete; /* 0x00D2 */ u8 field_0xd2; @@ -344,7 +344,7 @@ private: /* 0x0160 */ J2DAnmTevRegKey* field_0x160; /* 0x0164 */ int field_0x164; /* 0x0168 */ CPaneMgrAlpha* mpHeaderTxtPane[2]; - /* 0x0170 */ char* mpHeaderTxt[2]; + /* 0x0170 */ TEXT_SPAN mpHeaderTxt[2]; /* 0x0178 */ u8 mHeaderTxtType; // 0: Select Menu 1: YesNo Menu /* 0x0179 */ u8 mHeaderAnmComplete; /* 0x017A */ u8 field_0x17a; diff --git a/include/d/d_meter2_info.h b/include/d/d_meter2_info.h index 51f07f4dc3..683e555386 100644 --- a/include/d/d_meter2_info.h +++ b/include/d/d_meter2_info.h @@ -67,9 +67,9 @@ public: s16 decFloatingMessageTimer(); void resetFloatingMessage(); void decMsgKeyWaitTimer(); - void getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); - void getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); - void getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry); + void getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); + void getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); + void getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry); f32 getStringLength(J2DTextBox* i_textbox, char* i_string); f32 getStringLength(JUTFont* i_font, f32 param_2, f32 param_3, char* i_string); void onDirectUseItem(int); @@ -348,15 +348,15 @@ inline CPaneMgr* dMeter2Info_getMeterItemPanePtr(s32 i_idx) { return g_meter2_info.getMeterItemPanePtr(i_idx); } -inline void dMeter2Info_getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getString(i_stringID, o_string, i_msgEntry); } -inline void dMeter2Info_getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getStringKanji(i_stringID, o_string, i_msgEntry); } -inline void dMeter2Info_getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { +inline void dMeter2Info_getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { g_meter2_info.getStringKana(i_stringID, o_string, i_msgEntry); } diff --git a/include/d/d_msg_class.h b/include/d/d_msg_class.h index 8ebf3ff5b7..307d48f291 100644 --- a/include/d/d_msg_class.h +++ b/include/d/d_msg_class.h @@ -5,6 +5,7 @@ #include "JSystem/JMessage/JMessage.h" #include "SSystem/SComponent/c_xyz.h" #include "dusk/endian.h" +#include "dusk/string.hpp" #if REGION_JPN #define D_MSG_CLASS_PAGE_CNT_MAX 30 @@ -67,7 +68,7 @@ struct jmessage_tReference : public JMessage::TReference { void pageSend(); void selectMessage(); void inputNumber(); - char* getWord(int); + TEXT_SPAN getWord(int); void resetWord(); void setCharactor(u16); void addCharactor(u16); @@ -228,11 +229,11 @@ struct jmessage_tReference : public JMessage::TReference { f32 getSelRubyCharSpace() { return mSelRubyCharSpace; } f32 getRubySize() { return mRubySize; } f32 getRubyCharSpace() { return mRubyCharSpace; } - char* getSelTextPtr(int idx) { return mSelText[idx]; } - char* getSelRubyPtr(int idx) { return mSelRuby[idx]; } - char* getTextPtr() { return mText; } - char* getTextSPtr() { return mTextS; } - char* getRubyPtr() { return mRuby; } + TEXT_SPAN getSelTextPtr(int idx) { return mSelText[idx]; } + TEXT_SPAN getSelRubyPtr(int idx) { return mSelRuby[idx]; } + TEXT_SPAN getTextPtr() { return mText; } + TEXT_SPAN getTextSPtr() { return mTextS; } + TEXT_SPAN getRubyPtr() { return mRuby; } u8 getSelectRubyFlag() { return mSelectRubyFlag; } f32 getSelTBoxWidth() { return mSelTBoxWidth; } u8 getSelectPos() { return mSelectPos; } @@ -463,7 +464,7 @@ struct jmessage_tRenderingProcessor : public JMessage::TRenderingProcessor { f32 getLineLength(int); void do_strcat(char*, bool, bool, bool); void do_rubyset(void const*, u32); - void do_rubystrcat(char*, char*, f32, f32); + void do_rubystrcat(char*, TEXT_SPAN, f32, f32); void do_name1(); void do_numset(s16); void push_word(); diff --git a/include/d/d_msg_object.h b/include/d/d_msg_object.h index d2ac4ecb2a..6cf9ea7681 100644 --- a/include/d/d_msg_object.h +++ b/include/d/d_msg_object.h @@ -53,8 +53,8 @@ public: void demoMessageGroupLocal(); void endFlowGroupLocal(); void changeGroupLocal(s16); - bool getStringLocal(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, char*, - char*, char*, s16*); + bool getStringLocal(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, TEXT_SPAN, + TEXT_SPAN, TEXT_SPAN, s16*); bool isGetItemMessage(); bool isKanbanMessage(); bool isHowlMessage(); @@ -121,7 +121,7 @@ public: static void endFlowGroup(); static void changeGroup(s16); static bool getString(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, - char*, char*, char*, s16*); + TEXT_SPAN, TEXT_SPAN, TEXT_SPAN, s16*); static void* getMsgDtPtr(); static void setProcessID(fpc_ProcID); static msg_class* getActor(); @@ -246,12 +246,12 @@ public: static void setWord(const char* i_word); void setWordLocal(const char* i_word) { - strcpy(mWord, i_word); + SAFE_STRCPY(mWord, i_word); } static void setSelectWord(int i_no, const char* i_word); void setSelectWordLocal(int i_no, const char* i_word) { - strcpy(mSelectWord[i_no], i_word); + SAFE_STRCPY(mSelectWord[i_no], i_word); } jmessage_tSequenceProcessor* getSequenceProcessor() { return mpSeqProc; } @@ -426,8 +426,8 @@ inline void dMsgObject_setTalkActor(fopAc_ac_c* actor) { } inline bool dMsgObject_getString(u32 i_msgId, J2DTextBox* i_tbox, J2DTextBox* i_rubyTbox, - JUTFont* i_font, COutFont_c* i_outFont, char* o_text, - char* o_ruby, char* o_textS, s16* param_8) { + JUTFont* i_font, COutFont_c* i_outFont, TEXT_SPAN o_text, + TEXT_SPAN o_ruby, TEXT_SPAN o_textS, s16* param_8) { return dMsgObject_getMsgObjectClass()->getString(i_msgId, i_tbox, i_rubyTbox, i_font, i_outFont, o_text, o_ruby, o_textS, param_8); } diff --git a/include/d/d_msg_string_base.h b/include/d/d_msg_string_base.h index 6f3a751b22..4624393f06 100644 --- a/include/d/d_msg_string_base.h +++ b/include/d/d_msg_string_base.h @@ -20,12 +20,12 @@ public: f32 getStringPageLocal(u32, u8, u8, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); u8 getPageMax(int); - f32 getMessageLocal(u32, char*); + f32 getMessageLocal(u32, TEXT_SPAN); virtual f32 getString(u32, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); virtual f32 getStringPage(u32, u8, u8, J2DTextBox*, J2DTextBox*, JUTFont*, COutFont_c*, u8); - virtual f32 getMessage(u32, char*); + virtual f32 getMessage(u32, TEXT_SPAN); virtual void resetStringLocal(J2DTextBox*); virtual void drawOutFontLocal(J2DTextBox*, f32); virtual void drawFontLocal(J2DTextBox*, u8, f32, f32, f32, f32, u32, u8); diff --git a/include/d/d_msg_unit.h b/include/d/d_msg_unit.h index 400df98f25..47e40e31cf 100644 --- a/include/d/d_msg_unit.h +++ b/include/d/d_msg_unit.h @@ -6,14 +6,14 @@ class dMsgUnit_c { public: dMsgUnit_c(); - void setTag(int, int, char*, bool); + void setTag(int, int, TEXT_SPAN, bool); virtual ~dMsgUnit_c(); }; extern dMsgUnit_c g_msg_unit; -inline void dMsgUnit_setTag(int param_0, int param_1, char* param_2) { +inline void dMsgUnit_setTag(int param_0, int param_1, TEXT_SPAN param_2) { g_msg_unit.setTag(param_0, param_1, param_2, true); } diff --git a/include/d/d_name.h b/include/d/d_name.h index 3ac6592457..ddd99127c6 100644 --- a/include/d/d_name.h +++ b/include/d/d_name.h @@ -127,7 +127,7 @@ public: u8 isInputEnd() { return mIsInputEnd; } char* getInputStrPtr() { return mInputStr; } void hideIcon() { mSelIcon->setAlphaRate(0.0f); } - void setNextNameStr(char* i_name) { strcpy(mNextNameStr,i_name); } + void setNextNameStr(char* i_name) { SAFE_STRCPY(mNextNameStr,i_name); } void draw() { _draw(); } private: @@ -140,9 +140,9 @@ private: /* 0x02C */ J2DAnmTextureSRTKey* mCursorTexKey; /* 0x030 */ int mCurTexAnmF; /* 0x034 */ CPaneMgrAlpha* mNameCursor[8]; - /* 0x054 */ char* mNameText[8]; + /* 0x054 */ TEXT_SPAN mNameText[8]; /* 0x074 */ CPaneMgr* mMojiIcon[65]; - /* 0x178 */ char* mMojiText[65]; + /* 0x178 */ TEXT_SPAN mMojiText[65]; /* 0x27C */ J2DPane* mMojiPane; /* 0x280 */ J2DPane* mMenuPane; /* 0x284 */ CPaneMgr* mMenuIcon[4]; diff --git a/include/d/d_save.h b/include/d/d_save.h index e2719e0a98..69d4d3174d 100644 --- a/include/d/d_save.h +++ b/include/d/d_save.h @@ -488,21 +488,25 @@ public: } #if TARGET_PC u16 getDeathCount() const { return mDeathCount; } -#endif + TEXT_SPAN getPlayerName() const { return const_cast(mPlayerName); } +#else char* getPlayerName() const { return const_cast(mPlayerName); } +#endif void setPlayerName(const char* i_name) { #if AVOID_UB - strncpy(mPlayerName, i_name, sizeof(mPlayerName) - 1); - mPlayerName[sizeof(mPlayerName) - 1] = '\0'; + dusk::SafeStringCopyTruncate(mPlayerName, i_name); #else strcpy(mPlayerName, i_name); #endif } +#if TARGET_PC + TEXT_SPAN getHorseName() const { return const_cast(mHorseName); } +#else char* getHorseName() const { return const_cast(mHorseName); } +#endif void setHorseName(const char* i_name) { #if AVOID_UB - strncpy(mHorseName, i_name, sizeof(mHorseName) - 1); - mHorseName[sizeof(mHorseName) - 1] = '\0'; + dusk::SafeStringCopyTruncate(mHorseName, i_name); #else strcpy(mHorseName, i_name); #endif diff --git a/include/d/d_select_cursor.h b/include/d/d_select_cursor.h index 1ca226611b..e98db19731 100644 --- a/include/d/d_select_cursor.h +++ b/include/d/d_select_cursor.h @@ -51,7 +51,7 @@ public: f32 getPositionX() const { return mPositionX; } f32 getPositionY() const { return mPositionY; } - void refreshAspectScale(); + void refreshAspectScale(f32 param_0); #endif void onUpdateFlag() { mUpdateFlag = true; } diff --git a/include/d/d_stage.h b/include/d/d_stage.h index bd8e7d5728..04a6115fd0 100644 --- a/include/d/d_stage.h +++ b/include/d/d_stage.h @@ -1225,7 +1225,7 @@ public: #endif } static JKRExpHeap* getMemoryBlockHeap(int i_no) { return mMemoryBlock[i_no]; } - static char* getDemoArcName() { return mDemoArcName; } + static TEXT_SPAN getDemoArcName() { return mDemoArcName; } static void offNoChangeRoom() { mNoChangeRoom = false; } static void onNoChangeRoom() { mNoChangeRoom = true; } diff --git a/include/dusk/achievements.h b/include/dusk/achievements.h index 5c2758b6b7..c93ea5d626 100644 --- a/include/dusk/achievements.h +++ b/include/dusk/achievements.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "nlohmann/json.hpp" @@ -47,6 +47,7 @@ public: // Signals are visible to all achievement checks within the same tick, then cleared. void signal(const char* key); bool hasSignal(const char* key) const; + int signalCount(const char* key) const; std::vector getAchievements() const; @@ -62,7 +63,7 @@ private: void processEntry(Entry& e); std::vector m_entries; - std::unordered_set m_signals; + std::unordered_map m_signals; bool m_loaded = false; bool m_dirty = false; }; diff --git a/include/dusk/crash_handler.h b/include/dusk/crash_handler.h new file mode 100644 index 0000000000..c2cfadf5d1 --- /dev/null +++ b/include/dusk/crash_handler.h @@ -0,0 +1,7 @@ +#pragma once + +namespace dusk::crash_handler { + +void install(); + +} // namespace dusk::crash_handler diff --git a/include/dusk/logging.h b/include/dusk/logging.h index 9b31b96bf2..54350af4bf 100644 --- a/include/dusk/logging.h +++ b/include/dusk/logging.h @@ -12,6 +12,7 @@ namespace dusk { void InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel); void ShutdownFileLogging(); const char* GetLogFilePath(); + int GetLogFileDescriptor(); void SendToStubLog(AuroraLogLevel level, const char* module, const char* message); } @@ -19,7 +20,11 @@ extern bool StubLogEnabled; extern aurora::Module DuskLog; +#ifndef NDEBUG #define STUB_LOG() DuskLog.debug("{} is a stub", __FUNCTION__) +#else +#define STUB_LOG() +#endif #if TARGET_PC #define STUB_RET(...) \ diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 6adac9bb1f..5526d57f27 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -15,6 +15,12 @@ enum class BloomMode : int { Dusk = 2, }; +enum class DepthOfFieldMode : int { + Off = 0, + Classic = 1, + Dusk = 2, +}; + enum class Resampler : int { Bilinear = 0, Area = 1, @@ -45,6 +51,12 @@ enum class FrameInterpMode : u8 { Unlimited = 2, }; +enum class MenuScaling : u8 { + GameCube = 0, + Wii = 1, + Dusklight = 2, +}; + namespace config { template <> struct ConfigEnumRange { @@ -52,6 +64,12 @@ struct ConfigEnumRange { static constexpr auto max = BloomMode::Dusk; }; +template <> +struct ConfigEnumRange { + static constexpr auto min = DepthOfFieldMode::Off; + static constexpr auto max = DepthOfFieldMode::Dusk; +}; + template <> struct ConfigEnumRange { static constexpr auto min = Resampler::Bilinear; @@ -81,6 +99,12 @@ struct ConfigEnumRange { static constexpr auto min = FrameInterpMode::Off; static constexpr auto max = FrameInterpMode::Unlimited; }; + +template <> +struct ConfigEnumRange { + static constexpr auto min = MenuScaling::GameCube; + static constexpr auto max = MenuScaling::Dusklight; +}; } // namespace config // Persistent user settings @@ -133,6 +157,7 @@ struct UserSettings { ConfigVar instantText; ConfigVar sunsSong; ConfigVar autoSave; + ConfigVar enhancedMapMenus; // Preferences ConfigVar enableMirrorMode; @@ -142,17 +167,18 @@ struct UserSettings { ConfigVar enableAchievementToasts; ConfigVar enableControllerToasts; ConfigVar enableDiscordPresence; + ConfigVar menuScalingMode; // Graphics ConfigVar bloomMode; ConfigVar bloomMultiplier; + ConfigVar depthOfFieldMode; ConfigVar disableWaterRefraction; ConfigVar enableTextureReplacements; ConfigVar enableFrameInterpolation; ConfigVar internalResolutionScale; ConfigVar shadowResolutionMultiplier; ConfigVar resampler; - ConfigVar enableDepthOfField; ConfigVar enableMapBackground; ConfigVar disableCutscenePillarboxing; @@ -176,6 +202,8 @@ struct UserSettings { ConfigVar invertCameraYAxis; ConfigVar invertFirstPersonXAxis; ConfigVar invertFirstPersonYAxis; + ConfigVar invertAirSwimX; + ConfigVar invertAirSwimY; ConfigVar freeCameraSensitivity; ConfigVar debugFlyCam; ConfigVar debugFlyCamLockEvents; @@ -212,6 +240,7 @@ struct UserSettings { ConfigVar liveSplitEnabled; ConfigVar showSpeedrunRTATimer; ConfigVar recordingMode; + ConfigVar removeQuestMapMarkers; ConfigVar showInputViewer; ConfigVar showInputViewerGyro; } game; @@ -242,6 +271,8 @@ struct UserSettings { UserSettings& getSettings(); +bool tphd_active(); + void registerSettings(); // Transient settings diff --git a/include/dusk/string.hpp b/include/dusk/string.hpp index 7de1b1ad75..af91ff8fe9 100644 --- a/include/dusk/string.hpp +++ b/include/dusk/string.hpp @@ -1,22 +1,55 @@ #ifndef DUSK_STRING_HPP #define DUSK_STRING_HPP - -#include "global.h" -#include -#include +#include namespace dusk { -inline void strncpyProxy(char* dst, const char* src, size_t count) { -#if _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4996) +struct TextSpan { + char* buffer; + size_t size; + + constexpr operator char*() const { + return buffer; + } + + constexpr TextSpan(char* buffer, size_t size) : buffer(buffer), size(size) { } + + template + constexpr TextSpan(char (&buffer)[BufSize]) : buffer(buffer), size(BufSize) { + } + + constexpr TextSpan() : buffer(nullptr), size(0) { } + + constexpr TextSpan operator++(int) { + const auto prev = *this; + + if (size > 0) [[likely]] { + size--; + } + buffer++; + + return prev; + } + + constexpr char& operator*() const { + if (size == 0) [[unlikely]] { + CrashSpawnEmpty(); + } + + return *buffer; + } + +private: + static void CrashSpawnEmpty(); +}; + +#if TARGET_PC +#define TEXT_SPAN dusk::TextSpan +#else +#define TEXT_SPAN char* #endif - strncpy(dst, src, count); -#if _MSC_VER -#pragma warning(pop) -#endif -} + +void SafeStringCopyTruncate(char* buffer, size_t bufSize, const char* src); /** * Copy a string to a fixed-size array. @@ -25,13 +58,28 @@ inline void strncpyProxy(char* dst, const char* src, size_t count) { template void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); + SafeStringCopyTruncate(buffer, BufSize, src); +} - if (buffer == src) { - CRASH("Cannot copy string to same buffer"); - } +void SafeStringCopy(char* buffer, size_t bufSize, const char* src); +void SafeStringCat(char* buffer, size_t bufSize, const char* src); +int SafeStringVPrintf(char* buffer, size_t bufSize, const char* src, std::va_list args); - strncpyProxy(buffer, src, BufSize); - buffer[BufSize - 1] = 0; +inline void SafeStringCopy(TextSpan dst, const char* src) { + SafeStringCopy(dst.buffer, dst.size, src); +} + +inline void SafeStringCat(TextSpan dst, const char* src) { + SafeStringCat(dst.buffer, dst.size, src); +} + +inline int SafeStringPrintf(TextSpan dst, const char* format, ...) { + std::va_list args; + va_start(args, format); + const auto ret = SafeStringVPrintf(dst.buffer, dst.size, format, args); + va_end(args); + + return ret; } /** @@ -41,18 +89,40 @@ void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { template void SafeStringCopy(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); - if (buffer == src) { - CRASH("Cannot copy string to same buffer"); - } - - if (strlen(src) > BufSize - 1) { - CRASH("Destination buffer too small!"); - } - - strncpyProxy(buffer, src, BufSize); - buffer[BufSize - 1] = 0; + SafeStringCopy(buffer, BufSize, src); } +template +void SafeStringCat(char (&buffer)[BufSize], const char* src) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + SafeStringCat(buffer, BufSize, src); +} + +template +int SafeStringPrintf(char (&buffer)[BufSize], const char* format, ...) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + + std::va_list args; + va_start(args, format); + const auto ret = SafeStringVPrintf(buffer, BufSize, format, args); + va_end(args); + + return ret; +} + +#if TARGET_PC +#define SAFE_STRCPY dusk::SafeStringCopy +#define SAFE_STRCAT dusk::SafeStringCat +#define SAFE_SPRINTF dusk::SafeStringPrintf +#define SAFE_STRCPY_BOUNDED dusk::SafeStringCopy +#define SAFE_STRCAT_BOUNDED dusk::SafeStringCat +#else +#define SAFE_STRCPY strcpy +#define SAFE_STRCAT strcat +#define SAFE_SPRINTF sprintf +#define SAFE_STRCPY_BOUNDED strcpy +#define SAFE_STRCPY_BOUNDED strcat +#endif } #endif // DUSK_STRING_HPP diff --git a/include/f_op/f_op_msg_mng.h b/include/f_op/f_op_msg_mng.h index 9bb8bc0ff3..330347fc18 100644 --- a/include/f_op/f_op_msg_mng.h +++ b/include/f_op/f_op_msg_mng.h @@ -50,7 +50,7 @@ fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, fopAc_ac_c* i_talkActor, u32 param_2 fpc_ProcID fopMsgM_messageSet(u32 i_msgIdx, u32 param_1); fpc_ProcID fopMsgM_messageSetDemo(u32 i_msgidx); msg_class* fopMsgM_SearchByID(fpc_ProcID i_id); -char* fopMsgM_messageGet(char* i_stringBuf, u32 i_msgId); +TEXT_SPAN fopMsgM_messageGet(TEXT_SPAN i_stringBuf, u32 i_msgId); fpc_ProcID fop_Timer_create(s16 i_procName, u8 i_mode, u32 i_limitMs, u8 i_type, u8 param_4, f32 param_5, f32 param_6, f32 param_7, f32 param_8, fopMsgCreateFunc i_createFunc); diff --git a/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h b/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h index f4d5345507..0d875ad652 100644 --- a/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h +++ b/libs/JSystem/include/JSystem/J2DGraph/J2DTextBox.h @@ -4,6 +4,7 @@ #include "JSystem/J2DGraph/J2DMaterial.h" #include "JSystem/J2DGraph/J2DPane.h" #include "dusk/endian.h" +#include "dusk/string.hpp" class J2DMaterial; class JUTFont; @@ -98,7 +99,8 @@ public: void initiate(ResFONT const*, char const*, s16, J2DTextBoxHBinding, J2DTextBoxVBinding); void private_readStream(J2DPane*, JSURandomInputStream*, JKRArchive*); - char* getStringPtr() const; + TEXT_SPAN getStringPtr() const; + dusk::TextSpan getSpan() const; s32 setString(s16, char const*, ...); s32 setString(char const*, ...); diff --git a/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h b/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h index 7463562c3e..075152d0c5 100644 --- a/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h +++ b/libs/JSystem/include/JSystem/J3DGraphBase/J3DMatBlock.h @@ -1575,6 +1575,12 @@ struct J3DAlphaComp { u8 getRef1() const { return mRef1; } void load() const { +#ifdef AVOID_UB + if (mID > 255) { + J3DGDSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0); + return; + } +#endif J3DGDSetAlphaCompare((GXCompare)getComp0(), mRef0, (GXAlphaOp)getOp(), (GXCompare)getComp1(), mRef1); } diff --git a/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp b/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp index beb9e0075a..2cede2c49a 100644 --- a/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp +++ b/libs/JSystem/src/J2DGraph/J2DMaterialFactory.cpp @@ -8,6 +8,8 @@ #include #include +#include "dusk/string.hpp" + J2DMaterialFactory::J2DMaterialFactory(J2DMaterialBlock const& param_0) { mMaterialNum = param_0.field_0x8; mpMaterialInitData = JSUConvertOffsetToPtr(¶m_0, param_0.field_0xc); @@ -92,7 +94,7 @@ J2DMaterial* J2DMaterialFactory::create(J2DMaterial* param_0, int index, u32 par } if (local_380 == NULL && J2DScreen::getDataManage() != NULL) { char acStack_230[257]; - strcpy(acStack_230, param_3->getName(texNo)); + SAFE_STRCPY(acStack_230, param_3->getName(texNo)); local_380 = J2DScreen::getDataManage()->get(acStack_230); } } @@ -111,7 +113,7 @@ J2DMaterial* J2DMaterialFactory::create(J2DMaterial* param_0, int index, u32 par } if (local_388 == NULL && J2DScreen::getDataManage() != NULL) { char acStack_334[257]; - strcpy(acStack_334, param_4->getName(param_0->getTevBlock()->getFontNo())); + SAFE_STRCPY(acStack_334, param_4->getName(param_0->getTevBlock()->getFontNo())); local_388 = J2DScreen::getDataManage()->get(acStack_334); } } diff --git a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp index 970f120140..d999b51b9f 100644 --- a/libs/JSystem/src/J2DGraph/J2DTextBox.cpp +++ b/libs/JSystem/src/J2DGraph/J2DTextBox.cpp @@ -309,8 +309,12 @@ void J2DTextBox::draw(f32 posX, f32 posY, f32 param_2, J2DTextBoxHBinding hBind) } } -char* J2DTextBox::getStringPtr() const { +TEXT_SPAN J2DTextBox::getStringPtr() const { +#if TARGET_PC + return { mStringPtr, mStringLength }; +#else return mStringPtr; +#endif } s32 J2DTextBox::setString(char const* string, ...) { @@ -330,7 +334,7 @@ s32 J2DTextBox::setString(char const* string, ...) { if (mStringPtr) { mStringLength = len + 1; - strcpy(mStringPtr, string); + SAFE_STRCPY_BOUNDED(mStringPtr, mStringLength, string); } va_end(args); diff --git a/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp b/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp index b9cd8acb15..71068a3591 100644 --- a/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp +++ b/libs/JSystem/src/JAHostIO/JAHVirtualNode.cpp @@ -2,6 +2,8 @@ #include #include +#include "dusk/string.hpp" + u32 JAHVirtualNode::smVirNodeNum; void JAHVirtualNode::virtualMessage(JAHControl& control) { @@ -77,7 +79,7 @@ void JAHVirtualNode::setVirNodeName(const char* name) { // clang-format off JUT_ASSERT(141, size<32); // clang-format on - strcpy(mName, name); + SAFE_STRCPY(mName, name); } JAHVirtualNode::JAHVirtualNode(const char* name) : mTree(this) { diff --git a/libs/JSystem/src/JAHostIO/JAHioNode.cpp b/libs/JSystem/src/JAHostIO/JAHioNode.cpp index 9a30991e4f..e6c342eeeb 100644 --- a/libs/JSystem/src/JAHostIO/JAHioNode.cpp +++ b/libs/JSystem/src/JAHostIO/JAHioNode.cpp @@ -4,7 +4,9 @@ #include "JSystem/JAHostIO/JAHioMessage.h" #include "JSystem/JAHostIO/JAHioMgr.h" #include "JSystem/JAHostIO/JAHioNode.h" + #include "JSystem/JHostIO/JORServer.h" +#include "dusk/string.hpp" JAHioNode* JAHioNode::smCurrentNode; @@ -32,7 +34,7 @@ void JAHioNode::updateNode() { void JAHioNode::setNodeName(const char* name) { int size = strlen(name) + 1; JUT_ASSERT(51, size < 32); - strcpy(mName, name); + SAFE_STRCPY(mName, name); } void JAHioNode::genMessage(JORMContext* mctx) { diff --git a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp index d14ff70298..41ad35380d 100644 --- a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp +++ b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp @@ -9,6 +9,8 @@ #include #include +#include "dusk/string.hpp" + JASHeap* JASWaveArcLoader::sAramHeap; JASHeap* JASWaveArcLoader::getRootHeap() { @@ -22,7 +24,7 @@ char JASWaveArcLoader::sCurrentDir[DIR_MAX] = "/AudioRes/Waves/"; void JASWaveArcLoader::setCurrentDir(char const* dir) { JUT_ASSERT(40, std::strlen(dir) < DIR_MAX - 1); - strcpy(sCurrentDir, dir); + SAFE_STRCPY(sCurrentDir, dir); u32 len = strlen(sCurrentDir); if (sCurrentDir[len - 1] != '/') { JUT_ASSERT(45, len + 1 < DIR_MAX); @@ -170,8 +172,8 @@ void JASWaveArc::setFileName(char const* fileName) { length = length + strlen(fileName); char* path = JKR_NEW_ARRAY_ARGS(char, length + 1, JASKernel::getSystemHeap(), -4); JUT_ASSERT(322, path); - strcpy(path, currentDir); - strcat(path, fileName); + SAFE_STRCPY_BOUNDED(path, length + 1, currentDir); + SAFE_STRCAT_BOUNDED(path, length + 1, fileName); path[length] = '\0'; int entryNum = DVDConvertPathToEntrynum(path); JKR_DELETE_ARRAY(path); diff --git a/libs/JSystem/src/JKernel/JKRFileCache.cpp b/libs/JSystem/src/JKernel/JKRFileCache.cpp index 97b0d6fdde..1a97ddefa3 100644 --- a/libs/JSystem/src/JKernel/JKRFileCache.cpp +++ b/libs/JSystem/src/JKernel/JKRFileCache.cpp @@ -8,6 +8,7 @@ #include #include +#include "dusk/string.hpp" #include "global.h" JKRFileCache* JKRFileCache::mount(const char* path, JKRHeap* heap, const char* param_3) { diff --git a/libs/JSystem/src/JKernel/JKRFileLoader.cpp b/libs/JSystem/src/JKernel/JKRFileLoader.cpp index e0e531aaa9..4a7fa00d10 100644 --- a/libs/JSystem/src/JKernel/JKRFileLoader.cpp +++ b/libs/JSystem/src/JKernel/JKRFileLoader.cpp @@ -4,10 +4,11 @@ #define MSL_USE_INLINES 1 // needed to inline tolower call. not inlined elsewhere in the repo -#include #include +#include #include #include "JSystem/JKernel/JKRHeap.h" +#include "dusk/string.hpp" #include "global.h" JKRFileLoader* JKRFileLoader::sCurrentVolume; @@ -104,7 +105,7 @@ const char* JKRFileLoader::fetchVolumeName(char* buffer, s32 bufferSize, const c static char rootPath[2] = "/"; if (strcmp(path, "/") == 0) { - strcpy(buffer, rootPath); + SAFE_STRCPY_BOUNDED(buffer, bufferSize, rootPath); return rootPath; } diff --git a/libs/JSystem/src/JKernel/JKRThread.cpp b/libs/JSystem/src/JKernel/JKRThread.cpp index 31c2cbe6e5..8c945aba32 100644 --- a/libs/JSystem/src/JKernel/JKRThread.cpp +++ b/libs/JSystem/src/JKernel/JKRThread.cpp @@ -7,6 +7,8 @@ #include "global.h" #include +#include "dusk/string.hpp" + #if TARGET_PC #include "dusk/os.h" #endif @@ -274,7 +276,7 @@ void JKRThreadSwitch::draw(JKRThreadName_* thread_name_list, JUTConsole* console if (!thread_print_name) { char buffer[16]; - sprintf(buffer, "%d", loadInfo->getId()); + SAFE_SPRINTF(buffer, "%d", loadInfo->getId()); thread_print_name = buffer; } diff --git a/libs/JSystem/src/JParticle/JPABaseShape.cpp b/libs/JSystem/src/JParticle/JPABaseShape.cpp index ff346984cd..add61135fe 100644 --- a/libs/JSystem/src/JParticle/JPABaseShape.cpp +++ b/libs/JSystem/src/JParticle/JPABaseShape.cpp @@ -76,6 +76,7 @@ void JPARegistAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { } void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; JPABaseEmitter* emtr = work->mpEmtr; GXColor prm = ptcl->mPrmClr; prm.r = COLOR_MULTI(prm.r, emtr->mGlobalPrmClr.r); @@ -87,6 +88,7 @@ void JPARegistPrmAlpha(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { } void JPARegistPrmAlphaEnv(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; JPABaseEmitter* emtr = work->mpEmtr; GXColor prm = ptcl->mPrmClr; GXColor env = ptcl->mEnvClr; @@ -225,6 +227,7 @@ void JPAGenTexCrdMtxPrj(JPAEmitterWorkData* param_0) { } void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) { + ZoneScoped; JPABaseShape* shape = work->mpRes->getBsp(); f32 dVar16 = work->mpEmtr->mTick; f32 dVar15 = 0.5f * (1.0f + shape->getTilingS()); @@ -256,6 +259,7 @@ void JPAGenCalcTexCrdMtxAnm(JPAEmitterWorkData* work) { } void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1) { + ZoneScoped; JPABaseShape* shape = work->mpRes->getBsp(); f32 dVar16 = param_1->mAge; f32 dVar15 = 0.5f * (1.0f + shape->getTilingS()); @@ -286,14 +290,17 @@ void JPALoadCalcTexCrdMtxAnm(JPAEmitterWorkData* work, JPABaseParticle* param_1) } void JPALoadTex(JPAEmitterWorkData* work) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(work->mpRes->getBsp()->getTexIdx()), GX_TEXMAP0); } void JPALoadTexAnm(JPAEmitterWorkData* work) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(work->mpEmtr->mTexAnmIdx), GX_TEXMAP0); } void JPALoadTexAnm(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { + ZoneScoped; work->mpResMgr->load(work->mpRes->getTexIdx(ptcl->mTexAnmIdx), GX_TEXMAP0); } @@ -446,6 +453,7 @@ void JPADrawBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; JGeometry::TVec3 pos; #if TARGET_PC Mtx ptclPosMtx; @@ -475,6 +483,7 @@ void JPADrawRotBillboard(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; if (work->mpRes->getUsrIdx() == 0x89d7) { int a = 0; } @@ -518,6 +527,7 @@ void JPADrawYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) { return; } + ZoneScoped; JGeometry::TVec3 local_48; MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48); Mtx local_38; @@ -542,6 +552,7 @@ void JPADrawRotYBillboard(JPAEmitterWorkData* work, JPABaseParticle* param_1) { return; } + ZoneScoped; JGeometry::TVec3 local_48; MTXMultVec(work->mPosCamMtx, ¶m_1->mPosition, &local_48); f32 sinRot = JMASSin(param_1->mRotateAngle); @@ -1268,6 +1279,8 @@ void JPADrawStripeX(JPAEmitterWorkData* param_0) { } void JPADrawEmitterCallBackB(JPAEmitterWorkData* work) { + ZoneScoped; + JPABaseEmitter* emtr = work->mpEmtr; if (emtr->mpEmtrCallBack == NULL) { return; @@ -1282,6 +1295,7 @@ void JPADrawParticleCallBack(JPAEmitterWorkData* work, JPABaseParticle* ptcl) { return; } + ZoneScoped; emtr->mpPtclCallBack->draw(emtr, ptcl); } diff --git a/libs/JSystem/src/JParticle/JPAExTexShape.cpp b/libs/JSystem/src/JParticle/JPAExTexShape.cpp index bb9510050e..cba10eb119 100644 --- a/libs/JSystem/src/JParticle/JPAExTexShape.cpp +++ b/libs/JSystem/src/JParticle/JPAExTexShape.cpp @@ -6,6 +6,8 @@ #include void JPALoadExTex(JPAEmitterWorkData* work) { + ZoneScoped; + JPAExTexShape* ets = work->mpRes->getEts(); GXTexCoordID secTexCoordID = GX_TEXCOORD1; diff --git a/libs/JSystem/src/JUtility/JUTConsole.cpp b/libs/JSystem/src/JUtility/JUTConsole.cpp index 3be4a370ef..c2c023a2d8 100644 --- a/libs/JSystem/src/JUtility/JUTConsole.cpp +++ b/libs/JSystem/src/JUtility/JUTConsole.cpp @@ -1,13 +1,14 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep -#include "JSystem/JUtility/JUTConsole.h" +#include +#include #include "JSystem/J2DGraph/J2DOrthoGraph.h" #include "JSystem/JKernel/JKRHeap.h" #include "JSystem/JUtility/JUTAssert.h" +#include "JSystem/JUtility/JUTConsole.h" #include "JSystem/JUtility/JUTDirectPrint.h" #include "JSystem/JUtility/JUTVideo.h" -#include -#include +#include "dusk/string.hpp" #include "global.h" JUTConsoleManager* JUTConsoleManager::sManager; @@ -204,10 +205,10 @@ void JUTConsole::doDraw(JUTConsole::EConsoleType consoleType) const { mFont->drawString_scale((int)f31, sp94, mFontSizeX, mFontSizeY, spA8, TRUE); f31 += mFontSizeX * 13.0f; if (sp88) { - sprintf(spB8, "ALL"); + SAFE_SPRINTF(spB8, "ALL"); } else { f32 f29 = sp8C / (f32)(sp90 - mHeight); - sprintf(spB8, "%3d%%(%dL)", (int)(100.0 * f29), sp90); + SAFE_SPRINTF(spB8, "%3d%%(%dL)", (int)(100.0 * f29), sp90); } mFont->drawString_scale(f31, sp94, mFontSizeX, mFontSizeY, spB8, TRUE); } diff --git a/libs/JSystem/src/JUtility/JUTException.cpp b/libs/JSystem/src/JUtility/JUTException.cpp index 514ec6dd8a..54b234118c 100644 --- a/libs/JSystem/src/JUtility/JUTException.cpp +++ b/libs/JSystem/src/JUtility/JUTException.cpp @@ -9,6 +9,8 @@ #include #include #include + +#include "dusk/string.hpp" #ifdef __REVOLUTION_SDK__ #include #else @@ -845,8 +847,8 @@ bool JUTException::queryMapAddress(char* mapPath, u32 address, s32 section_id, u bool begin_with_newline) { if (mapPath) { char buffer[80]; - strcpy(buffer, mapPath); - strcat(buffer, ".map"); + SAFE_STRCPY(buffer, mapPath); + SAFE_STRCAT(buffer, ".map"); if (queryMapAddress_single(buffer, address, section_id, out_addr, out_size, out_line, line_length, print, begin_with_newline) == true) { diff --git a/libs/freeverb/denormals.h b/libs/freeverb/denormals.h index d68cb444f7..9193798abe 100644 --- a/libs/freeverb/denormals.h +++ b/libs/freeverb/denormals.h @@ -13,20 +13,33 @@ inline denormal_state denormals_enable() } inline void denormals_restore(denormal_state saved) { _mm_setcsr(saved); } -#elif defined(__aarch64__) || defined(_M_ARM64) +#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) #include +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) +#include +#endif using denormal_state = uint64_t; inline denormal_state denormals_enable() { +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + denormal_state saved = static_cast(_ReadStatusReg(ARM64_FPCR)); + _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved | (1ULL << 24))); // FZ + return saved; +#else denormal_state saved; asm volatile("mrs %0, fpcr" : "=r"(saved)); asm volatile("msr fpcr, %0" :: "r"(saved | (1ULL << 24))); // FZ return saved; +#endif } inline void denormals_restore(denormal_state saved) { +#if defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM64) || defined(_M_ARM64EC)) + _WriteStatusReg(ARM64_FPCR, static_cast<__int64>(saved)); +#else asm volatile("msr fpcr, %0" :: "r"(saved)); +#endif } #elif defined(__arm__) || defined(_M_ARM) diff --git a/platforms/android/app/build.gradle b/platforms/android/app/build.gradle index 2dc8575f86..8527cd1f4d 100644 --- a/platforms/android/app/build.gradle +++ b/platforms/android/app/build.gradle @@ -2,6 +2,9 @@ plugins { id 'com.android.application' } +def versionNameStr = (System.getenv("DUSK_VERSION") ?: "v0.1.0").replaceFirst("^v", "") +def versionCodeInt = (System.getenv("DUSK_VERSION_CODE") ?: "100000").toInteger() + def duskRepoDir = rootProject.projectDir.parentFile.parentFile def duskGeneratedAssetsDir = layout.buildDirectory.dir('generated/assets/dusklight').get().asFile def syncDuskAssets = tasks.register('syncDuskAssets', Sync) { @@ -20,8 +23,8 @@ android { applicationId 'dev.twilitrealm.dusk' minSdk 26 targetSdk 36 - versionCode 1 - versionName '0.1.0' + versionCode versionCodeInt + versionName versionNameStr } buildTypes { diff --git a/platforms/android/app/src/main/AndroidManifest.xml b/platforms/android/app/src/main/AndroidManifest.xml index f5a0365856..cd687963e1 100644 --- a/platforms/android/app/src/main/AndroidManifest.xml +++ b/platforms/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | @@ -185,9 +204,71 @@ public class DuskActivity extends SDLActivity { finishFolderDialog(Activity.RESULT_CANCELED, null); } }); + } + + private boolean requiresManageStoragePermission() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; + } + + private boolean hasManageStoragePermission() { + return !requiresManageStoragePermission() || Environment.isExternalStorageManager(); + } + + private boolean requestManageStoragePermission() { + if (!requiresManageStoragePermission()) { + return true; + } + + awaitingManageStoragePermission = true; + runOnUiThread(() -> { + if (tryStartManageStorageIntent( + new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + .setData(Uri.parse("package:" + getPackageName()))) || + tryStartManageStorageIntent( + new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))) + { + return; + } + + finishFolderDialogWithError("Unable to request Android file access permission"); + }); return true; } + private boolean tryStartManageStorageIntent(Intent intent) { + try { + startActivityForResult(intent, MANAGE_STORAGE_REQUEST_CODE); + return true; + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Unable to open all-files access settings.", e); + return false; + } + } + + private void resumeFolderDialogAfterPermissionGrant() { + awaitingManageStoragePermission = false; + if (folderDialogUserdata == 0) { + return; + } + + if (hasManageStoragePermission()) { + openFolderDialog(); + return; + } + + finishFolderDialogWithError( + "Allow \"All files access\" for Dusklight before choosing a custom data folder"); + } + + private void finishFolderDialogWithError(String error) { + long userdata = folderDialogUserdata; + folderDialogUserdata = 0; + awaitingManageStoragePermission = false; + if (userdata != 0) { + nativeFolderDialogResult(userdata, null, error); + } + } + private void finishFolderDialog(int resultCode, Intent data) { long userdata = folderDialogUserdata; folderDialogUserdata = 0; diff --git a/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java index 42f5a911f7..5c54cde863 100644 --- a/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/platforms/android/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -61,7 +61,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh private static final String TAG = "SDL"; private static final int SDL_MAJOR_VERSION = 3; private static final int SDL_MINOR_VERSION = 4; - private static final int SDL_MICRO_VERSION = 4; + private static final int SDL_MICRO_VERSION = 8; /* // Display InputType.SOURCE/CLASS of events and devices // @@ -570,7 +570,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getNaturalOrientation() { int result = SDL_ORIENTATION_UNKNOWN; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Configuration config = activity.getResources().getConfiguration(); Display display = activity.getWindowManager().getDefaultDisplay(); @@ -590,7 +590,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static int getCurrentRotation() { int result = 0; - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity != null) { Display display = activity.getWindowManager().getDefaultDisplay(); switch (display.getRotation()) { @@ -1292,7 +1292,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh public static double getDiagonal() { DisplayMetrics metrics = new DisplayMetrics(); - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity == null) { return 0.0; } @@ -1940,7 +1940,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return; } - Activity activity = (Activity)getContext(); + Activity activity = getContext(); if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{permission}, requestCode); } else { diff --git a/platforms/freedesktop/1024x1024/apps/dusklight.png b/platforms/freedesktop/1024x1024/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/1024x1024/apps/dusklight.png rename to platforms/freedesktop/1024x1024/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/128x128/apps/dusklight.png b/platforms/freedesktop/128x128/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/128x128/apps/dusklight.png rename to platforms/freedesktop/128x128/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/16x16/apps/dusklight.png b/platforms/freedesktop/16x16/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/16x16/apps/dusklight.png rename to platforms/freedesktop/16x16/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/256x256/apps/dusklight.png b/platforms/freedesktop/256x256/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/256x256/apps/dusklight.png rename to platforms/freedesktop/256x256/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/32x32/apps/dusklight.png b/platforms/freedesktop/32x32/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/32x32/apps/dusklight.png rename to platforms/freedesktop/32x32/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/48x48/apps/dusklight.png b/platforms/freedesktop/48x48/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/48x48/apps/dusklight.png rename to platforms/freedesktop/48x48/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/512x512/apps/dusklight.png b/platforms/freedesktop/512x512/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/512x512/apps/dusklight.png rename to platforms/freedesktop/512x512/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/64x64/apps/dusklight.png b/platforms/freedesktop/64x64/apps/dev.twilitrealm.dusk.png similarity index 100% rename from platforms/freedesktop/64x64/apps/dusklight.png rename to platforms/freedesktop/64x64/apps/dev.twilitrealm.dusk.png diff --git a/platforms/freedesktop/dusklight.desktop b/platforms/freedesktop/dev.twilitrealm.dusk.desktop similarity index 86% rename from platforms/freedesktop/dusklight.desktop rename to platforms/freedesktop/dev.twilitrealm.dusk.desktop index 4f6ba034a9..b1ad2831a2 100644 --- a/platforms/freedesktop/dusklight.desktop +++ b/platforms/freedesktop/dev.twilitrealm.dusk.desktop @@ -3,7 +3,7 @@ Name=Dusklight GenericName=Dusklight Comment=PC port of a classic adventure game Exec=dusklight -Icon=dusklight +Icon=dev.twilitrealm.dusk Terminal=false Type=Application Categories=Game; diff --git a/res/rml/prelaunch.rcss b/res/rml/prelaunch.rcss index f5d7240d56..73efc18086 100644 --- a/res/rml/prelaunch.rcss +++ b/res/rml/prelaunch.rcss @@ -447,6 +447,7 @@ body.animate-in .intro-item { top: auto; text-align: right; font-size: 16dp; + gap: 8dp; } #disc-status { @@ -468,6 +469,7 @@ body.animate-in .intro-item { top: 32dp; text-align: right; font-size: 16dp; + gap: 8dp; } .update { diff --git a/src/Z2AudioLib/Z2SoundMgr.cpp b/src/Z2AudioLib/Z2SoundMgr.cpp index b861475118..6978a61585 100644 --- a/src/Z2AudioLib/Z2SoundMgr.cpp +++ b/src/Z2AudioLib/Z2SoundMgr.cpp @@ -183,7 +183,7 @@ bool Z2SoundMgr::startSound(JAISoundID soundID, JAISoundHandle* handle, const JG return streamMgr_.startSound(soundID, handle, posPtr); default: char error[64]; - sprintf(error, "Unknown Sound-Type id :%08x\n", (u32)soundID); + SAFE_SPRINTF(error, "Unknown Sound-Type id :%08x\n", (u32)soundID); JUT_WARN(277, "%s", error); } diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index e2fe7a890c..9bec9da6d0 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -57,6 +57,7 @@ #include "dusk/settings.h" #include "res/Object/Alink.h" #include +#include #endif static int daAlink_Create(fopAc_ac_c* i_this); diff --git a/src/d/actor/d_a_alink_cut.inc b/src/d/actor/d_a_alink_cut.inc index f33d2935c5..ba3c0ba89e 100644 --- a/src/d/actor/d_a_alink_cut.inc +++ b/src/d/actor/d_a_alink_cut.inc @@ -1165,6 +1165,10 @@ int daAlink_c::procCutFinishInit(int i_type) { const daAlink_cutParamTbl* cutParams = &cutParamTable[i_type]; BOOL is_proc_frontRoll = mProcID == PROC_FRONT_ROLL; +#if TARGET_PC + mIsRollstab = (is_proc_frontRoll != FALSE) && (i_type == CUT_FINISH_PARAM_STAB); +#endif + commonProcInit(PROC_CUT_FINISH); setCutType(cutParams->m_cutType); field_0x3198 = cutParams->m_recoilAnmID; diff --git a/src/d/actor/d_a_alink_demo.inc b/src/d/actor/d_a_alink_demo.inc index 55e261cc13..b1eb71a85d 100644 --- a/src/d/actor/d_a_alink_demo.inc +++ b/src/d/actor/d_a_alink_demo.inc @@ -4359,7 +4359,7 @@ void daAlink_c::dungeonReturnWarp() { s16 angle = dComIfGs_getWarpPlayerAngleY(); s8 room = dComIfGs_getWarpRoomNo(); char name[8]; - strcpy(name, dComIfGs_getWarpStageName()); + SAFE_STRCPY(name, dComIfGs_getWarpStageName()); dComIfGs_setRestartRoom(pos, angle, room); dComIfGp_setNextStage(name, -1, dComIfGs_getRestartRoomNo(), -1, 0.0f, 12, 0, @@ -4535,7 +4535,7 @@ int daAlink_c::procCoWarpInit(int param_0, int param_1) { if (checkBossRoom() && fopAcM_GetRoomNo(this) == 50) { char stageName[32]; - strcpy(stageName, dComIfGp_getStartStageName()); + SAFE_STRCPY(stageName, dComIfGp_getStartStageName()); for (int i = 0; i < 32; i++) { if ((s64)stageName[i] == 0) { diff --git a/src/d/actor/d_a_alink_swim.inc b/src/d/actor/d_a_alink_swim.inc index 038d710e80..19131e404a 100644 --- a/src/d/actor/d_a_alink_swim.inc +++ b/src/d/actor/d_a_alink_swim.inc @@ -216,7 +216,7 @@ void daAlink_c::setSpeedAndAngleSwim() { if (checkEventRun()) { var_r28 = mMoveAngle; } else { - var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle)); + var_r28 = shape_angle.y + (16384.0f * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimX ? -1.0f : 1.0f))); } cLib_addCalcAngleS(&shape_angle.y, var_r28, mpHIO->mSwim.m.mUnderwaterTurnRate, mpHIO->mSwim.m.mUnderwaterMaxTurn, mpHIO->mSwim.m.mUnderwaterMinTurn); @@ -835,7 +835,7 @@ void daAlink_c::setSwimMoveAnime() { } else { s16 var_r24; if (checkInputOnR() && !checkEventRun()) { - var_r24 = 13653.0f * cM_scos(mStickAngle); + var_r24 = 13653.0f * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertAirSwimY ? -1.0f : 1.0f)); } else { var_r24 = 0; } diff --git a/src/d/actor/d_a_alink_wolf.inc b/src/d/actor/d_a_alink_wolf.inc index 67fde3a7a7..43b60e9d42 100644 --- a/src/d/actor/d_a_alink_wolf.inc +++ b/src/d/actor/d_a_alink_wolf.inc @@ -4005,7 +4005,13 @@ int daAlink_c::procWolfHowlDemoInit() { } else if (name == fpcNm_Tag_WaraHowl_e) { mZ2WolfHowlMgr.setCorrectCurve(static_cast(field_0x27f4)->getTuneId()); } else { + #if TARGET_PC + if (mZ2WolfHowlMgr.getCorrectCurveID() != 9) { + mZ2WolfHowlMgr.setCorrectCurve(-1); + } + #else mZ2WolfHowlMgr.setCorrectCurve(-1); + #endif } } else { #if TARGET_PC diff --git a/src/d/actor/d_a_b_gnd.cpp b/src/d/actor/d_a_b_gnd.cpp index d96e6314af..0f982e59e8 100644 --- a/src/d/actor/d_a_b_gnd.cpp +++ b/src/d/actor/d_a_b_gnd.cpp @@ -2192,6 +2192,9 @@ static void damage_check(b_gnd_class* i_this) { i_this->mDamageInvulnerabilityTimer = 100; } } + #if TARGET_PC + dusk::AchievementSystem::get().signal("ganondorf_hit"); + #endif } cXyz hitmark_size(1.0f, 1.0f, 1.0f); @@ -2218,6 +2221,7 @@ static void damage_check(b_gnd_class* i_this) { i_this->field_0xc7c = 0; dScnPly_c::setPauseTimer(7); a_this->health = 100; + dusk::AchievementSystem::get().signal("ganondorf_knocked_down"); } break; } diff --git a/src/d/actor/d_a_bg_obj.cpp b/src/d/actor/d_a_bg_obj.cpp index fb07a6de9c..facffee251 100644 --- a/src/d/actor/d_a_bg_obj.cpp +++ b/src/d/actor/d_a_bg_obj.cpp @@ -5,24 +5,25 @@ #include "d/dolzel_rel.h" // IWYU pragma: keep -#include "d/actor/d_a_bg_obj.h" -#include "JSystem/J3DGraphBase/J3DMaterial.h" #include -#include #include +#include +#include "JSystem/J3DGraphBase/J3DMaterial.h" +#include "SSystem/SComponent/c_math.h" +#include "d/actor/d_a_bg_obj.h" #include "d/actor/d_a_set_bgobj.h" #include "d/d_s_play.h" -#include "SSystem/SComponent/c_math.h" +#include "dusk/string.hpp" static const char* getBmdName(int param_0, int param_1) { static char l_bmdName[16]; switch (param_1) { case 0: - sprintf(l_bmdName, "model%d.bmd", param_0); + SAFE_SPRINTF(l_bmdName, "model%d.bmd", param_0); break; default: - sprintf(l_bmdName, "model%d_%d.bmd", param_0, param_1); + SAFE_SPRINTF(l_bmdName, "model%d_%d.bmd", param_0, param_1); break; } @@ -34,10 +35,10 @@ static const char* getBtkName(int param_0, int param_1) { switch (param_1) { case 0: - sprintf(l_btkName, "model%d.btk", param_0); + SAFE_SPRINTF(l_btkName, "model%d.btk", param_0); break; default: - sprintf(l_btkName, "model%d_%d.btk", param_0, param_1); + SAFE_SPRINTF(l_btkName, "model%d_%d.btk", param_0, param_1); break; } @@ -49,10 +50,10 @@ static const char* getBrkName(int param_0, int param_1) { switch (param_1) { case 0: - sprintf(l_brkName, "model%d.brk", param_0); + SAFE_SPRINTF(l_brkName, "model%d.brk", param_0); break; default: - sprintf(l_brkName, "model%d_%d.brk", param_0, param_1); + SAFE_SPRINTF(l_brkName, "model%d_%d.brk", param_0, param_1); break; } @@ -62,7 +63,7 @@ static const char* getBrkName(int param_0, int param_1) { static const char* getDzbName(int param_0) { static char l_dzbName[16]; - sprintf(l_dzbName, "model%d.dzb", param_0); + SAFE_SPRINTF(l_dzbName, "model%d.dzb", param_0); return l_dzbName; } @@ -95,12 +96,12 @@ u8* daBgObj_c::spec_data_c::initTexShareBlock(u8* i_dataPtr) { u8* dataPos = i_dataPtr + 8; for (; i < mTexShareNum; i++) { - strcpy(sp48, (char*)dataPos); + SAFE_STRCPY(sp48, (char*)dataPos); int len = strlen((char*)dataPos); dataPos += len + 1; if (*dataPos != 0) { - strcpy(sp8, (char*)dataPos); + SAFE_STRCPY(sp8, (char*)dataPos); dataPos += strlen((char*)dataPos) + 1; } else if (*dataPos == 0 && dataPos[1] == 1) { dataPos += 2; @@ -658,14 +659,14 @@ void daBgObj_c::doShareTexture() { u8* spec_res_name = mSpecData.mpTexShareBlock + 8; for (int i = 0; i < mSpecData.mTexShareNum; i++) { - strcpy(res_name, (char*)spec_res_name); + SAFE_STRCPY(res_name, (char*)spec_res_name); spec_res_name += strlen((char*)spec_res_name) + 1; J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes(daSetBgObj_c::getArcName(this), res_name); if (*spec_res_name != 0) { - strcpy(share_res_name, (char*)spec_res_name); + SAFE_STRCPY(share_res_name, (char*)spec_res_name); spec_res_name += strlen((char*)spec_res_name) + 1; J3DModelData* shareModelData = diff --git a/src/d/actor/d_a_door_dbdoor00.cpp b/src/d/actor/d_a_door_dbdoor00.cpp index c659a47cb2..edac3c14f8 100644 --- a/src/d/actor/d_a_door_dbdoor00.cpp +++ b/src/d/actor/d_a_door_dbdoor00.cpp @@ -51,7 +51,7 @@ static char* l_bmd_base_name = "door-pushDouble_"; char* daDbDoor00_c::getBmdName() { static char l_bmdName[32]; - sprintf(l_bmdName, "%s%02d.bmd", l_bmd_base_name, door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(l_bmdName, "%s%02d.bmd", l_bmd_base_name, door_param2_c::getDoorModel(this)); return l_bmdName; } diff --git a/src/d/actor/d_a_door_knob00.cpp b/src/d/actor/d_a_door_knob00.cpp index 50d5723261..ea4a9c8679 100644 --- a/src/d/actor/d_a_door_knob00.cpp +++ b/src/d/actor/d_a_door_knob00.cpp @@ -55,7 +55,7 @@ static char* l_bmd_base_name = "door-knob_"; char* daKnob20_c::getBmd() { static char l_bmdName[32]; - sprintf(l_bmdName, "%s%02d.bmd", l_bmd_base_name, knob_param_c::getDoorModel(this)); + SAFE_SPRINTF(l_bmdName, "%s%02d.bmd", l_bmd_base_name, knob_param_c::getDoorModel(this)); return l_bmdName; } diff --git a/src/d/actor/d_a_door_shutter.cpp b/src/d/actor/d_a_door_shutter.cpp index ebd1c9a185..ba825fe4bd 100644 --- a/src/d/actor/d_a_door_shutter.cpp +++ b/src/d/actor/d_a_door_shutter.cpp @@ -65,10 +65,10 @@ char* daDoor20_c::getBmdName() { case 10: case 12: default: - sprintf(bmdName, "door-shutter_%02d.bmd", door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(bmdName, "door-shutter_%02d.bmd", door_param2_c::getDoorModel(this)); break; case 9: - sprintf(bmdName, "door-knob_%02d.bmd", door_param2_c::getDoorModel(this)); + SAFE_SPRINTF(bmdName, "door-knob_%02d.bmd", door_param2_c::getDoorModel(this)); break; } return bmdName; diff --git a/src/d/actor/d_a_door_spiral.cpp b/src/d/actor/d_a_door_spiral.cpp index b7c7567787..d0c34c6dbd 100644 --- a/src/d/actor/d_a_door_spiral.cpp +++ b/src/d/actor/d_a_door_spiral.cpp @@ -94,9 +94,9 @@ const char* daSpiral_c::getBmd(int i_type) { const char* daSpiral_c::getBmd2(int i_type) { static char bmdName[32]; if (i_type == daSpiral_TYPE_DOWN_e) { - sprintf(bmdName, "door-stairSpiralU.bmd"); + SAFE_SPRINTF(bmdName, "door-stairSpiralU.bmd"); } else { - sprintf(bmdName, "door-stairSpiralD.bmd"); + SAFE_SPRINTF(bmdName, "door-stairSpiralD.bmd"); } return bmdName; diff --git a/src/d/actor/d_a_kago.cpp b/src/d/actor/d_a_kago.cpp index 9444237f12..efdad44516 100644 --- a/src/d/actor/d_a_kago.cpp +++ b/src/d/actor/d_a_kago.cpp @@ -3530,6 +3530,15 @@ void daKago_c::action() { #endif mStickY = mDoCPd_c::getStickY(PAD_1); +#ifdef TARGET_PC + if(dusk::getSettings().game.invertAirSwimX) { + mStickX = -mStickX; + } + if(dusk::getSettings().game.invertAirSwimY) { + mStickY = -mStickY; + } +#endif + u8 prevIsWaterfall = mIsWaterfall; mIsWaterfall = FALSE; fpcM_Search(s_waterfall, this); diff --git a/src/d/actor/d_a_kytag04.cpp b/src/d/actor/d_a_kytag04.cpp index 4afb24d4d2..b496d48c54 100644 --- a/src/d/actor/d_a_kytag04.cpp +++ b/src/d/actor/d_a_kytag04.cpp @@ -10,6 +10,7 @@ #include "d/actor/d_a_player.h" #include "d/d_com_inf_game.h" #include +#include static int daKytag04_Draw(kytag04_class* i_this) { dScnKy_env_light_c* kankyo = dKy_getEnvlight(); @@ -260,8 +261,10 @@ static int daKytag04_Create(fopAc_ac_c* i_this) { a_this->mNeedDropNum = i_this->current.angle.z & 0xFF; - #if DUSK_TPHD - a_this->mNeedDropNum = 12; + #if TARGET_PC + if (dusk::tphd_active()) { + a_this->mNeedDropNum = 12; + } #endif int phase_state = dComIfG_resLoad(&a_this->mPhase, "Kytag04"); @@ -270,8 +273,11 @@ static int daKytag04_Create(fopAc_ac_c* i_this) { a_this->mStageNo = (i_this->current.angle.z >> 8) & 0xFF; a_this->mExitID = fopAcM_GetParam(i_this) & 0xFF; a_this->mNeedDropNum = i_this->current.angle.z & 0xFF; - #if DUSK_TPHD - a_this->mNeedDropNum = 12; + + #if TARGET_PC + if (dusk::tphd_active()) { + a_this->mNeedDropNum = 12; + } #endif a_this->field_0x5b5 = fopAcM_GetParam(i_this) >> 0x10; diff --git a/src/d/actor/d_a_mg_rod.cpp b/src/d/actor/d_a_mg_rod.cpp index 956a9e3cff..3471cd342a 100644 --- a/src/d/actor/d_a_mg_rod.cpp +++ b/src/d/actor/d_a_mg_rod.cpp @@ -25,7 +25,10 @@ #include #include +#if TARGET_PC +#include "dusk/settings.h" #include "dusk/version.hpp" +#endif class dmg_rod_HIO_c : public JORReflexible { public: @@ -1137,8 +1140,14 @@ static int lure_standby(dmg_rod_class* i_this) { dComIfGp_setDoStatusForce(42, 0); } - i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1) * mDoCPd_c::getStickX3D(PAD_1); - if (mDoCPd_c::getStickX3D(PAD_1) < 0.0f) { + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + i_this->rod_stick_x = stick_x * stick_x; + if (stick_x < 0.0f) { i_this->rod_stick_x *= -1.0f; } @@ -3671,7 +3680,13 @@ static void uki_standby(dmg_rod_class* i_this) { cLib_addCalc2(&i_this->field_0x150c, substickX, 0.5f, 0.2f); if (i_this->field_0x1508 > 0.3f && i_this->play_cam_mode < 5) { - ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * mDoCPd_c::getStickX3D(PAD_1)); + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + ANGLE_ADD(i_this->field_0x1418, (-500.0f + VREG_F(3)) * stick_x); } cMtx_YrotS(*calc_mtx, i_this->field_0x1418); @@ -5043,8 +5058,15 @@ static void play_camera(dmg_rod_class* i_this) { static f32 old_stick_x = 0.0f; static f32 old_stick_sx = 0.0f; + f32 stick_x = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + stick_x = -stick_x; + } +#endif + if ( - (mDoCPd_c::getStickX3D(PAD_1) >= 0.8f && old_stick_x < 0.8f) || (mDoCPd_c::getStickX3D(PAD_1) <= -0.8f && old_stick_x > -0.8f) + (stick_x >= 0.8f && old_stick_x < 0.8f) || (stick_x <= -0.8f && old_stick_x > -0.8f) #if VERSION != VERSION_SHIELD_DEBUG || (mDoCPd_c::getSubStickX3D(PAD_1) >= 0.8f && old_stick_sx < 0.8f) || (mDoCPd_c::getSubStickX3D(PAD_1) <= -0.8f && old_stick_sx > -0.8f) #endif @@ -5060,7 +5082,7 @@ static void play_camera(dmg_rod_class* i_this) { } if (i_this->play_cam_timer >= 15) { - if (mDoCPd_c::getStickX3D(PAD_1) >= 0.5f + if (stick_x >= 0.5f #if VERSION != VERSION_SHIELD_DEBUG || mDoCPd_c::getSubStickX3D(PAD_1) >= 0.5f #endif @@ -5082,8 +5104,8 @@ static void play_camera(dmg_rod_class* i_this) { } } - old_stick_x = mDoCPd_c::getStickX3D(PAD_1); - old_stick_sx = mDoCPd_c::getSubStickX(PAD_1); + old_stick_x = stick_x; + old_stick_sx = mDoCPd_c::getSubStickX3D(PAD_1); if (i_this->play_cam_timer == 1) { if (i_this->field_0xf81 == 0) { @@ -5788,7 +5810,14 @@ static int dmg_rod_Execute(dmg_rod_class* i_this) { i_this->rod_stick_x = mDoCPd_c::getStickX3D(PAD_1); i_this->rod_stick_y = mDoCPd_c::getStickY(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + i_this->rod_stick_x = -i_this->rod_stick_x; + } + i_this->rod_substick_x = mDoCPd_c::getSubStickX3D(PAD_1); +#else i_this->rod_substick_x = mDoCPd_c::getSubStickX(PAD_1); +#endif i_this->prev_rod_substick_y = i_this->rod_substick_y; i_this->rod_substick_y = mDoCPd_c::getSubStickY(PAD_1); diff --git a/src/d/actor/d_a_movie_player.cpp b/src/d/actor/d_a_movie_player.cpp index 1f8bdeecd7..1760fa713b 100644 --- a/src/d/actor/d_a_movie_player.cpp +++ b/src/d/actor/d_a_movie_player.cpp @@ -26,15 +26,16 @@ #include "f_op/f_op_overlap_mng.h" +#include "JSystem/JAudio2/JASCriticalSection.h" + +#if TARGET_PC #include "dusk/gx_helper.h" #include "dusk/os.h" #include "dusk/layout.hpp" - -#include "JSystem/JAudio2/JASCriticalSection.h" - #if MOVIE_SUPPORT #include "turbojpeg.h" #endif +#endif inline s32 daMP_NEXT_READ_SIZE(daMP_THPReadBuffer* readBuf) { return *(BE(s32)*)readBuf->ptr; @@ -3912,13 +3913,20 @@ static BOOL daMP_ProperTimingForGettingNextFrame() { return TRUE; } } else { - s32 frameRate = daMP_ActivePlayer.header.frameRate * 100.0f; #if TARGET_PC - // DUSK HACK: We only fire retrace callbacks *half* as often as the game expects, - // because we only run them once per frame, and normally there should be two scans - // per game frame. - frameRate *= 2; -#endif + const f32 fps = daMP_ActivePlayer.header.frameRate; + if (fps > 0.0f) { + const f32 elapsed = std::chrono::duration( + std::chrono::steady_clock::now() - daMP_ActivePlayer.thpPlaybackClock).count(); + const s32 desired = static_cast(elapsed * fps); + if (desired != daMP_ActivePlayer.prevCount) { + daMP_ActivePlayer.prevCount = desired; + daMP_ActivePlayer.curCount = desired; + return TRUE; + } + } +#else + s32 frameRate = daMP_ActivePlayer.header.frameRate * 100.0f; if (VIGetTvFormat() == VI_PAL) { daMP_ActivePlayer.curCount = daMP_ActivePlayer.retaceCount * frameRate / 5000; } else { @@ -3929,6 +3937,7 @@ static BOOL daMP_ProperTimingForGettingNextFrame() { daMP_ActivePlayer.prevCount = daMP_ActivePlayer.curCount; return TRUE; } +#endif } return FALSE; @@ -4133,6 +4142,9 @@ static BOOL daMP_THPPlayerPlay() { daMP_ActivePlayer.prevCount = 0; daMP_ActivePlayer.curCount = 0; daMP_ActivePlayer.retaceCount = -1; +#if TARGET_PC + daMP_ActivePlayer.thpPlaybackClock = std::chrono::steady_clock::now(); +#endif return TRUE; } @@ -4482,7 +4494,7 @@ int daMP_c::daMP_c_Init() { JUT_ASSERT(9507, 0 <= movieNo && movieNo <= 99); char path[32]; - sprintf(path, "/Movie/demo_movie%02d_%02d.thp", demoNo, movieNo); + SAFE_SPRINTF(path, "/Movie/demo_movie%02d_%02d.thp", demoNo, movieNo); if (!daMP_ActivePlayer_Init(path)) { daMP_Fail_alloc = TRUE; diff --git a/src/d/actor/d_a_ni.cpp b/src/d/actor/d_a_ni.cpp index 8d97925078..b569b28e04 100644 --- a/src/d/actor/d_a_ni.cpp +++ b/src/d/actor/d_a_ni.cpp @@ -943,6 +943,11 @@ static int ni_play(ni_class* i_this) { s16 var_r28 = 0x4000; i_this->mPadMainStickX = mDoCPd_c::getStickX3D(PAD_1); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + i_this->mPadMainStickX = -i_this->mPadMainStickX; + } +#endif i_this->mPadMainStickY = mDoCPd_c::getStickY(PAD_1); i_this->mPadSubStickY = mDoCPd_c::getSubStickY(PAD_1); i_this->mPadSubStickX = mDoCPd_c::getSubStickX(PAD_1); diff --git a/src/d/actor/d_a_npc_hanjo.cpp b/src/d/actor/d_a_npc_hanjo.cpp index ef5d1671d0..175bee3d1e 100644 --- a/src/d/actor/d_a_npc_hanjo.cpp +++ b/src/d/actor/d_a_npc_hanjo.cpp @@ -15,6 +15,8 @@ #include "Z2AudioLib/Z2Instances.h" #include +#include "dusk/string.hpp" + static int l_bmdData[4][2] = { {14, 1}, {26, 2}, {25, 2}, {3, 4}, @@ -1336,8 +1338,8 @@ int daNpc_Hanjo_c::cutAppearHawker(int param_1) { home.angle.y += 0x8000; setAngle(home.angle.y); initTalk(0xcf, NULL); - strcpy(acStack_98, l_evtList[9].eventName); - strcat(acStack_98, "@"); + SAFE_STRCPY(acStack_98, l_evtList[9].eventName); + SAFE_STRCAT(acStack_98, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_98); dComIfGp_getEvent()->onSkipFade(); dComIfGp_getVibration().StartShock( 9, 15, cXyz(0.0f, 1.0f, 0.0f)); diff --git a/src/d/actor/d_a_npc_saru.cpp b/src/d/actor/d_a_npc_saru.cpp index 500daea704..366ed865a8 100644 --- a/src/d/actor/d_a_npc_saru.cpp +++ b/src/d/actor/d_a_npc_saru.cpp @@ -11,6 +11,8 @@ #include "d/actor/d_a_e_ym.h" #include +#include "dusk/string.hpp" + enum saru_TW_RES_File_ID { /* BMDR */ /* 0x4 */ BMDR_SARU_BARA_TW = 0x4, @@ -956,8 +958,8 @@ int daNpc_Saru_c::cutYmLook(int param_1) { if (dComIfGp_getEventManager().getIsAddvance(param_1) != 0) { switch (iVar1) { case 0: - strcpy(acStack_88, l_evtList[2].eventName); - strcat(acStack_88, "@"); + SAFE_STRCPY(acStack_88, l_evtList[2].eventName); + SAFE_STRCAT(acStack_88, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_88); dComIfGp_getEvent()->onSkipFade(); field_0xfd9 = 1; diff --git a/src/d/actor/d_a_npc_shop0.cpp b/src/d/actor/d_a_npc_shop0.cpp index 1dd4b3e31f..6743a1b859 100644 --- a/src/d/actor/d_a_npc_shop0.cpp +++ b/src/d/actor/d_a_npc_shop0.cpp @@ -8,6 +8,8 @@ #include "d/actor/d_a_npc_shop0.h" #include +#include "dusk/string.hpp" + static int createHeapCallBack(fopAc_ac_c* i_this) { return static_cast(i_this)->createHeap(); } @@ -184,8 +186,8 @@ int daNpc_Shop0_c::init() { static char l_fileName[21]; J3DAnmTransform* daNpc_Shop0_c::getTrnsfrmAnmP(int i_fileIndex, char** i_fileName) { - strcpy(l_fileName, i_fileName[i_fileIndex]); - strcat(l_fileName, ".bck"); + SAFE_STRCPY(l_fileName, i_fileName[i_fileIndex]); + SAFE_STRCAT(l_fileName, ".bck"); return (J3DAnmTransform*) dComIfG_getObjectRes(getResName(), l_fileName); } diff --git a/src/d/actor/d_a_npc_yelia.cpp b/src/d/actor/d_a_npc_yelia.cpp index 67b1cbcffc..80d6df02a6 100644 --- a/src/d/actor/d_a_npc_yelia.cpp +++ b/src/d/actor/d_a_npc_yelia.cpp @@ -9,6 +9,8 @@ #include "d/actor/d_a_demo_item.h" #include +#include "dusk/string.hpp" + static daNpc_GetParam1 l_bmdData[3] = { {3, 1}, {3, 4}, @@ -1137,8 +1139,8 @@ BOOL daNpc_Yelia_c::cutTakeWoodStatue(int i_staffId) { mItemId = fpcM_ERROR_PROCESS_ID_e; mEventTimer = timer; Z2GetAudioMgr()->muteSceneBgm(90, 0.0f); - strcpy(name, l_evtList[EVENT_TAKE_WOODSTATUE].eventName); - strcat(name, "@"); + SAFE_STRCPY(name, l_evtList[EVENT_TAKE_WOODSTATUE].eventName); + SAFE_STRCAT(name, "@"); dComIfGp_getEvent()->setSkipZev(this, name); dComIfGp_getEvent()->onSkipFade(); break; diff --git a/src/d/actor/d_a_npc_ykw.cpp b/src/d/actor/d_a_npc_ykw.cpp index e2c97df58a..468445063a 100644 --- a/src/d/actor/d_a_npc_ykw.cpp +++ b/src/d/actor/d_a_npc_ykw.cpp @@ -20,6 +20,8 @@ #include "m_Do/m_Do_ext.h" #include +#include "dusk/string.hpp" + #if DEBUG class daNpc_ykW_HIO_c : public mDoHIO_entry_c { public: @@ -1411,11 +1413,11 @@ int daNpc_ykW_c::cutGoIntoBossRoom(int param_0) { if (skip != 0 && (prm == 0 || prm == 4)) { if (prm == 0) { - strcpy(unkStrBuf1, l_evtList[4].eventName); + SAFE_STRCPY(unkStrBuf1, l_evtList[4].eventName); } else { - strcpy(unkStrBuf1, l_evtList[5].eventName); + SAFE_STRCPY(unkStrBuf1, l_evtList[5].eventName); } - strcat(unkStrBuf1, "@"); + SAFE_STRCAT(unkStrBuf1, "@"); dComIfGp_getEvent()->setSkipZev(this, unkStrBuf1); } } diff --git a/src/d/actor/d_a_obj_drop.cpp b/src/d/actor/d_a_obj_drop.cpp index e1c5dcc583..e49d0790d6 100644 --- a/src/d/actor/d_a_obj_drop.cpp +++ b/src/d/actor/d_a_obj_drop.cpp @@ -26,6 +26,8 @@ daObjDrop_HIO_c l_HIO; #endif +#include + static void* searchParentSub(void* pproc, void* pdata) { daObjDrop_c* pdrop = (daObjDrop_c*)pdata; fopAc_ac_c* pym = (fopAc_ac_c*)pproc; @@ -103,8 +105,12 @@ void daObjDrop_c::dropGet() { dComIfGs_setLightDropNum(dComIfGp_getStartStageDarkArea(), num + 1); if (dComIfGp_getStartStageDarkArea() == 2 && - #if DUSK_TPHD - dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 11) + #if TARGET_PC + ((dusk::tphd_active() && + dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 11) || + !dusk::tphd_active() && + dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 15 + )) #else dComIfGs_getLightDropNum(dComIfGp_getStartStageDarkArea()) == 15) #endif diff --git a/src/d/actor/d_a_obj_flag.cpp b/src/d/actor/d_a_obj_flag.cpp index b3f55ff1d2..dcf0640e6b 100644 --- a/src/d/actor/d_a_obj_flag.cpp +++ b/src/d/actor/d_a_obj_flag.cpp @@ -192,7 +192,7 @@ inline int daObjFlag_c::createHeap() { tmp = true; char resName[12]; - sprintf(resName, "flag%02d.bmd", angle); + SAFE_SPRINTF(resName, "flag%02d.bmd", angle); shape_angle.setall(0); current.angle.setall(0); diff --git a/src/d/actor/d_a_obj_flag2.cpp b/src/d/actor/d_a_obj_flag2.cpp index 4741f4d5a3..35e0cac3bc 100644 --- a/src/d/actor/d_a_obj_flag2.cpp +++ b/src/d/actor/d_a_obj_flag2.cpp @@ -370,7 +370,7 @@ int daObjFlag2_c::createHeap() { s8 flagNum = (u8)shape_angle.x; if (mFlagValid) { char acStack_40[16]; - sprintf(acStack_40, "flag%02d.bti", flagNum); + SAFE_SPRINTF(acStack_40, "flag%02d.bti", flagNum); shape_angle.setall(0); current.angle.setall(0); ResTIMG* image = (ResTIMG*)dComIfG_getObjectRes(mFlagName, "flag.bti"); @@ -409,7 +409,7 @@ int daObjFlag2_c::create() { mFlagValid = false; } else { mFlagValid = true; - sprintf(mFlagName, "FlagObj%02d", flagNum); + SAFE_SPRINTF(mFlagName, "FlagObj%02d", flagNum); int rv = dComIfG_resLoad(&mFlagPhase, mFlagName); if (rv != cPhs_COMPLEATE_e) { return rv; diff --git a/src/d/actor/d_a_obj_flag3.cpp b/src/d/actor/d_a_obj_flag3.cpp index fc696db1ab..501cb792f6 100644 --- a/src/d/actor/d_a_obj_flag3.cpp +++ b/src/d/actor/d_a_obj_flag3.cpp @@ -299,7 +299,7 @@ int daObjFlag3_c::createHeap() { s8 flagNum = (u8)shape_angle.x; if (mFlagValid) { char acStack_40[16]; - sprintf(acStack_40, "flag%02d.bti", flagNum); + SAFE_SPRINTF(acStack_40, "flag%02d.bti", flagNum); shape_angle.setall(0); current.angle.setall(0); ResTIMG* image = (ResTIMG*)dComIfG_getObjectRes(mFlagName, "flag.bti"); @@ -486,7 +486,7 @@ int daObjFlag3_c::create() { mFlagValid = false; } else { mFlagValid = true; - sprintf(mFlagName, "FlagObj%02d", flagNum); + SAFE_SPRINTF(mFlagName, "FlagObj%02d", flagNum); int rv = dComIfG_resLoad(&mFlagPhase, mFlagName); if (rv != cPhs_COMPLEATE_e) { return rv; diff --git a/src/d/actor/d_a_obj_gra2.cpp b/src/d/actor/d_a_obj_gra2.cpp index 550b680e06..ff52818c42 100644 --- a/src/d/actor/d_a_obj_gra2.cpp +++ b/src/d/actor/d_a_obj_gra2.cpp @@ -5,13 +5,14 @@ #include "d/dolzel_rel.h" // IWYU pragma: keep -#include "d/actor/d_a_obj_gra2.h" #include "d/actor/d_a_npc4.h" +#include "d/actor/d_a_obj_gra2.h" #include "d/actor/d_a_tag_gra.h" #include "d/d_bg_w.h" #include "d/d_cc_uty.h" -#include "d/d_com_inf_game.h" #include "d/d_com_inf_actor.h" +#include "d/d_com_inf_game.h" +#include "dusk/string.hpp" #if DEBUG #include "d/d_debug_viewer.h" #endif @@ -539,7 +540,7 @@ const char* daObj_GrA_c::getResName() { u8 daObj_GrA_c::getMode() { u32 uVar1 = fopAcM_GetParam(this) >> 28 & 3; - strcpy(field_0x744, "Obj_grA"); + SAFE_STRCPY(field_0x744, "Obj_grA"); switch (uVar1) { case 1: diff --git a/src/d/actor/d_a_obj_item.cpp b/src/d/actor/d_a_obj_item.cpp index e45de1933d..2c95c040be 100644 --- a/src/d/actor/d_a_obj_item.cpp +++ b/src/d/actor/d_a_obj_item.cpp @@ -268,6 +268,11 @@ int daItem_c::_daItem_create() { } m_itemNo = daItem_prm::getItemNo(this); +#if TARGET_PC + if (dusk::getSettings().game.noHeartDrops && isHeart(m_itemNo)) { + return cPhs_ERROR_e; + } +#endif BOOL flag = dItem_data::chkFlag(m_itemNo, 2); #if DEBUG diff --git a/src/d/actor/d_a_obj_sekizoa.cpp b/src/d/actor/d_a_obj_sekizoa.cpp index 19f4446b87..ac0ca0d5b1 100644 --- a/src/d/actor/d_a_obj_sekizoa.cpp +++ b/src/d/actor/d_a_obj_sekizoa.cpp @@ -8,6 +8,8 @@ #include "d/actor/d_a_tag_kmsg.h" #include #include + +#include "dusk/string.hpp" #include "f_op/f_op_actor_mng.h" #include "f_op/f_op_msg.h" @@ -1364,8 +1366,8 @@ int daObj_Sekizoa_c::cutStart(int i_staffIdx) { daObj_SMTile_c* actor_4 = (daObj_SMTile_c*)mActorMngrs[4].getActorP(); actor_4->reset(); dComIfGp_getEvent()->setPt2(actor_4); - strcpy(acStack_90, l_evtList[2].eventName); - strcat(acStack_90, "@"); + SAFE_STRCPY(acStack_90, l_evtList[2].eventName); + SAFE_STRCAT(acStack_90, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_90); dComIfGp_getEvent()->onSkipFade(); if (daNpcT_getPlayerInfoFromPlayerList(1, fopAcM_GetRoomNo(this), &c_stack_9c, @@ -1856,8 +1858,8 @@ int daObj_Sekizoa_c::cutGoal(int i_staffIdx) { switch (prm) { case 0: if (mType == TYPE_0) { - strcpy(acStack_9c, l_evtList[6].eventName); - strcat(acStack_9c, "@"); + SAFE_STRCPY(acStack_9c, l_evtList[6].eventName); + SAFE_STRCAT(acStack_9c, "@"); dComIfGp_getEvent()->setSkipZev(this, acStack_9c); dComIfGp_getEvent()->onSkipFade(); } @@ -2222,8 +2224,8 @@ int daObj_Sekizoa_c::cutExtinction(int i_staffIdx) { cStack_b0 += actor_0->current.pos; daPy_getPlayerActorClass()->setPlayerPosAndAngle(&cStack_b0, actor_0->shape_angle.y - -0x8000, 0); - strcpy(acStack_a4, l_evtList[9].eventName); - strcat(acStack_a4, "@"); + SAFE_STRCPY(acStack_a4, l_evtList[9].eventName); + SAFE_STRCAT(acStack_a4, "@"); dComIfGp_getEvent()->setSkipZev(actor_0, acStack_a4); dComIfGp_getEvent()->onSkipFade(); actor_0->pullMasterSword(); diff --git a/src/d/actor/d_a_set_bgobj.cpp b/src/d/actor/d_a_set_bgobj.cpp index 792f14840a..f898f34ffb 100644 --- a/src/d/actor/d_a_set_bgobj.cpp +++ b/src/d/actor/d_a_set_bgobj.cpp @@ -26,7 +26,7 @@ int daSetBgObj_c::CreateInit() { int daSetBgObj_c::create() { fopAcM_ct(this, daSetBgObj_c); - sprintf(mArcName, "%s", getArcName(this)); + SAFE_SPRINTF(mArcName, "%s", getArcName(this)); int phase = dComIfG_resLoad(&mPhase, mArcName); if (phase == cPhs_COMPLEATE_e) { diff --git a/src/d/actor/d_a_tag_evt.cpp b/src/d/actor/d_a_tag_evt.cpp index 8dc5954ef0..e68c980b23 100644 --- a/src/d/actor/d_a_tag_evt.cpp +++ b/src/d/actor/d_a_tag_evt.cpp @@ -8,6 +8,8 @@ #include "f_op/f_op_actor_mng.h" #include +#include "dusk/string.hpp" + static char* l_evtNameList[] = { NULL, "JUMP_DEMOSTAGE", @@ -26,7 +28,7 @@ int daTag_Evt_c::create() { cPhs_Step phase = dComIfG_resLoad(&mPhase, l_resFileName); if (phase == cPhs_COMPLEATE_e) { eventInfo.setArchiveName(l_resFileName); - strcpy(field_0x568, "TagEvt"); + SAFE_STRCPY(field_0x568, "TagEvt"); getParam(); field_0x572 = -1; } diff --git a/src/d/actor/d_a_tag_msg.cpp b/src/d/actor/d_a_tag_msg.cpp index 6e052f74ea..55b135964a 100644 --- a/src/d/actor/d_a_tag_msg.cpp +++ b/src/d/actor/d_a_tag_msg.cpp @@ -11,6 +11,8 @@ #include "d/d_debug_viewer.h" #include +#include "dusk/string.hpp" + static int createHeapCallBack(fopAc_ac_c* i_this) { daTag_Msg_c* msg = (daTag_Msg_c*)i_this; return msg->createHeap(); @@ -219,7 +221,7 @@ void daTag_Msg_c::getParam() { scale.x *= 100.0f; scale.y *= 100.0f; - strcpy(mStaffName, "Tag_ms"); + SAFE_STRCPY(mStaffName, "Tag_ms"); } char* daTag_Msg_c::mEvtCutTBL[2] = { diff --git a/src/d/actor/d_a_title.cpp b/src/d/actor/d_a_title.cpp index 5c9e503ea3..cfe4003158 100644 --- a/src/d/actor/d_a_title.cpp +++ b/src/d/actor/d_a_title.cpp @@ -250,7 +250,7 @@ void daTitle_c::loadWait_proc() { text[i]->setFont(mpFont); text[i]->setString(0x80, ""); - char* msg = text[i]->getStringPtr(); + TEXT_SPAN msg = text[i]->getStringPtr(); fopMsgM_messageGet(msg, 100); } diff --git a/src/d/d_bg_parts.cpp b/src/d/d_bg_parts.cpp index 3c378bf325..9dc7be181d 100644 --- a/src/d/d_bg_parts.cpp +++ b/src/d/d_bg_parts.cpp @@ -8,6 +8,8 @@ #include "JSystem/JKernel/JKRSolidHeap.h" #include +#include "dusk/string.hpp" + void dBgp_c::material_c::draw() { material_c* material = this; do { @@ -255,7 +257,7 @@ void dBgp_c::share_c::reset() { const char* dBgp_c::share_c::getArcName() { static char arcName[8]; - sprintf(arcName, "@mt%04x", mId); + SAFE_SPRINTF(arcName, "@mt%04x", mId); return arcName; } @@ -430,7 +432,7 @@ dBgp_c::packet_c::packet_c() { void dBgp_c::create(s8 i_roomNo, void* i_data) { mPointer = i_data; mPacket.setRoomNo(i_roomNo); - strcpy(mArcName, dComIfG_getRoomArcName(i_roomNo)); + SAFE_STRCPY(mArcName, dComIfG_getRoomArcName(i_roomNo)); if (mPointer != NULL) { JKRExpHeap* block = dStage_roomControl_c::getMemoryBlock(i_roomNo); @@ -457,7 +459,7 @@ void dBgp_c::create(s8 i_roomNo, void* i_data) { unit_group_class* unitGroup = mapUnit->groups; for (int i = 0; i < mapUnit->num; i++) { char resName[16]; - sprintf(resName, "bp%04d.dzb", i); + SAFE_SPRINTF(resName, "bp%04d.dzb", i); cBgD_t* dzb = (cBgD_t*)dComIfG_getStageRes(mArcName, resName); if (dzb != NULL) { @@ -534,7 +536,7 @@ int dBgp_c::remove() { const char* dBgp_c::getArcName(u16 i_id, u16 i_arg) { static char arcName[8]; - sprintf(arcName, "@%03x%03x", i_id, i_arg); + SAFE_SPRINTF(arcName, "@%03x%03x", i_id, i_arg); return arcName; } diff --git a/src/d/d_cc_uty.cpp b/src/d/d_cc_uty.cpp index 218cb362f4..91fc6677c1 100644 --- a/src/d/d_cc_uty.cpp +++ b/src/d/d_cc_uty.cpp @@ -16,6 +16,7 @@ #if TARGET_PC #include "dusk/achievements.h" #include "dusk/settings.h" +#include "d/actor/d_a_alink.h" #endif static int plCutLRC[58] = { @@ -448,6 +449,10 @@ fopAc_ac_c* cc_at_check(fopAc_ac_c* i_enemy, dCcU_AtInfo* i_AtInfo) { #if TARGET_PC if (fopAcM_GetGroup(i_enemy) == fopAc_ENEMY_e) { dusk::AchievementSystem::get().signal("enemy_killed"); + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab) { + dusk::AchievementSystem::get().signal("rollstab_kill"); + } } #endif } diff --git a/src/d/d_com_inf_game.cpp b/src/d/d_com_inf_game.cpp index 845076845b..97dae780e1 100644 --- a/src/d/d_com_inf_game.cpp +++ b/src/d/d_com_inf_game.cpp @@ -25,6 +25,9 @@ #include "m_Do/m_Do_graphic.h" #include #include +#include + +#include "dusk/string.hpp" void dComIfG_play_c::ct() { mWindowNum = 0; @@ -2571,8 +2574,11 @@ u8 dComIfG_getNowCalcRegion() { bool dComIfGp_isLightDropMapVisible() { for (int i = 0; i < 3; i++) { - #if DUSK_TPHD - if (dComIfGs_isLightDropGetFlag(i) != FALSE && dComIfGs_getLightDropNum(i) < 12) { + #if TARGET_PC + if (dComIfGs_isLightDropGetFlag(i) != FALSE && + ((dusk::tphd_active() && dComIfGs_getLightDropNum(i) < 12) || + (!dusk::tphd_active() && dComIfGs_getLightDropNum(i) < 16))) + { #else if (dComIfGs_isLightDropGetFlag(i) != FALSE && dComIfGs_getLightDropNum(i) < 16) { #endif @@ -2653,7 +2659,7 @@ static void dComIfGs_setWarpItemData(int param_0, char const* i_stage, cXyz i_po void dComIfG_play_c::setWarpItemData(char const* i_stage, cXyz i_pos, s16 i_angle, s8 i_roomNo, u8 param_4, u8 param_5) { - strcpy(mItemInfo.mWarpItemData.mWarpItemStage, i_stage); + SAFE_STRCPY(mItemInfo.mWarpItemData.mWarpItemStage, i_stage); mItemInfo.mWarpItemData.mWarpItemPos.set(i_pos); mItemInfo.mWarpItemData.mWarpItemAngle = i_angle; mItemInfo.mWarpItemData.mWarpItemRoom = i_roomNo; @@ -2740,7 +2746,7 @@ void* dComIfG_getOldStageRes(char const* i_resName) { char* dComIfG_getRoomArcName(int i_roomNo) { static char buf[32]; - sprintf(buf, "R%02d_00", i_roomNo); + SAFE_SPRINTF(buf, "R%02d_00", i_roomNo); return buf; } diff --git a/src/d/d_com_static.cpp b/src/d/d_com_static.cpp index ee05d34170..585d39869b 100644 --- a/src/d/d_com_static.cpp +++ b/src/d/d_com_static.cpp @@ -351,13 +351,13 @@ const char* daSetBgObj_c::getArcName(fopAc_ac_c* i_this) { u32 r30 = fopAcM_GetParam(i_this); u16 r29 = fopAcM_GetParam(i_this); - sprintf(arcName, "@bg%04x", r29); + SAFE_SPRINTF(arcName, "@bg%04x", r29); if (DEBUG && r30 & 0x80000000) { OS_REPORT("\e[43;30m旧仕様の地形ユニットMoveBGが残っています!!!\n\e[m"); u16 r28 = r30 >> 12 & 0x1FF; u16 r27 = r30 & 0xFFF; - sprintf(arcName, "@%03x%03x", r28, (u16)r27); + SAFE_SPRINTF(arcName, "@%03x%03x", r28, (u16)r27); } return arcName; } diff --git a/src/d/d_debug_camera.cpp b/src/d/d_debug_camera.cpp index 131a2c05ae..9ff5b0fcf8 100644 --- a/src/d/d_debug_camera.cpp +++ b/src/d/d_debug_camera.cpp @@ -1349,11 +1349,11 @@ void dDbgCamera_c::monitor() { for (var_r27 = 0; var_r27 < 4; var_r27++) { if (var_r27 == mCmdMode) { - strcat(spC0, "____ "); + SAFE_STRCAT(spC0, "____ "); break; } - strcat(spC0, " "); + SAFE_STRCAT(spC0, " "); } Report(pos_x, pos_y, 7, " %s", spC0); diff --git a/src/d/d_ev_camera.cpp b/src/d/d_ev_camera.cpp index 736f9f7a82..fad6d1a523 100644 --- a/src/d/d_ev_camera.cpp +++ b/src/d/d_ev_camera.cpp @@ -13,6 +13,8 @@ #include "d/actor/d_a_alink.h" #include +#include "dusk/string.hpp" + #ifdef __MWERKS__ #define LOAD_4BYTE_STRING_LITERAL(x) (*(u32*)(x)) #else @@ -46,7 +48,7 @@ int dCamera_c::StartEventCamera(int param_0, int param_1, ...) { for (int i = 0; i < 8; i++) { char* param_name = va_arg(args, char*); if (param_name != NULL) { - strcpy(mEventData.mEventParams[i].name, param_name); + SAFE_STRCPY(mEventData.mEventParams[i].name, param_name); mEventData.mEventParams[i].field_0x10 = va_arg(args, int); mEventData.mEventParams[i].value = va_arg(args, uintptr_t); } else { @@ -341,18 +343,23 @@ bool dCamera_c::getEvXyzData(cXyz* i_data, char* i_event, cXyz param_2) { return 1; } +#if TARGET_PC +template +bool dCamera_c::getEvStringData(char (&i_data)[N], char* i_event, char* param_2) { +#else bool dCamera_c::getEvStringData(char* i_data, char* i_event, char* param_2) { +#endif if (chkFlag(0x20000000)) { int index = searchEventArgData(i_event); if (index == -1) { - strcpy(i_data, param_2); + SAFE_STRCPY(i_data, param_2); } else { - strcpy(i_data, (char*)mEventData.mEventParams[index].value); + SAFE_STRCPY(i_data, (char*)mEventData.mEventParams[index].value); } } else if (dComIfGp_evmng_getMySubstanceNum(mEventData.mStaffIdx, i_event) != 0) { - strcpy(i_data, dComIfGp_evmng_getMyStringP(mEventData.mStaffIdx, i_event)); + SAFE_STRCPY(i_data, dComIfGp_evmng_getMyStringP(mEventData.mStaffIdx, i_event)); } else { - strcpy(i_data, param_2); + SAFE_STRCPY(i_data, param_2); #if DEBUG if (mCurCamStyleTimer == 0 && mCamSetup.CheckFlag(0x40)) { OS_REPORT("camera: event: %16s: %s (d)\n", i_event, i_data); @@ -369,6 +376,11 @@ bool dCamera_c::getEvStringData(char* i_data, char* i_event, char* param_2) { return 1; } +#if TARGET_PC +// Used in another TU, so force instantiation to avoid linker issues. +template bool dCamera_c::getEvStringData(char (&i_data)[12], char* i_event, char* param_2); +#endif + char* dCamera_c::getEvStringPntData(char* i_event, char* param_1) { char* string = NULL; @@ -520,7 +532,7 @@ bool dCamera_c::fixedFrameEvCamera() { #if DEBUG if (strlen(fframe_p->mRelUseMask) != 2) { OSReport("camera: event: bad length -> xx\n"); - strcpy(fframe_p->mRelUseMask, "xx"); + SAFE_STRCPY(fframe_p->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x32e, "0"); OSPanic("d_ev_camera.cpp", 0x32e, "Halt"); } @@ -613,7 +625,7 @@ bool dCamera_c::fixedFrameEvCamera() { } fframe_p->field_0x4 = relationalPos(fframe_p->mpRelActor, &sp44); - } else if (fframe_p->mRelUseMask[1] == 116) { + } else if (fframe_p->mRelUseMask[1] == 't') { fframe_p->field_0x4 = attentionPos(fframe_p->mpRelActor) + sp44; } else { fframe_p->field_0x4 = sp44; @@ -866,7 +878,7 @@ bool dCamera_c::fixedPositionEvCamera() { getEvFloatData(&fpos_p->field_0x38, "Radius", 100000.0f); getEvFloatData(&fpos_p->field_0x34, "StartRadius", fpos_p->field_0x38); fpos_p->field_0x1 = getEvFloatData(&fpos_p->field_0x2c, "Bank", 0.0f); - getEvStringData(&fpos_p->field_0x48, "RelUseMask", "o"); + getEvStringData(fpos_p->field_0x48, "RelUseMask", "o"); fpos_p->field_0x0 = getEvIntData(&fpos_p->field_0x4c, "Timer", -1); if ((fpos_p->field_0x40 = getEvActor("Target", "@PLAYER")) == NULL) { @@ -877,7 +889,7 @@ bool dCamera_c::fixedPositionEvCamera() { fpos_p->field_0x44 = fopAcM_GetID(fpos_p->field_0x40); fpos_p->field_0x3c = getEvActor("RelActor"); - if (fpos_p->field_0x3c && isRelChar(fpos_p->field_0x48)) { + if (fpos_p->field_0x3c && isRelChar(fpos_p->field_0x48[0])) { fpos_p->field_0x4 = relationalPos(fpos_p->field_0x3c, &sp24); } else { fpos_p->field_0x4 = sp24; @@ -1021,7 +1033,7 @@ bool dCamera_c::transEvCamera(int param_1) { getEvIntData(&trans->mTransType, "TransType", 0); trans->mRelActor = getEvActor("RelActor"); - getEvStringData(&trans->mRelUseMask, "RelUseMask", "--oo"); + getEvStringData(trans->mRelUseMask, "RelUseMask", "--oo"); getEvFloatData(&trans->mCushion, "Cushion", 1.0f); if (trans->mRelActor) { @@ -1034,36 +1046,36 @@ bool dCamera_c::transEvCamera(int param_1) { mAdditionVec = MidnaAdditionVec; } - if (trans->mRelUseMask == 119) { + if (trans->mRelUseMask[0] == 'w') { trans->mStartCenter += mAdditionVec; } - if (trans->mRelUseMask == 87) { + if (trans->mRelUseMask[0] == 'W') { trans->mStartCenter -= mAdditionVec; } - if (trans->field_0x49 == 119) { + if (trans->mRelUseMask[1] == 'w') { trans->mStartEye += mAdditionVec; } - if (trans->field_0x49 == 87) { + if (trans->mRelUseMask[1] == 'W') { trans->mStartEye -= mAdditionVec; } - if (trans->field_0x4a == 119) { + if (trans->mRelUseMask[2] == 'w') { trans->mCenter += mAdditionVec; } - if (trans->field_0x4a == 87) { + if (trans->mRelUseMask[2] == 'W') { trans->mCenter -= mAdditionVec; } - if (trans->field_0x4b == 119) { + if (trans->mRelUseMask[3] == 'w') { trans->mEye += mAdditionVec; } - if (trans->field_0x4b == 87) { + if (trans->mRelUseMask[3] == 'W') { trans->mEye -= mAdditionVec; } } - if (trans->field_0x49 == 114) { + if (trans->mRelUseMask[1] == 'r') { my_vec_0 = relationalPos(trans->mRelActor, &trans->mStartCenter); if ((mTicks & 1) != 0) { trans->mStartEye.x = -trans->mStartEye.x; @@ -1075,14 +1087,14 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 110 || trans->field_0x49 == 110) { + if (trans->mRelUseMask[0] == 'n' || trans->mRelUseMask[1] == 'n') { cSGlobe cStack_7b8(mEye - positionOf(trans->mRelActor)); cSAngle acStack_898 = cStack_7b8.U() - directionOf(trans->mRelActor); if (acStack_898 < cSAngle::_0) { - if (trans->mRelUseMask == 110) { + if (trans->mRelUseMask[0] == 'n') { trans->mStartCenter.x = -trans->mStartCenter.x; } - if (trans->field_0x49 == 110) { + if (trans->mRelUseMask[1] == 'n') { trans->mStartEye.x = -trans->mStartEye.x; } } @@ -1094,15 +1106,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->field_0x4a == 110 || trans->field_0x4b == 110) { + if (trans->mRelUseMask[2] == 'n' || trans->mRelUseMask[3] == 'n') { cSGlobe cStack_7c0(mEye - positionOf(trans->mRelActor)); cSAngle acStack_89c = cStack_7c0.U() - directionOf(trans->mRelActor); if (acStack_89c < cSAngle::_0) { - if (trans->field_0x4a == 110) { + if (trans->mRelUseMask[2] == 'n') { trans->mCenter.x = -trans->mCenter.x; } - if (trans->field_0x4b == 110) { + if (trans->mRelUseMask[3] == 'n') { trans->mEye.x = -trans->mEye.x; } } @@ -1114,15 +1126,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 78 || trans->field_0x49 == 78) { + if (trans->mRelUseMask[0] == 'N' || trans->mRelUseMask[1] == 'N') { cSGlobe cStack_7c8(mEye - positionOf(trans->mRelActor)); cSAngle acStack_8a0 = cStack_7c8.U() - directionOf(trans->mRelActor); if (acStack_8a0 > cSAngle::_0) { - if (trans->mRelUseMask == 78) { + if (trans->mRelUseMask[0] == 'N') { trans->mStartCenter.x = -trans->mStartCenter.x; } - if (trans->field_0x49 == 78) { + if (trans->mRelUseMask[1] == 'N') { trans->mStartEye.x = -trans->mStartEye.x; } } @@ -1134,15 +1146,15 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->field_0x4a == 78 || trans->field_0x4b == 78) { + if (trans->mRelUseMask[2] == 'N' || trans->mRelUseMask[3] == 'N') { cSGlobe cStack_7d0(mEye - positionOf(trans->mRelActor)); cSAngle acStack_8a4 = cStack_7d0.U() - directionOf(trans->mRelActor); if (acStack_8a4 > cSAngle::_0) { - if (trans->field_0x4a == 78) { + if (trans->mRelUseMask[2] == 'N') { trans->mCenter.x = -trans->mCenter.x; } - if (trans->field_0x4b == 78) { + if (trans->mRelUseMask[3] == 'N') { trans->mEye.x = -trans->mEye.x; } } @@ -1154,21 +1166,21 @@ bool dCamera_c::transEvCamera(int param_1) { } } - if (trans->mRelUseMask == 102) { + if (trans->mRelUseMask[0] == 'f') { cSGlobe cStack_7d8(trans->mStartCenter); cStack_7d8.U(directionOf(trans->mRelActor) + cStack_7d8.U()); trans->mStartCenter = attentionPos(trans->mRelActor) + cStack_7d8.Xyz(); - trans->mRelUseMask = 120; + trans->mRelUseMask[0] = 'x'; } - if (trans->field_0x49 == 102) { + if (trans->mRelUseMask[1] == 'f') { cSGlobe cStack_7e0(trans->mStartEye); cStack_7e0.U(directionOf(trans->mRelActor) + cStack_7e0.U()); trans->mStartEye = attentionPos(trans->mRelActor) + cStack_7e0.Xyz(); - trans->field_0x49 = 120; + trans->mRelUseMask[1] = 'x'; } - if (trans->field_0x4a == 112) { + if (trans->mRelUseMask[2] == 'p') { cXyz sp114(trans->mCenter); cXyz sp120 = relationalPos(trans->mRelActor, &sp114); f32 fVar1 = cXyz(sp120 - positionOf(mpPlayerActor)).abs(); @@ -1178,14 +1190,14 @@ bool dCamera_c::transEvCamera(int param_1) { if (fVar1 < fVar2) { trans->mCenter.x = -trans->mCenter.x; } - } else if (trans->field_0x4a == 102) { + } else if (trans->mRelUseMask[2] == 'f') { cSGlobe cStack_7e8(trans->mCenter); cStack_7e8.U(directionOf(trans->mRelActor) + cStack_7e8.U()); trans->mCenter = attentionPos(trans->mRelActor) + cStack_7e8.Xyz(); - trans->field_0x4a = 120; + trans->mRelUseMask[2] = 'x'; } - if (trans->field_0x4b == 112) { + if (trans->mRelUseMask[3] == 'p') { cXyz sp12c = trans->mEye; cXyz sp138(relationalPos(trans->mRelActor, &sp12c)); f32 fVar3 = cXyz(sp138 - positionOf(mpPlayerActor)).abs(); @@ -1195,7 +1207,7 @@ bool dCamera_c::transEvCamera(int param_1) { if (fVar3 < fVar4) { trans->mEye.x = -trans->mEye.x; } - } else if (trans->field_0x4b == 114) { + } else if (trans->mRelUseMask[3] == 'r') { my_vec_0 = relationalPos(trans->mRelActor, &trans->mCenter); if ((mTicks & 1) != 0) { trans->mEye.x = -trans->mEye.x; @@ -1205,19 +1217,19 @@ bool dCamera_c::transEvCamera(int param_1) { if (lineBGCheck(&my_vec_0, &my_vec_1, 0x4007)) { trans->mEye.x = -trans->mEye.x; } - } else if (trans->field_0x4b == 102) { + } else if (trans->mRelUseMask[3] == 'f') { cSGlobe cStack_7f0(trans->mEye); cStack_7f0.U(directionOf(trans->mRelActor) + cStack_7f0.U()); trans->mEye = attentionPos(trans->mRelActor) + cStack_7f0.Xyz(); - trans->field_0x4b = 120; + trans->mRelUseMask[3] = 'x'; } } else { - if (trans->field_0x4a == 97) { + if (trans->mRelUseMask[2] == 'a') { cXyz cStack_320 = dCamMath::xyzRotateY(trans->mCenter, cSAngle(mViewCache.mDirection.U().Inv())); trans->mCenter = mViewCache.mCenter + cStack_320; } - if (trans->field_0x4b == 97) { + if (trans->mRelUseMask[3] == 'a') { cXyz cStack_32c = dCamMath::xyzRotateY(trans->mEye, cSAngle(mViewCache.mDirection.U().Inv())); trans->mEye = mViewCache.mEye + cStack_32c; } @@ -1246,47 +1258,47 @@ bool dCamera_c::transEvCamera(int param_1) { } if (trans->mRelActor) { - if (trans->mRelUseMask == 116) { + if (trans->mRelUseMask[0] == 't') { pos.mXyz_1 = attentionPos(trans->mRelActor) + trans->mStartCenter; - } else if (trans->mRelUseMask == 99) { + } else if (trans->mRelUseMask[0] == 'c') { cSGlobe cStack_7f8(trans->mStartCenter); cStack_7f8.U(trans->field_0x60.U() + cStack_7f8.U()); pos.mXyz_1 = attentionPos(trans->mRelActor) + cStack_7f8.Xyz(); - } else if (trans->mRelUseMask == 119 || trans->mRelUseMask == 87) { + } else if (trans->mRelUseMask[0] == 'w' || trans->mRelUseMask[0] == 'W') { pos.mXyz_1 = relationalPos(trans->mRelActor, &trans->mStartCenter); } else { - if (isRelChar(trans->mRelUseMask)) { + if (isRelChar(trans->mRelUseMask[0])) { pos.mXyz_1 = relationalPos(trans->mRelActor, &trans->mStartCenter); } else { pos.mXyz_1 = trans->mStartCenter; } } - if (trans->field_0x49 == 116) { + if (trans->mRelUseMask[1] == 't') { pos.mXyz_0 = attentionPos(trans->mRelActor) + trans->mStartEye; - } else if (trans->field_0x49 == 99) { + } else if (trans->mRelUseMask[1] == 'c') { cSGlobe cStack_800(trans->mStartEye); cStack_800.U(trans->field_0x60.U() + cStack_800.U()); pos.mXyz_0 = attentionPos(trans->mRelActor) + cStack_800.Xyz(); - } else if (trans->field_0x49 == 119 || trans->field_0x49 == 87) { + } else if (trans->mRelUseMask[1] == 'w' || trans->mRelUseMask[1] == 'W') { pos.mXyz_0 = relationalPos(trans->mRelActor, &trans->mStartEye); } else { - if (isRelChar(trans->field_0x49)) { + if (isRelChar(trans->mRelUseMask[1])) { pos.mXyz_0 = relationalPos(trans->mRelActor, &trans->mStartEye); } else { pos.mXyz_0 = trans->mStartEye; } } - if (trans->field_0x4a == 116) { + if (trans->mRelUseMask[2] == 't') { pos2.mXyz_1 = attentionPos(trans->mRelActor) + trans->mCenter; - } else if (trans->field_0x4a == 99) { + } else if (trans->mRelUseMask[2] == 'c') { cSGlobe cStack_808(trans->mCenter); cStack_808.U(trans->field_0x60.U() + cStack_808.U()); pos2.mXyz_1 = attentionPos(trans->mRelActor) + cStack_808.Xyz(); - } else if (trans->field_0x4a == 119 || trans->field_0x4a == 87) { + } else if (trans->mRelUseMask[2] == 'w' || trans->mRelUseMask[2] == 'W') { pos2.mXyz_1 = relationalPos(trans->mRelActor, &trans->mCenter); - } else if (isRelChar(trans->field_0x4a)) { + } else if (isRelChar(trans->mRelUseMask[2])) { pos2.mXyz_1 = relationalPos(trans->mRelActor, &trans->mCenter); } else if (trans->mTransType == 2) { pos2.mXyz_1 = dCamMath::xyzRotateY(trans->mCenter, directionOf(trans->mRelActor)); @@ -1294,16 +1306,16 @@ bool dCamera_c::transEvCamera(int param_1) { pos2.mXyz_1 = trans->mCenter; } - if (trans->field_0x4b == 116) { + if (trans->mRelUseMask[3] == 't') { pos2.mXyz_0 = attentionPos(trans->mRelActor) + trans->mEye; - } else if (trans->field_0x4b == 99) { + } else if (trans->mRelUseMask[3] == 'c') { cSGlobe cStack_810(trans->mEye); cStack_810.U(trans->field_0x60.U() + cStack_810.U()); pos2.mXyz_0 = attentionPos(trans->mRelActor) + cStack_810.Xyz(); } else { - if (trans->field_0x4b == 119 || trans->field_0x4b == 87) { + if (trans->mRelUseMask[3] == 'w' || trans->mRelUseMask[3] == 'W') { pos2.mXyz_0 = relationalPos(trans->mRelActor, &trans->mEye); - } else if (isRelChar(trans->field_0x4b)) { + } else if (isRelChar(trans->mRelUseMask[3])) { pos2.mXyz_0 = relationalPos(trans->mRelActor, &trans->mEye); } else if (trans->mTransType == 2) { pos2.mXyz_0 = dCamMath::xyzRotateY(trans->mEye, directionOf(trans->mRelActor)); @@ -3420,11 +3432,11 @@ bool dCamera_c::fixedFramesEvCamera() { fframes_p->field_0x0 = getEvIntData(&fframes_p->mTimer, "Timer", const_1_val); - getEvStringData(&fframes_p->mRelUseMask, "RelUseMask", "oo"); + getEvStringData(fframes_p->mRelUseMask, "RelUseMask", "oo"); #if DEBUG - if (strlen(&fframes_p->mRelUseMask) != 2) { + if (strlen(fframes_p->mRelUseMask) != 2) { OSReport("camera: event: bad length -> xx\n"); - strcpy(&fframes_p->mRelUseMask, "xx"); + SAFE_STRCPY(fframes_p->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x129c, "Halt"); OSPanic("d_ev_camera.cpp", 0x129c, "Halt"); } @@ -3442,13 +3454,13 @@ bool dCamera_c::fixedFramesEvCamera() { sp30 = fframes_p->field_0x1c[1][iVar1]; sp3c = fframes_p->field_0x1c[0][iVar1]; - if (fframes_p->mRelActor && fframes_p->mRelUseMask == 111) { + if (fframes_p->mRelActor && fframes_p->mRelUseMask[0] == 111) { fframes_p->field_0x4 = relationalPos(fframes_p->mRelActor, &sp30); } else { fframes_p->field_0x4 = sp30; } - if (fframes_p->mRelActor && fframes_p->field_0x31 == 111) { + if (fframes_p->mRelActor && fframes_p->mRelUseMask[1] == 111) { fframes_p->field_0x10 = relationalPos(fframes_p->mRelActor, &sp3c); } else { fframes_p->field_0x10 = sp3c; @@ -3959,7 +3971,7 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mSet1 = 0; char use1[8]; - strcpy(use1, "xxxxxx"); + SAFE_STRCPY(use1, "xxxxxx"); iVar1 = getEvFloatListData(&bspTrans->mSet1, "Set1"); if (iVar1 != 0) { @@ -3970,7 +3982,7 @@ bool dCamera_c::bspTransEvCamera() { #if DEBUG if (strlen(use1) != 6) { OSReport("camera: event: bad length -> xxxxxx\n"); - strcpy(use1, "xxxxxx"); + SAFE_STRCPY(use1, "xxxxxx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x14f9, "0"); OSPanic("d_ev_camera.cpp", 0x14f9, "Halt"); } @@ -3979,7 +3991,7 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mSet2 = 0; char use2[8]; - strcpy(use2, "xxxxxx"); + SAFE_STRCPY(use2, "xxxxxx"); iVar1 = getEvFloatListData(&bspTrans->mSet2, "Set2"); if (iVar1 != 0) { @@ -3990,7 +4002,7 @@ bool dCamera_c::bspTransEvCamera() { #if DEBUG if (strlen(use2) != 6) { OSReport_Error("camera: event: bad length -> xxxxxx\n"); - strcpy(use2, "xxxxxx"); + SAFE_STRCPY(use2, "xxxxxx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x1509, "0"); OSPanic("d_ev_camera.cpp", 0x1509, "Halt"); } @@ -4000,12 +4012,12 @@ bool dCamera_c::bspTransEvCamera() { bspTrans->mRelActorID = -1; bspTrans->mRelActor = getEvActor("RelActor"); if (bspTrans->mRelActor) { - getEvStringData(&bspTrans->mRelUseMask, "RelUseMask", "oo"); + getEvStringData(bspTrans->mRelUseMask, "RelUseMask", "oo"); #if DEBUG - if (strlen(&bspTrans->mRelUseMask) != 2) { + if (strlen(bspTrans->mRelUseMask) != 2) { OSReport_Error("camera: event: bad length -> xx\n"); - strcpy(&bspTrans->mRelUseMask, "xx"); + SAFE_STRCPY(bspTrans->mRelUseMask, "xx"); JUTAssertion::showAssert(JUTAssertion::getSDevice(), "d_ev_camera.cpp", 0x1515, "0"); OSPanic("d_ev_camera.cpp", 0x1515, "Halt"); } @@ -4041,13 +4053,13 @@ bool dCamera_c::bspTransEvCamera() { } pos; if (bspTrans->mRelActor != NULL) { - if (isRelChar(bspTrans->mRelUseMask)) { + if (isRelChar(bspTrans->mRelUseMask[0])) { pos.sp48 = relationalPos(bspTrans->mRelActor, &bspTrans->field_0x94); } else { pos.sp48 = bspTrans->field_0x94; } - if (isRelChar(bspTrans->field_0xb9)) { + if (isRelChar(bspTrans->mRelUseMask[1])) { pos.sp3c = relationalPos(bspTrans->mRelActor, &bspTrans->field_0xa0); } else { pos.sp3c = bspTrans->field_0xa0; diff --git a/src/d/d_event.cpp b/src/d/d_event.cpp index 73891276df..3eba7aa041 100644 --- a/src/d/d_event.cpp +++ b/src/d/d_event.cpp @@ -13,6 +13,8 @@ #include "SSystem/SComponent/c_counter.h" #include +#include "dusk/string.hpp" + namespace { static u8 event_debug_evnt() { #if DEBUG @@ -756,8 +758,8 @@ int dEv_defaultSkipZev(void* actor, int parameter) { char* skipName; switch (parameter) { case 0: - strcpy(eventName, data->data.event_name); - strcat(eventName, "$0"); + SAFE_STRCPY(eventName, data->data.event_name); + SAFE_STRCAT(eventName, "$0"); eventID = dComIfGp_getEventManager().getEventIdx(eventName, 0xFF, -1); OS_REPORT("%06d: event: [%d] %s!\n", g_Counter.mCounter0, eventID, eventName); break; @@ -804,8 +806,8 @@ int dEv_defaultSkipStb(void* actor, int parameter) { char* skipName; switch (parameter) { case 0: - strcpy(eventName, data->data.event_name); - strcat(eventName, "$0"); + SAFE_STRCPY(eventName, data->data.event_name); + SAFE_STRCAT(eventName, "$0"); eventID = dComIfGp_getEventManager().getEventIdx(eventName, 0xFF, -1); OS_REPORT("%06d: event: [%d] %s!\n", g_Counter.mCounter0, eventID, eventName); break; @@ -851,7 +853,7 @@ void dEvt_control_c::setSkipProc(void* skipActor, dEvt_SkipCb skipCb, int skipPa void dEvt_control_c::setSkipZev(void* skipActor, char* eventName) { setSkipProc(skipActor, dEv_defaultSkipZev, 1); - strcpy(mSkipEventName, eventName); + SAFE_STRCPY(mSkipEventName, eventName); } void dEvt_control_c::onSkipFade() { diff --git a/src/d/d_event_manager.cpp b/src/d/d_event_manager.cpp index 598b973b84..d1d56be90f 100644 --- a/src/d/d_event_manager.cpp +++ b/src/d/d_event_manager.cpp @@ -15,6 +15,8 @@ #include "SSystem/SComponent/c_counter.h" #include +#include "dusk/string.hpp" + #if DEBUG static dEvM_HIO_c l_HIO; #endif @@ -388,7 +390,7 @@ void dEvent_manager_c::roomInit(int roomNo) { } char arcname[8]; - strcpy(arcname, dComIfG_getRoomArcName(roomNo)); + SAFE_STRCPY(arcname, dComIfG_getRoomArcName(roomNo)); char* res = (char*)dComIfG_getStageRes(arcname, DataFileName); int i; @@ -836,7 +838,7 @@ s16 dEvent_manager_c::getEventIdx(const char* eventName, u8 mapToolID, s32 roomN case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(mapdata->data.event_name, 0xFF, roomNo); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(map_tool_name, 0xFF, roomNo); default: JUT_ASSERT(1278, FALSE); @@ -876,7 +878,7 @@ s16 dEvent_manager_c::getEventIdx(fopAc_ac_c* actor, u8 mapToolID) { case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(actor, mapdata->data.event_name, 0xFF); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(actor, map_tool_name, 0xFF); default: JUT_ASSERT(1341, FALSE); @@ -901,7 +903,7 @@ s16 dEvent_manager_c::getEventIdx(fopAc_ac_c* actor, const char* eventName, u8 m case dStage_MapEvent_dt_TYPE_STB: return getEventIdx(actor, mapdata->data.event_name, 0xFF); case dStage_MapEvent_dt_TYPE_MAPTOOLCAMERA: - sprintf(map_tool_name, "MapToolCamera%d", mapToolID); + SAFE_SPRINTF(map_tool_name, "MapToolCamera%d", mapToolID); return getEventIdx(actor, map_tool_name, 0xFF); default: JUT_ASSERT(1376, FALSE); @@ -1040,7 +1042,7 @@ int dEvent_manager_c::getMyStaffId(const char* staffName, fopAc_ac_c* actor, int dEvDtStaff_c* staff = getBase().getStaffP(staff_id); if (staff->getType() != dEvDtStaff_c::TYPE_ALL) { char buf[20]; - strcpy(buf, staff->getName()); + SAFE_STRCPY(buf, staff->getName()); char* ptr = NULL; if (!hasDp) { @@ -1310,7 +1312,7 @@ void dEvent_manager_c::issueStaff(const char* staffname) { fopAcM_Search((fopAcIt_JudgeFunc)extraOnObjectCallBack, NULL); } else { char nameBuf[32]; - strcpy(nameBuf, staffname); + SAFE_STRCPY(nameBuf, staffname); fopAc_ac_c* actor = fopAcM_searchFromName4Event(nameBuf, -1); fopAcM_OnStatus(actor, fopAcStts_STAFF_EXTRA_e); } @@ -1321,7 +1323,7 @@ void dEvent_manager_c::cancelStaff(const char* staffname) { fopAcM_Search((fopAcIt_JudgeFunc)extraOffObjectCallBack, NULL); } else { char nameBuf[32]; - strcpy(nameBuf, staffname); + SAFE_STRCPY(nameBuf, staffname); fopAc_ac_c* actor = fopAcM_searchFromName4Event(nameBuf, -1); fopAcM_OffStatus(actor, fopAcStts_STAFF_EXTRA_e); } diff --git a/src/d/d_file_sel_info.cpp b/src/d/d_file_sel_info.cpp index 0c987e6d63..d54bc4424d 100644 --- a/src/d/d_file_sel_info.cpp +++ b/src/d/d_file_sel_info.cpp @@ -14,6 +14,7 @@ #include #include +#include "dusk/string.hpp" #include "dusk/version.hpp" dFile_info_c::dFile_info_c(JKRArchive* i_archive, u8 param_1) { @@ -80,9 +81,11 @@ void dFile_info_c::screenSet() { info_text[2] = (J2DTextBox*)mFileInfo.Scr->search(MULTI_CHAR('w_time01')); info_text[3] = (J2DTextBox*)mFileInfo.Scr->search(MULTI_CHAR('w_ptim01')); +#define INFO_TEXT_SIZE 0x40 + for (int i = 0; i < 4; i++) { info_text[i]->setFont(mFileInfo.mFont); - info_text[i]->setString(0x40, ""); + info_text[i]->setString(INFO_TEXT_SIZE, ""); } mPlayerName = info_text[0]->getStringPtr(); mSaveStatus = info_text[1]->getStringPtr(); @@ -99,9 +102,9 @@ int dFile_info_c::setSaveData(dSv_save_c* i_savedata, BOOL i_validChksum, u8 i_d i_savedata->getPlayer().getPlayerStatusA().setLife(dComIfGs_getLife()); setHeartCnt(i_savedata); i_savedata->getPlayer().getPlayerStatusA().setLife(12); - strcpy(mPlayerName, dComIfGs_getPlayerName()); - strcpy(mSaveDate, ""); - strcpy(mPlayTime, ""); + SAFE_STRCPY(mPlayerName, dComIfGs_getPlayerName()); + SAFE_STRCPY(mSaveDate, ""); + SAFE_STRCPY(mPlayTime, ""); dMeter2Info_getString(0x4D, mSaveStatus, NULL); // New Quest Log result = 2; } else { @@ -110,7 +113,7 @@ int dFile_info_c::setSaveData(dSv_save_c* i_savedata, BOOL i_validChksum, u8 i_d } } else { setHeartCnt(i_savedata); - strcpy(mPlayerName, player_name); + SAFE_STRCPY(mPlayerName, player_name); setSaveDate(i_savedata); setPlayTime(i_savedata); result = 0; @@ -173,13 +176,13 @@ void dFile_info_c::setSaveDate(dSv_save_c* i_savedata) { #if TARGET_PC if (dusk::version::isRegionJpn()) { - sprintf(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday, + SAFE_SPRINTF(mSaveDate, "%d.%02d.%02d %02d:%02d", time.year, time.mon + 1, time.mday, time.hour, time.min); } else if (dusk::version::isRegionPal() && dComIfGs_getPalLanguage() != dSv_player_config_c::LANGUAGE_ENGLISH) { - sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mday, time.mon + 1, time.year, time.hour, + SAFE_SPRINTF(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mday, time.mon + 1, time.year, time.hour, time.min); } else { - sprintf(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mon + 1, time.mday, time.year, time.hour, + SAFE_SPRINTF(mSaveDate, "%02d/%02d/%d %02d:%02d", time.mon + 1, time.mday, time.year, time.hour, time.min); } #elif (VERSION == VERSION_GCN_JPN) || (VERSION == VERSION_WII_JPN) @@ -204,11 +207,11 @@ void dFile_info_c::setPlayTime(dSv_save_c* i_savedata) { // 3599940 = 999:59 in seconds if (time >= 3599940) { - sprintf(mPlayTime, "999:59"); + SAFE_SPRINTF(mPlayTime, "999:59"); } else { u32 min = (time % 3600) / 60; u32 hours = time / 3600; - sprintf(mPlayTime, "%d:%02d", hours, min); + SAFE_SPRINTF(mPlayTime, "%d:%02d", hours, min); } } diff --git a/src/d/d_file_select.cpp b/src/d/d_file_select.cpp index 17d5f2c4e4..40962abf0a 100644 --- a/src/d/d_file_select.cpp +++ b/src/d/d_file_select.cpp @@ -23,6 +23,8 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" + static s32 SelStartFrameTbl[3] = { 59, 99, @@ -3507,7 +3509,7 @@ void dFile_select_c::headerTxtSet(u16 i_msgId, u8 i_type, u8 param_3) { } if (i_msgId == 0xFFFF) { - strcpy(mHeaderStringPtr[dispIdx], ""); + SAFE_STRCPY(mHeaderStringPtr[dispIdx], ""); } else { static f32 fontsize[2] = {21.0f, 27.0f}; #if VERSION == VERSION_GCN_JPN @@ -3750,74 +3752,220 @@ bool dFile_select_c::yesnoWakuAlpahAnm(u8 param_1) { #if TARGET_PC void dFile_select_c::fileSelectWide() { - mYnSel.ScrYn->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mYnSel.ScrYn->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - m3mSel.Scr3m->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - m3mSel.Scr3m->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - fileSel.Scr->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Spirals - fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mSelIcon) { - mSelIcon->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; } - - if (mSelIcon2) { - mSelIcon2->refreshAspectScale(); + + // Reset all panes + mSelDt.ScrDt->scale(1.0f, 1.0f); + mSelDt.ScrDt->translate(0.0f, 0.0f); + for (PaneCache& entry : mSelDtPanes) { + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + for (PaneCache& entry : fileSelPanes) { + J2DPane* pane = fileSel.Scr->search(entry.tag); + pane->setBasePosition(J2DBasePosition_4); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + bool wideScaling = dusk::getSettings().game.menuScalingMode.getValue() != dusk::MenuScaling::GameCube; + const f32 rootScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleUp : 1.0f; + const f32 childScale = wideScaling ? mDoGph_gInf_c::hudAspectScaleDown : 1.0f; + const f32 rootTransX = wideScaling ? mDoGph_gInf_c::getSafeMinXF() : 0.0f; + + mYnSel.ScrYn->scale(rootScale, 1.0f); + mYnSel.ScrYn->translate(rootTransX, 0.0f); + + mYnSel.ScrYn->search(MULTI_CHAR('w_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_no_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('w_yes_t'))->scale(childScale, 1.0f); + mYnSel.ScrYn->search(MULTI_CHAR('f_yes_t'))->scale(childScale, 1.0f); + + m3mSel.Scr3m->scale(rootScale, 1.0f); + m3mSel.Scr3m->translate(rootTransX, 0.0f); + + m3mSel.Scr3m->search(MULTI_CHAR('w_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_sta'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_del'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('w_cop_t'))->scale(childScale, 1.0f); + m3mSel.Scr3m->search(MULTI_CHAR('f_cop_t'))->scale(childScale, 1.0f); + + fileSel.Scr->scale(rootScale, 1.0f); + fileSel.Scr->translate(rootTransX, 0.0f); + + fileSel.Scr->search(MULTI_CHAR('t_for'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('t_for1'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_btn_n'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_n_bk00'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + fileSel.Scr->search(MULTI_CHAR('w_dat_i0'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + + mCpSel.Scr->search(MULTI_CHAR('w_dat_i1'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_dat_i2'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk01'))->scale(childScale, 1.0f); + mCpSel.Scr->search(MULTI_CHAR('w_n_bk02'))->scale(childScale, 1.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case (dusk::MenuScaling::GameCube): + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(1.0f); + } + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(1.0f); + } + break; + case (dusk::MenuScaling::Wii): + // Icons + mSelDt.ScrDt->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mSelDt.ScrDt->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case (dusk::MenuScaling::Dusklight): + constexpr f32 minAspect = 4.0f / 2.94f; + constexpr f32 wideAspect = 16.0f / 9.0f + 0.05f; + constexpr f32 ultraAspect = 21.0f / 9.0f + 0.05f; + const f32 screenAspect = mDoGph_gInf_c::getAspect(); + + const f32 wideScaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + const f32 ultraScaleFactor = 1.0f + 0.115f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); + + const f32 wideShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (wideScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + const f32 ultraShiftFactor = mSelDt.ScrDt->search(MULTI_CHAR('gray_n'))->getTranslateX() * (ultraScaleFactor - mDoGph_gInf_c::hudAspectScaleDown); + + for (PaneCache& entry : mSelDtPanes) { + const size_t index = &entry - mSelDtPanes; + J2DPane* pane = mSelDt.ScrDt->search(entry.tag); + pane->setBasePosition(J2DBasePosition_0); + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + if (screenAspect >= minAspect && screenAspect <= wideAspect) { // Handle widescreen + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX + 11.0f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX + 8.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + if (screenAspect > 1.75f) { + pane->translate((entry.origTransX - 21.5f) * wideScaleFactor, pane->getTranslateY()); + } else { // Between 4:3 and 16:9 + pane->translate((entry.origTransX - 12.0f) * wideScaleFactor, pane->getTranslateY()); + } + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * wideScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + wideShiftFactor - 60.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else if (screenAspect >= minAspect && screenAspect >= wideAspect && screenAspect <= ultraAspect) // Handle ultrawide + { + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->translate((entry.origTransX + 18.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->translate((entry.origTransX - 40.0f) * ultraScaleFactor, pane->getTranslateY()); + } + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->translate(entry.origTransX * ultraScaleFactor, pane->getTranslateY()); + } + if (index <= 6) { // Icons + pane->translate(mDoGph_gInf_c::hudAspectScaleDown * entry.origTransX + ultraShiftFactor - 62.0f * (1.0f - mDoGph_gInf_c::hudAspectScaleDown), pane->getTranslateY()); + } + } else { // 4:3/default behavior + pane->setBasePosition(J2DBasePosition_4); + pane->translate(entry.origTransX, pane->getTranslateY()); + if (entry.tag == MULTI_CHAR('gray_n')) { // Slots + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base')) { // Slots BG + pane->scale(1.0f, 1.0f); + } + if (entry.tag == MULTI_CHAR('b_base1')) { // Magic Armor Slot BG + pane->scale(1.0f, 1.0f); + } + if (index <= 6) { // Icons + pane->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + } + } + } + + // Selection Cursor + if (mSelIcon) { + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + if (mSelIcon2) { + mSelIcon2->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + + // Spirals + fileSel.Scr->search(MULTI_CHAR('w_uzu00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu01'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu02'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu03'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu04'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu05'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu06'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu07'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu08'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + fileSel.Scr->search(MULTI_CHAR('w_uzu09'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + break; } - #endif } #endif @@ -4036,7 +4184,7 @@ void dFile_select_c::errDispInitSet(char* i_errMesg) { mErrorMsgTxtPane[mErrorTxtDispIdx]->setAlpha(0xFF); mErrorMsgTxtPane[mErrorTxtDispIdx ^ 1]->setAlpha(0); - strcpy(mErrorMsgStringPtr[mErrorTxtDispIdx], i_errMesg); + SAFE_STRCPY_BOUNDED(mErrorMsgStringPtr[mErrorTxtDispIdx], i_errMesg); if (field_0x014a) { errorMoveAnmInitSet(2859, 2849); @@ -4245,7 +4393,7 @@ void dFile_select_c::MemCardLoadWait() { if (mDoMemCd_getDataVersion() != 6) { char errmsg[264]; // "Savedata version is different\n\nVersion %d\n\nFormatting data." - sprintf(errmsg, "セーブデータのバージョンが違います\n\nバージョン %d\n\nデータを初期化します。", mDoMemCd_getDataVersion()); + SAFE_SPRINTF(errmsg, "セーブデータのバージョンが違います\n\nバージョン %d\n\nデータを初期化します。", mDoMemCd_getDataVersion()); errDispInitSet(errmsg); field_0x0280 = false; mWindowCloseMsgDispCb = NULL; @@ -5091,7 +5239,7 @@ void dFile_select_c::MemCardErrYesNoCursorMoveAnm() { void dFile_select_c::errorTxtSet(u16 i_msgId) { if (i_msgId == 0xffff) { - strcpy(mErrorMsgStringPtr[mErrorTxtDispIdx ^ 1], ""); + SAFE_STRCPY(mErrorMsgStringPtr[mErrorTxtDispIdx ^ 1], ""); } else { fileSel.mMessageString->getString( i_msgId, (J2DTextBox*)mErrorMsgTxtPane[mErrorTxtDispIdx ^ 1]->getPanePtr(), NULL, diff --git a/src/d/d_map.cpp b/src/d/d_map.cpp index fa9049fb00..8cecead15a 100644 --- a/src/d/d_map.cpp +++ b/src/d/d_map.cpp @@ -931,6 +931,13 @@ bool renderingAmap_c::isDrawIconSingle2(dTres_c::data_s const* i_data, bool para } break; case 5: +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + break; + } +#endif if (((i_data->mNo == 255 || (i_data->mNo != 255 && !dComIfGs_isTbox(i_data->mNo))) && (i_data->mSwBit == 255 || (i_data->mSwBit != 255 && dComIfGs_isSwitch(i_data->mSwBit, i_data->mRoomNo)))) && diff --git a/src/d/d_menu_calibration.cpp b/src/d/d_menu_calibration.cpp index 0cc238e5d2..bc256547d7 100644 --- a/src/d/d_menu_calibration.cpp +++ b/src/d/d_menu_calibration.cpp @@ -14,7 +14,10 @@ #include "m_Do/m_Do_controller_pad.h" #include -// Need 0xC bytes of padding with no symbol between dMenu_Calibration_c::__vtable and the end of .data +#include "dusk/string.hpp" + +// Need 0xC bytes of padding with no symbol between dMenu_Calibration_c::__vtable and the end of +// .data // This is likely caused by the vtable of an abstract base class getting put there and then stripped out. // Not sure which abstract base class could go there though, so we simulate it with some dummy classes for now. class dummy_abstract_class { @@ -252,7 +255,7 @@ void dMenu_Calibration_c::setCalibrationValue() { void dMenu_Calibration_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -264,7 +267,7 @@ void dMenu_Calibration_c::setAButtonString(u16 i_stringID) { void dMenu_Calibration_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -276,7 +279,7 @@ void dMenu_Calibration_c::setBButtonString(u16 i_stringID) { void dMenu_Calibration_c::setStepString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 3; i++) { - strcpy(mpStepString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpStepString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 3; i++) { @@ -288,7 +291,7 @@ void dMenu_Calibration_c::setStepString(u16 i_stringID) { void dMenu_Calibration_c::setExplainString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 3; i++) { - strcpy(mpExplainString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpExplainString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 3; i++) { diff --git a/src/d/d_menu_collect.cpp b/src/d/d_menu_collect.cpp index 612806d372..e9b477d1ea 100644 --- a/src/d/d_menu_collect.cpp +++ b/src/d/d_menu_collect.cpp @@ -97,79 +97,157 @@ dMenu_Collect2D_c::~dMenu_Collect2D_c() { #if TARGET_PC void dMenu_Collect2D_c::menuCollectWide() { - // Main Canvas - mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); - mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); - - // Pieces of Heart - mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Scents - mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Quiver - mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Wallet - mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Poes - mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Fish Bestiary - mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Letters - mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hidden Skills - mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Green Tunic - mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Zora Armor - mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Magic Armor - mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Shield - mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Hylian Shield - mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Ordon Sword - mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Master Sword - mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Bugs - mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Collection" Text - mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Save" Text - mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // "Options" Text - mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Name Text - mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - // Item Description Text - mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); - - #if TARGET_PC - if (mpDrawCursor) { - mpDrawCursor->refreshAspectScale(); + // Get pre-scale values for each pane + if (!cachedPanes) { + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + if (!entry.cached) { + entry.origTransX = pane->getTranslateX(); + entry.origTransY = pane->getTranslateY(); + entry.cached = true; + } + } + cachedPanes = true; + } + + // Reset all panes + mpScreen->scale(1.0f, 1.0f); + mpScreen->translate(0.0f, 0.0f); + for (PaneCache& entry : mpScreenPanes) { + J2DPane* pane = mpScreen->search(entry.tag); + pane->scale(1.0f, 1.0f); + pane->translate(entry.origTransX, entry.origTransY); + } + + // Reset button overlay + mpScreenIcon->translate(0.0f, 0.0f); + + switch (dusk::getSettings().game.menuScalingMode) { + case dusk::MenuScaling::GameCube: + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; + case dusk::MenuScaling::Wii: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Button Overlay + mpScreenIcon->translate(-mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // "Save" Text + mpScreen->search(MULTI_CHAR('sa_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Options" Text + mpScreen->search(MULTI_CHAR('op_tex_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Pieces of Heart + mpScreen->search(MULTI_CHAR('heart_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Scents + mpScreen->search(MULTI_CHAR('wolf_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Quiver + mpScreen->search(MULTI_CHAR('item_0_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Wallet + mpScreen->search(MULTI_CHAR('item_1_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Poes + mpScreen->search(MULTI_CHAR('item_2_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Fish Bestiary + mpScreen->search(MULTI_CHAR('fish_3_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Letters + mpScreen->search(MULTI_CHAR('lett_4_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hidden Skills + mpScreen->search(MULTI_CHAR('maki_5_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Green Tunic + mpScreen->search(MULTI_CHAR('fuku_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Zora Armor + mpScreen->search(MULTI_CHAR('fuku_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Magic Armor + mpScreen->search(MULTI_CHAR('fuku_n2'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Shield + mpScreen->search(MULTI_CHAR('tate_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Hylian Shield + mpScreen->search(MULTI_CHAR('tate_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Ordon Sword + mpScreen->search(MULTI_CHAR('ken_n0'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Master Sword + mpScreen->search(MULTI_CHAR('ken_n1'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Bugs + mpScreen->search(MULTI_CHAR('kabu_6n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Text + mpScreen->search(MULTI_CHAR('t_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('f_t00'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Name Text + mpScreen->search(MULTI_CHAR('itemn_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Item Description Text + mpScreen->search(MULTI_CHAR('infotxtn'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); + } + break; + case dusk::MenuScaling::Dusklight: + // Main Canvas + mpScreen->scale(mDoGph_gInf_c::hudAspectScaleUp, 1.0f); + mpScreen->translate(mDoGph_gInf_c::getSafeMinXF(), 0.0f); + + // Save/Options Buttons + mpScreen->search(MULTI_CHAR('sa_op_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // "Collection" Title Bar + mpScreen->search(MULTI_CHAR('title_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + // Main Central Elements + mpScreen->search(MULTI_CHAR('menu_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('w_er_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + mpScreen->search(MULTI_CHAR('center_n'))->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.0f); + + const f32 leftShift = 48.0f * (mDoGph_gInf_c::hudAspectScaleUp - 1.0f); // Shifting certain items left to keep center (> 4:3 only) + + // Item Name/Description Text + J2DPane* info_n = mpScreen->search(MULTI_CHAR('info_n')); + static f32 infoTransX_orig = info_n->getTranslateX(); + info_n->translate(infoTransX_orig - leftShift, info_n->getTranslateY()); + + // Designs + J2DPane* lavel_n = mpScreen->search(MULTI_CHAR('lavel_n')); + static f32 lavelTransX_orig = lavel_n->getTranslateX(); + lavel_n->translate(lavelTransX_orig - leftShift, lavel_n->getTranslateY()); + + // Fused Shadow/Mirror Background + J2DPane* modelbgn = mpScreen->search(MULTI_CHAR('modelbgn')); + static f32 modelbgnTransX_orig = modelbgn->getTranslateX(); // Get pre-scale value + modelbgn->setBasePosition(J2DBasePosition_0); + modelbgn->scale(mDoGph_gInf_c::hudAspectScaleDown, 1.3f); + f32 modelbgn_scaleFactor = 1.0f + 0.16f * (mDoGph_gInf_c::hudAspectScaleDown - 1.0f); + modelbgn->translate((modelbgnTransX_orig - 12.0f) * modelbgn_scaleFactor, modelbgn->getTranslateY()); + + // Selection Cursor + if (mpDrawCursor) { + mpDrawCursor->refreshAspectScale(1.0f); + } + break; } - #endif } #endif @@ -2167,18 +2245,18 @@ void dMenu_Collect2D_c::_draw() { if (mItemNameString == 0) { #if REGION_JPN - char* stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1')))->getStringPtr(); + TEXT_SPAN stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1')))->getStringPtr(); #else - char* stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('f_text1')))->getStringPtr(); + TEXT_SPAN stringPtr1 = static_cast(mpScreen->search(MULTI_CHAR('f_text1')))->getStringPtr(); #endif - strcpy(stringPtr1, ""); + SAFE_STRCPY(stringPtr1, ""); #if REGION_JPN - char* stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('i_text0')))->getStringPtr(); + TEXT_SPAN stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('i_text0')))->getStringPtr(); #else - char* stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('f_text0')))->getStringPtr(); + TEXT_SPAN stringPtr0 = static_cast(mpScreen->search(MULTI_CHAR('f_text0')))->getStringPtr(); #endif - strcpy(stringPtr0, ""); + SAFE_STRCPY(stringPtr0, ""); } else { #if REGION_JPN J2DTextBox* textBox1 = static_cast(mpScreen->search(MULTI_CHAR('i_text1'))); @@ -2229,13 +2307,13 @@ void dMenu_Collect2D_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_a_tag[i]))->getStringPtr(); - strcpy(stringPtr, ""); + SAFE_STRCPY(stringPtr, ""); } } else { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_a_tag[i]))->getStringPtr(); dMeter2Info_getStringKanji(i_stringID, stringPtr, NULL); } @@ -2253,13 +2331,13 @@ void dMenu_Collect2D_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_b_tag[i]))->getStringPtr(); - strcpy(stringPtr, ""); + SAFE_STRCPY(stringPtr, ""); } } else { for (int i = 0; i < 5; i++) { - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreenIcon->search(text_b_tag[i]))->getStringPtr(); dMeter2Info_getStringKanji(i_stringID, stringPtr, NULL); } @@ -2278,7 +2356,7 @@ void dMenu_Collect2D_c::setItemNameString(u8 param_0, u8 param_1) { setItemNameStringNull(); } else { #if REGION_JPN - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n00')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n01')))->getStringPtr(); @@ -2288,7 +2366,7 @@ void dMenu_Collect2D_c::setItemNameString(u8 param_0, u8 param_1) { stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n03')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); #else - char* stringPtr = + TEXT_SPAN stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n04')))->getStringPtr(); dMeter2Info_getStringKanji(uVar6, stringPtr, NULL); stringPtr = static_cast(mpScreen->search(MULTI_CHAR('item_n05')))->getStringPtr(); @@ -2306,22 +2384,22 @@ void dMenu_Collect2D_c::setItemNameStringNull() { mItemNameString = 0; #if REGION_JPN J2DTextBox* textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n00')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n01')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n02')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n03')); #else J2DTextBox* textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n04')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n05')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n06')); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); textBox = (J2DTextBox*)mpScreen->search(MULTI_CHAR('item_n07')); #endif - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } dMenu_Collect3D_c::dMenu_Collect3D_c(JKRExpHeap* param_0, dMenu_Collect2D_c* param_1, diff --git a/src/d/d_menu_dmap.cpp b/src/d/d_menu_dmap.cpp index 51cb11cdd1..609d8ef6be 100644 --- a/src/d/d_menu_dmap.cpp +++ b/src/d/d_menu_dmap.cpp @@ -26,6 +26,8 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" + #if (PLATFORM_WII || PLATFORM_SHIELD) #define POINTER_OPT dComIfGs_getOptPointer() #else @@ -116,9 +118,9 @@ dMenu_DmapBg_c::dMenu_DmapBg_c(JKRExpHeap* i_heap, STControl* i_stick) { mapScreenInit(); char archive_path[32]; - strcpy(archive_path, "/res/FieldMap/D_MN10.arc"); + SAFE_STRCPY(archive_path, "/res/FieldMap/D_MN10.arc"); char stage_name[8]; - strcpy(stage_name, dComIfGp_getStartStageName()); + SAFE_STRCPY(stage_name, dComIfGp_getStartStageName()); archive_path[18] = stage_name[4]; archive_path[19] = stage_name[5]; @@ -135,6 +137,16 @@ dMenu_DmapBg_c::dMenu_DmapBg_c(JKRExpHeap* i_heap, STControl* i_stick) { memset(&field_0xd80, 0, 20); buttonIconScreenInit(); field_0xdd0 = 0; + +#if TARGET_PC + mpPoeCountIcon = JKR_NEW J2DPicture((ResTIMG*)JKRGetNameResource("ni_item_icon_pou.bti", dComIfGp_getItemIconArchive())); + + mpPoeCountPane = JKR_NEW J2DTextBox(); + if (mpPoeCountPane != nullptr) { + mpPoeCountPane->setFontSize(15.0f, 15.0f); + mpPoeCountPane->setFont(mDoExt_getMesgFont()); + } +#endif } void dMenu_DmapBg_c::mapScreenInit() { @@ -382,7 +394,7 @@ void dMenu_DmapBg_c::setAButtonString(u32 i_msgNo) { }; for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - strcpy(((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), ""); } else { dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(cont_at[i]))->getStringPtr(), NULL); } @@ -399,7 +411,7 @@ void dMenu_DmapBg_c::setBButtonString(u32 i_msgNo) { }; for (int i = 0; i < 5; i++) { if (i_msgNo == 0) { - strcpy(((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), ""); } else { dMeter2Info_getStringKanji(i_msgNo, ((J2DTextBox*)mButtonScreen->search(cont_bt[i]))->getStringPtr(), NULL); } @@ -431,7 +443,7 @@ void dMenu_DmapBg_c::setCButtonString(u32 i_msgNo) { if (msgNo == 0) { for (i = 0; i < 2; i++) { - strcpy(((J2DTextBox*)mButtonScreen->search(c_tag[i]))->getStringPtr(), ""); + SAFE_STRCPY(((J2DTextBox*)mButtonScreen->search(c_tag[i]))->getStringPtr(), ""); } mpCButton->setAlphaRate(0.5f); } else { @@ -762,6 +774,14 @@ dMenu_DmapBg_c::~dMenu_DmapBg_c() { mDoExt_destroyExpHeap(mpTalkHeap); mpTalkHeap = NULL; } + +#if TARGET_PC + JKR_DELETE(mpPoeCountIcon); + mpPoeCountIcon = NULL; + + JKR_DELETE(mpPoeCountPane); + mpPoeCountPane = NULL; +#endif } void dMenu_DmapBg_c::setAllAlphaRate(f32 i_rate, bool param_2) { @@ -1014,6 +1034,35 @@ void dMenu_DmapBg_c::draw() { } mButtonScreen->draw(field_0xd94, field_0xd98, grafContext); + +#if TARGET_PC + if (dusk::getSettings().game.enhancedMapMenus) { + int nowPoeCount = 0; + int totalPoeCount = 0; + dMenuMapCommon_c::getDmapPoeCount(dComIfGp_getStartStageName(), nowPoeCount, totalPoeCount); + if (dComIfGs_isEventBit(dSv_event_flag_c::F_0456) && totalPoeCount > 0) { + const f32 x = field_0xd94 + mDoGph_gInf_c::ScaleHUDXLeft(80.0f); + const f32 y = 410.0f; + constexpr f32 iconsize = 48.0f * 0.8f; + + if (mpPoeCountIcon != nullptr) + mpPoeCountIcon->draw(x - 35.0f, y - 25.0f, iconsize, iconsize, false, false, false); + + char counter_text[6]; + snprintf(counter_text, sizeof(counter_text), "%d/%d", nowPoeCount, totalPoeCount); + mpPoeCountPane->setString(counter_text); + + mpPoeCountPane->setCharColor(0x000000FF); + mpPoeCountPane->setGradColor(0x000000FF); + mpPoeCountPane->draw(x + 1, y + 1, FB_WIDTH, HBIND_LEFT); + + mpPoeCountPane->setCharColor(0xC8C8C8FF); + mpPoeCountPane->setGradColor(0xC8C8C8FF); + mpPoeCountPane->draw(x, y, FB_WIDTH, HBIND_LEFT); + } + } +#endif + grafContext->scissor(scissor_left, scissor_top, scissor_width, scissor_height); grafContext->setScissor(); grafContext->setup2D(); diff --git a/src/d/d_menu_dmap_map.cpp b/src/d/d_menu_dmap_map.cpp index 0bb9717952..c4029b766e 100644 --- a/src/d/d_menu_dmap_map.cpp +++ b/src/d/d_menu_dmap_map.cpp @@ -69,6 +69,13 @@ bool renderingDmap_c::isDrawIconSingle2(dTres_c::data_s const* i_data, bool para JUT_ASSERT(1044, FALSE); break; case 5: +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + break; + } +#endif if ((i_data->mNo == 0xFF || (i_data->mNo != 0xFF && !dComIfGs_isTbox(i_data->mNo))) && (i_data->mSwBit == 0xFF || (i_data->mSwBit != 0xFF && dComIfGs_isSwitch(i_data->mSwBit, i_data->mRoomNo))) && param_1) { rt = true; } @@ -352,7 +359,14 @@ f32 dMenu_StageMapCtrl_c::getPixelStageSizeZ() const { f32 dMenu_StageMapCtrl_c::getPixelCenterX() const { f32 var_f31 = dMpath_c::getCenterX(); + #if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + return (1.0f / field_0xbc) * (field_0x9c + var_f31); + } + else return (1.0f / field_0xbc) * (field_0x9c - var_f31); + #else return (1.0f / field_0xbc) * (field_0x9c - var_f31); + #endif } f32 dMenu_StageMapCtrl_c::getPixelCenterZ() const { @@ -411,7 +425,18 @@ inline static f32 rightModeCnvPos(f32 param_0) { void dMenu_StageMapCtrl_c::cnvPosTo2Dpos(f32 param_0, f32 param_1, f32* param_2, f32* param_3) const { if (param_2 != NULL) { + #if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + *param_2 = + (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (field_0x9c + param_0)); + } else { + *param_2 = + (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c)); + } + #else *param_2 = (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c)); + #endif + } if (param_3 != NULL) { @@ -908,7 +933,9 @@ void dMenu_StageMapCtrl_c::move() { void dMenu_DmapMapCtrl_c::draw() { if (field_0xef != 0) { - setPos(field_0xeb, field_0xec, field_0x9c, field_0xa0, field_0xbc, true, field_0xd8); + setPos(field_0xeb, field_0xec, + IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -field_0x9c :) field_0x9c, + field_0xa0, field_0xbc, true, field_0xd8); } } diff --git a/src/d/d_menu_fishing.cpp b/src/d/d_menu_fishing.cpp index 5e1b124b38..6a3d0aefe8 100644 --- a/src/d/d_menu_fishing.cpp +++ b/src/d/d_menu_fishing.cpp @@ -16,6 +16,7 @@ #include "m_Do/m_Do_graphic.h" #include +#include "dusk/string.hpp" #include "dusk/version.hpp" typedef void (dMenu_Fishing_c::*initFunc)(); @@ -372,7 +373,7 @@ void dMenu_Fishing_c::screenSetDoIcon() { void dMenu_Fishing_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -384,7 +385,7 @@ void dMenu_Fishing_c::setAButtonString(u16 i_stringID) { void dMenu_Fishing_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -419,29 +420,29 @@ void dMenu_Fishing_c::setFishParam(int i_fishIdx, u16 i_fishCount, u8 i_fishSize dComIfGp_setMessageCountNumber(i_fishSize); mpString->getString(0x597, field_0x10c[i][i_fishIdx], NULL, NULL, NULL, 0); // "inches" char* stringPtr = field_0x10c[i][i_fishIdx]->getStringPtr(); - strcpy(strBuff1, stringPtr); + SAFE_STRCPY(strBuff1, stringPtr); int j; for (j = 0; strBuff1[j + fishSizeFigure] != 0; j++) { strBuff2[j] = strBuff1[j + fishSizeFigure]; } strBuff2[j] = 0; strBuff1[fishSizeFigure] = 0; - strcpy(field_0x10c[i][i_fishIdx]->getStringPtr(), strBuff1); - strcpy(field_0x16c[i][i_fishIdx]->getStringPtr(), strBuff2); + SAFE_STRCPY(field_0x10c[i][i_fishIdx]->getStringPtr(), strBuff1); + SAFE_STRCPY(field_0x16c[i][i_fishIdx]->getStringPtr(), strBuff2); // part two, i_fishCount dComIfGp_setMessageCountNumber(i_fishCount); mpString->getString(0x598, field_0x13c[i][i_fishIdx], NULL, NULL, NULL, 0); // "fish" stringPtr = field_0x13c[i][i_fishIdx]->getStringPtr(); - strcpy(strBuff1, stringPtr); + SAFE_STRCPY(strBuff1, stringPtr); int k; for (k = 0; strBuff1[k + fishCountFigure] != 0; k++) { strBuff2[k] = strBuff1[k + fishCountFigure]; } strBuff2[k] = 0; strBuff1[fishCountFigure] = 0; - strcpy(field_0x13c[i][i_fishIdx]->getStringPtr(), strBuff1); - strcpy(field_0x19c[i][i_fishIdx]->getStringPtr(), strBuff2); + SAFE_STRCPY(field_0x13c[i][i_fishIdx]->getStringPtr(), strBuff1); + SAFE_STRCPY(field_0x19c[i][i_fishIdx]->getStringPtr(), strBuff2); } } diff --git a/src/d/d_menu_fmap.cpp b/src/d/d_menu_fmap.cpp index d463b2328d..b8a69ddc55 100644 --- a/src/d/d_menu_fmap.cpp +++ b/src/d/d_menu_fmap.cpp @@ -22,6 +22,7 @@ #include "d/d_msg_scrn_explain.h" #include "d/d_stage.h" #include "dusk/memory.h" +#include "dusk/string.hpp" #include "f_op/f_op_msg_mng.h" static dMf_HIO_c g_fmHIO; @@ -126,7 +127,7 @@ const char* dMenuFmap_getStartStageName(void* i_fieldData) { if (!strcmp(dComIfGp_getStartStageName(), data[i].mStageName)) { // !@bug: probably supposed to be data[i].mVirtualStageName, but doesn't matter // because the two entries have the same virtual stage name - strcpy(virtual_stage, data->mVirtualStageName); + SAFE_STRCPY(virtual_stage, data->mVirtualStageName); return virtual_stage; } } @@ -197,7 +198,7 @@ dMenu_Fmap_c::dMenu_Fmap_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i mIsWarpMap = false; mProcess = PROC_REGION_MAP; - strcpy(mMarkedStageName, ""); + SAFE_STRCPY(mMarkedStageName, ""); if (g_fmapHIO.mpArcData != NULL && g_fmapHIO.mpArcData->isMounted()) { mProcess = i_process; mpMapArchive = g_fmapHIO.mpArcData; @@ -525,6 +526,16 @@ void dMenu_Fmap_c::_move() { } mpDraw2DBack->setSpotTextureFadeAlpha(mSpotTextureFadeAlpha); } + +#if TARGET_PC + u8 region = mpDraw2DBack->getSelectRegion(); + if (region != 0xFF && mpDraw2DBack->isShowRegion(region)) { + mpDraw2DTop->mSelectRegionNo = region; + } else { + mpDraw2DTop->mSelectRegionNo = 0xFF; + } + +#endif } void dMenu_Fmap_c::_draw() { @@ -1853,7 +1864,7 @@ bool dMenu_Fmap_c::isRoomCheck(int i_stageNo, int i_roomNo) { for (; i < mDataNumMax; i++) { if (checked_data[i]) continue; - strcpy(stage_name, stages[i].mName); + SAFE_STRCPY(stage_name, stages[i].mName); for (int k = 0; k < 64; k++) { local_e0[k] = false; @@ -2072,14 +2083,14 @@ bool dMenu_Fmap_c::readAreaData(u8 i_regionNo, bool i_isSelectedRegion) { for (; i < mDataNumMax; i++) { if (checked_data[i]) continue; - strcpy(tmp_stage_name, mTmpStageName); + SAFE_STRCPY(tmp_stage_name, mTmpStageName); resetRoomDataBit(); if (i_isSelectedRegion) { - strcpy(mTmpStageName, stages[i].mName); + SAFE_STRCPY(mTmpStageName, stages[i].mName); } else { - strcpy(mTmpStageName, tmp_stage_name); + SAFE_STRCPY(mTmpStageName, tmp_stage_name); } - strcpy(tmp_stage_name, stages[i].mName); + SAFE_STRCPY(tmp_stage_name, stages[i].mName); for (int k = 0; k < 64; k++) { local_e0[k] = false; @@ -2155,7 +2166,7 @@ bool dMenu_Fmap_c::readAreaData(u8 i_regionNo, bool i_isSelectedRegion) { mpDraw2DBack->setStageOriginXZ(mSpotNum, stages[stage_index].mOffsetX, stages[stage_index].mOffsetZ); mAreaName[mSpotNum] = stages[stage_index].mAreaName; - strcpy(mStageName[mSpotNum], stage_name); + SAFE_STRCPY(mStageName[mSpotNum], stage_name); mSpotNum++; } @@ -2203,7 +2214,7 @@ bool dMenu_Fmap_c::readRoomData(char const* i_stageName, dMenu_Fmap_stage_data_c dMenu_Fmap_stage_arc_data_c* room_data = NULL; char stage_path[20]; - sprintf(stage_path, "%s/stage.dat", i_stageName); + SAFE_SPRINTF(stage_path, "%s/stage.dat", i_stageName); if (readFieldMapData((void**)&room_data, stage_path, false, false)) { ((dMenuMapCommon_c::RoomData_c*)o_roomData)->setRoomData(room_data); } @@ -2217,7 +2228,7 @@ bool dMenu_Fmap_c::readRoomData(char const* i_stageName, dMenu_Fmap_stage_data_c void* dzs_data = NULL; char room_path[20]; - sprintf(room_path, "%s/room%d.dzs", i_stageName, room_nos[i]); + SAFE_SPRINTF(room_path, "%s/room%d.dzs", i_stageName, room_nos[i]); if (readRoomDzsData(&dzs_data, 0x1500, room_path)) { dMenu_Fmap_data_c* map_data = JKR_NEW dMenu_Fmap_data_c(); @@ -2308,9 +2319,9 @@ void dMenu_Fmap_c::decodeFieldMapData() { bool local_3f = false; if (j == 7) { local_3f = true; - sprintf(tex_path, "tex/region8.bti"); + SAFE_SPRINTF(tex_path, "tex/region8.bti"); } else { - sprintf(tex_path, "tex/region%d.bti", regions[i].mTextureReadNum); + SAFE_SPRINTF(tex_path, "tex/region%d.bti", regions[i].mTextureReadNum); } if (readFieldMapData((void**)&mRegionTexture[j], tex_path, true, local_3f)) { @@ -2428,7 +2439,7 @@ bool dMenu_Fmap_c::removeAreaData() { } for (int i = 0; i < 20; i++) { - strcpy(mStageName[i], ""); + SAFE_STRCPY(mStageName[i], ""); } field_0x305 = false; @@ -2539,11 +2550,11 @@ void dMenu_Fmap_c::drawIcon(f32 param_0, bool param_1) { fopAc_ac_c* player = daPy_getPlayerActorClass(); pos.set(dMapInfo_n::getMapPlayerPos()); angle = player->shape_angle.y; - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); angle = dComIfGs_getPlayerFieldLastStayAngleY(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } u8 is_portal_demo1 = 0; @@ -2600,6 +2611,13 @@ void dMenu_Fmap_c::drawLightDropIcon() { } void dMenu_Fmap_c::drawBatsumarkIcon() { +#if TARGET_PC + if (dusk::getSettings().game.removeQuestMapMarkers && + dComIfGs_isEventBit(dSv_event_flag_c::saveBitLabels[0x190])) + { + return; + } +#endif drawIcon(5, 0x12); } @@ -2625,11 +2643,11 @@ void dMenu_Fmap_c::drawPlayEnterIcon() { if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapRestartPos()); angle = dComIfGs_getRestartRoomAngleY(); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); angle = dComIfGs_getPlayerFieldLastStayAngleY(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } mpDraw2DBack->setIcon2DPos(0x15, stage_name, pos.x, pos.z, cM_sht2d(angle), 0, false); } @@ -2771,10 +2789,10 @@ void dMenu_Fmap_c::arrowPosInit() { char stage_name[8]; if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapPlayerPos()); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } f32 fVar1 = 0.0f; @@ -2827,7 +2845,7 @@ void dMenu_Fmap_c::tableArrowPosInit(bool param_0) { } static char* stage_name[4] = {"F_SP115", "F_SP113", "F_SP109", "F_SP108"}; - strcpy(mMarkedStageName, stage_name[iVar5]); + SAFE_STRCPY(mMarkedStageName, stage_name[iVar5]); if (param_0) { f32 pos1_x, pos2_x, pos1_z, pos2_z, icon_x, icon_z; @@ -2856,7 +2874,7 @@ void dMenu_Fmap_c::yamibossArrowPosInit() { void dMenu_Fmap_c::howlArrowPosInit() { u8 type = dMeter2Info_getGoldWolfMapType(); static char* stage_name[6] = {"F_SP104", "F_SP122", "F_SP122", "F_SP124", "F_SP111", "F_SP116"}; - strcpy(mMarkedStageName, stage_name[type - 2]); + SAFE_STRCPY(mMarkedStageName, stage_name[type - 2]); static const int i_swBit[6] = {0x41, 0x29, 0x2a, 0x32, 0x79, 0x32}; f32 icon_x, icon_z; if (searchIcon(6, i_swBit[type - 2], &icon_x, &icon_z)) { @@ -2893,10 +2911,10 @@ cXyz* dMenu_Fmap_c::getPlayerPos2D() { char stage_name[8]; if (dComIfGs_isPlayerFieldLastStayFieldDataExistFlag()) { pos.set(dMapInfo_n::getMapPlayerPos()); - strcpy(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); + SAFE_STRCPY(stage_name, dMenuFmap_getStartStageName(mpFieldDat)); } else { pos = dComIfGs_getPlayerFieldLastStayPos(); - strcpy(stage_name, dComIfGs_getPlayerFieldLastStayName()); + SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); } mpDraw2DBack->calcAllMapPos2D(stage_name, pos.x - mpDraw2DBack->getStageTransX(), pos.z - mpDraw2DBack->getStageTransZ(), diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index a63f6ec282..cbfffc3fc2 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -1973,6 +1973,11 @@ void dMenu_Fmap2DBack_c::stageMapMove(STControl* i_stick, u8 param_1, bool param if (stick_value >= slow_bound && param_2 && field_0x1238 != 2) { bVar6 = true; s16 angle = i_stick->getAngleStick(); +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + angle = -angle; + } +#endif f32 local_68 = mTexMaxX - mTexMinX; f32 spot_zoom = getSpotMapZoomRate(); f32 region_zoom = getRegionMapZoomRate(mRegionCursor); @@ -2482,6 +2487,18 @@ dMenu_Fmap2DTop_c::dMenu_Fmap2DTop_c(JKRExpHeap* i_heap, STControl* i_stick) { set3DStickString(0x524); #endif +#if TARGET_PC + mpPoeCountIcon = JKR_NEW J2DPicture((ResTIMG*)JKRGetNameResource("ni_item_icon_pou.bti", dComIfGp_getItemIconArchive())); + + mpPoeCountPane = JKR_NEW J2DTextBox(); + if (mpPoeCountPane != nullptr) { + mpPoeCountPane->setFontSize(15.0f, 15.0f); + mpPoeCountPane->setFont(mDoExt_getMesgFont()); + } + + mSelectRegionNo = 0xFF; +#endif + setHIO(true); } @@ -2540,6 +2557,14 @@ dMenu_Fmap2DTop_c::~dMenu_Fmap2DTop_c() { } JKR_DELETE(mpAnm); mpAnm = NULL; + +#if TARGET_PC + JKR_DELETE(mpPoeCountIcon); + mpPoeCountIcon = NULL; + + JKR_DELETE(mpPoeCountPane); + mpPoeCountPane = NULL; +#endif } void dMenu_Fmap2DTop_c::_execute() { @@ -2636,6 +2661,35 @@ void dMenu_Fmap2DTop_c::draw() { ctx->scissor(mTransX, 0.0f, FB_WIDTH, FB_HEIGHT); ctx->setScissor(); mpTitleScreen->draw(mTransX, mTransY, ctx); + +#if TARGET_PC + if (dusk::getSettings().game.enhancedMapMenus) { + int nowPoeCount = 0; + int totalPoeCount = 0; + dMenuMapCommon_c::getFmapPoeCount(mSelectRegionNo, nowPoeCount, totalPoeCount); + if (dComIfGs_isEventBit(dSv_event_flag_c::F_0456) && totalPoeCount > 0) { + const f32 x = mTransX + mDoGph_gInf_c::ScaleHUDXRight(485.0f); + const f32 y = 380.0f; + constexpr f32 iconsize = 48.0f * 0.8f; + + if (mpPoeCountIcon != nullptr) + mpPoeCountIcon->draw(x - 35.0f, y - 25.0f, iconsize, iconsize, false, false, false); + + char counter_text[6]; + snprintf(counter_text, sizeof(counter_text), "%d/%d", nowPoeCount, totalPoeCount); + mpPoeCountPane->setString(counter_text); + + mpPoeCountPane->setCharColor(0x000000FF); + mpPoeCountPane->setGradColor(0x000000FF); + mpPoeCountPane->draw(x + 1, y + 1, FB_WIDTH, HBIND_LEFT); + + mpPoeCountPane->setCharColor(0xC8C8C8FF); + mpPoeCountPane->setGradColor(0xC8C8C8FF); + mpPoeCountPane->draw(x, y, FB_WIDTH, HBIND_LEFT); + } + } +#endif + ctx->scissor(scissor_left, scissor_top, scissor_width, scissor_height); ctx->setScissor(); if (mpScrnExplain) { @@ -2681,7 +2735,7 @@ void dMenu_Fmap2DTop_c::setTitleNameString(u32 param_0) { #endif for (int i = 0; i < 7; i++) { if (param_0 == 0) { - strcpy(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setTitleNameString_font_name[i]))) ->getStringPtr(), ""); } else { @@ -2704,7 +2758,7 @@ void dMenu_Fmap2DTop_c::setAreaNameString(u32 param_0) { #endif for (int i = 0; i < 3; i++) { if (param_0 == 0) { - strcpy(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) + SAFE_STRCPY(((J2DTextBox*)(mpTitleScreen->search(setAreaNameString_area_name[i]))) ->getStringPtr(), ""); } else { @@ -2817,7 +2871,7 @@ void dMenu_Fmap2DTop_c::setCrossLRString(u32 param_0) { if (param_0 == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* text_box = static_cast(mpTitleScreen->search(juji_c[i])); - strcpy(text_box->getStringPtr(), ""); + SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('juy_sha0'))->show(); mAlphaDpad = 1; @@ -2842,7 +2896,7 @@ void dMenu_Fmap2DTop_c::set3DStickString(u32 param_0) { if (param_0 == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* text_box = static_cast(mpTitleScreen->search(ast_c[i])); - strcpy(text_box->getStringPtr(), ""); + SAFE_STRCPY(text_box->getStringPtr(), ""); } mpTitleScreen->search(MULTI_CHAR('as_sha0'))->show(); mAlphaAnalogStick = 1; diff --git a/src/d/d_menu_insect.cpp b/src/d/d_menu_insect.cpp index 88e05226e8..8e6ad49b5b 100644 --- a/src/d/d_menu_insect.cpp +++ b/src/d/d_menu_insect.cpp @@ -352,12 +352,12 @@ void dMenu_Insect_c::explain_open_init() { "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength1))); - strcat(local_b8, local_78); + SAFE_STRCAT(local_b8, local_78); snprintf(cStack_d8, 32, "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength2))); - strcat(cStack_d8, local_98); + SAFE_STRCAT(cStack_d8, local_98); mpSelect_c->setString("", local_b8, cStack_d8); mpSelect_c->setRubyString("", "", ""); mpSelect_c->selAnimeInit(2, field_0xf7 + 1, 0, length, 0); @@ -730,7 +730,7 @@ u8 dMenu_Insect_c::dpdMove() { void dMenu_Insect_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -742,7 +742,7 @@ void dMenu_Insect_c::setAButtonString(u16 i_stringID) { void dMenu_Insect_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_item_explain.cpp b/src/d/d_menu_item_explain.cpp index dd3af54571..bebcb2cdb0 100644 --- a/src/d/d_menu_item_explain.cpp +++ b/src/d/d_menu_item_explain.cpp @@ -508,12 +508,12 @@ void dMenu_ItemExplain_c::move_select_init() { "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength1))); - strcat(local_64, local_88); + SAFE_STRCAT(local_64, local_88); snprintf(cStack78, 20, "\x1B" "CR[%d]", (int)(0.5f * (length - stringLength2))); - strcat(cStack78, local_80); + SAFE_STRCAT(cStack78, local_80); mpSelect_c->setString("", local_64, cStack78); mpSelect_c->setRubyString("", "", ""); mpSelect_c->selAnimeInit(2, field_0xe2 + 1, 0, length, 0); diff --git a/src/d/d_menu_letter.cpp b/src/d/d_menu_letter.cpp index 4bfda4b9cd..cd7da6a6f4 100644 --- a/src/d/d_menu_letter.cpp +++ b/src/d/d_menu_letter.cpp @@ -501,10 +501,10 @@ void dMenu_Letter_c::read_open_init() { field_0x3e2 = mpString->getPageMax(D_MENU_LETTER_LINE_MAX); if (field_0x3e2 > 1) { char acStack_30[20]; - sprintf(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); + SAFE_SPRINTF(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); for (int i = 0; i < 2; i++) { field_0x1e4[i]->show(); - strcpy(field_0x1e4[i]->getStringPtr(), acStack_30); + SAFE_STRCPY(field_0x1e4[i]->getStringPtr(), acStack_30); } } else { for (int i = 0; i < 2; i++) { @@ -654,10 +654,10 @@ void dMenu_Letter_c::read_next_fadein_init() { D_MENU_LETTER_LINE_MAX, text2, text1, NULL, NULL, 0); } char acStack_30[10]; - sprintf(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); + SAFE_SPRINTF(acStack_30, "%d/%d", field_0x3e3, field_0x3e2); for (int i = 0; i < 2; i++) { field_0x1e4[i]->show(); - strcpy(field_0x1e4[i]->getStringPtr(), acStack_30); + SAFE_STRCPY(field_0x1e4[i]->getStringPtr(), acStack_30); } } @@ -1273,7 +1273,7 @@ void dMenu_Letter_c::copyDMYMenu() { void dMenu_Letter_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -1286,7 +1286,7 @@ void dMenu_Letter_c::setAButtonString(u16 i_stringID) { void dMenu_Letter_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_map_common.cpp b/src/d/d_menu_map_common.cpp index 0017975c4e..5ae820931a 100644 --- a/src/d/d_menu_map_common.cpp +++ b/src/d/d_menu_map_common.cpp @@ -394,8 +394,21 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4 icon_size_y *= _c7c; } +#if TARGET_PC + f32 rotation = mIconInfo[info_idx].rotation; + if (dusk::getSettings().game.enableMirrorMode && + (mIconInfo[info_idx].icon_no == ICON_LINK_e || + mIconInfo[info_idx].icon_no == ICON_LINK_ENTER_e)) + { + rotation = -rotation; + } + + mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z, + rotation); +#else mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z, mIconInfo[info_idx].rotation); +#endif if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) { mPictures[mIconInfo[info_idx].icon_no]->setAlpha((180.0f * _c80) / 255.0f); @@ -747,3 +760,113 @@ void dMenuMapCommon_c::debugIcon() { setIconInfo(ICON_LV8_WARP_e, pos_x, pos_y, 0.0f, scale, 1.0f, 1); } } + +#if TARGET_PC +static constexpr struct { + std::string_view stagename; + u8 switch_no; + s8 region_id; + u8 save_id; +} l_poeInfo[] = { + // Dungeons + {"D_MN06", 0x19, -1, 0x15}, + {"D_MN06", 0x18, -1, 0x15}, + {"D_MN07", 0x54, -1, 0x16}, + {"D_MN07", 0x55, -1, 0x16}, + {"D_MN10", 0x1E, -1, 0x13}, + {"D_MN10", 0x1F, -1, 0x13}, + {"D_MN10", 0x20, -1, 0x13}, + {"D_MN10", 0x21, -1, 0x13}, + {"D_MN11", 0x72, -1, 0x14}, + {"D_MN11", 0x15, -1, 0x14}, + {"D_MN11", 0x7F, -1, 0x14}, + + // Sub-Dungeons + {"D_SB01", 0x45, 4, 0x19}, + {"D_SB01", 0x46, 4, 0x19}, + {"D_SB01", 0x47, 4, 0x19}, + {"D_SB02", 0x60, 2, 0x19}, + {"D_SB03", 0x5E, 3, 0x1A}, + {"D_SB03", 0x5F, 3, 0x1A}, + {"D_SB03", 0x5D, 3, 0x1A}, + {"D_SB05", 0xA, 3, 0x1B}, + {"D_SB05", 0xB, 3, 0x1B}, + {"D_SB07", 0xF, 4, 0x1B}, + {"D_SB07", 0x10, 4, 0x1B}, + + // Field Spots + {"F_SP108", 0x5D, 1, 0x2}, + {"F_SP109", 0x5E, 2, 0x3}, + {"F_SP109", 0x5F, 2, 0x3}, + {"F_SP110", 0x59, 2, 0x3}, + {"F_SP111", 0x57, 2, 0x3}, + {"F_SP111", 0x58, 2, 0x3}, + {"F_SP113", 0x49, 3, 0x4}, + {"F_SP113", 0x4A, 3, 0x4}, + {"F_SP114", 0x7C, 5, 0x8}, + {"F_SP114", 0x7D, 5, 0x8}, + {"F_SP114", 0x7B, 5, 0x8}, + {"F_SP114", 0x7E, 5, 0x8}, + {"F_SP114", 0x7F, 5, 0x8}, + {"F_SP115", 0x46, 3, 0x4}, + {"F_SP115", 0x47, 3, 0x4}, + {"F_SP115", 0x4B, 3, 0x4}, + {"F_SP115", 0x4C, 3, 0x4}, + {"F_SP115", 0x4D, 3, 0x4}, + {"F_SP117", 0xF, 1, 0x7}, + {"F_SP117", 0x1E, 1, 0x7}, + {"F_SP117", 0x10, 1, 0x7}, + {"F_SP117", 0x11, 1, 0x7}, + {"F_SP118", 0x78, 4, 0xA}, + {"F_SP118", 0x5A, 4, 0xA}, + {"F_SP121", 0x3A, 2, 0x6}, + {"F_SP121", 0x39, 1, 0x6}, + {"F_SP121", 0x33, 3, 0x6}, + {"F_SP121", 0x3B, 3, 0x6}, + {"F_SP122", 0x49, 3, 0x6}, + {"F_SP122", 0x30, 3, 0x6}, + {"F_SP122", 0x47, 3, 0x6}, + {"F_SP124", 0x5B, 4, 0xA}, + {"F_SP124", 0x5C, 4, 0xA}, + {"F_SP124", 0x5D, 4, 0xA}, + {"F_SP124", 0x33, 4, 0xA}, + {"F_SP126", 0x48, 3, 0x4}, + {"F_SP128", 0x40, 2, 0x3}, + {"R_SP160", 0x1F, 3, 0x9}, +}; + +void dMenuMapCommon_c::getDmapPoeCount(const std::string& stageName, int& nowCount, int& totalCount) { + nowCount = 0; + totalCount = 0; + + for (const auto& i : l_poeInfo) { + if (stageName == i.stagename) { + if (dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == i.save_id) { + nowCount += dComIfGs_isSwitch(i.switch_no, -1); + } else { + nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + } + totalCount++; + } + } +} + +void dMenuMapCommon_c::getFmapPoeCount(const int regionNo, int& nowCount, int& totalCount) { + nowCount = 0; + totalCount = 0; + + if (regionNo < 0) + return; + + for (const auto& i : l_poeInfo) { + if (regionNo == i.region_id) { + if (dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == i.save_id) { + nowCount += dComIfGs_isSwitch(i.switch_no, -1); + } else { + nowCount += dComIfGs_isSaveSwitch(i.save_id, i.switch_no); + } + totalCount++; + } + } +} +#endif \ No newline at end of file diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index af322a1c01..62f30d2d99 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -2107,7 +2107,7 @@ void dMenu_Option_c::setAButtonString(u16 i_stringID) { if (stringId == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* textBox = (J2DTextBox*)mpScreenIcon->search(text_a_tag[i]); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -2127,7 +2127,7 @@ void dMenu_Option_c::setBButtonString(u16 i_stringID) { if (stringId == 0) { for (int i = 0; i < 5; i++) { J2DTextBox* textBox = (J2DTextBox*)mpScreenIcon->search(text_b_tag[i]); - strcpy(textBox->getStringPtr(), ""); + SAFE_STRCPY(textBox->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index ba2b86b760..5cdb8fbc04 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -1203,7 +1203,7 @@ void dMenu_Ring_c::setNameString(u32 i_stringID) { if (mNameStringID != i_stringID) { for (int i = 0; i < 4; i++) { if (i_stringID == 0) { - strcpy(textBox[i]->getStringPtr(), ""); + SAFE_STRCPY(textBox[i]->getStringPtr(), ""); } else { mpString->getString(i_stringID, textBox[i], NULL, NULL, NULL, 0); } diff --git a/src/d/d_menu_save.cpp b/src/d/d_menu_save.cpp index 53dbe37ca4..d73e4ff5e8 100644 --- a/src/d/d_menu_save.cpp +++ b/src/d/d_menu_save.cpp @@ -816,8 +816,8 @@ void dMenu_save_c::saveQuestion() { field_0x9c = 0; field_0x17a = 0; - strcpy(mpHeaderTxt[mHeaderTxtType], ""); - strcpy(mpHeaderTxt[mHeaderTxtType ^ 1], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType ^ 1], ""); field_0x64 = 0; field_0x50 = 1; field_0x40->setFrame(field_0x50); @@ -2033,7 +2033,7 @@ void dMenu_save_c::saveYesNoCancelMove() { void dMenu_save_c::headerTxtSet(u16 msgID) { if (msgID == 0xFFFF) { - strcpy(mpHeaderTxt[mHeaderTxtType ^ 1], ""); + SAFE_STRCPY(mpHeaderTxt[mHeaderTxtType ^ 1], ""); } else { mSaveSel.mMsgString->getString( msgID, (J2DTextBox*)mpHeaderTxtPane[mHeaderTxtType ^ 1]->getPanePtr(), NULL, @@ -2230,7 +2230,7 @@ void dMenu_save_c::errYesNoCursorMoveAnm() { void dMenu_save_c::errorTxtSet(u16 msgID) { if (msgID == 0xFFFF) { - strcpy(mpErrTxt[mErrTxtType ^ 1], ""); + SAFE_STRCPY(mpErrTxt[mErrTxtType ^ 1], ""); } else { J2DTextBox* tbox = (J2DTextBox*)mpErrTxtPane[mErrTxtType ^ 1]->getPanePtr(); mSaveSel.mMsgString->getString(msgID, tbox, NULL, mSaveSel.font[0], NULL, 0); @@ -2817,7 +2817,7 @@ void dMenu_save_c::menuSaveWide() { #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif } diff --git a/src/d/d_menu_skill.cpp b/src/d/d_menu_skill.cpp index 78ab82af94..2692d33c3f 100644 --- a/src/d/d_menu_skill.cpp +++ b/src/d/d_menu_skill.cpp @@ -610,7 +610,7 @@ void dMenu_Skill_c::setPageText() { void dMenu_Skill_c::setAButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpAButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpAButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -622,7 +622,7 @@ void dMenu_Skill_c::setAButtonString(u16 i_stringID) { void dMenu_Skill_c::setBButtonString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 5; i++) { - strcpy(mpBButtonString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpBButtonString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 5; i++) { @@ -634,7 +634,7 @@ void dMenu_Skill_c::setBButtonString(u16 i_stringID) { void dMenu_Skill_c::setNameString(u16 i_stringID) { if (i_stringID == 0) { for (int i = 0; i < 4; i++) { - strcpy(mpNameString[i]->getStringPtr(), ""); + SAFE_STRCPY(mpNameString[i]->getStringPtr(), ""); } } else { for (int i = 0; i < 4; i++) { diff --git a/src/d/d_meter2_draw.cpp b/src/d/d_meter2_draw.cpp index 8e824f583b..3c342ea7d4 100644 --- a/src/d/d_meter2_draw.cpp +++ b/src/d/d_meter2_draw.cpp @@ -2245,11 +2245,11 @@ void dMeter2Draw_c::drawButtonA(u8 i_action, f32 i_posX, f32 i_posY, f32 i_textP mp_string = getActionString(0x15, 1, NULL); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); } } else { for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpAText[i]->getPanePtr())->getStringPtr(), mp_string); } } @@ -2308,7 +2308,7 @@ void dMeter2Draw_c::drawButtonB(u8 i_action, bool param_1, f32 i_posX, f32 i_pos JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpBText[i]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpBText[i]->getPanePtr())->getStringPtr(), mp_string); } if (i_action == 0x26 || i_action == 0x2E) { @@ -2391,7 +2391,7 @@ void dMeter2Draw_c::drawButtonZ(u8 i_action) { JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpXYText[i][2]->getPanePtr())->getStringPtr(), mp_string); + SAFE_STRCPY(static_cast(mpXYText[i][2]->getPanePtr())->getStringPtr(), mp_string); } mpButtonXY[2]->scale(g_drawHIO.mButtonZScale, g_drawHIO.mButtonZScale); @@ -2490,7 +2490,7 @@ void dMeter2Draw_c::drawButtonXY(int i_no, u8 i_itemNo, u8 i_action, bool param_ JUT_ASSERT(0, strlen(mp_string) < (64)); for (int i = 0; i < 5; i++) { - strcpy(static_cast(mpXYText[i][i_no]->getPanePtr())->getStringPtr(), + SAFE_STRCPY(static_cast(mpXYText[i][i_no]->getPanePtr())->getStringPtr(), mp_string); } @@ -3243,7 +3243,7 @@ char* dMeter2Draw_c::getActionString(u8 i_action, u8 i_type, u8* param_2) { }; static char i_text_buf[32]; - strcpy(i_text_buf, ""); + SAFE_STRCPY(i_text_buf, ""); if (param_2 != NULL) { *param_2 = 1; diff --git a/src/d/d_meter2_info.cpp b/src/d/d_meter2_info.cpp index bb533e6370..63e22056f5 100644 --- a/src/d/d_meter2_info.cpp +++ b/src/d/d_meter2_info.cpp @@ -15,6 +15,8 @@ #include +#include "dusk/string.hpp" + enum ITEMICON_RES_FILE_ID { ITEMICON_BTI_ARI_MESU_00=0x3, ITEMICON_BTI_ARI_OSU_00=0x4, @@ -350,8 +352,8 @@ void dMeter2Info_c::decMsgKeyWaitTimer() { } } -void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getString(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -372,7 +374,7 @@ void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_ // check if i_stringID equals the message entry "Message ID" if (i_stringID == bmg_inf->entries[i].message_id) { string_ptr = (char*)(string_data + bmg_inf->entries[i].string_offset); // use entry "String Offset" to get string pointer - strcpy(o_string, string_ptr); + SAFE_STRCPY(o_string, string_ptr); if (i_msgEntry != NULL) { memcpy(i_msgEntry, &bmg_inf->entries[i], sizeof(JMSMesgEntry_c)); @@ -387,8 +389,8 @@ void dMeter2Info_c::getString(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_ } } -void dMeter2Info_c::getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getStringKana(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -456,8 +458,8 @@ void dMeter2Info_c::getStringKana(u32 i_stringID, char* o_string, JMSMesgEntry_c } } -void dMeter2Info_c::getStringKanji(u32 i_stringID, char* o_string, JMSMesgEntry_c* i_msgEntry) { - strcpy(o_string, ""); +void dMeter2Info_c::getStringKanji(u32 i_stringID, TEXT_SPAN o_string, JMSMesgEntry_c* i_msgEntry) { + SAFE_STRCPY(o_string, ""); u8* msgRes; if (mMsgResource == NULL) { @@ -668,7 +670,7 @@ void dMeter2Info_c::resetMeterString() { void dMeter2Info_c::setWarpInfo(const char* i_stageName, const cXyz& i_position, s16 i_angle, u8 i_roomNo, u8 param_4, u8 i_warpPlayerNo) { - strcpy(mWarpInfo.mStageName, i_stageName); + SAFE_STRCPY(mWarpInfo.mStageName, i_stageName); mWarpInfo.mPosition = i_position; mWarpInfo.mAngle = (s16)i_angle; mWarpInfo.mRoomNo = (u8)i_roomNo; @@ -1592,7 +1594,7 @@ void dMeter2Info_c::setMiniGameCount(s8 i_count) { } void dMeter2Info_c::setSaveStageName(const char* i_stageName) { - strcpy(mSaveStageName, i_stageName); + SAFE_STRCPY(mSaveStageName, i_stageName); } s16 dMeter2Info_getNowLifeGauge() { diff --git a/src/d/d_meter_button.cpp b/src/d/d_meter_button.cpp index f89a483c39..1f47969f47 100644 --- a/src/d/d_meter_button.cpp +++ b/src/d/d_meter_button.cpp @@ -22,6 +22,8 @@ #include "dusk/string.hpp" #endif +#include "dusk/string.hpp" + #if VERSION == VERSION_GCN_JPN #define STR_BUF_LEN 528 #else @@ -257,7 +259,7 @@ void dMeterButton_c::draw() { if (mMsgID != 0xFFFF) { char tmp_buf[STR_BUF_LEN]; - strcpy(tmp_buf, static_cast(mpTm_c[0]->getPanePtr())->getStringPtr()); + SAFE_STRCPY(tmp_buf, static_cast(mpTm_c[0]->getPanePtr())->getStringPtr()); mpTextScreen->draw(0.0f, 0.0f, graf_ctx); #if VERSION == VERSION_GCN_JPN @@ -268,7 +270,7 @@ void dMeterButton_c::draw() { NULL, 8); #endif mpString_c->drawOutFont(static_cast(mpTm_c[0]->getPanePtr()), -1.0f); - strcpy(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), tmp_buf); + SAFE_STRCPY(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), tmp_buf); } for (int i = 0; i < 2; i++) { @@ -1210,7 +1212,7 @@ void dMeterButton_c::screenInitButton() { field_0x360 = mpTextBox[0]->getBounds().i.x; for (int i = 0; i < 2; i++) { - strcpy(mButtonText[i], ""); + SAFE_STRCPY(mButtonText[i], ""); field_0x2e8[i] = 0.0f; field_0x4be[i] = BUTTON_NONE_e; field_0x2f4[i] = 0.0f; @@ -1906,12 +1908,12 @@ void dMeterButton_c::updateText(u32 i_flags) { if (getString) { mMsgID = dMeter2Info_getFloatingMessageID(); - strcpy(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), buf1); - strcpy(static_cast(mpTm_c[1]->getPanePtr())->getStringPtr(), buf3); + SAFE_STRCPY(static_cast(mpTm_c[0]->getPanePtr())->getStringPtr(), buf1); + SAFE_STRCPY(static_cast(mpTm_c[1]->getPanePtr())->getStringPtr(), buf3); for (int i = 0; i < 2; i++) { if (field_0x0ec[i] != NULL) { - strcpy( + SAFE_STRCPY( static_cast(field_0x0ec[i]->getPanePtr())->getStringPtr(), buf2); } @@ -3023,7 +3025,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } } - strcpy(mButtonText[param_2], i_string); + SAFE_STRCPY(mButtonText[param_2], i_string); if (param_2 == 0) { if (param_3 != 0) { @@ -3033,7 +3035,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } for (int i = 0; i < 5; i++) { - strcpy(mpTextBox[i]->getStringPtr(), i_string); + SAFE_STRCPY(mpTextBox[i]->getStringPtr(), i_string); } return; } @@ -3045,7 +3047,7 @@ void dMeterButton_c::setString(char* i_string, u8 i_button, u8 param_2, u8 param } for (int i = 0; i < 5; i++) { - strcpy(mpTextBox[5 + i]->getStringPtr(), i_string); + SAFE_STRCPY(mpTextBox[5 + i]->getStringPtr(), i_string); } } @@ -3300,7 +3302,7 @@ void dMeterButton_c::hide_button(u8 i_button) { field_0x4b8[1] = 0; field_0x4bc[1] = 0; - strcpy(mButtonText[1], ""); + SAFE_STRCPY(mButtonText[1], ""); mpText[0]->alphaAnimeStart(mpText[1]->getAlphaTimer()); mpText[0]->setAlphaRate(mpText[1]->getAlphaRate()); mpText[1]->alphaAnimeStart(0); @@ -3310,14 +3312,14 @@ void dMeterButton_c::hide_button(u8 i_button) { field_0x4be[0] = BUTTON_NONE_e; field_0x4b8[0] = 0; field_0x4bc[0] = 0; - strcpy(mButtonText[0], ""); + SAFE_STRCPY(mButtonText[0], ""); } } else if (field_0x4be[1] == i_button) { field_0x4be[1] = BUTTON_NONE_e; field_0x4b8[1] = 0; field_0x4bc[1] = 0; field_0x2f4[0] = 0.0f; - strcpy(mButtonText[1], ""); + SAFE_STRCPY(mButtonText[1], ""); } } diff --git a/src/d/d_meter_string.cpp b/src/d/d_meter_string.cpp index c28d6d5481..c99e7c564e 100644 --- a/src/d/d_meter_string.cpp +++ b/src/d/d_meter_string.cpp @@ -186,8 +186,8 @@ int dMeterString_c::_delete() { int dMeterString_c::createString(int i_stringID) { char str_buf[32]; dMeter2Info_getString(i_stringID, str_buf, NULL); - strcpy(static_cast(mpScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), str_buf); - strcpy(static_cast(mpScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), str_buf); + SAFE_STRCPY(static_cast(mpScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), str_buf); + SAFE_STRCPY(static_cast(mpScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), str_buf); mAnimFrame = 40.0f; mPikariAnimFrame = -1.0f; diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index f67d6054e6..01d2ab48e6 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -308,33 +308,33 @@ static u8 getOutFontNumberType(int param_0) { } #if TARGET_PC || VERSION == VERSION_GCN_PAL -static void setPlayerName(char* i_player_name, u8 param_2) { +static void setPlayerName(TEXT_SPAN i_player_name, u8 param_2) { if (param_2 != 0) { - strcpy(i_player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(i_player_name, dComIfGs_getPlayerName()); u32 name_length = strlen(i_player_name); char last = i_player_name[name_length - 1]; if (last == 0x73 || last == 0x53 || last == 0x7a || last == 0x5a || last == 0x78 || last == 0x58 || last == 0xdf) { - strcat(i_player_name, "'"); + SAFE_STRCAT(i_player_name, "'"); } else { - strcat(i_player_name, "s"); + SAFE_STRCAT(i_player_name, "s"); } } else { - strcpy(i_player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(i_player_name, dComIfGs_getPlayerName()); } } -static void setHorseName(char* i_horse_name, u8 param_2) { +static void setHorseName(TEXT_SPAN i_horse_name, u8 param_2) { if (param_2 != 0) { - strcpy(i_horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(i_horse_name, dComIfGs_getHorseName()); u32 name_length = strlen(i_horse_name); char last = i_horse_name[name_length - 1]; if (last == 0x73 || last == 0x53 || last == 0x7a || last == 0x5a || last == 0x78 || last == 0x58 || last == 0xdf) { - strcat(i_horse_name, "'"); + SAFE_STRCAT(i_horse_name, "'"); } else { - strcat(i_horse_name, "s"); + SAFE_STRCAT(i_horse_name, "s"); } } else { - strcpy(i_horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(i_horse_name, dComIfGs_getHorseName()); } } #endif @@ -707,7 +707,7 @@ void jmessage_tReference::inputNumber() { getObjectPtr()->setInputValue(new_input_val); } -char* jmessage_tReference::getWord(int i_no) { +TEXT_SPAN jmessage_tReference::getWord(int i_no) { if (i_no >= 10) { JUT_WARN(1093, "%s", "message stack over!!"); JUT_ASSERT(1094, i_no < (10)); @@ -1093,14 +1093,14 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; int number = dComIfGp_getMessageCountNumber() / 100; - sprintf(buffer, "%d", number); + SAFE_SPRINTF(buffer, "%d", number); push_word(buffer); return true; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; int number = dComIfGp_getMessageCountNumber() % 100; - sprintf(buffer, "%d", number); + SAFE_SPRINTF(buffer, "%d", number); push_word(buffer); return true; } @@ -1120,7 +1120,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz } char player_name[100]; - strcpy(player_name, dComIfGs_getPlayerName()); + SAFE_STRCPY(player_name, dComIfGs_getPlayerName()); push_word(player_name); return true; } @@ -1130,7 +1130,7 @@ bool jmessage_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_siz } char horse_name[100]; - strcpy(horse_name, dComIfGs_getHorseName()); + SAFE_STRCPY(horse_name, dComIfGs_getHorseName()); push_word(horse_name); return true; } @@ -1779,7 +1779,7 @@ void jmessage_tMeasureProcessor::do_rubyset(void const* i_data, u32 i_size) { void jmessage_tMeasureProcessor::push_word(char* i_word) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); - strcpy(pReference->getWord(field_0x4b), i_word); + SAFE_STRCPY(pReference->getWord(field_0x4b), i_word); stack_pushCurrent(pReference->getWord(field_0x4b)); field_0x4b++; } @@ -2403,7 +2403,7 @@ bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_si u8 sel_bomb_num = objectPtr->getSelectBombNum(); dMsgUnit_setTag(7, sel_bomb_num, buffer); - strcpy((char*)pReference->getWord(field_0xb5), buffer); + SAFE_STRCPY((TEXT_SPAN)pReference->getWord(field_0xb5), buffer); push_word(); return true; } @@ -2413,7 +2413,7 @@ bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_si s16 sel_bomb_price = objectPtr->getSelectBombPrice(); dMsgUnit_setTag(1, sel_bomb_price, buffer); - strcpy((char*)pReference->getWord(field_0xb5), buffer); + SAFE_STRCPY((TEXT_SPAN)pReference->getWord(field_0xb5), buffer); push_word(); return true; } @@ -2760,13 +2760,13 @@ void jmessage_tRenderingProcessor::do_begin(void const* pEntry, char const* pszT } field_0x11c = 0; - strcpy(pReference->getTextPtr(), ""); - strcpy(pReference->getTextSPtr(), ""); - strcpy(pReference->getRubyPtr(), ""); + SAFE_STRCPY(pReference->getTextPtr(), ""); + SAFE_STRCPY(pReference->getTextSPtr(), ""); + SAFE_STRCPY(pReference->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pReference->getSelTextPtr(i), ""); - strcpy(pReference->getSelRubyPtr(i), ""); + SAFE_STRCPY(pReference->getSelTextPtr(i), ""); + SAFE_STRCPY(pReference->getSelRubyPtr(i), ""); } if (1.0f != pReference->getDistanceScale()) { @@ -2791,12 +2791,12 @@ void jmessage_tRenderingProcessor::do_end() { if (dMsgObject_getSelectWordFlag() != 0) { for (int i = 0; i < dMsgObject_getSelectWordFlag(); i++) { char buffer[200]; - strcpy(buffer, dMsgObject_getSelectWord(i)); + SAFE_STRCPY(buffer, dMsgObject_getSelectWord(i)); if (pReference->getSelectNum() == 2) { - strcat(pReference->getSelTextPtr(i + 1), buffer); + SAFE_STRCAT(pReference->getSelTextPtr(i + 1), buffer); } else if (pReference->getSelectNum() == 3) { - strcat(pReference->getSelTextPtr(i), buffer); + SAFE_STRCAT(pReference->getSelTextPtr(i), buffer); } } } @@ -3108,7 +3108,7 @@ bool jmessage_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_s char buffer[40]; u8 bombNum = pReference->getObjectPtr()->getSelectBombNum(); dMsgUnit_setTag(7, bombNum, buffer); - strcpy(pReference->getWord(field_0x14f), buffer); + SAFE_STRCPY(pReference->getWord(field_0x14f), buffer); push_word(); return 1; } @@ -3116,7 +3116,7 @@ bool jmessage_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_s char buffer[40]; s16 bombPrice = pReference->getObjectPtr()->getSelectBombPrice(); dMsgUnit_setTag(1, bombPrice, buffer); - strcpy(pReference->getWord(field_0x14f), buffer); + SAFE_STRCPY(pReference->getWord(field_0x14f), buffer); push_word(); return 1; } @@ -3501,7 +3501,7 @@ void jmessage_tRenderingProcessor::do_color(u8 i_colorNo) { mGCColor = getFontGCColorTable(i_colorNo, reference_p->getFukiKind()); char buffer[40]; - sprintf(buffer, + SAFE_SPRINTF(buffer, "\x1B" "CC[%08x]" "\x1B" @@ -3529,7 +3529,7 @@ void jmessage_tRenderingProcessor::do_scale(f32 param_1) { } char buffer[32]; - sprintf(buffer, + SAFE_SPRINTF(buffer, "\x1B" "FX[%d]" "\x1B" @@ -3541,7 +3541,7 @@ void jmessage_tRenderingProcessor::do_scale(f32 param_1) { void jmessage_tRenderingProcessor::do_linedown(s16 param_0) { char buffer[16]; - sprintf(buffer, "\x1B" "CD[%d]", param_0); + SAFE_SPRINTF(buffer, "\x1B" "CD[%d]", param_0); do_strcat(buffer, false, true, false); } @@ -3551,11 +3551,11 @@ void jmessage_tRenderingProcessor::do_transY(s16 i_transY, bool unused) { char buffer1[16]; if (i_transY < 0) { - sprintf(buffer0, "\x1B" "CU[%d]", -i_transY); - sprintf(buffer1, "\x1B" "CD[%d]", -i_transY); + SAFE_SPRINTF(buffer0, "\x1B" "CU[%d]", -i_transY); + SAFE_SPRINTF(buffer1, "\x1B" "CD[%d]", -i_transY); } else { - sprintf(buffer0, "\x1B" "CD[%d]", i_transY); - sprintf(buffer1, "\x1B" "CU[%d]", i_transY); + SAFE_SPRINTF(buffer0, "\x1B" "CD[%d]", i_transY); + SAFE_SPRINTF(buffer1, "\x1B" "CU[%d]", i_transY); } field_0x4c -= i_transY; @@ -3643,7 +3643,7 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x11c += strlen(i_str); if (field_0x14e != 0) { if (field_0x11c < 50) { - strcat(pReference->getSelTextPtr(field_0x14e - 1), i_str); + SAFE_STRCAT(pReference->getSelTextPtr(field_0x14e - 1), i_str); } else { JUT_WARN(5316, "%s", "TextBox Alloc Byte Over!!"); } @@ -3662,22 +3662,22 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par if (pReference->getCharAlpha() < 255.0f) { pReference->addCharAlpha(); if (field_0x148 != 0) { - char* textPtr = pReference->getTextPtr(); + TEXT_SPAN textPtr = pReference->getTextPtr(); textPtr[field_0x148] = 0; - strcat(textPtr, field_0x184); + SAFE_STRCAT(textPtr, field_0x184); } if (field_0x14a != 0) { - char* textPtr = pReference->getTextSPtr(); + TEXT_SPAN textPtr = pReference->getTextSPtr(); textPtr[field_0x14a] = 0; - strcat(textPtr, field_0x184); + SAFE_STRCAT(textPtr, field_0x184); } u32 charColor = (mCCColor & 0xFFFFFF00) | ((int)pReference->getCharAlpha() & 0xFF); u32 gradColor = (mGCColor & 0xFFFFFF00) | ((int)pReference->getCharAlpha() & 0xFF); char buffer[36]; - sprintf(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", charColor, gradColor); + SAFE_SPRINTF(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", charColor, gradColor); int length = 0; length = strlen(buffer); @@ -3686,9 +3686,9 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x148 = strlen(pReference->getTextPtr()); field_0x14a = strlen(pReference->getTextSPtr()); - strcpy(field_0x184, i_str); - strcat(pReference->getTextPtr(), buffer); - strcat(pReference->getTextSPtr(), buffer); + SAFE_STRCPY(field_0x184, i_str); + SAFE_STRCAT(pReference->getTextPtr(), buffer); + SAFE_STRCAT(pReference->getTextSPtr(), buffer); } else { JUT_WARN(5362, "%s", "TextBox Alloc Byte Over!!"); } @@ -3699,9 +3699,9 @@ void jmessage_tRenderingProcessor::do_strcat(char* i_str, bool param_2, bool par field_0x14a = 0; } - strcat(pReference->getTextPtr(), i_str); + SAFE_STRCAT(pReference->getTextPtr(), i_str); if (param_3) { - strcat(pReference->getTextSPtr(), i_str); + SAFE_STRCAT(pReference->getTextSPtr(), i_str); } } else { JUT_WARN(5380, "%s", "TextBox Alloc Byte Over!!"); @@ -3729,7 +3729,7 @@ void jmessage_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_size) { buffer[0] = pRuby[index++]; buffer[1] = pRuby[index++]; buffer[2] = 0; - strcat(field_0x152, (const char*)buffer); + SAFE_STRCAT(field_0x152, (const char*)buffer); int character = (((char)buffer[0] & 0xFF) << 8) | ((char)buffer[1] & 0xFF); if (field_0x14e != 0) { @@ -3751,7 +3751,7 @@ void jmessage_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_size) { } } -void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, char* i_dst, f32 i_charSpace, f32 param_4) { +void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, TEXT_SPAN i_dst, f32 i_charSpace, f32 param_4) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); if (pReference->isCharSend()) { if (0.0f != param_4) { @@ -3760,18 +3760,18 @@ void jmessage_tRenderingProcessor::do_rubystrcat(char* i_src, char* i_dst, f32 i if (cursor_trans >= 1.0f) { char buffer[16]; snprintf(buffer, sizeof(buffer) - 1, "\x1B" "CR[%d]", (int)cursor_trans); - strcat(i_dst, buffer); + SAFE_STRCAT(i_dst, buffer); field_0x12c += (int)cursor_trans; } else if (cursor_trans <= -1.0f) { char buffer[16]; snprintf(buffer, sizeof(buffer) - 1, "\x1B" "CL[%d]", (int)-cursor_trans); - strcat(i_dst, buffer); + SAFE_STRCAT(i_dst, buffer); field_0x12c += (int)cursor_trans; } field_0x12c += field_0x128 + i_charSpace; } - strcat(i_dst, i_src); + SAFE_STRCAT(i_dst, i_src); } } @@ -4046,13 +4046,13 @@ bool jmessage_string_tMeasureProcessor::do_tag(u32 i_tag, void const* i_data, u3 break; case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); stack_pushCurrent(buffer); break; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); stack_pushCurrent(buffer); break; } @@ -4469,11 +4469,11 @@ void jmessage_string_tRenderingProcessor::do_begin(void const* pEntry, char cons void jmessage_string_tRenderingProcessor::do_end() { if (mpReference->getPanePtr() != NULL) { - strcpy(mpReference->getPanePtr()->getStringPtr(), field_0x54); + SAFE_STRCPY(mpReference->getPanePtr()->getStringPtr(), field_0x54); } if (mpReference->getRubyPanePtr() != NULL) { - strcpy(mpReference->getRubyPanePtr()->getStringPtr(), field_0x254); + SAFE_STRCPY(mpReference->getRubyPanePtr()->getStringPtr(), field_0x254); } } @@ -4604,13 +4604,13 @@ bool jmessage_string_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, break; case MSGTAG_CURRENT_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() / 100); push_word(buffer); break; } case MSGTAG_MAX_LETTER_PAGE: { char buffer[4]; - sprintf(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); + SAFE_SPRINTF(buffer, "%d", dComIfGp_getMessageCountNumber() % 100); push_word(buffer); break; } @@ -5086,7 +5086,7 @@ void jmessage_string_tRenderingProcessor::do_strcat(char* i_str) { if (getLineCountNowPage() >= 0) { field_0x54e += strlen(i_str); if (field_0x54e < ARRAY_SIZE(field_0x54)) { - strcat(field_0x54, i_str); + SAFE_STRCAT(field_0x54, i_str); } else { JUT_WARN(7531, "%s", "Message Alloc Byte Over!!"); } @@ -5117,7 +5117,7 @@ void jmessage_string_tRenderingProcessor::do_rubyset(void const* i_data, u32 i_s bytes[0] = pRuby[i++]; bytes[1] = pRuby[i++]; bytes[2] = 0; - strcat(field_0x454, (const char*)bytes); + SAFE_STRCAT(field_0x454, (const char*)bytes); int character = (((char)bytes[0] & 0xFF) << 8) | ((char)bytes[1] & 0xFF); field_0x44 += charSpace + fontSize.mSizeX * ((f32)pFont->getWidth(character) / pFont->getCellWidth()); @@ -5135,7 +5135,7 @@ void jmessage_string_tRenderingProcessor::do_rubystrcat(char* i_str) { if (getLineCountNowPage() >= 0) { field_0x550 += strlen(i_str); if (field_0x550 < ARRAY_SIZE(field_0x254)) { - strcat(field_0x254, i_str); + SAFE_STRCAT(field_0x254, i_str); } else { JUT_WARN(7613, "%s", "Message Alloc Byte Over!!"); } @@ -5209,7 +5209,7 @@ void jmessage_string_tRenderingProcessor::do_color(u8 i_colorNo) { } char buffer[32]; - sprintf(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", ccColor, gcColor); + SAFE_SPRINTF(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", ccColor, gcColor); do_strcat(buffer); } @@ -5220,13 +5220,13 @@ void jmessage_string_tRenderingProcessor::do_scale(f32 i_scale) { s16 scaleY = 0.5f + fontSize.mSizeY * i_scale; char buffer[32]; - sprintf(buffer, "\x1b" "FX[%d]" "\x1b" "FY[%d]", scaleX, scaleY); + SAFE_SPRINTF(buffer, "\x1b" "FX[%d]" "\x1b" "FY[%d]", scaleX, scaleY); do_strcat(buffer); } void jmessage_string_tRenderingProcessor::do_linedown(s16 i_lineNo) { char buffer[16]; - sprintf(buffer, "\x1B" "CD[%d]", i_lineNo); + SAFE_SPRINTF(buffer, "\x1B" "CD[%d]", i_lineNo); do_strcat(buffer); } @@ -5245,6 +5245,6 @@ void jmessage_string_tRenderingProcessor::do_numset(s16 i_num) { } void jmessage_string_tRenderingProcessor::push_word(char const* i_word) { - strcpy(field_0x486, i_word); + SAFE_STRCPY(field_0x486, i_word); stack_pushCurrent(field_0x486); } diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 44b5c66571..d4cff01f31 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -461,12 +461,12 @@ int dMsgObject_c::_execute() { mpCtrl->setMessageCode(mpRefer->getRevoMessageID()); mpRefer->setRevoMessageID(0); jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); - strcpy(pRef->getTextPtr(), ""); - strcpy(pRef->getTextSPtr(), ""); - strcpy(pRef->getRubyPtr(), ""); + SAFE_STRCPY(pRef->getTextPtr(), ""); + SAFE_STRCPY(pRef->getTextSPtr(), ""); + SAFE_STRCPY(pRef->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pRef->getSelTextPtr(i), ""); - strcpy(pRef->getSelRubyPtr(i), ""); + SAFE_STRCPY(pRef->getSelTextPtr(i), ""); + SAFE_STRCPY(pRef->getSelRubyPtr(i), ""); } } field_0x4ca = mpCtrl->update(); @@ -1021,12 +1021,12 @@ void dMsgObject_c::continueProc() { offAutoMessageFlagLocal(); setMessageIndex(field_0x100->msg_idx, field_0x100->field_0xf0, true); mpScrnDraw->fukiPosCalc(pRef->getFukiPosType()); - strcpy(pRef->getTextPtr(), ""); - strcpy(pRef->getTextSPtr(), ""); - strcpy(pRef->getRubyPtr(), ""); + SAFE_STRCPY(pRef->getTextPtr(), ""); + SAFE_STRCPY(pRef->getTextSPtr(), ""); + SAFE_STRCPY(pRef->getRubyPtr(), ""); for (int i = 0; i < 3; i++) { - strcpy(pRef->getSelTextPtr(i), ""); - strcpy(pRef->getSelRubyPtr(i), ""); + SAFE_STRCPY(pRef->getSelTextPtr(i), ""); + SAFE_STRCPY(pRef->getSelRubyPtr(i), ""); } mpScrnDraw->arwAnimeInit(); mpRenProc->setTextInitPos(mpScrnDraw->getTextBoxPosX(), mpScrnDraw->getTextBoxPosY()); @@ -1737,8 +1737,8 @@ void dMsgObject_c::changeGroupLocal(s16 param_1) { } bool dMsgObject_c::getStringLocal(u32 param_1, J2DTextBox* param_2, J2DTextBox* param_3, - JUTFont* param_4, COutFont_c* param_5, char* param_6, - char* param_7, char* param_8, s16* param_9) { + JUTFont* param_4, COutFont_c* param_5, TEXT_SPAN param_6, + TEXT_SPAN param_7, TEXT_SPAN param_8, s16* param_9) { if (field_0x4cd == 0) { if (getStatusLocal() == 1) { s16 groupID = getMessageGroup(param_1); @@ -1781,13 +1781,13 @@ bool dMsgObject_c::getStringLocal(u32 param_1, J2DTextBox* param_2, J2DTextBox* *param_9 = mpRefer->getCharSoundInfo().field_0x40e; } if (param_6 != NULL) { - strcpy(param_6, pRef->getTextPtr()); + SAFE_STRCPY(param_6, pRef->getTextPtr()); } if (param_7 != NULL) { - strcpy(param_7, pRef->getRubyPtr()); + SAFE_STRCPY(param_7, pRef->getRubyPtr()); } if (param_8 != NULL) { - strcpy(param_8, pRef->getTextSPtr()); + SAFE_STRCPY(param_8, pRef->getTextSPtr()); } mpCtrl->reset(); mpCtrl->resetResourceCache(); @@ -2309,8 +2309,8 @@ void dMsgObject_c::changeGroup(s16 param_0) { } bool dMsgObject_c::getString(u32 param_0, J2DTextBox* param_1, J2DTextBox* param_2, - JUTFont* param_3, COutFont_c* param_4, char* param_5, char* param_6, - char* param_7, s16* param_8) { + JUTFont* param_3, COutFont_c* param_4, TEXT_SPAN param_5, TEXT_SPAN param_6, + TEXT_SPAN param_7, s16* param_8) { return dMsgObject_getMsgObjectClass()->getStringLocal( param_0, param_1, param_2, param_3, param_4, param_5, param_6, param_7, param_8); } diff --git a/src/d/d_msg_scrn_3select.cpp b/src/d/d_msg_scrn_3select.cpp index 27a0bf5e1b..216135b10c 100644 --- a/src/d/d_msg_scrn_3select.cpp +++ b/src/d/d_msg_scrn_3select.cpp @@ -294,15 +294,15 @@ void dMsgScrn3Select_c::setString(char* mpText0, char* mpText1, char* mpText2) { } if (mpTmSel_c[0] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[0]->getPanePtr()))->getStringPtr(), mpText0); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[0]->getPanePtr()))->getStringPtr(), mpText0); } if (mpTmSel_c[1] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[1]->getPanePtr()))->getStringPtr(), mpText1); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[1]->getPanePtr()))->getStringPtr(), mpText1); } if (mpTmSel_c[2] != NULL) { - strcpy(((J2DTextBox*)(mpTmSel_c[2]->getPanePtr()))->getStringPtr(), mpText2); + SAFE_STRCPY(((J2DTextBox*)(mpTmSel_c[2]->getPanePtr()))->getStringPtr(), mpText2); } } @@ -323,15 +323,15 @@ void dMsgScrn3Select_c::setRubyString(char* pText0, char* pText1, char* pText2) } if (mpTmrSel_c[0] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[0]->getPanePtr()))->getStringPtr(), pText0); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[0]->getPanePtr()))->getStringPtr(), pText0); } if (mpTmrSel_c[1] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[1]->getPanePtr()))->getStringPtr(), pText1); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[1]->getPanePtr()))->getStringPtr(), pText1); } if (mpTmrSel_c[2] != NULL) { - strcpy(((J2DTextBox*)(mpTmrSel_c[2]->getPanePtr()))->getStringPtr(), pText2); + SAFE_STRCPY(((J2DTextBox*)(mpTmrSel_c[2]->getPanePtr()))->getStringPtr(), pText2); } } diff --git a/src/d/d_msg_scrn_base.cpp b/src/d/d_msg_scrn_base.cpp index fdd9e57f9c..f402f8983b 100644 --- a/src/d/d_msg_scrn_base.cpp +++ b/src/d/d_msg_scrn_base.cpp @@ -108,9 +108,9 @@ void dMsgScrnBase_c::setString(char* mpText, char* i_stringB) { if (mpTm_c[i] != NULL) { JUT_ASSERT(262, ((J2DTextBox*)(mpTm_c[i]->getPanePtr()))->getStringAllocByte() > strlen(mpText)); if (i == 0) { - strcpy(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), mpText); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), mpText); } else { - strcpy(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), i_stringB); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[i]->getPanePtr())->getStringPtr(), i_stringB); } } } @@ -120,7 +120,7 @@ void dMsgScrnBase_c::setRubyString(char* mpText) { for (int i = 0; i < 3; i++) { if (mpTmr_c[i] != NULL) { JUT_ASSERT(288, ((J2DTextBox*)(mpTmr_c[i]->getPanePtr()))->getStringAllocByte() > strlen(mpText)); - strcpy(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), mpText); + SAFE_STRCPY(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), mpText); } } } diff --git a/src/d/d_msg_scrn_explain.cpp b/src/d/d_msg_scrn_explain.cpp index 945f75e927..bc6275e10c 100644 --- a/src/d/d_msg_scrn_explain.cpp +++ b/src/d/d_msg_scrn_explain.cpp @@ -317,7 +317,7 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { } char string_buf[STR_BUF_LEN]; - strcpy(string_buf, ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr()); + SAFE_STRCPY(string_buf, ((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr()); mpTxScreen->draw(0.0f, 0.0f, (J2DGrafContext*)i_graf); #if VERSION == VERSION_GCN_JPN @@ -327,7 +327,7 @@ void dMsgScrnExplain_c::draw(J2DOrthoGraph* i_graf) { #endif mpString_c->drawOutFont((J2DTextBox*)mpTm_c[0]->getPanePtr(), -1.0f); - strcpy(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), string_buf); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), string_buf); if (mpSelect_c != NULL && (field_0x64 == 1 || field_0x64 == 2)) { // the magic numbers here are relative to the framebuffer size, but were likely @@ -370,11 +370,11 @@ void dMsgScrnExplain_c::open_request_proc() { bool rt = dMsgObject_getString(mOpenMsgId, tbox, rubyTbox, mDoExt_getMesgFont(), mpOutFont, text, ruby, textShadow, &field_0x5c); if (rt) { - strcpy(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), text); - strcpy(((J2DTextBox*)mpTm_c[1]->getPanePtr())->getStringPtr(), textShadow); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[0]->getPanePtr())->getStringPtr(), text); + SAFE_STRCPY(((J2DTextBox*)mpTm_c[1]->getPanePtr())->getStringPtr(), textShadow); for (int i = 0; i < 2; i++) { if (mpTmr_c[i] != NULL) { - strcpy(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), ruby); + SAFE_STRCPY(((J2DTextBox*)mpTmr_c[i]->getPanePtr())->getStringPtr(), ruby); } } @@ -483,12 +483,12 @@ void dMsgScrnExplain_c::move_select_init() { "\x1B" "CR[%d]", (int)var_f28); - strcat(string_buf_yes, msg_buf_yes); + SAFE_STRCAT(string_buf_yes, msg_buf_yes); snprintf(string_buf_no, 20, "\x1B" "CR[%d]", (int)var_f27); - strcat(string_buf_no, msg_buf_no); + SAFE_STRCAT(string_buf_no, msg_buf_no); mpSelect_c->setString("", string_buf_yes, string_buf_no); mpSelect_c->setRubyString("", "", ""); diff --git a/src/d/d_msg_string_base.cpp b/src/d/d_msg_string_base.cpp index 7346047661..003375d6b4 100644 --- a/src/d/d_msg_string_base.cpp +++ b/src/d/d_msg_string_base.cpp @@ -126,7 +126,7 @@ u8 dMsgStringBase_c::getPageMax(int param_0) { return pageMax; } -f32 dMsgStringBase_c::getMessageLocal(u32 param_1, char* param_2) { +f32 dMsgStringBase_c::getMessageLocal(u32 param_1, TEXT_SPAN param_2) { if (dMeter2Info_getMsgResource() != NULL) { if (param_1 > 5000) { if (field_0x1c != dMeter2Info_getStageMsgResource()) { @@ -146,7 +146,7 @@ f32 dMsgStringBase_c::getMessageLocal(u32 param_1, char* param_2) { mpCtrl->render(); mpCtrl->reset(); mpCtrl->resetResourceCache(); - strcpy(param_2, mpRenProc->getString()); + SAFE_STRCPY(param_2, mpRenProc->getString()); return 0.0f; } @@ -165,7 +165,7 @@ f32 dMsgStringBase_c::getStringPage(u32 param_0, u8 param_1, u8 param_2, J2DText return getStringPageLocal(param_0, param_1, param_2, param_3, param_4, param_5, param_6, param_7); } -f32 dMsgStringBase_c::getMessage(u32 param_0, char* param_1) { +f32 dMsgStringBase_c::getMessage(u32 param_0, TEXT_SPAN param_1) { return getMessageLocal(param_0, param_1); } diff --git a/src/d/d_msg_unit.cpp b/src/d/d_msg_unit.cpp index 7bbe8b3d70..55fc588d4e 100644 --- a/src/d/d_msg_unit.cpp +++ b/src/d/d_msg_unit.cpp @@ -42,21 +42,21 @@ dMsgUnit_c::dMsgUnit_c() {} dMsgUnit_c::~dMsgUnit_c() {} #if REGION_JPN -void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { +void dMsgUnit_c::setTag(int i_type, int i_value, TEXT_SPAN o_buffer, bool param_4) { *o_buffer = 0; bool stack9 = false; bool stack8 = false; int value = i_value; if (i_type == 0x10000) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); return; } if (i_type == 0x10001) { int tens_digit = i_value / 10; int ones_digit = i_value % 10; - sprintf(o_buffer, "%d-%d", tens_digit, ones_digit); + SAFE_SPRINTF(o_buffer, "%d-%d", tens_digit, ones_digit); return; } @@ -108,7 +108,7 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { } if (i_type == 9 && param_4 == true) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); stack8 = true; } @@ -170,9 +170,9 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { int uVar5Len = strlen(uVar5); if (uVar5Len == 0) { if (stack8) { - strcat(o_buffer, value2); + SAFE_STRCAT(o_buffer, value2); } else { - sprintf(o_buffer, "%d%s", i_value, value2); + SAFE_SPRINTF(o_buffer, "%d%s", i_value, value2); } } else { char unkCharArr[7]; @@ -185,11 +185,11 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { unkCharArr[6] = 0; if (stack8) { - strcat(o_buffer, unkCharArr); - strcat(o_buffer, uVar5); - strcat(o_buffer, value2); + SAFE_STRCAT(o_buffer, unkCharArr); + SAFE_STRCAT(o_buffer, uVar5); + SAFE_STRCAT(o_buffer, value2); } else { - sprintf(o_buffer, "%d%s%s%s", i_value, unkCharArr, uVar5, value2); + SAFE_SPRINTF(o_buffer, "%d%s%s%s", i_value, unkCharArr, uVar5, value2); } } } @@ -197,17 +197,17 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { if (i_type == 3 && param_4 == true) { char buffer[20]; setTag(4, 0, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } if (i_type == 4 && param_4 == true) { char buffer[20]; setTag(5, value, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } } #else -void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { +void dMsgUnit_c::setTag(int i_type, int i_value, TEXT_SPAN o_buffer, bool param_4) { *o_buffer = 0; bool stack9 = false; bool stack8 = false; @@ -218,14 +218,14 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { int minutes; // sp34 if (i_type == 0x10000) { - sprintf(o_buffer, "%d", i_value); + SAFE_SPRINTF(o_buffer, "%d", i_value); return; } if (i_type == 0x10001) { tens_digit = i_value / 10; ones_digit = i_value % 10; - sprintf(o_buffer, "%d-%d", tens_digit, ones_digit); + SAFE_SPRINTF(o_buffer, "%d-%d", tens_digit, ones_digit); return; } @@ -238,7 +238,7 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { seconds = 59; } if (minutes != 0 || seconds != 0) { - sprintf(o_buffer, "%d:%02d", minutes, seconds); + SAFE_SPRINTF(o_buffer, "%d:%02d", minutes, seconds); } return; @@ -254,11 +254,11 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { iVar8b = ((s32)(1000000.0f * dayTime) % 250000) / 1000000.0f; f32 iVar9 = 60.0f * (iVar8b / 0.25f); - sprintf(o_buffer, "%d:%02d", (s32)hour, (s32)minute); + SAFE_SPRINTF(o_buffer, "%d:%02d", (s32)hour, (s32)minute); } else { if (i_type == 9 && param_4 == true) { int value = i_value; - sprintf(o_buffer, "%d", value); + SAFE_SPRINTF(o_buffer, "%d", value); stack8 = true; } @@ -359,21 +359,21 @@ void dMsgUnit_c::setTag(int i_type, int i_value, char* o_buffer, bool param_4) { } if (strcmp(uVar5, "") == 0) { - sprintf(o_buffer, "%d%s", i_value, uVar5); + SAFE_SPRINTF(o_buffer, "%d%s", i_value, uVar5); } else { - sprintf(o_buffer, "%d %s", i_value, uVar5); + SAFE_SPRINTF(o_buffer, "%d %s", i_value, uVar5); } } if (i_type == 3 && param_4 == true) { char buffer[20]; setTag(4, 0, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } if (i_type == 4 && param_4 == true) { char buffer[20]; setTag(5, param_2b, buffer, false); - strcat(o_buffer, buffer); + SAFE_STRCAT(o_buffer, buffer); } } } diff --git a/src/d/d_name.cpp b/src/d/d_name.cpp index abc7512f14..e4c2a0f8f0 100644 --- a/src/d/d_name.cpp +++ b/src/d/d_name.cpp @@ -908,7 +908,7 @@ void dName_c::setNameText() { #if REGION_JPN if (mChrInfo[i].mMojiSet == 2) { #endif - sprintf(mNameText[i], + SAFE_SPRINTF(mNameText[i], "\x1b" "CD\x1b" "CR\x1b" @@ -919,7 +919,7 @@ void dName_c::setNameText() { ); #if REGION_JPN } else { - sprintf(mNameText[i], + SAFE_SPRINTF(mNameText[i], "\x1b" "CD\x1b" "CR\x1b" @@ -1330,7 +1330,7 @@ void dName_c::mojiListChange() { char buf[74]; for (int i = 0; i < 65; i++) { - strcpy(buf, "\x1B" + SAFE_STRCPY(buf, "\x1B" "CD" "\x1B" "CR" @@ -1338,15 +1338,15 @@ void dName_c::mojiListChange() { "CC[000000]" "\x1B" "GM[0]"); - strcat(buf, mojiSet[i]); - strcat(buf, "\x1B" + SAFE_STRCAT(buf, mojiSet[i]); + SAFE_STRCAT(buf, "\x1B" "HM" "\x1B" "CC[ffffff]" "\x1B" "GM[0]"); - strcat(buf, mojiSet[i]); - strcpy(mMojiText[i], buf); + SAFE_STRCAT(buf, mojiSet[i]); + SAFE_STRCPY(mMojiText[i], buf); } #if TARGET_PC || REGION_PAL || REGION_JPN @@ -1430,10 +1430,10 @@ void dName_c::selectCursorPosSet(int row) { #if TARGET_PC void dName_c::nameWide() { - //Resize Select Icon + // Resize Select Icon #if TARGET_PC if (mSelIcon) { - mSelIcon->refreshAspectScale(); + mSelIcon->refreshAspectScale(mDoGph_gInf_c::hudAspectScaleUp); } #endif diff --git a/src/d/d_particle.cpp b/src/d/d_particle.cpp index 9b89afc7b1..1c53313826 100644 --- a/src/d/d_particle.cpp +++ b/src/d/d_particle.cpp @@ -1263,7 +1263,7 @@ bool dPa_control_c::readScene(u8 param_0, mDoDvdThd_toMainRam_c** param_1) { JUT_ASSERT(2647, !mSceneCount++); field_0x18 = param_0; static char jpcName[32]; - sprintf(jpcName, "/res/Particle/Pscene%03d.jpc", param_0); + SAFE_SPRINTF(jpcName, "/res/Particle/Pscene%03d.jpc", param_0); *param_1 = mDoDvdThd_toMainRam_c::create(jpcName, 0, m_resHeap); return 1; } diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index f2e7d5d926..4445d9d1bb 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -163,22 +163,22 @@ void dScnPly_reg_childHIO_c::genMessage(JORMContext* mctx) { char textbuf[8]; for (int i = 0; i < 20; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], -100000.0f, 100000.0f); } for (int i = 20; i < 25; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], 0.0f, 1.0f); } for (int i = 25; i < 30; i++) { - sprintf(textbuf, " F(%02d)", i); + SAFE_SPRINTF(textbuf, " F(%02d)", i); mctx->genSlider(textbuf, &mFloatReg[i], -1.0f, 1.0f); } for (int i = 0; i < 10; i++) { - sprintf(textbuf, " S(%02d)", i); + SAFE_SPRINTF(textbuf, " S(%02d)", i); mctx->genSlider(textbuf, &mShortReg[i], -0x8000, 0x7FFF); } } diff --git a/src/d/d_s_room.cpp b/src/d/d_s_room.cpp index 0fcdb58ac0..259bc37e3b 100644 --- a/src/d/d_s_room.cpp +++ b/src/d/d_s_room.cpp @@ -178,7 +178,7 @@ static int loadDemoArchive(int i_roomNo) { int bank2 = entries[dComIfG_play_c::getLayerNo(i_roomNo)].bank2; JUT_ASSERT(353, 0 <= bank2 && bank2 < 100); - sprintf(dStage_roomControl_c::getDemoArcName(), "Demo%02d_%02d", bank, bank2); + SAFE_SPRINTF(dStage_roomControl_c::getDemoArcName(), "Demo%02d_%02d", bank, bank2); if (!dComIfG_setObjectRes(dStage_roomControl_c::getDemoArcName(), 0, (JKRHeap*)NULL)) { const char* name = dStage_roomControl_c::getDemoArcName(); *dStage_roomControl_c::getDemoArcName() = 0; diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index fc1cbc4efe..af2a6db28a 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -2034,7 +2034,7 @@ void flagFile_c::listenPropertyEvent(const JORPropertyEvent* i_event) { const char* start_stage_name = dComIfGp_getStartStageName(); char filename[64]; - sprintf(filename, "_%02d%02d%02d%02d%02d-%s.zff", time.mon + 1, time.mday, time.hour, time.min, time.sec, start_stage_name); + SAFE_SPRINTF(filename, "_%02d%02d%02d%02d%02d-%s.zff", time.mon + 1, time.mday, time.hour, time.min, time.sec, start_stage_name); OS_REPORT("write to %s\n", filename); JORFile file; diff --git a/src/d/d_select_cursor.cpp b/src/d/d_select_cursor.cpp index 0773433b4d..1268865cb6 100644 --- a/src/d/d_select_cursor.cpp +++ b/src/d/d_select_cursor.cpp @@ -577,7 +577,7 @@ void dSelect_cursor_c::moveCenter(J2DPane* i_pane, f32 i_x, f32 i_y) { } #ifdef TARGET_PC -void dSelect_cursor_c::refreshAspectScale() { - mParam1 = mBaseParam1 * mDoGph_gInf_c::hudAspectScaleUp; +void dSelect_cursor_c::refreshAspectScale(f32 param_0) { + mParam1 = mBaseParam1 * param_0; } #endif diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index f3cdfed17b..5836feccd2 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -482,7 +482,7 @@ void* dStage_roomControl_c::roomDzs_c::add(u8 i_no, u8 roomNo) { void** dzs = m_dzs + i_no; if (*dzs == NULL) { char dzsName[20]; - sprintf(dzsName, "%s/room%d.dzs", dComIfGp_getStartStageName(), roomNo); + SAFE_SPRINTF(dzsName, "%s/room%d.dzs", dComIfGp_getStartStageName(), roomNo); JUT_ASSERT(1167, strlen(dzsName) <= sizeof(dzsName)); u32 expandSize = @@ -2396,7 +2396,7 @@ static void readMult(dStage_dt_c* i_stage, dStage_Multi_c* multi, bool useOldRes if (dzs == NULL) { char dzsName[11]; - sprintf(dzsName, "room%d.dzs", info->mRoomNo); + SAFE_SPRINTF(dzsName, "room%d.dzs", info->mRoomNo); JUT_ASSERT(3548, strlen(dzsName) <= sizeof(dzsName)); if (useOldRes) { diff --git a/src/d/d_timer.cpp b/src/d/d_timer.cpp index cb40e3444a..8bc5cac570 100644 --- a/src/d/d_timer.cpp +++ b/src/d/d_timer.cpp @@ -1224,8 +1224,8 @@ BOOL dDlst_TimerScrnDraw_c::closeAnime() { int dDlst_TimerScrnDraw_c::createGetIn(cXyz i_pos) { char string[104]; dMeter2Info_getString(0x3E4, string, NULL); // "GOAT IN!" - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); if (mCowID < 50) { m_getin_info[mCowID].bck_frame = 40.0f; @@ -1313,8 +1313,8 @@ int dDlst_TimerScrnDraw_c::createGetIn(cXyz i_pos) { s32 dDlst_TimerScrnDraw_c::createStart(u16 i_messageID) { char string[112]; dMeter2Info_getString(i_messageID, string, NULL); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); - strcpy(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in_s')))->getStringPtr(), string); + SAFE_STRCPY(static_cast(mpGetInScreen->search(MULTI_CHAR('get_in')))->getStringPtr(), string); if (mCowID == 0) { m_getin_info[mCowID].bck_frame = 40.0f; diff --git a/src/dusk/achievements.cpp b/src/dusk/achievements.cpp index a8e4ff3916..6c4de43487 100644 --- a/src/dusk/achievements.cpp +++ b/src/dusk/achievements.cpp @@ -11,6 +11,7 @@ #include "d/actor/d_a_alink.h" #include "d/actor/d_a_ni.h" #include "d/actor/d_a_npc4.h" +#include "d/actor/d_a_b_gnd.h" #include "d/actor/d_a_b_ob.h" #include "d/actor/d_a_player.h" #include "d/d_demo.h" @@ -18,6 +19,7 @@ #include "f_pc/f_pc_name.h" #include "f_op/f_op_actor_mng.h" #include "f_pc/f_pc_name.h" +#include "dusk/logging.h" #include #include @@ -35,6 +37,9 @@ static void* s_cucco_play_search(void* i_actor, void*) { } static void checkGoatHerding(Achievement& a, int32_t threshMs) { + if (strcmp(dComIfGp_getStartStageName(), "F_SP00") != 0) { + return; + } if (dMeter2Info_getMaxCount() != 20 || dMeter2Info_getNowCount() != 20) { return; } @@ -65,6 +70,25 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "three_heart_clear", + "Hero Mode", + "Defeat Ganondorf with only 3 heart containers.", + AchievementCategory::Challenge, + false, 0, 0, false + }, + [](Achievement& a, json&) { + const auto* link = static_cast(daPy_getPlayerActorClass()); + if (link == nullptr || link->mProcID != daAlink_c::PROC_GANON_FINISH) { + return; + } + if (dComIfGs_getMaxLife() < 20) { + a.progress = 1; + } + }, + {} + }, { { "completionist", @@ -201,7 +225,7 @@ std::vector AchievementSystem::makeEntries() { hasAncientDoc = true; } } - if (!hasJewelRod || !hasAncientDoc) { + if (!hasJewelRod || (!hasAncientDoc && !dComIfGs_isEventBit(dSv_event_flag_c::F_0302))) { return; } @@ -265,7 +289,7 @@ std::vector AchievementSystem::makeEntries() { { "hylian_loach", "Legendary Catch", - "Catch a Hylian Loach.", + "Obtain the Hylian Loach in your fishing journal.", AchievementCategory::Collection, false, 0, 0, false }, @@ -280,7 +304,7 @@ std::vector AchievementSystem::makeEntries() { { "all_fish", "Gone Fishin'", - "Catch all 6 species of fish.", + "Obtain all 6 species of fish in your fishing journal.", AchievementCategory::Collection, true, 6, 0, false }, @@ -392,7 +416,7 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { - if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() <= 15) { + if (daNpcF_chkEvtBit(0x1F9) && dComIfGs_getMaxLife() < 20) { a.progress = 1; } }, @@ -442,7 +466,7 @@ std::vector AchievementSystem::makeEntries() { { "dark_hammer_one_hit", "Mortal Edge", - "Defeat Dark Hammer in a single hit.", + "Defeat Darkhammer in a single hit.", AchievementCategory::Misc, false, 0, 0, false }, @@ -506,6 +530,29 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "rollstab_triple", + "Surgical Skewer", + "Kill 3 enemies with a single rollstab.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + static int rollstabKills = 0; + const auto* link = static_cast(daPy_getPlayerActorClass()); + const bool inRollstab = link != nullptr && link->mProcID == daAlink_c::PROC_CUT_FINISH && link->mIsRollstab; + if (!inRollstab) { + rollstabKills = 0; + return; + } + rollstabKills += AchievementSystem::get().signalCount("rollstab_kill"); + if (rollstabKills >= 3) { + a.progress = 1; + } + }, + {} + }, // Minigame { { @@ -600,6 +647,9 @@ std::vector AchievementSystem::makeEntries() { false, 0, 0, false }, [](Achievement& a, json&) { + if (strcmp(dComIfGp_getStartStageName(), "F_SP114") != 0) { + return; + } const int32_t bestMs = dComIfGs_getRaceGameTime(); if (dComIfGs_isEventBit(dSv_event_flag_c::F_0481) && bestMs > 0 && bestMs <= 70000) { @@ -681,7 +731,7 @@ std::vector AchievementSystem::makeEntries() { { "long_jump_attack", "Long Jump Attack", - "Travel more than 20 meters in a single jump attack before landing.", + "Travel more than 15 meters in a single jump attack before landing.", AchievementCategory::Misc, false, 0, 0, false }, @@ -711,7 +761,7 @@ std::vector AchievementSystem::makeEntries() { inJump = false; const float dx = link->current.pos.x - startX; const float dz = link->current.pos.z - startZ; - if (dx * dx + dz * dz >= 2000.0f * 2000.0f) { + if (dx * dx + dz * dz >= 1500.0f * 1500.0f) { a.progress = 1; } } else if (link->mProcID != daAlink_c::PROC_CUT_JUMP) { @@ -800,6 +850,66 @@ std::vector AchievementSystem::makeEntries() { }, {} }, + { + { + "ganondorf_3hit", + "Autospin Annihilation", + "Finish off Ganondorf in the final duel after only 3 attacks.", + AchievementCategory::Misc, + false, 0, 0, false + }, + [](Achievement& a, json&) { + auto& sys = AchievementSystem::get(); + const auto* link = static_cast(daPy_getPlayerActorClass()); + + static int autospinCount = 0; + static int pendingHits = 0; + static bool invalidated = false; + static bool wasInFight = false; + + auto* gnd = static_cast(fopAcM_SearchByName(fpcNm_B_GND_e)); + const bool inFight = gnd != nullptr && !gnd->checkRide(); + + if (inFight && !wasInFight) { + autospinCount = 0; + pendingHits = 0; + invalidated = false; + } + wasInFight = inFight; + + if (!inFight) { + return; + } + + const bool hitOccurred = sys.hasSignal("ganondorf_hit"); + const bool knockedDown = sys.hasSignal("ganondorf_knocked_down"); + + if (hitOccurred && knockedDown) { + // Spin completing an autospin: pendingHits should be exactly 1 (the spin attack) + if (pendingHits == 1) { + autospinCount++; + pendingHits = 0; + } else { + invalidated = true; + } + } else if (hitOccurred) { + pendingHits++; + if (pendingHits > 1) { + invalidated = true; + } + } + + if (link != nullptr && link->mProcID == daAlink_c::PROC_GANON_FINISH) { + if (!invalidated && autospinCount == 3) { + a.progress = 1; + } + autospinCount = 0; + pendingHits = 0; + invalidated = false; + } + }, + {} + }, // Glitched { { @@ -1012,6 +1122,55 @@ std::vector AchievementSystem::makeEntries() { a.progress = 1; }, {} + }, + { + { + "early_city", + "Early City", + "Obtain the Double Clawshots without obtaining the Dominion Rod.", + AchievementCategory::Glitched, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (daPy_getPlayerActorClass() == nullptr) { + return; + } + bool hasDoubleClawshot = false; + bool hasDominionRod = false; + for (int i = 0; i < 24; ++i) { + const auto item = dComIfGs_getItem(i, false); + if (item == dItemNo_W_HOOKSHOT_e) { + hasDoubleClawshot = true; + } + if (item == dItemNo_COPY_ROD_e || item == dItemNo_COPY_ROD_2_e) { + hasDominionRod = true; + } + } + if (hasDoubleClawshot && !hasDominionRod) { + a.progress = 1; + } + }, + {} + }, + { + { + "early_kakariko", + "Gorge Skip", + "Collect the Kakariko warp portal without warping the gorge bridge.", + AchievementCategory::Glitched, + false, 0, 0, false + }, + [](Achievement& a, json&) { + if (dComIfGs_isEventBit(dSv_event_flag_c::M_018) || dComIfGp_getStageStagInfo() == nullptr) { + return; + } + const bool savedPortal = g_dComIfG_gameInfo.info.getSavedata().getSave(dStage_SaveTbl_ELDIN).getBit().isSwitch(31); + const bool livePortal = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()) == dStage_SaveTbl_ELDIN && dComIfGs_isSaveSwitch(31); + if (savedPortal || livePortal) { + a.progress = 1; + } + }, + {} } }; } @@ -1088,11 +1247,17 @@ void AchievementSystem::clearAll() { } void AchievementSystem::signal(const char* key) { - m_signals.insert(key); + m_signals[key]++; } bool AchievementSystem::hasSignal(const char* key) const { - return m_signals.count(key) > 0; + const auto it = m_signals.find(key); + return it != m_signals.end() && it->second > 0; +} + +int AchievementSystem::signalCount(const char* key) const { + const auto it = m_signals.find(key); + return it != m_signals.end() ? it->second : 0; } void AchievementSystem::clearOne(const char* key) { diff --git a/src/dusk/config.cpp b/src/dusk/config.cpp index 1f9e7c54a6..c0edd84591 100644 --- a/src/dusk/config.cpp +++ b/src/dusk/config.cpp @@ -172,10 +172,12 @@ namespace dusk::config { template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; + template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; template class ConfigImpl; + template class ConfigImpl; template class ConfigImpl; } diff --git a/src/dusk/crash_handler.cpp b/src/dusk/crash_handler.cpp new file mode 100644 index 0000000000..c79e2e16c8 --- /dev/null +++ b/src/dusk/crash_handler.cpp @@ -0,0 +1,754 @@ +#if !defined(_WIN32) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include "dusk/crash_handler.h" + +#include "dusk/logging.h" +#include "version.h" + +#include +#include +#include +#include + +#if defined(_WIN32) + +#include + +#include + +#if defined(DUSK_CRASH_DBGHELP) +#include +#endif + +#else + +#include +#include +#include +#include +#include +#include + +#if defined(__APPLE__) +#include +#include +#else +#include +#include +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif +#endif + +#endif + +#ifndef DUSK_ARCH +#define DUSK_ARCH "unknown" +#endif + +namespace dusk::crash_handler { +namespace { + +constexpr int kStderrFd = 2; +constexpr int kMaxFrames = 128; +constexpr char kHexDigits[] = "0123456789abcdef"; + +struct CrashContext { + uintptr_t moduleBase = 0; + char modulePath[1024] = {}; + uint8_t buildId[64] = {}; + unsigned buildIdLen = 0; + unsigned pdbAge = 0; +}; +CrashContext g_ctx; + +void rawWrite(int fd, const char* data, size_t len) { + if (fd < 0) { + return; + } +#if defined(_WIN32) + _write(fd, data, static_cast(len)); +#else + while (len > 0) { + const ssize_t written = ::write(fd, data, len); + if (written <= 0) { + return; + } + data += written; + len -= static_cast(written); + } +#endif +} + +void writeStr(int fd, const char* s) { + if (s != nullptr) { + rawWrite(fd, s, std::strlen(s)); + } +} + +void writeHex(int fd, unsigned long long value) { + char buf[2 + 16]; + size_t o = sizeof(buf); + do { + buf[--o] = kHexDigits[value & 0xF]; + value >>= 4; + } while (value != 0); + buf[--o] = 'x'; + buf[--o] = '0'; + rawWrite(fd, buf + o, sizeof(buf) - o); +} + +void writeDec(int fd, unsigned int value) { + char buf[10]; + size_t o = sizeof(buf); + do { + buf[--o] = static_cast('0' + value % 10); + value /= 10; + } while (value != 0); + rawWrite(fd, buf + o, sizeof(buf) - o); +} + +void writeHexBytes(int fd, const uint8_t* data, unsigned len) { + char buf[2]; + for (unsigned i = 0; i < len; ++i) { + buf[0] = kHexDigits[data[i] >> 4]; + buf[1] = kHexDigits[data[i] & 0xF]; + rawWrite(fd, buf, 2); + } +} + +const char* moduleName() { + const char* name = g_ctx.modulePath; + for (const char* p = g_ctx.modulePath; *p != '\0'; ++p) { + if (*p == '/' || *p == '\\') { + name = p + 1; + } + } + return name[0] != '\0' ? name : "(unknown)"; +} + +const char* symbolFor(uintptr_t pc, unsigned long long* disp) { +#if defined(_WIN32) && defined(DUSK_CRASH_DBGHELP) + alignas(SYMBOL_INFO) static char storage[sizeof(SYMBOL_INFO) + 512]; + auto* sym = reinterpret_cast(storage); + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = 511; + DWORD64 d = 0; + if (SymFromAddr(GetCurrentProcess(), pc, &d, sym)) { + *disp = d; + return sym->Name; + } + return nullptr; +#elif defined(_WIN32) + (void)pc; + (void)disp; + return nullptr; +#else + Dl_info info; + if (dladdr(reinterpret_cast(pc), &info) != 0 && info.dli_sname != nullptr) { + const auto base = reinterpret_cast(info.dli_saddr); + *disp = pc >= base ? pc - base : 0; + return info.dli_sname; + } + return nullptr; +#endif +} + +void emitFrame(int fd, int index, uintptr_t pc) { + writeStr(fd, "#"); + if (index < 10) { + writeStr(fd, "0"); + } + writeDec(fd, static_cast(index)); + writeStr(fd, " abs="); + writeHex(fd, pc); + writeStr(fd, " rva="); + writeHex(fd, pc >= g_ctx.moduleBase ? pc - g_ctx.moduleBase : 0ull); + writeStr(fd, " "); + writeStr(fd, moduleName()); + unsigned long long disp = 0; + const char* sym = symbolFor(pc, &disp); + if (sym != nullptr && sym[0] != '\0') { + writeStr(fd, " "); + writeStr(fd, sym); + writeStr(fd, "+"); + writeHex(fd, disp); + } + writeStr(fd, "\n"); +} + +void emitHeader(int fd, const char* reason, unsigned long long code, bool hasCode, + uintptr_t faultAddr, uintptr_t crashPc, bool crashPcKnown) { + writeStr(fd, "\n==================== DUSKLIGHT CRASHED ====================\n"); + writeStr(fd, "Build: " DUSK_WC_DESCRIBE " (" DUSK_WC_BRANCH ")\n"); + writeStr(fd, "Revision: " DUSK_WC_REVISION " Date: " DUSK_WC_DATE + " Type: " DUSK_BUILD_TYPE "\n"); + writeStr(fd, "Platform: " DUSK_PLATFORM_NAME " / " DUSK_ARCH "\n"); + writeStr(fd, "Module: "); + writeStr(fd, g_ctx.modulePath[0] != '\0' ? g_ctx.modulePath : "(unknown)"); + writeStr(fd, "\nModule base: "); + writeHex(fd, g_ctx.moduleBase); + writeStr(fd, "\nBuild-ID: "); + if (g_ctx.buildIdLen != 0) { + writeHexBytes(fd, g_ctx.buildId, g_ctx.buildIdLen); +#if defined(_WIN32) + if (g_ctx.pdbAge != 0) { + writeStr(fd, " (Age="); + writeDec(fd, g_ctx.pdbAge); + writeStr(fd, ")"); + } +#endif + } else { + writeStr(fd, "(unavailable)"); + } + writeStr(fd, "\nReason: "); + writeStr(fd, reason); + if (hasCode) { + writeStr(fd, " ("); + writeHex(fd, code); + writeStr(fd, ")"); + } + writeStr(fd, "\nFault addr: "); + writeHex(fd, faultAddr); + writeStr(fd, "\nCrash PC: "); + if (crashPcKnown) { + writeHex(fd, crashPc); + writeStr(fd, " rva="); + writeHex(fd, crashPc >= g_ctx.moduleBase ? crashPc - g_ctx.moduleBase : 0ull); + } else { + writeStr(fd, "(unavailable on this platform)"); + } + writeStr(fd, "\n"); + writeStr(fd, "Backtrace:\n"); +} + +void emitFooter(int fd) { + writeStr(fd, "========================================================\n"); +} + +#if defined(_WIN32) + +LONG g_inHandler = 0; +LPTOP_LEVEL_EXCEPTION_FILTER g_prevFilter = nullptr; + +void captureBuildId() { + const auto* base = reinterpret_cast(g_ctx.moduleBase); + if (base == nullptr) { + return; + } + const auto* dos = reinterpret_cast(base); + if (dos->e_magic != IMAGE_DOS_SIGNATURE) { + return; + } + const auto* nt = reinterpret_cast(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) { + return; + } + const IMAGE_DATA_DIRECTORY& dir = + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; + if (dir.VirtualAddress == 0 || dir.Size == 0) { + return; + } + const auto* dbg = reinterpret_cast(base + dir.VirtualAddress); + const unsigned count = dir.Size / sizeof(IMAGE_DEBUG_DIRECTORY); + for (unsigned i = 0; i < count; ++i) { + if (dbg[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) { + continue; + } + const auto* cv = base + dbg[i].AddressOfRawData; + if (std::memcmp(cv, "RSDS", 4) != 0) { + continue; + } + std::memcpy(g_ctx.buildId, cv + 4, sizeof(GUID)); + g_ctx.buildIdLen = sizeof(GUID); + std::memcpy(&g_ctx.pdbAge, cv + 4 + sizeof(GUID), sizeof(g_ctx.pdbAge)); + break; + } +} + +const char* exceptionName(DWORD code) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + default: + return "EXCEPTION"; + } +} + +int captureBacktraceWin(CONTEXT ctx, uintptr_t* out, int cap) { + int n = 0; + while (n < cap) { +#if defined(_M_X64) + const DWORD64 ip = ctx.Rip; +#elif defined(_M_ARM64) + const DWORD64 ip = ctx.Pc; +#else + const DWORD64 ip = 0; +#endif + if (ip == 0) { + break; + } + out[n++] = static_cast(ip); +#if defined(_M_X64) || defined(_M_ARM64) + DWORD64 imageBase = 0; + PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(ip, &imageBase, nullptr); + if (fn != nullptr) { + PVOID handlerData = nullptr; + DWORD64 establisherFrame = 0; + RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, ip, fn, &ctx, &handlerData, + &establisherFrame, nullptr); + continue; + } +#if defined(_M_X64) + if (ctx.Rsp == 0) { + break; + } + ctx.Rip = *reinterpret_cast(ctx.Rsp); + ctx.Rsp += sizeof(DWORD64); +#else + if (ctx.Lr == 0 || ctx.Lr == ip) { + break; + } + ctx.Pc = ctx.Lr; + ctx.Lr = 0; +#endif +#else + break; +#endif + } + return n; +} + +void emit(int fd, EXCEPTION_POINTERS* ep) { + if (fd < 0) { + return; + } + + const DWORD code = ep->ExceptionRecord->ExceptionCode; + const uintptr_t pc = reinterpret_cast(ep->ExceptionRecord->ExceptionAddress); + uintptr_t faultAddr = 0; + if (code == EXCEPTION_ACCESS_VIOLATION && ep->ExceptionRecord->NumberParameters >= 2) { + faultAddr = static_cast(ep->ExceptionRecord->ExceptionInformation[1]); + } + + emitHeader(fd, exceptionName(code), code, true, faultAddr, pc, true); + + uintptr_t frames[kMaxFrames]; + const int frameCount = captureBacktraceWin(*ep->ContextRecord, frames, kMaxFrames); + for (int i = 0; i < frameCount; ++i) { + emitFrame(fd, i, frames[i]); + } + + emitFooter(fd); +} + +LONG WINAPI windowsHandler(EXCEPTION_POINTERS* ep) { + if (InterlockedCompareExchange(&g_inHandler, 1, 0) != 0) { + return EXCEPTION_CONTINUE_SEARCH; + } + emit(kStderrFd, ep); + const int logFd = dusk::GetLogFileDescriptor(); + if (logFd >= 0) { + emit(logFd, ep); + } + if (g_prevFilter != nullptr) { + return g_prevFilter(ep); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +#else + +constexpr int kSignals[] = {SIGSEGV, SIGBUS, SIGABRT, SIGILL, SIGFPE}; +constexpr int kSignalCount = static_cast(sizeof(kSignals) / sizeof(kSignals[0])); +constexpr int kAltStackSize = 128 * 1024; + +volatile std::sig_atomic_t g_inHandler = 0; +char g_altStack[kAltStackSize]; +struct sigaction g_prev[kSignalCount]; +std::terminate_handler g_prevTerminate = nullptr; + +void crashRegs(void* ucv, uintptr_t& pc, uintptr_t& lr, uintptr_t& fp) { + pc = 0; + lr = 0; + fp = 0; + if (ucv == nullptr) { + return; + } + auto* uc = static_cast(ucv); +#if defined(__APPLE__) +#if defined(__aarch64__) || defined(__arm64__) + pc = static_cast(uc->uc_mcontext->__ss.__pc); + lr = static_cast(uc->uc_mcontext->__ss.__lr); + fp = static_cast(uc->uc_mcontext->__ss.__fp); +#elif defined(__x86_64__) + pc = static_cast(uc->uc_mcontext->__ss.__rip); + fp = static_cast(uc->uc_mcontext->__ss.__rbp); +#endif +#elif defined(__ANDROID__) +#if defined(__aarch64__) + pc = static_cast(uc->uc_mcontext.pc); + lr = static_cast(uc->uc_mcontext.regs[30]); + fp = static_cast(uc->uc_mcontext.regs[29]); +#elif defined(__x86_64__) + pc = static_cast(uc->uc_mcontext.gregs[REG_RIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_RBP]); +#elif defined(__arm__) + pc = static_cast(uc->uc_mcontext.arm_pc); + lr = static_cast(uc->uc_mcontext.arm_lr); + fp = static_cast(uc->uc_mcontext.arm_fp); +#elif defined(__i386__) + pc = static_cast(uc->uc_mcontext.gregs[REG_EIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_EBP]); +#endif +#elif defined(__linux__) +#if defined(__x86_64__) + pc = static_cast(uc->uc_mcontext.gregs[REG_RIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_RBP]); +#elif defined(__aarch64__) + pc = static_cast(uc->uc_mcontext.pc); + lr = static_cast(uc->uc_mcontext.regs[30]); + fp = static_cast(uc->uc_mcontext.regs[29]); +#elif defined(__i386__) + pc = static_cast(uc->uc_mcontext.gregs[REG_EIP]); + fp = static_cast(uc->uc_mcontext.gregs[REG_EBP]); +#endif +#endif +} + +bool pcNearFunctionEntry(uintptr_t pc) { + constexpr uintptr_t kPrologueWindow = 20; + Dl_info info; + if (dladdr(reinterpret_cast(pc), &info) == 0 || info.dli_saddr == nullptr) { + return false; + } + const auto start = reinterpret_cast(info.dli_saddr); + return pc >= start && pc - start <= kPrologueWindow; +} + +int captureBacktraceFP(uintptr_t pc, uintptr_t lr, uintptr_t fp, uintptr_t* out, int cap) { + int n = 0; + if (pc != 0 && n < cap) { + out[n++] = pc; + } + bool dedupeLr = false; + if (lr != 0 && lr != pc && n < cap && pcNearFunctionEntry(pc)) { + out[n++] = lr; + dedupeLr = true; + } + uintptr_t cur = fp; + uintptr_t prev = 0; + constexpr uintptr_t kMaxFrameSpan = 16u << 20; + while (n < cap) { + if (cur == 0 || (cur & (sizeof(uintptr_t) - 1)) != 0 || cur <= prev) { + break; + } + const auto* slot = reinterpret_cast(cur); + const uintptr_t next = slot[0]; + const uintptr_t ret = slot[1]; + if (ret == 0) { + break; + } + const bool skip = dedupeLr && ret == lr; + dedupeLr = false; + if (!skip) { + out[n++] = ret; + } + if (next != 0 && next > cur && next - cur > kMaxFrameSpan) { + break; + } + prev = cur; + cur = next; + } + return n; +} + +struct UnwindState { + uintptr_t* pcs; + int count; + int cap; + int skip; +}; + +_Unwind_Reason_Code unwindCb(struct _Unwind_Context* ctx, void* arg) { + auto* s = static_cast(arg); + const uintptr_t ip = static_cast(_Unwind_GetIP(ctx)); + if (ip == 0) { + return _URC_END_OF_STACK; + } + if (s->skip > 0) { + --s->skip; + return _URC_NO_REASON; + } + if (s->count >= s->cap) { + return _URC_END_OF_STACK; + } + s->pcs[s->count++] = ip; + return _URC_NO_REASON; +} + +int captureBacktrace(uintptr_t* pcs, int cap, int skip) { + UnwindState s{pcs, 0, cap, skip}; + _Unwind_Backtrace(&unwindCb, &s); + return s.count; +} + +void prewarmUnwinder() { + uintptr_t warm[4]; + captureBacktrace(warm, 4, 0); +} + +#if defined(__APPLE__) + +void captureBuildId() { + const auto* header = reinterpret_cast(g_ctx.moduleBase); + if (header == nullptr || header->magic != MH_MAGIC_64) { + return; + } + const auto* lc = reinterpret_cast( + reinterpret_cast(header) + sizeof(struct mach_header_64)); + for (uint32_t i = 0; i < header->ncmds; ++i) { + if (lc->cmd == LC_UUID) { + const auto* uuid = reinterpret_cast(lc); + std::memcpy(g_ctx.buildId, uuid->uuid, sizeof(uuid->uuid)); + g_ctx.buildIdLen = sizeof(uuid->uuid); + return; + } + lc = reinterpret_cast( + reinterpret_cast(lc) + lc->cmdsize); + } +} + +#else + +bool segmentContains(const dl_phdr_info* info, uintptr_t addr) { + for (int i = 0; i < info->dlpi_phnum; ++i) { + const ElfW(Phdr)& ph = info->dlpi_phdr[i]; + if (ph.p_type != PT_LOAD) { + continue; + } + const uintptr_t start = info->dlpi_addr + ph.p_vaddr; + if (addr >= start && addr < start + ph.p_memsz) { + return true; + } + } + return false; +} + +bool readGnuBuildId(const dl_phdr_info* info) { + for (int i = 0; i < info->dlpi_phnum; ++i) { + const ElfW(Phdr)& ph = info->dlpi_phdr[i]; + if (ph.p_type != PT_NOTE) { + continue; + } + const auto* p = reinterpret_cast(info->dlpi_addr + ph.p_vaddr); + const uint8_t* end = p + ph.p_memsz; + while (p + sizeof(ElfW(Nhdr)) <= end) { + const auto* nh = reinterpret_cast(p); + const char* name = reinterpret_cast(nh + 1); + const uint8_t* desc = + reinterpret_cast(name + ((nh->n_namesz + 3) & ~3u)); + if (nh->n_type == NT_GNU_BUILD_ID && nh->n_namesz == 4 && + std::memcmp(name, "GNU", 4) == 0) { + unsigned n = nh->n_descsz; + if (n > sizeof(g_ctx.buildId)) { + n = sizeof(g_ctx.buildId); + } + std::memcpy(g_ctx.buildId, desc, n); + g_ctx.buildIdLen = n; + return true; + } + p = desc + ((nh->n_descsz + 3) & ~3u); + } + } + return false; +} + +int elfBuildIdCallback(dl_phdr_info* info, size_t, void* arg) { + const auto self = *static_cast(arg); + if (!segmentContains(info, self)) { + return 0; + } + readGnuBuildId(info); + return 1; +} + +void captureBuildId() { + uintptr_t self = reinterpret_cast(&install); + dl_iterate_phdr(&elfBuildIdCallback, &self); +} + +#endif + +const char* signalName(int sig) { + switch (sig) { + case SIGSEGV: + return "SIGSEGV (segmentation fault)"; + case SIGBUS: + return "SIGBUS (bus error)"; + case SIGABRT: + return "SIGABRT (abort)"; + case SIGILL: + return "SIGILL (illegal instruction)"; + case SIGFPE: + return "SIGFPE (floating point exception)"; + default: + return "unknown signal"; + } +} + +void emit(int fd, int sig, siginfo_t* info, const uintptr_t* frames, int frameCount, + uintptr_t pc) { + if (fd < 0) { + return; + } + const uintptr_t faultAddr = + info != nullptr ? reinterpret_cast(info->si_addr) : 0; + emitHeader(fd, signalName(sig), 0, false, faultAddr, pc, pc != 0); + for (int i = 0; i < frameCount; ++i) { + emitFrame(fd, i, frames[i]); + } + emitFooter(fd); +} + +void chainPrevious(int sig, siginfo_t* info, void* uc) { + for (int i = 0; i < kSignalCount; ++i) { + if (kSignals[i] != sig) { + continue; + } + const struct sigaction& o = g_prev[i]; + if ((o.sa_flags & SA_SIGINFO) != 0) { + if (o.sa_sigaction != nullptr) { + o.sa_sigaction(sig, info, uc); + return; + } + } else { + if (o.sa_handler == SIG_IGN) { + return; + } + if (o.sa_handler != SIG_DFL && o.sa_handler != nullptr) { + o.sa_handler(sig); + return; + } + } + break; + } + ::signal(sig, SIG_DFL); + ::raise(sig); +} + +void handler(int sig, siginfo_t* info, void* ucv) { + if (g_inHandler != 0) { + _exit(128 + sig); + } + g_inHandler = 1; + + uintptr_t pc = 0; + uintptr_t lr = 0; + uintptr_t fp = 0; + crashRegs(ucv, pc, lr, fp); + uintptr_t frames[kMaxFrames]; + int frameCount = captureBacktraceFP(pc, lr, fp, frames, kMaxFrames); + if (frameCount < 2) { + frameCount = captureBacktrace(frames, kMaxFrames, 2); + } + + emit(kStderrFd, sig, info, frames, frameCount, pc); + const int logFd = dusk::GetLogFileDescriptor(); + if (logFd >= 0) { + emit(logFd, sig, info, frames, frameCount, pc); + ::fsync(logFd); + } + + chainPrevious(sig, info, ucv); +} + +void writeTerminateMessage(int fd, const char* body, const char* what) { + writeStr(fd, "\nterminate: "); + writeStr(fd, body); + writeStr(fd, what); + writeStr(fd, "\n"); +} + +void onTerminate() { + const char* body = "unknown reason"; + const char* what = nullptr; + if (std::exception_ptr ep = std::current_exception()) { + try { + std::rethrow_exception(ep); + } catch (const std::exception& e) { + body = "uncaught exception: "; + what = e.what(); + } catch (...) { + body = "uncaught non-std exception"; + } + } else { + body = "no active exception"; + } + writeTerminateMessage(kStderrFd, body, what); + writeTerminateMessage(dusk::GetLogFileDescriptor(), body, what); + if (g_prevTerminate != nullptr) { + g_prevTerminate(); + } + std::abort(); +} + +#endif + +} // namespace + +void install() { +#if defined(_WIN32) + g_ctx.moduleBase = reinterpret_cast(GetModuleHandleW(nullptr)); + GetModuleFileNameA(nullptr, g_ctx.modulePath, sizeof(g_ctx.modulePath) - 1); + captureBuildId(); +#if defined(DUSK_CRASH_DBGHELP) + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); + SymInitialize(GetCurrentProcess(), nullptr, TRUE); +#endif + g_prevFilter = SetUnhandledExceptionFilter(&windowsHandler); +#else + Dl_info moduleInfo; + if (dladdr(reinterpret_cast(&install), &moduleInfo) != 0) { + g_ctx.moduleBase = reinterpret_cast(moduleInfo.dli_fbase); + if (moduleInfo.dli_fname != nullptr) { + std::strncpy(g_ctx.modulePath, moduleInfo.dli_fname, + sizeof(g_ctx.modulePath) - 1); + } + } + captureBuildId(); + prewarmUnwinder(); + + static stack_t altStack; + altStack.ss_sp = g_altStack; + altStack.ss_size = sizeof(g_altStack); + altStack.ss_flags = 0; + sigaltstack(&altStack, nullptr); + + struct sigaction sa; + std::memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = &handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + + for (int i = 0; i < kSignalCount; ++i) { + sigaction(kSignals[i], &sa, &g_prev[i]); + } + + g_prevTerminate = std::set_terminate(&onTerminate); +#endif +} + +} // namespace dusk::crash_handler diff --git a/src/dusk/data.cpp b/src/dusk/data.cpp index ce61965947..dc666dd236 100644 --- a/src/dusk/data.cpp +++ b/src/dusk/data.cpp @@ -525,7 +525,14 @@ bool validate_writable_data_path(const std::filesystem::path& path, std::string* try { io::FileStream::WriteAllText(probePath, "dusk"); } catch (const std::exception& e) { +#if defined(__ANDROID__) + set_error(errorOut, + fmt::format("{} could not write to the selected folder. On Android, allow " + "\"All files access\" for Dusklight and try again.", + AppName)); +#else set_error(errorOut, fmt::format("{} could not write to the selected folder.", AppName)); +#endif Log.warn("Failed write probe for custom data folder '{}': {}", io::fs_path_to_string(path), e.what()); return false; diff --git a/src/dusk/data.hpp b/src/dusk/data.hpp index c3ce495b46..bf20035a2d 100644 --- a/src/dusk/data.hpp +++ b/src/dusk/data.hpp @@ -15,7 +15,7 @@ #define DUSK_CAN_OPEN_DATA_FOLDER 0 #endif -#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || defined(__ANDROID__) +#if (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) #define DUSK_CAN_CHANGE_DATA_FOLDER 0 #else #define DUSK_CAN_CHANGE_DATA_FOLDER 1 diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index b2ef0309e3..be03d51e96 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -1,14 +1,15 @@ #include "dusk/frame_interpolation.h" -#include -#include "mtx.h" #include "f_op/f_op_camera_mng.h" #include "m_Do/m_Do_graphic.h" +#include "mtx.h" + +#include namespace { struct Recording { - std::unordered_map matrix_values; + absl::flat_hash_map matrix_values; }; bool s_initialized = false; @@ -26,7 +27,7 @@ uint64_t g_sim_tick_seq = 0; Recording g_current_recording; Recording g_previous_recording; -std::unordered_map g_replacements; +absl::flat_hash_map g_replacements; struct CameraSnapshot { cXyz eye{}; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 077b4c15bc..d8b85b2d63 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -322,8 +322,8 @@ namespace dusk { } ImGui::PushFont(ImGuiEngine::fontLarge); ImGuiTextCenter("Failed to initialize any graphics backend."); - ImGuiTextCenter("\nDusklight requires Vulkan 1.1+, or Direct X 12.0."); - ImGuiTextCenter("\nTry updating your Operating System and GPU drivers."); + ImGuiTextCenter("\nDusklight requires at least Vulkan 1.1 or Direct3D 12."); + ImGuiTextCenter("\nTry updating your operating system and GPU drivers."); const auto& style = ImGui::GetStyle(); const auto retrySize = ImGui::CalcTextSize("Retry (Auto backend)"); const auto quitSize = ImGui::CalcTextSize("Quit"); diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 3045e09df1..02326eceea 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -208,6 +208,27 @@ namespace dusk { daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0); daHorse_c* horse = dComIfGp_getHorseActor(); + double speedXzy = 0.0; + if (player != nullptr) { + speedXzy = sqrtf(player->speed.x * player->speed.x + + player->speed.z * player->speed.z + + player->speed.y * player->speed.y); + } + + ImGui::Text("Global"); + ImGuiStringViewText( + player != nullptr + ? fmt::format("Stage: {}\n", dComIfGp_getStartStageName()) + : "Stage: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Layer: {0}\n", dComIfG_play_c::getLayerNo(0)) + : "Layer: ?\n" + ); + + ImGui::Separator(); ImGui::Text("Link"); ImGuiStringViewText( player != nullptr @@ -217,14 +238,38 @@ namespace dusk { ImGuiStringViewText( player != nullptr - ? fmt::format("Angle: {0}\n", player->shape_angle.y) - : "Angle: ?\n" + ? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", player->speed.x, player->speed.y, player->speed.z) + : "Velocity (XYZ): ?, ?, ?\n" ); ImGuiStringViewText( player != nullptr - ? fmt::format("Speed: {: .4f}\n", player->speedF) - : "Speed: ?\n" + ? fmt::format("Speed (SpeedF): {: .4f}\n", player->speedF) + : "Speed (SpeedF): ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Speed (3D): {: .4f}\n", speedXzy) + : "Speed (3D): ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Angle: {0}\n", player->shape_angle.y) + : "Angle: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(player)) + : "Room: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Entry: {0}\n", dComIfGp_getStartStagePoint()) + : "Entry: ?\n" ); ImGui::Separator(); @@ -235,6 +280,18 @@ namespace dusk { : "Position: ?, ?, ?\n" ); + ImGuiStringViewText( + horse != nullptr + ? fmt::format("Velocity (XYZ): {: .4f}, {: .4f}, {: .4f}\n", horse->speed.x, horse->speed.y, horse->speed.z) + : "Velocity (XYZ): ?, ?, ?\n" + ); + + ImGuiStringViewText( + horse != nullptr + ? fmt::format("Speed (SpeedF): {: .4f}\n", horse->speedF) + : "Speed (SpeedF): ?\n" + ); + ImGuiStringViewText( horse != nullptr ? fmt::format("Angle: {0}\n", horse->shape_angle.y) @@ -243,8 +300,20 @@ namespace dusk { ImGuiStringViewText( horse != nullptr - ? fmt::format("Speed: {: .4f}\n", horse->speedF) - : "Speed: ?\n" + ? fmt::format("Room: {0}\n", fopAcM_GetRoomNo(horse)) + : "Room: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Saved Stage: {}\n", dComIfGs_getHorseRestartStageName()) + : "Saved Stage: ?\n" + ); + + ImGuiStringViewText( + player != nullptr + ? fmt::format("Saved Room: {0}\n", dComIfGs_getHorseRestartRoomNo()) + : "Saved Room: ?\n" ); ShowCornerContextMenu(m_playerInfoOverlayCorner, m_debugOverlayCorner); diff --git a/src/dusk/imgui/ImGuiProcessOverlay.cpp b/src/dusk/imgui/ImGuiProcessOverlay.cpp index 806a9ea0f6..5b4fff28db 100644 --- a/src/dusk/imgui/ImGuiProcessOverlay.cpp +++ b/src/dusk/imgui/ImGuiProcessOverlay.cpp @@ -46,7 +46,7 @@ namespace dusk { ImGui::TableNextColumn(); char id_buf[32]; - sprintf(id_buf, "%d", proc->id); + SAFE_SPRINTF(id_buf, "%d", proc->id); int flags = ImGuiTreeNodeFlags_SpanAllColumns; bool isLayer = fpcBs_Is_JustOfType(g_fpcNd_type, proc->subtype); diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index c09267bc27..ec2802de44 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -550,7 +550,7 @@ namespace dusk { char nameBuffer[8]; snprintf(nameBuffer, sizeof(nameBuffer), "%s", playerName); if (ImGui::InputText("##PlayerNameInput", nameBuffer, 8)) { - strcpy(dComIfGs_getPlayerName(), nameBuffer); + SAFE_STRCPY(dComIfGs_getPlayerName(), nameBuffer); } const char* horseName = dComIfGs_getHorseName(); @@ -559,7 +559,7 @@ namespace dusk { char horseNameBuffer[8]; snprintf(horseNameBuffer, sizeof(horseNameBuffer), "%s", horseName); if (ImGui::InputText("##HorseNameInput", horseNameBuffer, 8)) { - strcpy(dComIfGs_getHorseName(), horseNameBuffer); + SAFE_STRCPY(dComIfGs_getHorseName(), horseNameBuffer); } ImGui::Separator(); @@ -745,8 +745,8 @@ namespace dusk { ImGui::SameLine(); char nameBuffer[8]; snprintf(nameBuffer, sizeof(nameBuffer), "%s", returnPlace.mName); - if (ImGui::InputText("##SaveStageNameInput", nameBuffer, 8)) { - strcpy(returnPlace.mName, nameBuffer); + if (ImGui::InputText("##SaveStageNameInput", nameBuffer, sizeof(nameBuffer))) { + SAFE_STRCPY(returnPlace.mName, nameBuffer); } ImGui::Text("Room: "); @@ -787,8 +787,8 @@ namespace dusk { ImGui::SameLine(); char horseStageBuffer[8]; snprintf(horseStageBuffer, sizeof(horseStageBuffer), "%s", horsePlace.mName); - if (ImGui::InputText("##HorseStageNameInput", horseStageBuffer, 8)) { - strcpy(horsePlace.mName, horseStageBuffer); + if (ImGui::InputText("##HorseStageNameInput", horseStageBuffer, sizeof(horseStageBuffer))) { + SAFE_STRCPY(horsePlace.mName, horseStageBuffer); } ImGui::Text("Room: "); diff --git a/src/dusk/logging.cpp b/src/dusk/logging.cpp index 4319d2dc7e..b24f54878a 100644 --- a/src/dusk/logging.cpp +++ b/src/dusk/logging.cpp @@ -33,10 +33,17 @@ static constexpr std::string_view StubFragments[] = { "but selective updates are not implemented"sv, }; +#if _WIN32 +#define DUSK_FILENO _fileno +#else +#define DUSK_FILENO fileno +#endif + namespace { // On macOS, std::mutex becomes poisoned when its dtor is run. // We use this to check if the LogState is destroyed before attempting to acquire it. std::atomic g_logStateAlive(true); +std::atomic g_logFd(-1); struct LogState { std::mutex mutex; @@ -54,6 +61,7 @@ struct LogState { } std::lock_guard lock(mutex); if (file != nullptr) { + g_logFd.store(-1, std::memory_order_release); std::fflush(file); std::fclose(file); file = nullptr; @@ -232,6 +240,7 @@ void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraL } g_logState.filePath = logPath.u8string(); + g_logFd.store(DUSK_FILENO(g_logState.file), std::memory_order_release); aurora::g_config.logCallback = &aurora_log_callback; aurora::g_config.logLevel = logLevel; WriteLogLine(g_logState.file, "INFO", "dusk", "File logging initialized", 24); @@ -252,3 +261,7 @@ const char* dusk::GetLogFilePath() { return reinterpret_cast( g_logState.filePath.empty() ? nullptr : g_logState.filePath.c_str()); } + +int dusk::GetLogFileDescriptor() { + return g_logFd.load(std::memory_order_acquire); +} diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index adb5e36064..6257c11e6f 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -45,6 +45,7 @@ UserSettings g_userSettings = { .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, .autoSave {"game.autoSave", false}, + .enhancedMapMenus {"game.enhancedMapMenus", false}, // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, @@ -54,17 +55,18 @@ UserSettings g_userSettings = { .enableAchievementToasts {"game.enableAchievementToasts", true}, .enableControllerToasts {"game.enableControllerToasts", true}, .enableDiscordPresence {"game.enableDiscordPresence", true}, + .menuScalingMode {"game.menuScalingMode", MenuScaling::Wii}, // Graphics .bloomMode {"game.bloomMode", BloomMode::Dusk}, .bloomMultiplier {"game.bloomMultiplier", 1.0f}, + .depthOfFieldMode{"game.depthOfFieldMode", DepthOfFieldMode::Dusk}, .disableWaterRefraction {"game.disableWaterRefraction", false}, .enableTextureReplacements {"game.enableTextureReplacements", true}, .enableFrameInterpolation {"game.enableFrameInterpolation", FrameInterpMode::Off}, .internalResolutionScale {"game.internalResolutionScale", 0}, .shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1}, .resampler {"game.resampler", Resampler::Bilinear}, - .enableDepthOfField {"game.enableDepthOfField", true}, .enableMapBackground {"game.enableMapBackground", true}, .disableCutscenePillarboxing {"game.disableCutscenePillarboxing", false}, @@ -88,6 +90,8 @@ UserSettings g_userSettings = { .invertCameraYAxis {"game.invertCameraYAxis", false}, .invertFirstPersonXAxis {"game.invertFirstPersonXAxis", false}, .invertFirstPersonYAxis {"game.invertFirstPersonYAxis", false}, + .invertAirSwimX {"game.invertAirSwimX", false}, + .invertAirSwimY {"game.invertAirSwimY", false}, .freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f}, .debugFlyCam {"game.debugFlyCam", false}, .debugFlyCamLockEvents {"game.debugFlyCamLockEvents", true}, @@ -124,6 +128,7 @@ UserSettings g_userSettings = { .liveSplitEnabled {"game.liveSplitEnabled", false}, .showSpeedrunRTATimer {"game.showSpeedrunRTATimer", true}, .recordingMode {"game.recordingMode", false}, + .removeQuestMapMarkers {"game.removeQuestMapMarkers", false}, .showInputViewer {"game.showInputViewer", false}, .showInputViewerGyro {"game.showInputViewerGyro", false} }, @@ -176,6 +181,11 @@ UserSettings& getSettings() { return g_userSettings; } +bool tphd_active() { + const std::string& hdPath = g_userSettings.backend.hdContentPath; + return !hdPath.empty(); +} + void registerSettings() { // Video Register(g_userSettings.video.enableFullscreen); @@ -213,23 +223,26 @@ void registerSettings() { Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.autoSave); + Register(g_userSettings.game.enhancedMapMenus); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); Register(g_userSettings.game.invertFirstPersonXAxis); Register(g_userSettings.game.invertFirstPersonYAxis); + Register(g_userSettings.game.invertAirSwimX); + Register(g_userSettings.game.invertAirSwimY); Register(g_userSettings.game.freeCameraSensitivity); Register(g_userSettings.game.minimalHUD); Register(g_userSettings.game.pauseOnFocusLost); Register(g_userSettings.game.enableDiscordPresence); Register(g_userSettings.game.bloomMode); Register(g_userSettings.game.bloomMultiplier); + Register(g_userSettings.game.depthOfFieldMode); Register(g_userSettings.game.disableWaterRefraction); Register(g_userSettings.game.enableTextureReplacements); Register(g_userSettings.game.internalResolutionScale); Register(g_userSettings.game.resampler); Register(g_userSettings.game.shadowResolutionMultiplier); - Register(g_userSettings.game.enableDepthOfField); Register(g_userSettings.game.enableMapBackground); Register(g_userSettings.game.disableCutscenePillarboxing); Register(g_userSettings.game.enableFastIronBoots); @@ -249,6 +262,8 @@ void registerSettings() { Register(g_userSettings.game.liveSplitEnabled); Register(g_userSettings.game.showSpeedrunRTATimer); Register(g_userSettings.game.recordingMode); + Register(g_userSettings.game.menuScalingMode); + Register(g_userSettings.game.removeQuestMapMarkers); Register(g_userSettings.game.showInputViewer); Register(g_userSettings.game.showInputViewerGyro); Register(g_userSettings.game.fastSpinner); diff --git a/src/dusk/string.cpp b/src/dusk/string.cpp new file mode 100644 index 0000000000..e40094ef70 --- /dev/null +++ b/src/dusk/string.cpp @@ -0,0 +1,85 @@ +#include "dusk/string.hpp" +#include "fmt/format.h" + +namespace { +void strncpyProxy(char* dst, const char* src, size_t count) { +#if _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + strncpy(dst, src, count); +#if _MSC_VER +#pragma warning(pop) +#endif +} +} // namespace + +namespace dusk { + +void TextSpan::CrashSpawnEmpty() { + CRASH("Span is empty!"); +} + +void SafeStringCopyTruncate(char* buffer, size_t bufSize, const char* src) { + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + strncpyProxy(buffer, src, bufSize); + buffer[bufSize - 1] = 0; +} + +void SafeStringCopy(char* buffer, size_t bufSize, const char* src) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + const auto srcSize = strlen(src); + if (srcSize > bufSize - 1) [[unlikely]] { + const auto msg = fmt::format( + "Destination buffer too small! Need %d, have %d", + srcSize + 1, + bufSize); + CRASH("%s", msg.c_str()); + } + + strncpyProxy(buffer, src, bufSize); + buffer[bufSize - 1] = 0; +} + +void SafeStringCat(char* buffer, size_t bufSize, const char* src) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + if (buffer == src) [[unlikely]] { + CRASH("Cannot copy string to same buffer"); + } + + const auto dstSize = strnlen(buffer, bufSize); + const auto srcSize = strlen(src); + if (dstSize + srcSize + 1 > bufSize) [[unlikely]] { + CRASH("Destination buffer too small!"); + } + + memcpy(buffer + dstSize, src, srcSize); + buffer[dstSize + srcSize] = 0; +} + +int SafeStringVPrintf(char* buffer, size_t bufSize, const char* src, std::va_list args) { + if (bufSize == 0) [[unlikely]] { + CRASH("Target buffer cannot be size zero"); + } + + return vsnprintf(buffer, bufSize, src, args); +} + +} // namespace dusk \ No newline at end of file diff --git a/src/dusk/ui/controller_config.cpp b/src/dusk/ui/controller_config.cpp index a0b2b6baa9..20c599ecfd 100644 --- a/src/dusk/ui/controller_config.cpp +++ b/src/dusk/ui/controller_config.cpp @@ -275,6 +275,7 @@ ControllerConfigWindow::ControllerConfigWindow(bool prelaunch) { void ControllerConfigWindow::hide(bool close) { stop_rumble_test(); cancel_pending_binding(); + config::Save(); Window::hide(close); } diff --git a/src/dusk/ui/editor.cpp b/src/dusk/ui/editor.cpp index 196c9bf719..8aa3c3894d 100644 --- a/src/dusk/ui/editor.cpp +++ b/src/dusk/ui/editor.cpp @@ -196,7 +196,7 @@ Rml::String get_player_name() { if (!has_save_data()) { return ""; } - return dComIfGs_getPlayerName(); + return dComIfGs_getPlayerName().buffer; } void set_player_name(Rml::String name) { @@ -207,7 +207,7 @@ Rml::String get_horse_name() { if (!has_save_data()) { return ""; } - return dComIfGs_getHorseName(); + return dComIfGs_getHorseName().buffer; } void set_horse_name(Rml::String name) { diff --git a/src/dusk/ui/graphics_tuner.cpp b/src/dusk/ui/graphics_tuner.cpp index 0b75b27a89..8cea89e984 100644 --- a/src/dusk/ui/graphics_tuner.cpp +++ b/src/dusk/ui/graphics_tuner.cpp @@ -52,6 +52,8 @@ int get_value(GraphicsOption option) { return std::clamp( static_cast(getSettings().game.bloomMultiplier.getValue() * 100.0f + 0.5f), 0, 100); + case GraphicsOption::DepthOfFieldMode: + return static_cast(getSettings().game.depthOfFieldMode.getValue()); } return 0; } @@ -85,6 +87,10 @@ void set_value(GraphicsOption option, int value) { getSettings().game.bloomMode.setValue(static_cast(std::clamp( value, static_cast(BloomMode::Off), static_cast(BloomMode::Dusk)))); break; + case GraphicsOption::DepthOfFieldMode: + getSettings().game.depthOfFieldMode.setValue(static_cast(std::clamp( + value, static_cast(DepthOfFieldMode::Off), static_cast(DepthOfFieldMode::Dusk)))); + break; case GraphicsOption::BloomMultiplier: getSettings().game.bloomMultiplier.setValue(std::clamp(value, 0, 100) / 100.0f); break; @@ -214,6 +220,16 @@ Rml::String format_graphics_setting_value(GraphicsOption option, int value) { return "Dusklight"; } break; + case GraphicsOption::DepthOfFieldMode: + switch (static_cast(value)) { + case DepthOfFieldMode::Off: + return "Off"; + case DepthOfFieldMode::Classic: + return "Classic"; + case DepthOfFieldMode::Dusk: + return "Dusklight"; + } + break; case GraphicsOption::BloomMultiplier: return fmt::format("{}%", value); } diff --git a/src/dusk/ui/graphics_tuner.hpp b/src/dusk/ui/graphics_tuner.hpp index 6f8f113d13..2f5b9c5611 100644 --- a/src/dusk/ui/graphics_tuner.hpp +++ b/src/dusk/ui/graphics_tuner.hpp @@ -45,6 +45,7 @@ enum class GraphicsOption { Resampler, BloomMode, BloomMultiplier, + DepthOfFieldMode, }; Rml::String format_graphics_setting_value(GraphicsOption option, int value); diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index e5af8f2d50..4d5950f1f4 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -14,11 +14,13 @@ void applyPresetClassic() { auto& s = getSettings(); s.video.lockAspectRatio.setValue(true); s.game.bloomMode.setValue(BloomMode::Classic); + s.game.depthOfFieldMode.setValue(DepthOfFieldMode::Classic); s.game.enableAchievementToasts.setValue(false); s.game.enableControllerToasts.setValue(false); s.game.internalResolutionScale.setValue(1); s.game.shadowResolutionMultiplier.setValue(1); s.game.hideTvSettingsScreen.setValue(false); + s.game.menuScalingMode.setValue(MenuScaling::GameCube); AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT); } @@ -43,10 +45,13 @@ void applyPresetDusk() { s.game.enableFrameInterpolation.setValue(FrameInterpMode::Unlimited); s.game.sunsSong.setValue(true); s.game.bloomMode.setValue(BloomMode::Dusk); + s.game.depthOfFieldMode.setValue(DepthOfFieldMode::Dusk); s.game.internalResolutionScale.setValue(0); s.game.shadowResolutionMultiplier.setValue(4); s.game.enableGyroAim.setValue(true); s.game.autoSave.setValue(true); + s.game.menuScalingMode.setValue(MenuScaling::Dusklight); + s.game.enhancedMapMenus.setValue(true); } } // namespace diff --git a/src/dusk/ui/settings.cpp b/src/dusk/ui/settings.cpp index ce70d43ae6..727e3c1b34 100644 --- a/src/dusk/ui/settings.cpp +++ b/src/dusk/ui/settings.cpp @@ -70,6 +70,12 @@ constexpr std::array kGyroInputModeLabels = { "Mouse", }; +constexpr std::array kMenuScalingModeLabels = { + "GameCube", + "Wii", + "Dusklight", +}; + bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) { if (backend == "auto") { outBackend = BACKEND_AUTO; @@ -364,6 +370,9 @@ const Rml::String kBloomHelpText = "a higher-quality bloom pass."; const Rml::String kBloomBrightnessHelpText = "Configure bloom intensity. Higher values make bright areas glow more strongly."; +const Rml::String kDepthOfFieldHelpText = + "Configure the post-processing depth-of-field effect. Classic uses the original depth-of-field pass;" + " Dusklight uses a higher-quality depth-of-field pass."; const Rml::String kUnlockFramerateHelpText = "
Uses inter-frame interpolation to enable higher frame rates.

May introduce minor " "visual artifacts or animation glitches."; @@ -870,7 +879,18 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .valueMax = 100, .defaultValue = 100, .step = 10, - }, mPrelaunch); + }, + mPrelaunch); + graphics_tuner_control(*this, leftPane, rightPane, getSettings().game.depthOfFieldMode, + GraphicsTunerProps{ + .option = GraphicsOption::DepthOfFieldMode, + .title = "Depth of Field", + .helpText = kDepthOfFieldHelpText, + .valueMin = static_cast(DepthOfFieldMode::Off), + .valueMax = static_cast(DepthOfFieldMode::Dusk), + .defaultValue = static_cast(DepthOfFieldMode::Classic), + }, + mPrelaunch); leftPane.add_section("Rendering"); config_bool_select(leftPane, rightPane, getSettings().game.enableTextureReplacements, @@ -912,17 +932,17 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { config_int_select(leftPane, rightPane, getSettings().video.maxFrameRate, "Framerate Cap", "Limit the framerate to the specified value.", 30, 540, 1, [] { return getSettings().game.enableFrameInterpolation.getValue() != FrameInterpMode::Capped; }); - config_bool_select(leftPane, rightPane, getSettings().game.enableDepthOfField, - { - .key = "Enable Depth of Field", - }); config_bool_select(leftPane, rightPane, getSettings().game.enableMapBackground, { .key = "Enable Mini-Map Shadows", + .helpText = "Render a thick shadow around the mini-map. May impact performance." }); config_bool_select(leftPane, rightPane, getSettings().game.disableCutscenePillarboxing, { .key = "Disable Cutscene Pillarboxing", + .helpText = "Disable black bars on the left and right sides of the screen " + "during some cutscenes, particularly on ultra-wide displays. " + "Visuals beyond the original intended framing may appear buggy." }); }); @@ -971,6 +991,10 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); addOption("Invert First Person Y Axis", getSettings().game.invertFirstPersonYAxis, "Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings)."); + addOption("Invert Air/Swim X Axis", getSettings().game.invertAirSwimX, + "Invert horizontal movement while flying or swimming."); + addOption("Invert Air/Swim Y Axis", getSettings().game.invertAirSwimY, + "Invert vertical movement while flying or swimming."); leftPane.add_section("Gyro"); leftPane.register_control( @@ -1139,6 +1163,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Restores patched glitches from Wii USA 1.0, the first released version."); addOption("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation, "Enables rotating Link in the collection menu with the C-Stick."); + addOption("Hide Owl Statue Markers", getSettings().game.removeQuestMapMarkers, + "Removes completed Owl Statue markers from the map and Minimap."); leftPane.add_section("Difficulty"); leftPane.register_control( @@ -1193,6 +1219,8 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { "Link will not recoil when his sword hits walls."); addOption("No 2nd Fish for Cat", getSettings().game.no2ndFishForCat, "Skip needing to catch a second fish for Sera's cat."); + addOption("Show Poe Count on Map", getSettings().game.enhancedMapMenus, + "Displays collected/total number of Poe Souls for a region on the map."); addSpeedrunDisabledOption("Sun's Song (R+X)", getSettings().game.sunsSong, "Allows Wolf Link to howl and change the time of day."); addOption("Quick Transform (R+Y)", getSettings().game.enableQuickTransform, @@ -1450,8 +1478,44 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) { .helpText = "Show gyro sensor values in the input viewer.", .isDisabled = [] { return !getSettings().game.showInputViewer; }, }); - leftPane.add_section("Game"); + leftPane.register_control( + leftPane.add_select_button({ + .key = "Menu Scaling Mode", + .getValue = + [] { + return kMenuScalingModeLabels[static_cast( + getSettings().game.menuScalingMode.getValue())]; + }, + .isModified = + [] { + const auto& mode = getSettings().game.menuScalingMode; + return mode.getValue() != mode.getDefaultValue(); + }, + }), + rightPane, [](Pane& pane) { + for (int i = 0; i < static_cast(kMenuScalingModeLabels.size()); ++i) { + pane + .add_button({ + .text = kMenuScalingModeLabels[i], + .isSelected = + [i] { + return getSettings().game.menuScalingMode.getValue() == + static_cast(i); + ; + }, + }) + .on_pressed([i] { + mDoAud_seStartMenu(kSoundItemChange); + getSettings().game.menuScalingMode.setValue( + static_cast(i)); + ; + config::Save(); + }); + } + pane.add_rml("
Changes how the Collection and File Select menus scale to your " + "aspect ratio."); + }); config_bool_select(leftPane, rightPane, getSettings().game.hideTvSettingsScreen, { .key = "Skip TV Settings Screen", diff --git a/src/dusk/ui/ui.cpp b/src/dusk/ui/ui.cpp index 98c9ca5782..072bf70b66 100644 --- a/src/dusk/ui/ui.cpp +++ b/src/dusk/ui/ui.cpp @@ -15,6 +15,7 @@ #include "input.hpp" #include "prelaunch.hpp" #include "window.hpp" +#include "dusk/config.hpp" namespace dusk::ui { namespace { @@ -60,6 +61,7 @@ bool initialize() noexcept { } void shutdown() noexcept { + config::Save(); sDocumentStack.clear(); sPassiveDocuments.clear(); sConnectedGamepads.clear(); @@ -315,6 +317,7 @@ NavCommand map_nav_event(const Rml::Event& event) noexcept { case Rml::Input::KeyIdentifier::KI_ESCAPE: return NavCommand::Cancel; case Rml::Input::KeyIdentifier::KI_RETURN: + case Rml::Input::KeyIdentifier::KI_NUMPADENTER: return NavCommand::Confirm; case Rml::Input::KeyIdentifier::KI_F1: return event.GetParameter("shift_key", 0) ? NavCommand::None : NavCommand::Menu; diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index 72219e98dd..0ee76c325b 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -203,7 +203,7 @@ char fapGm_dataMem::mCsv[0x8000]; int dumpTagObject(void* i_object, void*) { char profname_str[64]; s16 profname = fopAcM_GetProfName(i_object); - sprintf(profname_str, "%d", profname); + SAFE_SPRINTF(profname_str, "%d", profname); if (fopAcM_IsActor(i_object)) { fopAc_ac_c* a_actor = (fopAc_ac_c*)i_object; diff --git a/src/f_op/f_op_actor.cpp b/src/f_op/f_op_actor.cpp index d2240327b0..d0c89605d3 100644 --- a/src/f_op/f_op_actor.cpp +++ b/src/f_op/f_op_actor.cpp @@ -275,7 +275,7 @@ static int fopAc_Draw(void* i_this) { char message[40]; char name[dStage_NAME_LENGTH]; fopAcM_getNameString(actor, name); - sprintf(message, "%s(描画処理)", name); + SAFE_SPRINTF(message, "%s(描画処理)", name); fapGm_HIO_c::stopCpuTimer(message); #endif @@ -368,7 +368,7 @@ static int fopAc_Execute(void* i_this) { char message[40]; char name[dStage_NAME_LENGTH]; fopAcM_getNameString(actor, name); - sprintf(message, "%s(計算処理)", name); + SAFE_SPRINTF(message, "%s(計算処理)", name); fapGm_HIO_c::stopCpuTimer(message); #endif diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index 25214c93bc..8620a1fc72 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -1393,12 +1393,6 @@ fpc_ProcID fopAcM_createItemForPresentDemo(cXyz const* i_pos, int i_itemNo, u8 p JUT_ASSERT(3214, 0 <= i_itemNo && i_itemNo < 256); dComIfGp_event_setGtItm(i_itemNo); - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { OS_REPORT("プレゼントデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Present Demo, it is a 'Miss'! return fpcM_ERROR_PROCESS_ID_e; @@ -1413,12 +1407,6 @@ fpc_ProcID fopAcM_createItemForTrBoxDemo(cXyz const* i_pos, int i_itemNo, int i_ JUT_ASSERT(3259, 0 <= i_itemNo && i_itemNo < 256); dComIfGp_event_setGtItm(i_itemNo); - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { OS_REPORT("ゲットデモ用なのに「ハズレ」です![%d]\n", i_itemNo); // Even though it is for a Get Demo, it is a 'Miss'! return fpcM_ERROR_PROCESS_ID_e; @@ -1544,12 +1532,6 @@ fpc_ProcID fopAcM_createItemFromTable(cXyz const* i_pos, int i_itemNo, int i_ite JUT_ASSERT(3655, 0 <= i_itemNo && i_itemNo <= 255 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - u8 tableNum; ItemTableList* tableList; tableList = (ItemTableList*)dComIfGp_getItemTable(); @@ -1593,12 +1575,6 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN JUT_ASSERT(3824, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { return fpcM_ERROR_PROCESS_ID_e; } @@ -1610,12 +1586,6 @@ fpc_ProcID fopAcM_createDemoItem(const cXyz* i_pos, int i_itemNo, int i_itemBitN fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY, int param_8) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - int _ = -1; u32 params = 0xFFFF0000 | param_8 << 8 | (i_itemNo & 0xFF); @@ -1632,12 +1602,6 @@ fpc_ProcID fopAcM_createItemForBoss(const cXyz* i_pos, int i_itemNo, int i_roomN fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, int param_6, int param_7) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - UNUSED(i_angle); UNUSED(param_6); fpc_ProcID ret = -1; @@ -1649,12 +1613,6 @@ fpc_ProcID fopAcM_createItemForMidBoss(const cXyz* i_pos, int i_itemNo, int i_ro fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1, 0x7, NULL); fopAc_ac_c* ret = item; @@ -1664,12 +1622,6 @@ fopAc_ac_c* fopAcM_createItemForDirectGet(const cXyz* i_pos, int i_itemNo, int i fopAc_ac_c* fopAcM_createItemForSimpleDemo(const cXyz* i_pos, int i_itemNo, int i_roomNo, const csXyz* i_angle, const cXyz* i_scale, f32 i_speedF, f32 i_speedY) { - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - fopAc_ac_c* item = fopAcM_fastCreateItem(i_pos, i_itemNo, i_roomNo, i_angle, i_scale, &i_speedF, &i_speedY, -1, 0x4, NULL); fopAc_ac_c* ret = item; @@ -1682,12 +1634,6 @@ fpc_ProcID fopAcM_createItem(const cXyz* i_pos, int i_itemNo, int i_itemBitNo, i JUT_ASSERT(4067, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return fpcM_ERROR_PROCESS_ID_e; - } - #endif - if (i_itemNo == dItemNo_NONE_e) { return fpcM_ERROR_PROCESS_ID_e; } @@ -1752,12 +1698,6 @@ fopAc_ac_c* fopAcM_fastCreateItem2(const cXyz* i_pos, int i_itemNo, int i_itemBi JUT_ASSERT(4202, 0 <= i_itemNo && i_itemNo < 256 && (-1 <= i_itemBitNo && i_itemBitNo < (dSv_info_c::DAN_ITEM + dSv_info_c::MEMORY_ITEM + dSv_info_c::ZONE_ITEM )) || i_itemBitNo == 255); // clang-format on - #if TARGET_PC - if (dusk::getSettings().game.noHeartDrops && isHeart(i_itemNo)) { - return NULL; - } - #endif - csXyz item_angle(csXyz::Zero); if (i_itemNo == dItemNo_NONE_e) { @@ -2352,7 +2292,7 @@ fopAc_ac_c* fopAcM_findObject4EventCB(fopAc_ac_c* i_actor, void* i_data) { fopAc_ac_c* fopAcM_searchFromName4Event(char const* i_name, s16 i_eventID) { fopAcM_search4ev_prm prm; prm.event_id = i_eventID; - strcpy(prm.name, i_name); + SAFE_STRCPY(prm.name, i_name); char* chr = std::strchr(prm.name, ':'); if (chr != NULL) { @@ -2499,8 +2439,8 @@ bool fopAcM_wt_c::waterCheck(cXyz const* i_pos) { return false; } -BOOL fopAcM_getNameString(const fopAc_ac_c* i_actor, char* o_name) { - strcpy(o_name, dStage_getName(fopAcM_GetProfName(i_actor), i_actor->argument)); +BOOL fopAcM_getNameString(const fopAc_ac_c* i_actor, TEXT_SPAN o_name) { + SAFE_STRCPY(o_name, dStage_getName(fopAcM_GetProfName(i_actor), i_actor->argument)); return TRUE; } diff --git a/src/f_op/f_op_msg_mng.cpp b/src/f_op/f_op_msg_mng.cpp index c0b6dd060a..a9ce8c78d0 100644 --- a/src/f_op/f_op_msg_mng.cpp +++ b/src/f_op/f_op_msg_mng.cpp @@ -226,7 +226,7 @@ fpc_ProcID fopMsgM_messageSetDemo(u32 i_msgidx) { return 0; } -char* fopMsgM_messageGet(char* i_stringBuf, u32 i_msgId) { +TEXT_SPAN fopMsgM_messageGet(TEXT_SPAN i_stringBuf, u32 i_msgId) { dMeter2Info_getString(i_msgId, i_stringBuf, NULL); return i_stringBuf; } diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 65a2b5b95a..caf823b065 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -928,6 +928,103 @@ void mDoGph_drawFilterQuad(s8 param_0, s8 param_1) { GXTexCoord2s8(0, 1); GXEnd(); } + +static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, u16 dstWidth, u16 dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) { + GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE); + GXCopyTex((void*)texID, false); + GXInitTexObj(pDst, (void*)texID, dstWidth, dstHeight, dstFmt, GX_CLAMP, GX_CLAMP, GX_FALSE); + GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); +} + +static void drawDepth_blurTex(TGXTexObj &dst) { + u32 hw = u32(JUTVideo::getManager()->getRenderWidth()) >> 1; + u32 hh = u32(JUTVideo::getManager()->getRenderHeight()) >> 1; + + Mtx44 ortho; + C_MTXOrtho(ortho, 0.0f, hh, 0.0f, hw, 0.0f, 10.0f); + GXLoadPosMtxImm(cMtx_getIdentity(), GX_PNMTX0); + GXSetProjection(ortho, GX_ORTHOGRAPHIC); + GXSetCurrentMtx(GX_PNMTX0); + GXClearVtxDesc(); + GXSetVtxDesc(GX_VA_POS, GX_DIRECT); + GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S8, 0); + + GXCreateFrameBuffer(hw, hh); + + auto divCopySrc = [&](int divNo) { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + GXSetTexCopySrc(0, 0, w, h); + }; + + enum { MaxTexNum = 4 }; + TGXTexObj tmpTex[MaxTexNum]; + auto divCopyTex = [&](uintptr_t texNo, int divNo) -> GXTexObj* { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + CopyToTexObj(&tmpTex[texNo], texNo, w, h); + return &tmpTex[texNo]; + }; + + auto divQuad = [&](int divNo) { + u32 w = u32(hw) >> divNo, h = u32(hh) >> divNo; + f32 x0 = 0.0f, y0 = 0.0f; + f32 x1 = w, y1 = h; + GXBegin(GX_QUADS, GX_VTXFMT0, 4); + GXPosition3f32(x0, y0, -5); + GXTexCoord2s8(0, 0); + GXPosition3f32(x1, y0, -5); + GXTexCoord2s8(1, 0); + GXPosition3f32(x1, y1, -5); + GXTexCoord2s8(1, 1); + GXPosition3f32(x0, y1, -5); + GXTexCoord2s8(0, 1); + GXEnd(); + }; + + u32 texMtxID = GX_TEXMTX0; + int angle = 0; + float blurScale = 0.003f; + GXSetNumTexGens(8); + GXSetNumTevStages(8); + for (int stage = 0; stage < 8; stage++) { + GXSetTexCoordGen((GXTexCoordID)stage, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); + mDoMtx_stack_c::transS( + (blurScale * cM_scos(angle)) * mDoGph_gInf_c::getInvScale(), blurScale * cM_ssin(angle), 0.0f); + GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); + texMtxID += 3; + angle += 0x2000; + + GXTevStageID tevStage = (GXTevStageID)stage; + GXSetTevOrder(tevStage, (GXTexCoordID)stage, GX_TEXMAP1, GX_COLOR_NULL); + GXSetTevColorIn(tevStage, GX_CC_ZERO, GX_CC_TEXC, GX_CC_A1, stage == 0 ? GX_CC_ZERO : GX_CC_CPREV); + GXSetTevColorOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + GXSetTevAlphaIn(tevStage, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); + GXSetTevAlphaOp(tevStage, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); + } + GXSetTevColor(GX_TEVREG1, {0, 0, 0, 256 / 8}); + + // assume the input tex obj is in GX_TEXMAP1 + int divNum = 3; + for (int i = 0; i < divNum; i++) { + // Apply blur filter. + divQuad(i); + + // Copy to next layer. + divCopySrc(i); + + // Set up for the next pass down. + GXTexObj* blurTex = divCopyTex(i, i + 1); + GXLoadTexObj(blurTex, GX_TEXMAP1); + } + + // upsample back to half-res buffer 0 + divQuad(0); + divCopySrc(0); + CopyToTexObj(&dst, 100, hw, hh); + + GXRestoreFrameBuffer(); +} #endif static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_2) { @@ -1081,6 +1178,21 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_ } #endif +#if TARGET_PC + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Off) + return; + + if (!(l_tevColor0.a > -255 && sp8 == 1)) + return; + + TGXTexObj blurTex; + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Dusk) + { + drawDepth_blurTex(blurTex); + GXLoadTexObj(&blurTex, GX_TEXMAP1); + } +#endif + GXSetTevColorS10(GX_TEVREG0, l_tevColor0); GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_ALPHA, GX_CH_GREEN, GX_CH_BLUE, GX_CH_RED); GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP3); @@ -1129,37 +1241,42 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_ param_1->x_orig + param_1->width, 0.0f, 10.0f); GXLoadPosMtxImm(cMtx_getIdentity(), 0); - #if DEBUG +#if DEBUG mDoMtx_stack_c::transS(g_kankyoHIO.navy.demo_focus_offset_x, g_kankyoHIO.navy.demo_focus_offset_y, 0.0f); - #else +#else mDoMtx_stack_c::transS(0.0025f, 0.0025f, 0.0f); - #endif - GXLoadTexMtxImm(mDoMtx_stack_c::get(), 0x1e, GX_MTX2x4); +#endif + GXLoadTexMtxImm(mDoMtx_stack_c::get(), GX_TEXMTX0, GX_MTX2x4); - #if DEBUG +#if DEBUG mDoMtx_stack_c::transS(-g_kankyoHIO.navy.demo_focus_offset_x, -g_kankyoHIO.navy.demo_focus_offset_y, 0.0f); - #else +#else mDoMtx_stack_c::transS(-0.0025f, -0.0025f, 0.0f); - #endif - GXLoadTexMtxImm(mDoMtx_stack_c::get(), 0x21, GX_MTX2x4); +#endif + GXLoadTexMtxImm(mDoMtx_stack_c::get(), GX_TEXMTX1, GX_MTX2x4); GXClearVtxDesc(); GXSetVtxDesc(GX_VA_POS, GX_DIRECT); GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_POS_XYZ, GX_S8, 0); - GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3c); - GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, 0x1e); - GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX0, 0x21); + GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0); + GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX1); GXSetNumChans(0); GXSetNumTexGens(3); GXSetNumTevStages(4); - GXSetProjection(ortho, GX_ORTHOGRAPHIC); - GXSetCurrentMtx(0); -#ifdef TARGET_PC - if (dusk::getSettings().game.enableDepthOfField) + GXSetProjection(ortho, GX_ORTHOGRAPHIC); + GXSetCurrentMtx(GX_PNMTX0); + +#if TARGET_PC + if (dusk::getSettings().game.depthOfFieldMode.getValue() == dusk::DepthOfFieldMode::Dusk) { + GXSetNumTevStages(3); + GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR_NULL); + } #endif + if (l_tevColor0.a > -255 && sp8 == 1) { GXBegin(GX_QUADS, GX_VTXFMT0, 4); GXPosition3s16(x_orig, y_orig_pos, -5); @@ -1334,19 +1451,8 @@ void mDoGph_gInf_c::bloom_c::remove() { } #if TARGET_PC -static void CopyToTexObj(GXTexObj* pDst, uintptr_t texID, u16 dstWidth, u16 dstHeight, GXTexFmt dstFmt = GX_TF_RGBA8) { - GXSetTexCopyDst(dstWidth, dstHeight, dstFmt, FALSE); - GXCopyTex((void*)texID, false); - GXInitTexObj(pDst, (void*)texID, dstWidth, dstHeight, dstFmt, GX_CLAMP, GX_CLAMP, GX_FALSE); - GXInitTexObjLOD(pDst, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_FALSE, GX_FALSE, GX_ANISO_1); -} - void mDoGph_gInf_c::bloom_c::draw2() { ZoneScoped; - // if (!dusk::getSettings().game.enableBloom) { - // return; - // } - bool enabled = mEnable; if (mMonoColor.a == 0 && !enabled) return; diff --git a/src/m_Do/m_Do_machine.cpp b/src/m_Do/m_Do_machine.cpp index db6beeacfd..91178963db 100644 --- a/src/m_Do/m_Do_machine.cpp +++ b/src/m_Do/m_Do_machine.cpp @@ -754,7 +754,7 @@ void myGXVerifyCallback(GXWarningLevel param_1, u32 param_2, const char* param_3 #endif int mDoMch_Create() { -#if !TARGET_PC // We want crash logs. +#ifdef NDEBUG if (mDoMain::developmentMode == 0 || !(OSGetConsoleType() & 0x10000000)) { OSReportDisable(); } diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 530dfaf976..51e339056f 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -48,6 +48,7 @@ #include #include "SSystem/SComponent/c_API.h" #include "dusk/app_info.hpp" +#include "dusk/crash_handler.h" #include "dusk/crash_reporting.h" #include "dusk/data.hpp" #include "dusk/dusk.h" @@ -149,7 +150,7 @@ s32 LOAD_COPYDATE(void*) { memcpy(buffer, readBuf, readLen); buffer[readLen] = '\0'; } else { - strcpy(buffer, "PC PORT BUILD"); + SAFE_STRCPY(buffer, "PC PORT BUILD"); DuskLog.warn("COPYDATE file not found at {}", COPYDATE_PATH); } @@ -552,6 +553,7 @@ int game_main(int argc, char* argv[]) { } ApplyCVarOverrides(parsed_arg_options["cvar"]); dusk::crash_reporting::initialize(); + dusk::crash_handler::install(); // TODO: How to handle this? // PADSetDefaultMapping(&defaultPadMapping, PAD_TYPE_STANDARD);