diff --git a/.github/workflows/uwp.yml b/.github/workflows/uwp.yml index 5f193c46..528bc4a3 100644 --- a/.github/workflows/uwp.yml +++ b/.github/workflows/uwp.yml @@ -62,12 +62,19 @@ jobs: rm -force ".\Content\Translations\*.po" - $currentDirectory = Get-Location - $certBytes = [System.Convert]::FromBase64String("${{ secrets.UWP_CERTIFICATE_FILE }}") - $certPath = Join-Path -Path $currentDirectory -ChildPath "_cert.pfx" - [IO.File]::WriteAllBytes("$certPath", $certBytes) + if ($env:GITHUB_EVENT_NAME -eq 'pull_request') { + Write-Output "Pull requests are not signed!" + $certPath = "" + $certPass = "" + } else { + $currentDirectory = Get-Location + $certBytes = [System.Convert]::FromBase64String("${{ secrets.UWP_CERTIFICATE_FILE }}") + $certPath = Join-Path -Path $currentDirectory -ChildPath "_cert.pfx" + [IO.File]::WriteAllBytes("$certPath", $certBytes) + $certPass = "${{ secrets.UWP_CERTIFICATE_PASSWORD }}" + } - cmake -B ".\_build\" -D CMAKE_BUILD_TYPE=${{ matrix.BuildType }} -D CMAKE_SYSTEM_NAME=WindowsStore -D CMAKE_SYSTEM_VERSION="10.0" -A $arch -D CMAKE_SYSTEM_PROCESSOR=$arch -D NCINE_ARCH_EXTENSIONS="${{ matrix.ArchExts }}" -D NCINE_STRIP_BINARIES=ON -D NCINE_UWP_CERTIFICATE_PATH="$certPath" -D NCINE_UWP_CERTIFICATE_PASSWORD="${{ secrets.UWP_CERTIFICATE_PASSWORD }}" -D NCINE_WITH_ANGLE="$withAngle" -D DEATH_TRACE=OFF + cmake -B ".\_build\" -D CMAKE_BUILD_TYPE=${{ matrix.BuildType }} -D CMAKE_SYSTEM_NAME=WindowsStore -D CMAKE_SYSTEM_VERSION="10.0" -A $arch -D CMAKE_SYSTEM_PROCESSOR=$arch -D NCINE_ARCH_EXTENSIONS="${{ matrix.ArchExts }}" -D NCINE_STRIP_BINARIES=ON -D NCINE_UWP_CERTIFICATE_PATH="$certPath" -D NCINE_UWP_CERTIFICATE_PASSWORD="$certPass" -D NCINE_WITH_ANGLE="$withAngle" -D DEATH_TRACE=OFF - name: 'Build' run: | diff --git a/README.md b/README.md index c02e77d9..e91dd4ed 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Jazz² Resurrection is reimplementation of the game **Jazz Jackrabbit 2** releas * Install dependencies: `sudo apt install libglew2.2 libglfw3 libsdl2-2.0-0 libopenal1 libvorbisfile3 libopenmpt0` * Alternatively, install provided `.deb` or `.rpm` package and dependencies should be installed automatically * Copy contents of original *Jazz Jackrabbit 2* directory to `‹Game›/Source/` - * If packages are used, the files must be copied to `~/.local/share/Jazz² Resurrection/Source/` or `/usr/share/Jazz² Resurrection/Source/` instead + * If packages are used, the files must be copied to `~/.local/share/Jazz² Resurrection/Source/` or `/usr/local/share/Jazz² Resurrection/Source/` instead, please follow instructions of specific package * Run `‹Game›/jazz2` or `‹Game›/jazz2_sdl2` application * If packages are used, the game should be visible in application list diff --git a/Sources/Common.h b/Sources/Common.h index 865d42bb..cf6f112c 100644 --- a/Sources/Common.h +++ b/Sources/Common.h @@ -33,6 +33,10 @@ #include +#if !defined(NCINE_INSTALL_PREFIX) && defined(DEATH_TARGET_UNIX) +# define NCINE_INSTALL_PREFIX "/usr/local" +#endif + // Check platform-specific capabilities #if defined(WITH_SDL) || defined(DEATH_TARGET_WINDOWS_RT) # define NCINE_HAS_GAMEPAD_RUMBLE diff --git a/Sources/Jazz2/ContentResolver.cpp b/Sources/Jazz2/ContentResolver.cpp index 8ed71b86..d526a8e4 100644 --- a/Sources/Jazz2/ContentResolver.cpp +++ b/Sources/Jazz2/ContentResolver.cpp @@ -208,7 +208,7 @@ namespace Jazz2 # elif defined(NCINE_OVERRIDE_CONTENT_PATH) _contentPath = NCINE_OVERRIDE_CONTENT_PATH; # else - _contentPath = INSTALL_PREFIX "/share/" NCINE_LINUX_PACKAGE "/Content/"; + _contentPath = NCINE_INSTALL_PREFIX "/share/" NCINE_LINUX_PACKAGE "/Content/"; # endif # if defined(NCINE_PACKAGED_CONTENT_PATH) // If Content is packaged with binaries, always use standard XDG paths for everything else @@ -235,9 +235,16 @@ namespace Jazz2 } else { _cachePath = "Cache/"_s; } - _sourcePath = INSTALL_PREFIX "/share/" NCINE_LINUX_PACKAGE "/Source/"; - if (!fs::DirectoryExists(_sourcePath)) { - _sourcePath = fs::CombinePath(fs::GetDirectoryName(_cachePath), "Source/"_s); + + // Prefer system-wide Source only if it exists and local one doesn't exist + _sourcePath = fs::CombinePath(fs::GetDirectoryName(_cachePath), "Source/"_s); + if (!fs::FindPathCaseInsensitive(fs::CombinePath(_sourcePath, "Anims.j2a"_s)) && + !fs::FindPathCaseInsensitive(fs::CombinePath(_sourcePath, "AnimsSw.j2a"_s))) { + auto systemWideSource = NCINE_INSTALL_PREFIX "/share/" NCINE_LINUX_PACKAGE "/Source/"; + if (fs::FindPathCaseInsensitive(fs::CombinePath(systemWideSource, "Anims.j2a"_s)) || + fs::FindPathCaseInsensitive(fs::CombinePath(systemWideSource, "AnimsSw.j2a"_s))) { + _sourcePath = systemWideSource; + } } } else { // Fallback to relative paths diff --git a/Sources/Jazz2/LevelHandler.cpp b/Sources/Jazz2/LevelHandler.cpp index 84c3c9b8..e8035307 100644 --- a/Sources/Jazz2/LevelHandler.cpp +++ b/Sources/Jazz2/LevelHandler.cpp @@ -1997,7 +1997,7 @@ namespace Jazz2 void LevelHandler::UpdateRichPresence() { -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) if (!PreferencesCache::EnableDiscordIntegration || !UI::DiscordRpcClient::Get().IsSupported()) { return; } diff --git a/Sources/Jazz2/PreferencesCache.cpp b/Sources/Jazz2/PreferencesCache.cpp index 2286c5ab..f36a466a 100644 --- a/Sources/Jazz2/PreferencesCache.cpp +++ b/Sources/Jazz2/PreferencesCache.cpp @@ -45,7 +45,7 @@ namespace Jazz2 #endif bool PreferencesCache::EnableLedgeClimb = true; WeaponWheelStyle PreferencesCache::WeaponWheel = WeaponWheelStyle::Enabled; -#if defined(DEATH_TARGET_ANDROID) || defined(DEATH_TARGET_EMSCRIPTEN) || defined(DEATH_TARGET_IOS) || !defined(DEATH_TARGET_SWITCH) || defined(DEATH_TARGET_WINDOWS_RT) +#if defined(DEATH_TARGET_ANDROID) || defined(DEATH_TARGET_EMSCRIPTEN) || defined(DEATH_TARGET_IOS) || defined(DEATH_TARGET_SWITCH) || defined(DEATH_TARGET_WINDOWS_RT) bool PreferencesCache::EnableRgbLights = false; #else bool PreferencesCache::EnableRgbLights = true; @@ -347,7 +347,7 @@ namespace Jazz2 auto& resolver = ContentResolver::Get(); fs::CreateDirectories(resolver.GetSourcePath()); -# if defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH) +# if defined(DEATH_TARGET_UNIX) StringView isSteamDeck = ::getenv("SteamDeck"); if (isSteamDeck == "1"_s) { GamepadButtonLabels = GamepadType::Steam; diff --git a/Sources/Jazz2/UI/DiscordRpcClient.cpp b/Sources/Jazz2/UI/DiscordRpcClient.cpp index 1b2fc081..ebe6b9d2 100644 --- a/Sources/Jazz2/UI/DiscordRpcClient.cpp +++ b/Sources/Jazz2/UI/DiscordRpcClient.cpp @@ -1,6 +1,6 @@ #include "DiscordRpcClient.h" -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) #include "../../nCine/Base/Algorithms.h" diff --git a/Sources/Jazz2/UI/DiscordRpcClient.h b/Sources/Jazz2/UI/DiscordRpcClient.h index 9af5f42e..c0cc15de 100644 --- a/Sources/Jazz2/UI/DiscordRpcClient.h +++ b/Sources/Jazz2/UI/DiscordRpcClient.h @@ -2,7 +2,7 @@ #include "../../Common.h" -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) #include "../../nCine/Threading/Thread.h" diff --git a/Sources/Jazz2/UI/Menu/GameplayOptionsSection.cpp b/Sources/Jazz2/UI/Menu/GameplayOptionsSection.cpp index 41afb400..254d6f16 100644 --- a/Sources/Jazz2/UI/Menu/GameplayOptionsSection.cpp +++ b/Sources/Jazz2/UI/Menu/GameplayOptionsSection.cpp @@ -203,7 +203,7 @@ namespace Jazz2::UI::Menu _animation = 0.0f; break; } -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) case GameplayOptionsItemType::EnableDiscordIntegration: { PreferencesCache::EnableDiscordIntegration = !PreferencesCache::EnableDiscordIntegration; if (!PreferencesCache::EnableDiscordIntegration) { diff --git a/Sources/Jazz2/UI/Menu/HighscoresSection.cpp b/Sources/Jazz2/UI/Menu/HighscoresSection.cpp index 53f02eea..be3a232c 100644 --- a/Sources/Jazz2/UI/Menu/HighscoresSection.cpp +++ b/Sources/Jazz2/UI/Menu/HighscoresSection.cpp @@ -38,7 +38,7 @@ namespace Jazz2::UI::Menu String name; std::uint64_t id = 0; -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) if (PreferencesCache::EnableDiscordIntegration && DiscordRpcClient::Get().IsSupported()) { name = DiscordRpcClient::Get().GetUserDisplayName(); id = DiscordRpcClient::Get().GetUserId(); diff --git a/Sources/Jazz2/UI/Menu/MainMenu.cpp b/Sources/Jazz2/UI/Menu/MainMenu.cpp index 5174e974..b9ab6fb6 100644 --- a/Sources/Jazz2/UI/Menu/MainMenu.cpp +++ b/Sources/Jazz2/UI/Menu/MainMenu.cpp @@ -634,7 +634,7 @@ namespace Jazz2::UI::Menu void MainMenu::UpdateRichPresence() { -#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) +#if (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) || defined(DEATH_TARGET_UNIX) if (!PreferencesCache::EnableDiscordIntegration || !DiscordRpcClient::Get().IsSupported()) { return; } diff --git a/Sources/Main.cpp b/Sources/Main.cpp index 3b2c956c..42aeb4de 100644 --- a/Sources/Main.cpp +++ b/Sources/Main.cpp @@ -159,18 +159,18 @@ class GameEventHandler : public IAppEventHandler, public IInputEventHandler, pub #if defined(DEATH_TARGET_ANDROID) void ApplyActivityIcon(); #endif - static void WriteCacheDescriptor(const StringView path, std::uint64_t currentVersion, std::int64_t animsModified); + static void WriteCacheDescriptor(StringView path, std::uint64_t currentVersion, std::int64_t animsModified); static void SaveEpisodeEnd(const LevelInitialization& levelInit); static void SaveEpisodeContinue(const LevelInitialization& levelInit); - static bool TryParseAddressAndPort(const StringView input, String& address, std::uint16_t& port); - static void ExtractPakFile(const StringView pakFile, const StringView targetPath); + static bool TryParseAddressAndPort(StringView input, String& address, std::uint16_t& port); + static void ExtractPakFile(StringView pakFile, StringView targetPath); }; void GameEventHandler::OnPreInitialize(AppConfiguration& config) { ZoneScopedC(0x888888); -#if defined(DEATH_TARGET_APPLE) || (defined(DEATH_TARGET_UNIX) && !defined(DEATH_TARGET_SWITCH)) || (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) +#if defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX) || (defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)) // Allow `/extract-pak` only on PC platforms if (config.argc() >= 3) { for (std::int32_t i = 0; i < config.argc() - 2; i++) { @@ -1585,7 +1585,7 @@ void GameEventHandler::HandleEndOfGame(const LevelInitialization& levelInit, boo SetStateHandler(std::move(mainMenu)); } -void GameEventHandler::WriteCacheDescriptor(const StringView path, std::uint64_t currentVersion, std::int64_t animsModified) +void GameEventHandler::WriteCacheDescriptor(StringView path, std::uint64_t currentVersion, std::int64_t animsModified) { auto so = fs::Open(path, FileAccess::Write); so->WriteValue(0x2095A59FF0BFBBEF); // Signature @@ -1681,7 +1681,7 @@ void GameEventHandler::SaveEpisodeContinue(const LevelInitialization& levelInit) } } -bool GameEventHandler::TryParseAddressAndPort(const StringView input, String& address, std::uint16_t& port) +bool GameEventHandler::TryParseAddressAndPort(StringView input, String& address, std::uint16_t& port) { auto portSep = input.findLast(':'); if (portSep) { @@ -1714,7 +1714,7 @@ bool GameEventHandler::TryParseAddressAndPort(const StringView input, String& ad } } -void GameEventHandler::ExtractPakFile(const StringView pakFile, const StringView targetPath) +void GameEventHandler::ExtractPakFile(StringView pakFile, StringView targetPath) { PakFile pak(pakFile); if (!pak.IsValid()) { diff --git a/Sources/Shared/IO/Compression/Lz4Stream.cpp b/Sources/Shared/IO/Compression/Lz4Stream.cpp index 442474e5..e1ec7567 100644 --- a/Sources/Shared/IO/Compression/Lz4Stream.cpp +++ b/Sources/Shared/IO/Compression/Lz4Stream.cpp @@ -198,7 +198,7 @@ namespace Death { namespace IO { namespace Compression { auto result = LZ4F_createDecompressionContext(&_ctx, LZ4F_VERSION); if (LZ4F_isError(result)) { - LOGE("LZ4F_createDecompressionContext() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_createDecompressionContext() failed with error 0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; } } @@ -237,7 +237,7 @@ namespace Death { namespace IO { namespace Compression { std::size_t consumedSize = bytesRead; std::size_t result = LZ4F_getFrameInfo(_ctx, &info, _inBuffer, &consumedSize); if (LZ4F_isError(result)) { - LOGE("LZ4F_getFrameInfo() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_getFrameInfo() failed with error 0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; return; } @@ -292,7 +292,7 @@ namespace Death { namespace IO { namespace Compression { std::size_t srcSize = _inLength - _inPos; auto result = LZ4F_decompress(_ctx, &_outBuffer[0], &dstSize, &_inBuffer[_inPos], &srcSize, nullptr); if (LZ4F_isError(result)) { - LOGE("LZ4F_decompress() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_decompress() failed with error 0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; return Stream::Invalid; } @@ -336,7 +336,7 @@ namespace Death { namespace IO { namespace Compression { { auto result = LZ4F_createCompressionContext(&_ctx, LZ4F_VERSION); if (LZ4F_isError(result)) { - LOGE("LZ4F_createCompressionContext() failed with error %0x%zx (%s)", result, LZ4F_getErrorName(result)); + LOGE("LZ4F_createCompressionContext() failed with error 0x%zx (%s)", result, LZ4F_getErrorName(result)); _state = State::Failed; } @@ -353,7 +353,7 @@ namespace Death { namespace IO { namespace Compression { std::size_t headerSize = LZ4F_compressBegin(_ctx, &_outBuffer[0], _outCapacity, &kPrefs); if (LZ4F_isError(result)) { - LOGE("LZ4F_compressBegin() failed with error %0x%zx (%s)", headerSize, LZ4F_getErrorName(headerSize)); + LOGE("LZ4F_compressBegin() failed with error 0x%zx (%s)", headerSize, LZ4F_getErrorName(headerSize)); _state = State::Failed; } @@ -431,7 +431,7 @@ namespace Death { namespace IO { namespace Compression { { std::size_t compressedSize = LZ4F_flush(_ctx, &_outBuffer[0], _outCapacity, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_flush() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_flush() failed with error 0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return false; } @@ -471,7 +471,7 @@ namespace Death { namespace IO { namespace Compression { std::size_t compressedSize = LZ4F_compressUpdate(_ctx, &_outBuffer[0], _outCapacity, buffer, bytesToWrite, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_compressUpdate() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_compressUpdate() failed with error 0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return Stream::Invalid; } @@ -484,7 +484,7 @@ namespace Death { namespace IO { namespace Compression { if (finish) { std::size_t compressedSize = LZ4F_compressEnd(_ctx, &_outBuffer[0], _outCapacity, nullptr); if (LZ4F_isError(compressedSize)) { - LOGE("LZ4F_compressEnd() failed with error %0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); + LOGE("LZ4F_compressEnd() failed with error 0x%zx (%s)", compressedSize, LZ4F_getErrorName(compressedSize)); _state = State::Failed; return Stream::Invalid; } diff --git a/Sources/Shared/IO/FileSystem.cpp b/Sources/Shared/IO/FileSystem.cpp index 6ff9dedb..4b50efc6 100644 --- a/Sources/Shared/IO/FileSystem.cpp +++ b/Sources/Shared/IO/FileSystem.cpp @@ -706,78 +706,95 @@ namespace Death { namespace IO { #if !defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_SWITCH) String FileSystem::FindPathCaseInsensitive(StringView path) { - if (Exists(path)) { + if (path.empty() || Exists(path)) { return path; } - std::size_t l = path.size(); - char* p = (char*)alloca(l + 1); - strncpy(p, path.data(), l); - p[l] = '\0'; - std::size_t rl = 0; - bool isAbsolute = (p[0] == '/' || p[0] == '\\'); + DIR* d = nullptr; + String result = path; + MutableStringView partialResult = result; + char* nextPartBegin; - String result(NoInit, path.size() + (isAbsolute ? 0 : 2)); + while (MutableStringView separator = partialResult.findLast('/')) { + if DEATH_UNLIKELY(separator.begin() == result.begin()) { + // Nothing left, only first slash of absolute path + break; + } - DIR* d; - if (isAbsolute) { - d = ::opendir("/"); - p = p + 1; - } else { - d = ::opendir("."); - result[0] = '.'; - result[1] = '\0'; - rl = 1; + partialResult = partialResult.prefix(separator.begin()); + separator[0] = '\0'; + d = ::opendir(result.data()); + separator[0] = '/'; + if (d != nullptr) { + nextPartBegin = separator.end(); + break; + } } - bool last = false; - char* c = strsep(&p, "/"); - while (c) { - if (d == nullptr) { - return {}; + if (d == nullptr) { + if (result[0] == '/' || result[0] == '\\') { + d = ::opendir("/"); + nextPartBegin = result.begin() + 1; + } else { + d = ::opendir("."); + nextPartBegin = result.begin(); } - if (last) { - ::closedir(d); + if DEATH_UNLIKELY(d == nullptr) { return {}; } + } + + while (true) { + partialResult = result.suffix(nextPartBegin); + MutableStringView nextSeparator = partialResult.findOr('/', result.end()); + if DEATH_UNLIKELY(nextSeparator.begin() == nextPartBegin) { + // Skip empty parts + nextPartBegin = nextSeparator.end(); + continue; + } - result[rl] = '/'; - rl += 1; - result[rl] = '\0'; + bool hasNextSeparator = (nextSeparator.begin() != result.end()); + if DEATH_LIKELY(hasNextSeparator) { + nextSeparator[0] = '\0'; + } struct dirent* entry = ::readdir(d); while (entry != nullptr) { - if (::strcasecmp(c, entry->d_name) == 0) { + if (::strcasecmp(partialResult.begin(), entry->d_name) == 0) { # if defined(__FreeBSD__) std::size_t fileNameLength = entry->d_namlen; # else std::size_t fileNameLength = std::strlen(entry->d_name); # endif - std::memcpy(&result[rl], entry->d_name, fileNameLength); - rl += fileNameLength; - + DEATH_DEBUG_ASSERT(partialResult.begin() + fileNameLength == nextSeparator.begin()); + std::memcpy(partialResult.begin(), entry->d_name, fileNameLength); ::closedir(d); + + nextPartBegin = nextSeparator.end(); + if (!hasNextSeparator || nextPartBegin == result.end()) { + if (hasNextSeparator) { + nextSeparator[0] = '/'; + } + return result; + } + d = ::opendir(result.data()); + if DEATH_UNLIKELY(d == nullptr) { + return {}; + } + nextSeparator[0] = '/'; break; } entry = ::readdir(d); } - if (entry == nullptr) { - strcpy(&result[rl], c); - rl += strlen(c); - last = true; + if DEATH_UNLIKELY(entry == nullptr) { + ::closedir(d); + return {}; } - - c = strsep(&p, "/"); } - - if (d != nullptr) { - ::closedir(d); - } - return result; } #endif @@ -2597,4 +2614,5 @@ namespace Death { namespace IO { } #endif } + }} diff --git a/Sources/Shared/IO/FileSystem.h b/Sources/Shared/IO/FileSystem.h index d83d957d..1897989c 100644 --- a/Sources/Shared/IO/FileSystem.h +++ b/Sources/Shared/IO/FileSystem.h @@ -131,17 +131,26 @@ namespace Death { namespace IO { ~FileSystem() = delete; #if defined(DEATH_TARGET_WINDOWS) || defined(DEATH_TARGET_SWITCH) - // Windows is already case in-sensitive by default + /** + * @brief Returns path with correct case on case-sensitive platforms (or nothing if path not found) + * + * Windows is already case-insensitive by default, so no validation is performed. + */ DEATH_ALWAYS_INLINE static Containers::StringView FindPathCaseInsensitive(Containers::StringView path) { return path; } + /** @overload */ DEATH_ALWAYS_INLINE static Containers::String FindPathCaseInsensitive(Containers::String&& path) { return path; } #else + /** + * @brief Returns path with correct case on case-sensitive platforms (or nothing if path not found) + */ static Containers::String FindPathCaseInsensitive(Containers::StringView path); + /** @overload */ DEATH_ALWAYS_INLINE static Containers::String FindPathCaseInsensitive(Containers::String&& path) { return FindPathCaseInsensitive(Containers::StringView{path}); } diff --git a/Sources/nCine/Application.cpp b/Sources/nCine/Application.cpp index 1c844628..6c87995c 100644 --- a/Sources/nCine/Application.cpp +++ b/Sources/nCine/Application.cpp @@ -175,7 +175,7 @@ static bool EnableVirtualTerminalProcessing(HANDLE consoleHandleOut) return (::GetConsoleMode(consoleHandleOut, &dwMode) && ::SetConsoleMode(consoleHandleOut, dwMode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING)); } -#elif (defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX)) && !defined(DEATH_TARGET_SWITCH) +#elif defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX) # include # include @@ -1072,7 +1072,7 @@ namespace nCine } else { __consoleType = ConsoleType::Redirect; } -# elif (defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX)) && !defined(DEATH_TARGET_SWITCH) +# elif defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX) # if defined(DEATH_TARGET_UNIX) ::setvbuf(stdout, nullptr, _IONBF, 0); ::setvbuf(stderr, nullptr, _IONBF, 0); @@ -1109,7 +1109,7 @@ namespace nCine } # endif -# if defined(WITH_BACKWARD) && (defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX) || defined(DEATH_TARGET_EMSCRIPTEN)) && !defined(DEATH_TARGET_SWITCH) +# if defined(WITH_BACKWARD) && (defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_UNIX) || defined(DEATH_TARGET_EMSCRIPTEN)) if (__consoleType >= ConsoleType::EscapeCodes) { __eh.FeatureFlags |= Backward::Flags::ColorizeOutput; } diff --git a/Sources/nCine/MainApplication.cpp b/Sources/nCine/MainApplication.cpp index ade281cf..1517168b 100644 --- a/Sources/nCine/MainApplication.cpp +++ b/Sources/nCine/MainApplication.cpp @@ -66,7 +66,8 @@ namespace nCine workingDirLength = (DWORD)(lastSlash - workingDir); *lastSlash = '\0'; if (!::SetCurrentDirectoryW(workingDir)) { - LOGE("Failed to change working directory with error 0x%08x", ::GetLastError()); + // LOGE cannot be used until tracing is initialized + //LOGE("Failed to change working directory with error 0x%08x", ::GetLastError()); workingDirLength = 0; } } else { @@ -82,7 +83,7 @@ namespace nCine #if defined(DEATH_TARGET_WINDOWS) if (workingDirLength > 0) { - LOGI("Current working directory: %s", Utf8::FromUtf16(workingDir, workingDirLength).data()); + LOGI("Using working directory: %s", Utf8::FromUtf16(workingDir, workingDirLength).data()); } #endif diff --git a/cmake/ncine_compiler_options.cmake b/cmake/ncine_compiler_options.cmake index 9ca461ad..d56d07a6 100644 --- a/cmake/ncine_compiler_options.cmake +++ b/cmake/ncine_compiler_options.cmake @@ -10,6 +10,10 @@ target_compile_definitions(${NCINE_APP} PUBLIC "NCINE_VERSION=\"${NCINE_VERSION} string(TIMESTAMP NCINE_BUILD_YEAR "%Y") target_compile_definitions(${NCINE_APP} PUBLIC "NCINE_BUILD_YEAR=\"${NCINE_BUILD_YEAR}\"") +if(UNIX) + target_compile_definitions(${NCINE_APP} PUBLIC "NCINE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"") +endif() + if(NCINE_OVERRIDE_CONTENT_PATH) message(STATUS "Using overriden `Content` path: ${NCINE_OVERRIDE_CONTENT_PATH}") target_compile_definitions(${NCINE_APP} PUBLIC "NCINE_OVERRIDE_CONTENT_PATH=\"${NCINE_OVERRIDE_CONTENT_PATH}\"") @@ -25,16 +29,6 @@ elseif(NCINE_LINUX_PACKAGE) endif() endif() -if(DEATH_CPU_USE_RUNTIME_DISPATCH) - target_compile_definitions(${NCINE_APP} PUBLIC "DEATH_CPU_USE_RUNTIME_DISPATCH") - if(DEATH_CPU_USE_IFUNC) - target_compile_definitions(${NCINE_APP} PUBLIC "DEATH_CPU_USE_IFUNC") - message(STATUS "Using GNU IFUNC for CPU-dependent functionality") - else() - message(STATUS "Using runtime dispatch for CPU-dependent functionality") - endif() -endif() - set(CMAKE_REQUIRED_QUIET ON) check_struct_has_member("struct tm" tm_gmtoff time.h DEATH_USE_GMTOFF_IN_TM) set(CMAKE_REQUIRED_QUIET OFF) diff --git a/cmake/ncine_helpers.cmake b/cmake/ncine_helpers.cmake index 10382577..d6dccf76 100644 --- a/cmake/ncine_helpers.cmake +++ b/cmake/ncine_helpers.cmake @@ -274,26 +274,37 @@ function(ncine_apply_compiler_options target) target_compile_features(${target} PUBLIC cxx_std_17) set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF) - target_compile_definitions(${target} PUBLIC "CMAKE_BUILD") - target_compile_definitions(${target} PRIVATE "INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"") + target_compile_definitions(${target} PRIVATE "CMAKE_BUILD") if(DEATH_DEBUG) - target_compile_definitions(${target} PUBLIC "DEATH_DEBUG") + target_compile_definitions(${target} PRIVATE "DEATH_DEBUG") else() - target_compile_definitions(${target} PUBLIC $<$:DEATH_DEBUG>) + target_compile_definitions(${target} PRIVATE $<$:DEATH_DEBUG>) endif() if(DEATH_TRACE) if(target_is_executable) message(STATUS "Runtime event tracing is enabled") endif() - target_compile_definitions(${target} PUBLIC "DEATH_TRACE") + target_compile_definitions(${target} PRIVATE "DEATH_TRACE") if(DEATH_TRACE_ASYNC) - target_compile_definitions(${target} PUBLIC "DEATH_TRACE_ASYNC") + target_compile_definitions(${target} PRIVATE "DEATH_TRACE_ASYNC") elseif(NCINE_WITH_THREADS AND target_is_executable) message(STATUS "Asynchronous processing of event tracing is explicitly disabled") endif() endif() + if(DEATH_CPU_USE_RUNTIME_DISPATCH) + target_compile_definitions(${target} PUBLIC "DEATH_CPU_USE_RUNTIME_DISPATCH") + if(DEATH_CPU_USE_IFUNC) + target_compile_definitions(${target} PUBLIC "DEATH_CPU_USE_IFUNC") + if(target_is_executable) + message(STATUS "Using GNU IFUNC for CPU-dependent functionality") + endif() + elseif(target_is_executable) + message(STATUS "Using runtime dispatch for CPU-dependent functionality") + endif() + endif() + if(NINTENDO_SWITCH) target_compile_definitions(${target} PUBLIC "DEATH_TARGET_SWITCH") elseif(WIN32) @@ -383,10 +394,10 @@ function(ncine_apply_compiler_options target) endif() # Suppress linker warning about templates - target_compile_options(${target} PUBLIC "/wd4251") + target_compile_options(${target} PRIVATE "/wd4251") # Suppress warnings about "conversion from '' to ''", # "conversion from 'size_t' to '', possible loss of data" - target_compile_options(${target} PUBLIC "/wd4244" "/wd4267") + target_compile_options(${target} PRIVATE "/wd4244" "/wd4267") # Adjust incremental linking #target_link_options(${target} PRIVATE $,/INCREMENTAL,/INCREMENTAL:NO>) @@ -407,7 +418,7 @@ function(ncine_apply_compiler_options target) #endif() if(MINGW OR MSYS) - target_link_options(${target} PUBLIC "-municode") + target_link_options(${target} PRIVATE "-municode") endif() if(NCINE_ARCH_EXTENSIONS AND UNIX AND NOT APPLE AND NOT ANDROID AND NOT NINTENDO_SWITCH) @@ -419,8 +430,8 @@ function(ncine_apply_compiler_options target) # Only in Debug - preserve debug information, it's probably added automatically on other platforms if(EMSCRIPTEN) - target_compile_options(${target} PUBLIC $<$:-g>) - target_link_options(${target} PUBLIC $<$:-g>) + target_compile_options(${target} PRIVATE $<$:-g>) + target_link_options(${target} PRIVATE $<$:-g>) endif() # Only in Debug