From f69bc6e1afed46c7082030a7a7b1ad0b5a17ede1 Mon Sep 17 00:00:00 2001
From: Death Killer <884052+deathkiller@users.noreply.github.com>
Date: Tue, 24 Oct 2023 18:43:29 +0200
Subject: [PATCH] Added `IsPausable`, `InitialState` and game version to cache
descriptor, improved console on Windows, refactoring
---
Sources/Jazz2.vcxproj | 2 +
Sources/Jazz2.vcxproj.filters | 6 +
Sources/Jazz2/Actors/Player.cpp | 2 +-
Sources/Jazz2/ILevelHandler.h | 1 +
Sources/Jazz2/IRootController.h | 2 +-
Sources/Jazz2/LevelHandler.cpp | 45 +++--
Sources/Jazz2/LevelHandler.h | 4 +
Sources/Jazz2/Multiplayer/MultiLevelHandler.h | 4 +
Sources/Jazz2/Multiplayer/NetworkManager.cpp | 6 +-
Sources/Jazz2/Multiplayer/NetworkManager.h | 3 +-
Sources/Jazz2/PreferencesCache.cpp | 8 +
Sources/Jazz2/PreferencesCache.h | 5 +-
Sources/Jazz2/UI/Menu/BeginSection.cpp | 2 +-
Sources/Jazz2/UI/Menu/IMenuContainer.h | 2 +-
Sources/Jazz2/UI/Menu/InGameMenu.cpp | 3 +-
Sources/Jazz2/UI/Menu/InGameMenu.h | 2 +-
Sources/Jazz2/UI/Menu/LoadingSection.cpp | 35 ++++
Sources/Jazz2/UI/Menu/LoadingSection.h | 20 ++
Sources/Jazz2/UI/Menu/MainMenu.cpp | 27 ++-
Sources/Jazz2/UI/Menu/MainMenu.h | 4 +-
Sources/Main.cpp | 170 ++++++++++++++---
Sources/Shared/Containers/ArrayView.h | 20 +-
Sources/Shared/Containers/String.cpp | 180 +++++++++---------
Sources/Shared/Containers/String.h | 5 +-
Sources/Shared/Containers/StringView.h | 27 +--
Sources/Shared/IO/Stream.h | 2 +-
Sources/nCine/AppConfiguration.cpp | 16 +-
Sources/nCine/AppConfiguration.h | 21 +-
Sources/nCine/Application.cpp | 15 +-
Sources/nCine/Base/Algorithms.cpp | 94 +++------
Sources/nCine/Base/Algorithms.h | 168 ++++++++++------
Sources/nCine/Graphics/BinaryShaderCache.cpp | 31 ++-
Sources/nCine/Input/JoyMapping.cpp | 1 -
Sources/nCine/MainApplication.cpp | 119 ++++++++----
Sources/nCine/MainApplication.h | 6 +
cmake/ncine_headers.cmake | 1 +
cmake/ncine_sources.cmake | 1 +
37 files changed, 691 insertions(+), 369 deletions(-)
create mode 100644 Sources/Jazz2/UI/Menu/LoadingSection.cpp
create mode 100644 Sources/Jazz2/UI/Menu/LoadingSection.h
diff --git a/Sources/Jazz2.vcxproj b/Sources/Jazz2.vcxproj
index d0977e71..ef0c88af 100644
--- a/Sources/Jazz2.vcxproj
+++ b/Sources/Jazz2.vcxproj
@@ -384,6 +384,7 @@
+
@@ -730,6 +731,7 @@
+
diff --git a/Sources/Jazz2.vcxproj.filters b/Sources/Jazz2.vcxproj.filters
index c59d9ba4..0bbb2ba8 100644
--- a/Sources/Jazz2.vcxproj.filters
+++ b/Sources/Jazz2.vcxproj.filters
@@ -1353,6 +1353,9 @@
Header Files\Shared\Threading
+
+ Header Files\Jazz2\UI\Menu
+
@@ -2252,6 +2255,9 @@
Source Files\Jazz2\Actors
+
+ Source Files\Jazz2\UI\Menu
+
diff --git a/Sources/Jazz2/Actors/Player.cpp b/Sources/Jazz2/Actors/Player.cpp
index 78104bb4..bc8311bf 100644
--- a/Sources/Jazz2/Actors/Player.cpp
+++ b/Sources/Jazz2/Actors/Player.cpp
@@ -161,7 +161,7 @@ namespace Jazz2::Actors
void Player::OnUpdate(float timeMult)
{
#if defined(DEATH_DEBUG)
- if (_levelHandler->PlayerActionPressed(_playerIndex, PlayerActions::ChangeWeapon)) {
+ if (PreferencesCache::AllowCheats && _levelHandler->PlayerActionPressed(_playerIndex, PlayerActions::ChangeWeapon)) {
float moveDistance = (_levelHandler->PlayerActionPressed(_playerIndex, PlayerActions::Run) ? 400.0f : 100.0f);
if (_levelHandler->PlayerActionHit(_playerIndex, PlayerActions::Left)) {
MoveInstantly(Vector2f(-moveDistance, 0.0f), MoveType::Relative | MoveType::Force);
diff --git a/Sources/Jazz2/ILevelHandler.h b/Sources/Jazz2/ILevelHandler.h
index 2a28bcba..024b3c6b 100644
--- a/Sources/Jazz2/ILevelHandler.h
+++ b/Sources/Jazz2/ILevelHandler.h
@@ -39,6 +39,7 @@ namespace Jazz2
virtual Tiles::TileMap* TileMap() = 0;
virtual GameDifficulty Difficulty() const = 0;
+ virtual bool IsPausable() const = 0;
virtual bool IsReforged() const = 0;
virtual Recti LevelBounds() const = 0;
virtual float ElapsedFrames() const = 0;
diff --git a/Sources/Jazz2/IRootController.h b/Sources/Jazz2/IRootController.h
index 74792ceb..ee9cf710 100644
--- a/Sources/Jazz2/IRootController.h
+++ b/Sources/Jazz2/IRootController.h
@@ -33,7 +33,7 @@ namespace Jazz2
virtual void ChangeLevel(LevelInitialization&& levelInit) = 0;
#if defined(WITH_MULTIPLAYER)
- virtual bool ConnectToServer(const char* address, std::uint16_t port) = 0;
+ virtual bool ConnectToServer(const StringView& address, std::uint16_t port) = 0;
virtual bool CreateServer(std::uint16_t port) = 0;
#endif
diff --git a/Sources/Jazz2/LevelHandler.cpp b/Sources/Jazz2/LevelHandler.cpp
index 18b9c547..aaf88c77 100644
--- a/Sources/Jazz2/LevelHandler.cpp
+++ b/Sources/Jazz2/LevelHandler.cpp
@@ -216,17 +216,20 @@ namespace Jazz2
{
float timeMult = theApplication().timeMult();
- UpdatePressedActions();
+ if (_pauseMenu == nullptr) {
+ UpdatePressedActions();
- if (PlayerActionHit(0, PlayerActions::Menu) && _pauseMenu == nullptr && _nextLevelType == ExitType::None) {
- PauseGame();
- }
+ if (PlayerActionHit(0, PlayerActions::Menu) && _nextLevelType == ExitType::None) {
+ PauseGame();
+ }
#if defined(DEATH_DEBUG)
- if (PlayerActionPressed(0, PlayerActions::ChangeWeapon) && PlayerActionHit(0, PlayerActions::Jump)) {
- _cheatsUsed = true;
- BeginLevelChange(ExitType::Warp | ExitType::FastTransition, nullptr);
- }
+ if (PreferencesCache::AllowCheats && PlayerActionPressed(0, PlayerActions::ChangeWeapon) && PlayerActionHit(0, PlayerActions::Jump)) {
+ _cheatsUsed = true;
+ BeginLevelChange(ExitType::Warp | ExitType::FastTransition, nullptr);
+ }
#endif
+ }
+
#if defined(WITH_AUDIO)
// Destroy stopped players and resume music after Sugar Rush
if (_sugarRushMusic != nullptr && _sugarRushMusic->isStopped()) {
@@ -245,7 +248,7 @@ namespace Jazz2
}
#endif
- if (_pauseMenu == nullptr) {
+ if (!IsPausable() || _pauseMenu == nullptr) {
if (_nextLevelType != ExitType::None) {
_nextLevelTime -= timeMult;
@@ -291,7 +294,7 @@ namespace Jazz2
}
}
- if (_difficulty != GameDifficulty::Multiplayer) {
+ if (/*_difficulty != GameDifficulty::Multiplayer*/true) {
if (!_players.empty()) {
auto& pos = _players[0]->GetPos();
int32_t tx1 = (int32_t)pos.X / Tiles::TileSet::DefaultTileSize;
@@ -1613,22 +1616,26 @@ namespace Jazz2
{
// Show in-game pause menu
_pauseMenu = std::make_shared(this);
- // Prevent updating of all level objects
- _rootNode->setUpdateEnabled(false);
+ if (IsPausable()) {
+ // Prevent updating of all level objects
+ _rootNode->setUpdateEnabled(false);
+ }
#if defined(WITH_AUDIO)
// Use low-pass filter on music and pause all SFX
if (_music != nullptr) {
_music->setLowPass(0.1f);
}
- for (auto& sound : _playingSounds) {
- if (sound->isPlaying()) {
- sound->pause();
+ if (IsPausable()) {
+ for (auto& sound : _playingSounds) {
+ if (sound->isPlaying()) {
+ sound->pause();
+ }
+ }
+ // If Sugar Rush music is playing, pause it and play normal music instead
+ if (_sugarRushMusic != nullptr && _music != nullptr) {
+ _music->play();
}
- }
- // If Sugar Rush music is playing, pause it and play normal music instead
- if (_sugarRushMusic != nullptr && _music != nullptr) {
- _music->play();
}
#endif
}
diff --git a/Sources/Jazz2/LevelHandler.h b/Sources/Jazz2/LevelHandler.h
index 2f74800e..1bfea8a8 100644
--- a/Sources/Jazz2/LevelHandler.h
+++ b/Sources/Jazz2/LevelHandler.h
@@ -80,6 +80,10 @@ namespace Jazz2
return _difficulty;
}
+ bool IsPausable() const override {
+ return true;
+ }
+
bool IsReforged() const override {
return _isReforged;
}
diff --git a/Sources/Jazz2/Multiplayer/MultiLevelHandler.h b/Sources/Jazz2/Multiplayer/MultiLevelHandler.h
index 87723dc4..5a7c3073 100644
--- a/Sources/Jazz2/Multiplayer/MultiLevelHandler.h
+++ b/Sources/Jazz2/Multiplayer/MultiLevelHandler.h
@@ -35,6 +35,10 @@ namespace Jazz2::Multiplayer
MultiLevelHandler(IRootController* root, NetworkManager* networkManager, const LevelInitialization& levelInit);
~MultiLevelHandler() override;
+ bool IsPausable() const override {
+ return false;
+ }
+
float GetAmbientLight() const override;
void SetAmbientLight(float value) override;
diff --git a/Sources/Jazz2/Multiplayer/NetworkManager.cpp b/Sources/Jazz2/Multiplayer/NetworkManager.cpp
index 580d89f9..56977d9e 100644
--- a/Sources/Jazz2/Multiplayer/NetworkManager.cpp
+++ b/Sources/Jazz2/Multiplayer/NetworkManager.cpp
@@ -18,6 +18,8 @@
// Undefine it again after include
#undef far
+#include
+
#define MAX_CLIENTS 64
namespace Jazz2::Multiplayer
@@ -39,7 +41,7 @@ namespace Jazz2::Multiplayer
}
}
- bool NetworkManager::CreateClient(INetworkHandler* handler, const char* address, std::uint16_t port, std::uint32_t clientData)
+ bool NetworkManager::CreateClient(INetworkHandler* handler, const StringView& address, std::uint16_t port, std::uint32_t clientData)
{
if (!_initialized || _host != nullptr) {
return false;
@@ -53,7 +55,7 @@ namespace Jazz2::Multiplayer
_state = NetworkState::Connecting;
ENetAddress addr = { };
- enet_address_set_host(&addr, address);
+ enet_address_set_host(&addr, String::nullTerminatedView(address).data());
addr.port = port;
ENetPeer* peer = enet_host_connect(_host, &addr, (std::size_t)NetworkChannel::Count, clientData);
diff --git a/Sources/Jazz2/Multiplayer/NetworkManager.h b/Sources/Jazz2/Multiplayer/NetworkManager.h
index 67356a19..d9931907 100644
--- a/Sources/Jazz2/Multiplayer/NetworkManager.h
+++ b/Sources/Jazz2/Multiplayer/NetworkManager.h
@@ -8,6 +8,7 @@
#include "../../nCine/Threading/ThreadSync.h"
#include
+#include
struct _ENetHost;
@@ -39,7 +40,7 @@ namespace Jazz2::Multiplayer
NetworkManager();
~NetworkManager();
- bool CreateClient(INetworkHandler* handler, const char* address, std::uint16_t port, std::uint32_t clientData);
+ bool CreateClient(INetworkHandler* handler, const StringView& address, std::uint16_t port, std::uint32_t clientData);
bool CreateServer(INetworkHandler* handler, std::uint16_t port);
void Dispose();
diff --git a/Sources/Jazz2/PreferencesCache.cpp b/Sources/Jazz2/PreferencesCache.cpp
index 1196995e..d0c4252a 100644
--- a/Sources/Jazz2/PreferencesCache.cpp
+++ b/Sources/Jazz2/PreferencesCache.cpp
@@ -14,6 +14,9 @@ using namespace Death::IO;
namespace Jazz2
{
+#if defined(WITH_MULTIPLAYER)
+ String PreferencesCache::InitialState;
+#endif
UnlockableEpisodes PreferencesCache::UnlockedEpisodes = UnlockableEpisodes::None;
RescaleMode PreferencesCache::ActiveRescaleMode = RescaleMode::None;
bool PreferencesCache::EnableFullscreen = false;
@@ -296,6 +299,11 @@ namespace Jazz2
} else if (arg == "/mute"_s) {
MasterVolume = 0.0f;
}
+#if defined(WITH_MULTIPLAYER)
+ else if (InitialState.empty() && (arg == "/server"_s || arg.hasPrefix("/connect:"_s))) {
+ InitialState = arg;
+ }
+#endif
}
}
diff --git a/Sources/Jazz2/PreferencesCache.h b/Sources/Jazz2/PreferencesCache.h
index 18d34a79..770bfeec 100644
--- a/Sources/Jazz2/PreferencesCache.h
+++ b/Sources/Jazz2/PreferencesCache.h
@@ -79,6 +79,9 @@ namespace Jazz2
static constexpr std::int32_t UnlimitedFps = 0;
static constexpr std::int32_t UseVsync = -1;
+#if defined(WITH_MULTIPLAYER)
+ static String InitialState;
+#endif
static UnlockableEpisodes UnlockedEpisodes;
// Graphics
@@ -148,9 +151,7 @@ namespace Jazz2
static constexpr float TouchPaddingMultiplier = 0.003f;
- /// Deleted copy constructor
PreferencesCache(const PreferencesCache&) = delete;
- /// Deleted assignment operator
PreferencesCache& operator=(const PreferencesCache&) = delete;
static String _configPath;
diff --git a/Sources/Jazz2/UI/Menu/BeginSection.cpp b/Sources/Jazz2/UI/Menu/BeginSection.cpp
index c45bfe86..d967692d 100644
--- a/Sources/Jazz2/UI/Menu/BeginSection.cpp
+++ b/Sources/Jazz2/UI/Menu/BeginSection.cpp
@@ -333,7 +333,7 @@ namespace Jazz2::UI::Menu
// TODO: Multiplayer
case (int32_t)Item::TODO_ConnectTo:
// TODO: Hardcoded address and port
- _root->ConnectToServer("127.0.0.1", 10666);
+ _root->ConnectToServer("127.0.0.1"_s, 7438);
break;
case (int32_t)Item::TODO_CreateServer:
// TODO: Hardcoded address and port
diff --git a/Sources/Jazz2/UI/Menu/IMenuContainer.h b/Sources/Jazz2/UI/Menu/IMenuContainer.h
index 4b1a361c..032c36cb 100644
--- a/Sources/Jazz2/UI/Menu/IMenuContainer.h
+++ b/Sources/Jazz2/UI/Menu/IMenuContainer.h
@@ -42,7 +42,7 @@ namespace Jazz2::UI::Menu
virtual void LeaveSection() = 0;
virtual void ChangeLevel(Jazz2::LevelInitialization&& levelInit) = 0;
#if defined(WITH_MULTIPLAYER)
- virtual bool ConnectToServer(const char* address, std::uint16_t port) = 0;
+ virtual bool ConnectToServer(const StringView& address, std::uint16_t port) = 0;
virtual bool CreateServer(std::uint16_t port) = 0;
#endif
virtual void ApplyPreferencesChanges(ChangedPreferencesType type) = 0;
diff --git a/Sources/Jazz2/UI/Menu/InGameMenu.cpp b/Sources/Jazz2/UI/Menu/InGameMenu.cpp
index ad6724a3..20751e6f 100644
--- a/Sources/Jazz2/UI/Menu/InGameMenu.cpp
+++ b/Sources/Jazz2/UI/Menu/InGameMenu.cpp
@@ -235,7 +235,7 @@ namespace Jazz2::UI::Menu
}
#if defined(WITH_MULTIPLAYER)
- bool InGameMenu::ConnectToServer(const char* address, std::uint16_t port)
+ bool InGameMenu::ConnectToServer(const StringView& address, std::uint16_t port)
{
return _root->_root->ConnectToServer(address, port);
}
@@ -264,6 +264,7 @@ namespace Jazz2::UI::Menu
}
if ((type & ChangedPreferencesType::Language) == ChangedPreferencesType::Language) {
+ // All sections have to be recreated to load new language
_sections.clear();
SwitchToSection();
}
diff --git a/Sources/Jazz2/UI/Menu/InGameMenu.h b/Sources/Jazz2/UI/Menu/InGameMenu.h
index eac7b866..c16b783f 100644
--- a/Sources/Jazz2/UI/Menu/InGameMenu.h
+++ b/Sources/Jazz2/UI/Menu/InGameMenu.h
@@ -31,7 +31,7 @@ namespace Jazz2::UI::Menu
void LeaveSection() override;
void ChangeLevel(Jazz2::LevelInitialization&& levelInit) override;
#if defined(WITH_MULTIPLAYER)
- bool ConnectToServer(const char* address, std::uint16_t port) override;
+ bool ConnectToServer(const StringView& address, std::uint16_t port) override;
bool CreateServer(std::uint16_t port) override;
#endif
void ApplyPreferencesChanges(ChangedPreferencesType type) override;
diff --git a/Sources/Jazz2/UI/Menu/LoadingSection.cpp b/Sources/Jazz2/UI/Menu/LoadingSection.cpp
new file mode 100644
index 00000000..2c1157f6
--- /dev/null
+++ b/Sources/Jazz2/UI/Menu/LoadingSection.cpp
@@ -0,0 +1,35 @@
+#include "LoadingSection.h"
+
+namespace Jazz2::UI::Menu
+{
+ LoadingSection::LoadingSection(const StringView& message)
+ : _message(message)
+ {
+ }
+
+ LoadingSection::LoadingSection(String&& message)
+ : _message(std::move(message))
+ {
+
+ }
+
+ void LoadingSection::OnUpdate(float timeMult)
+ {
+ }
+
+ void LoadingSection::OnDraw(Canvas* canvas)
+ {
+ Vector2i viewSize = canvas->ViewSize;
+ Vector2f center = Vector2f(viewSize.X * 0.5f, viewSize.Y * 0.5f);
+
+ constexpr float topLine = 131.0f;
+ std::int32_t charOffset = 0;
+
+ _root->DrawStringShadow(_message, charOffset, center.X, topLine + 40.0f, IMenuContainer::FontLayer,
+ Alignment::Top, Font::DefaultColor, 1.2f, 0.4f, 0.6f, 0.6f, 0.6f, 0.9f, 1.2f);
+ }
+
+ void LoadingSection::OnTouchEvent(const nCine::TouchEvent& event, const Vector2i& viewSize)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Sources/Jazz2/UI/Menu/LoadingSection.h b/Sources/Jazz2/UI/Menu/LoadingSection.h
new file mode 100644
index 00000000..c67f0d4e
--- /dev/null
+++ b/Sources/Jazz2/UI/Menu/LoadingSection.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "MenuSection.h"
+
+namespace Jazz2::UI::Menu
+{
+ class LoadingSection : public MenuSection
+ {
+ public:
+ LoadingSection(const StringView& message);
+ LoadingSection(String&& message);
+
+ void OnUpdate(float timeMult) override;
+ void OnDraw(Canvas* canvas) override;
+ void OnTouchEvent(const nCine::TouchEvent& event, const Vector2i& viewSize) override;
+
+ private:
+ String _message;
+ };
+}
\ No newline at end of file
diff --git a/Sources/Jazz2/UI/Menu/MainMenu.cpp b/Sources/Jazz2/UI/Menu/MainMenu.cpp
index ce4dcced..28b9b9c2 100644
--- a/Sources/Jazz2/UI/Menu/MainMenu.cpp
+++ b/Sources/Jazz2/UI/Menu/MainMenu.cpp
@@ -66,6 +66,30 @@ namespace Jazz2::UI::Menu
_canvasOverlay->setParent(nullptr);
}
+ void MainMenu::Reset()
+ {
+ bool shouldSwitch = false;
+ while (!_sections.empty()) {
+ if (_sections.size() == 1 && dynamic_cast(_sections.back().get())) {
+ if (shouldSwitch) {
+ auto& lastSection = _sections.back();
+ lastSection->OnShow(this);
+
+ if (_canvasBackground->ViewSize != Vector2i::Zero) {
+ Recti clipRectangle = lastSection->GetClipRectangle(_canvasBackground->ViewSize);
+ _upscalePass.SetClipRectangle(clipRectangle);
+ }
+ }
+ return;
+ }
+
+ _sections.pop_back();
+ shouldSwitch = true;
+ }
+
+ SwitchToSection();
+ }
+
void MainMenu::OnBeginFrame()
{
float timeMult = theApplication().timeMult();
@@ -327,7 +351,7 @@ namespace Jazz2::UI::Menu
}
#if defined(WITH_MULTIPLAYER)
- bool MainMenu::ConnectToServer(const char* address, std::uint16_t port)
+ bool MainMenu::ConnectToServer(const StringView& address, std::uint16_t port)
{
return _root->ConnectToServer(address, port);
}
@@ -353,6 +377,7 @@ namespace Jazz2::UI::Menu
}
if ((type & ChangedPreferencesType::Language) == ChangedPreferencesType::Language) {
+ // All sections have to be recreated to load new language
_sections.clear();
SwitchToSection();
}
diff --git a/Sources/Jazz2/UI/Menu/MainMenu.h b/Sources/Jazz2/UI/Menu/MainMenu.h
index aa694c95..f5b0edb9 100644
--- a/Sources/Jazz2/UI/Menu/MainMenu.h
+++ b/Sources/Jazz2/UI/Menu/MainMenu.h
@@ -35,6 +35,8 @@ namespace Jazz2::UI::Menu
MainMenu(IRootController* root, bool afterIntro);
~MainMenu() override;
+ void Reset();
+
void OnBeginFrame() override;
void OnInitializeViewport(int32_t width, int32_t height) override;
@@ -46,7 +48,7 @@ namespace Jazz2::UI::Menu
void LeaveSection() override;
void ChangeLevel(Jazz2::LevelInitialization&& levelInit) override;
#if defined(WITH_MULTIPLAYER)
- bool ConnectToServer(const char* address, std::uint16_t port) override;
+ bool ConnectToServer(const StringView& address, std::uint16_t port) override;
bool CreateServer(std::uint16_t port) override;
#endif
void ApplyPreferencesChanges(ChangedPreferencesType type) override;
diff --git a/Sources/Main.cpp b/Sources/Main.cpp
index e7ed17d4..4787ed79 100644
--- a/Sources/Main.cpp
+++ b/Sources/Main.cpp
@@ -25,6 +25,7 @@
#include "Jazz2/UI/Cinematics.h"
#include "Jazz2/UI/ControlScheme.h"
#include "Jazz2/UI/Menu/MainMenu.h"
+#include "Jazz2/UI/Menu/LoadingSection.h"
#include "Jazz2/UI/Menu/SimpleMessageSection.h"
#include "Jazz2/Compatibility/JJ2Anims.h"
@@ -76,6 +77,7 @@ class GameEventHandler : public IAppEventHandler, public IInputEventHandler, pub
static constexpr std::int32_t DefaultHeight = 405;
#if defined(WITH_MULTIPLAYER)
+ static constexpr std::uint16_t MultiplayerDefaultPort = 7438;
static constexpr std::uint32_t MultiplayerProtocolVersion = 1;
#endif
@@ -98,7 +100,7 @@ class GameEventHandler : public IAppEventHandler, public IInputEventHandler, pub
void ChangeLevel(LevelInitialization&& levelInit) override;
#if defined(WITH_MULTIPLAYER)
- bool ConnectToServer(const char* address, std::uint16_t port) override;
+ bool ConnectToServer(const StringView& address, std::uint16_t port) override;
bool CreateServer(std::uint16_t port) override;
bool OnPeerConnected(const Peer& peer, std::uint32_t clientData) override;
@@ -134,9 +136,11 @@ class GameEventHandler : public IAppEventHandler, public IInputEventHandler, pub
void RefreshCache();
void CheckUpdates();
#endif
+ static void WriteCacheDescriptor(const StringView& path, std::uint64_t currentVersion, std::int64_t animsModified);
static void SaveEpisodeEnd(const LevelInitialization& pendingLevelChange);
static void SaveEpisodeContinue(const LevelInitialization& pendingLevelChange);
static void UpdateRichPresence(const LevelInitialization& levelInit);
+ static bool TryParseAddressAndPort(const StringView& input, String& address, std::uint16_t& port);
};
void GameEventHandler::OnPreInit(AppConfiguration& config)
@@ -218,11 +222,40 @@ void GameEventHandler::OnInit()
thread.SetName("Parallel initialization");
# endif
+# if defined(WITH_MULTIPLAYER)
+ if (PreferencesCache::InitialState == "/server"_s) {
+ thread.Join();
+
+ auto mainMenu = std::make_unique(this, false);
+ mainMenu->SwitchToSection(_("Creating server..."));
+ SetStateHandler(std::move(mainMenu));
+
+ // TODO: Hardcoded port
+ CreateServer(MultiplayerDefaultPort);
+ } else if (PreferencesCache::InitialState.hasPrefix("/connect:"_s)) {
+ thread.Join();
+
+ String address; std::uint16_t port;
+ if (TryParseAddressAndPort(PreferencesCache::InitialState.exceptPrefix(9), address, port)) {
+ if (port == 0) {
+ port = MultiplayerDefaultPort;
+ }
+
+ auto mainMenu = std::make_unique(this, false);
+ mainMenu->SwitchToSection(_f("Connecting to %s:%u...", address.data(), port));
+ SetStateHandler(std::move(mainMenu));
+
+ ConnectToServer(address.data(), (std::uint16_t)port);
+ return;
+ }
+ }
+# endif
+
SetStateHandler(std::make_unique(this, "intro"_s, [thread](IRootController* root, bool endOfStream) mutable {
if ((root->GetFlags() & Jazz2::IRootController::Flags::IsVerified) != Jazz2::IRootController::Flags::IsVerified) {
return false;
}
-
+
thread.Join();
root->GoToMainMenu(endOfStream);
return true;
@@ -251,6 +284,33 @@ void GameEventHandler::OnInit()
CheckUpdates();
# endif
+# if defined(WITH_MULTIPLAYER)
+ if (PreferencesCache::InitialState == "/server"_s) {
+ LOGI("Starting server on port %u...", MultiplayerDefaultPort);
+
+ auto mainMenu = std::make_unique(this, false);
+ mainMenu->SwitchToSection(_("Creating server..."));
+ SetStateHandler(std::move(mainMenu));
+
+ // TODO: Hardcoded port
+ CreateServer(MultiplayerDefaultPort);
+ } else if (PreferencesCache::InitialState.hasPrefix("/connect:"_s)) {
+ String address; std::uint16_t port;
+ if (TryParseAddressAndPort(PreferencesCache::InitialState.exceptPrefix(9), address, port)) {
+ if (port == 0) {
+ port = MultiplayerDefaultPort;
+ }
+
+ auto mainMenu = std::make_unique(this, false);
+ mainMenu->SwitchToSection(_f("Connecting to %s:%u...", address.data(), port));
+ SetStateHandler(std::move(mainMenu));
+
+ ConnectToServer(address.data(), (std::uint16_t)port);
+ return;
+ }
+ }
+# endif
+
SetStateHandler(std::make_unique(this, "intro"_s, [](IRootController* root, bool endOfStream) {
root->GoToMainMenu(endOfStream);
return true;
@@ -373,9 +433,12 @@ void GameEventHandler::GoToMainMenu(bool afterIntro)
#if defined(WITH_MULTIPLAYER)
_networkManager = nullptr;
#endif
-
- SetStateHandler(std::make_unique(this, afterIntro));
- UpdateRichPresence({});
+ if (auto mainMenu = dynamic_cast(_currentHandler.get())) {
+ mainMenu->Reset();
+ } else {
+ SetStateHandler(std::make_unique(this, afterIntro));
+ UpdateRichPresence({});
+ }
});
}
@@ -457,8 +520,10 @@ void GameEventHandler::ChangeLevel(LevelInitialization&& levelInit)
}
#if defined(WITH_MULTIPLAYER)
-bool GameEventHandler::ConnectToServer(const char* address, std::uint16_t port)
+bool GameEventHandler::ConnectToServer(const StringView& address, std::uint16_t port)
{
+ LOGI("Connecting to %s:%u...", address, port);
+
if (_networkManager == nullptr) {
_networkManager = std::make_unique();
}
@@ -468,6 +533,8 @@ bool GameEventHandler::ConnectToServer(const char* address, std::uint16_t port)
bool GameEventHandler::CreateServer(std::uint16_t port)
{
+ LOGI("Creating server on port %u...", port);
+
if (_networkManager == nullptr) {
_networkManager = std::make_unique();
}
@@ -600,23 +667,26 @@ void GameEventHandler::RefreshCache()
return;
}
+ constexpr std::uint64_t currentVersion = parseVersion({ NCINE_VERSION, countof(NCINE_VERSION) - 1 });
+
auto& resolver = ContentResolver::Get();
+ auto cachePath = fs::CombinePath({ resolver.GetCachePath(), "Animations"_s, "cache.index"_s });
// Check cache state
{
- auto s = fs::Open(fs::CombinePath({ resolver.GetCachePath(), "Animations"_s, "cache.index"_s }), FileAccessMode::Read);
+ auto s = fs::Open(cachePath, FileAccessMode::Read);
if (s->GetSize() < 16) {
goto RecreateCache;
}
- uint64_t signature = s->ReadValue();
- uint8_t fileType = s->ReadValue();
- uint16_t version = s->ReadValue();
+ std::uint64_t signature = s->ReadValue();
+ std::uint8_t fileType = s->ReadValue();
+ std::uint16_t version = s->ReadValue();
if (signature != 0x2095A59FF0BFBBEF || fileType != ContentResolver::CacheIndexFile || version != Compatibility::JJ2Anims::CacheVersion) {
goto RecreateCache;
}
- uint8_t flags = s->ReadValue();
+ std::uint8_t flags = s->ReadValue();
if ((flags & 0x01) == 0x01) {
// Don't overwrite cache
LOGI("Cache is protected");
@@ -628,20 +698,39 @@ void GameEventHandler::RefreshCache()
if (!fs::IsReadableFile(animsPath)) {
animsPath = fs::FindPathCaseInsensitive(fs::CombinePath(resolver.GetSourcePath(), "AnimsSw.j2a"_s));
}
- int64_t animsCached = s->ReadValue();
- int64_t animsModified = fs::GetLastModificationTime(animsPath).GetValue();
+ std::int64_t animsCached = s->ReadValue();
+ std::int64_t animsModified = fs::GetLastModificationTime(animsPath).GetValue();
if (animsModified != 0 && animsCached != animsModified) {
goto RecreateCache;
}
// If some events were added, recreate cache
- uint16_t eventTypeCount = s->ReadValue();
- if (eventTypeCount != (uint16_t)EventType::Count) {
+ std::uint16_t eventTypeCount = s->ReadValue();
+ if (eventTypeCount != (std::uint16_t)EventType::Count) {
goto RecreateCache;
}
// Cache is up-to-date
- LOGI("Cache is already up-to-date");
+ std::uint64_t lastVersion = s->ReadValue();
+
+ // Close the file, so it can be writable for possible update
+ s = nullptr;
+
+ if (currentVersion != lastVersion) {
+ if ((lastVersion & 0xFFFFFFFFULL) == 0x0FFFFFFFULL) {
+ LOGI("Cache is already up-to-date, but created in experimental build v%i.%i.0", (lastVersion >> 48) & 0xFFFFULL, (lastVersion >> 32) & 0xFFFFULL);
+ } else {
+ LOGI("Cache is already up-to-date, but created in different build v%i.%i.%i", (lastVersion >> 48) & 0xFFFFULL, (lastVersion >> 32) & 0xFFFFULL, lastVersion & 0xFFFFFFFFULL);
+ }
+
+ WriteCacheDescriptor(cachePath, currentVersion, animsModified);
+
+ LOGI("Pruning binary shader cache...");
+ RenderResources::binaryShaderCache().prune();
+ } else {
+ LOGI("Cache is already up-to-date");
+ }
+
_flags |= Flags::IsVerified | Flags::IsPlayable;
return;
}
@@ -668,18 +757,13 @@ void GameEventHandler::RefreshCache()
RefreshCacheLevels();
- // Create cache index
- auto so = fs::Open(fs::CombinePath({ resolver.GetCachePath(), "Animations"_s, "cache.index"_s }), FileAccessMode::Write);
+ LOGI("Cache was recreated");
+ std::int64_t animsModified = fs::GetLastModificationTime(animsPath).GetValue();
+ WriteCacheDescriptor(cachePath, currentVersion, animsModified);
- so->WriteValue(0x2095A59FF0BFBBEF); // Signature
- so->WriteValue(ContentResolver::CacheIndexFile);
- so->WriteValue(Compatibility::JJ2Anims::CacheVersion);
- so->WriteValue(0x00); // Flags
- int64_t animsModified = fs::GetLastModificationTime(animsPath).GetValue();
- so->WriteValue(animsModified);
- so->WriteValue((uint16_t)EventType::Count);
+ LOGI("Pruning binary shader cache...");
+ RenderResources::binaryShaderCache().prune();
- LOGI("Cache was recreated");
_flags |= Flags::IsVerified | Flags::IsPlayable;
}
@@ -921,9 +1005,18 @@ void GameEventHandler::RefreshCacheLevels()
}
}
}
-
- LOGI("Pruning binary shader cache...");
- RenderResources::binaryShaderCache().prune();
+}
+
+void GameEventHandler::WriteCacheDescriptor(const StringView& path, std::uint64_t currentVersion, std::int64_t animsModified)
+{
+ auto so = fs::Open(path, FileAccessMode::Write);
+ so->WriteValue(0x2095A59FF0BFBBEF); // Signature
+ so->WriteValue(ContentResolver::CacheIndexFile);
+ so->WriteValue(Compatibility::JJ2Anims::CacheVersion);
+ so->WriteValue(0x00); // Flags
+ so->WriteValue(animsModified);
+ so->WriteValue((std::uint16_t)EventType::Count);
+ so->WriteValue(currentVersion);
}
void GameEventHandler::CheckUpdates()
@@ -1102,7 +1195,7 @@ void GameEventHandler::CheckUpdates()
Http::Request req(url, Http::InternetProtocol::V4);
Http::Response resp = req.Send("GET"_s, std::chrono::seconds(10));
if (resp.Status.Code == Http::HttpStatus::Ok && !resp.Body.empty() && resp.Body.size() < sizeof(_newestVersion) - 1) {
- std::uint64_t currentVersion = parseVersion(NCINE_VERSION);
+ constexpr std::uint64_t currentVersion = parseVersion({ NCINE_VERSION, countof(NCINE_VERSION) - 1 });
std::uint64_t latestVersion = parseVersion(StringView(reinterpret_cast(resp.Body.data()), resp.Body.size()));
if (currentVersion < latestVersion) {
std::memcpy(_newestVersion, resp.Body.data(), resp.Body.size());
@@ -1272,6 +1365,23 @@ void GameEventHandler::UpdateRichPresence(const LevelInitialization& levelInit)
#endif
}
+bool GameEventHandler::TryParseAddressAndPort(const StringView& input, String& address, std::uint16_t& port)
+{
+ auto portSep = input.findLast(':');
+ if (portSep == nullptr) {
+ return false;
+ }
+
+ address = String(input.prefix(portSep.begin()));
+ if (address.empty()) {
+ return false;
+ }
+
+ auto portString = input.suffix(portSep.begin() + 1);
+ port = (std::uint16_t)stou32(portString.data(), portString.size());
+ return true;
+}
+
#if defined(DEATH_TARGET_ANDROID)
std::unique_ptr CreateAppEventHandler()
{
diff --git a/Sources/Shared/Containers/ArrayView.h b/Sources/Shared/Containers/ArrayView.h
index 3c879364..7579ea67 100644
--- a/Sources/Shared/Containers/ArrayView.h
+++ b/Sources/Shared/Containers/ArrayView.h
@@ -54,7 +54,10 @@ namespace Death::Containers
public:
typedef T Type;
- constexpr /*implicit*/ ArrayView(std::nullptr_t = nullptr) noexcept : _data{}, _size {} {}
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a ArrayView or std::size_t */
+ template::value>::type> constexpr /*implicit*/ ArrayView(U) noexcept : _data{}, _size{} {}
+
+ constexpr /*implicit*/ ArrayView() noexcept : _data{}, _size{} {}
constexpr /*implicit*/ ArrayView(T* data, std::size_t size) noexcept : _data(data), _size(size) {}
@@ -172,7 +175,10 @@ namespace Death::Containers
public:
typedef void Type;
- constexpr /*implicit*/ ArrayView(std::nullptr_t = nullptr) noexcept : _data{}, _size{} {}
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a ArrayView or std::size_t */
+ template::value>::type> constexpr /*implicit*/ ArrayView(U) noexcept : _data{}, _size{} {}
+
+ constexpr /*implicit*/ ArrayView() noexcept : _data{}, _size{} {}
constexpr /*implicit*/ ArrayView(void* data, std::size_t size) noexcept : _data(data), _size(size) {}
@@ -233,7 +239,10 @@ namespace Death::Containers
public:
typedef const void Type;
- constexpr /*implicit*/ ArrayView(std::nullptr_t = nullptr) noexcept : _data{}, _size{} {}
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a ArrayView or std::size_t */
+ template::value>::type> constexpr /*implicit*/ ArrayView(U) noexcept : _data{}, _size{} {}
+
+ constexpr /*implicit*/ ArrayView() noexcept : _data{}, _size{} {}
constexpr /*implicit*/ ArrayView(const void* data, std::size_t size) noexcept : _data(data), _size(size) {}
@@ -356,7 +365,10 @@ namespace Death::Containers
Size = size_
};
- constexpr /*implicit*/ StaticArrayView(std::nullptr_t = nullptr) noexcept : _data{} {}
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a ArrayView or std::size_t */
+ template::value>::type> constexpr /*implicit*/ StaticArrayView(U) noexcept : _data{} {}
+
+ constexpr /*implicit*/ StaticArrayView() noexcept : _data{} {}
template::value && !std::is_same::value>::type> constexpr explicit StaticArrayView(U data)
noexcept : _data(data) {}
diff --git a/Sources/Shared/Containers/String.cpp b/Sources/Shared/Containers/String.cpp
index 827df3ea..d2bb044d 100644
--- a/Sources/Shared/Containers/String.cpp
+++ b/Sources/Shared/Containers/String.cpp
@@ -27,7 +27,7 @@ namespace Death::Containers
out._large.size |= std::size_t(view.flags() & StringViewFlags::Global);
return out;
}
- return String { view };
+ return String{view};
}
String String::nullTerminatedView(AllocatedInitT, StringView view) {
@@ -36,7 +36,7 @@ namespace Death::Containers
out._large.size |= std::size_t(view.flags() & StringViewFlags::Global);
return out;
}
- return String { AllocatedInit, view };
+ return String{AllocatedInit, view};
}
String String::nullTerminatedGlobalView(StringView view) {
@@ -45,7 +45,7 @@ namespace Death::Containers
out._large.size |= std::size_t(StringViewFlags::Global);
return out;
}
- return String { view };
+ return String{view};
}
String String::nullTerminatedGlobalView(AllocatedInitT, StringView view) {
@@ -54,7 +54,7 @@ namespace Death::Containers
out._large.size |= std::size_t(StringViewFlags::Global);
return out;
}
- return String { AllocatedInit, view };
+ return String{AllocatedInit, view};
}
inline void String::construct(NoInitT, const std::size_t size) {
@@ -112,7 +112,7 @@ namespace Death::Containers
String::String(const ArrayView view) : String{view.data(), view.size()} {}
- String::String(const char* const data) : String{data, data ? std::strlen(data) : 0} {}
+ String::String(std::nullptr_t, std::nullptr_t, std::nullptr_t, const char* const data) : String{data, data ? std::strlen(data) : 0} {}
String::String(const char* const data, const std::size_t size)
: _large{}
@@ -120,7 +120,7 @@ namespace Death::Containers
#if defined(DEATH_TARGET_32BIT)
// Compared to StringView construction which happens a lot this shouldn't, and the chance of strings > 1 GB on 32-bit
// is rare but possible and thus worth checking even in release
- DEATH_ASSERT(size < std::size_t { 1 } << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
+ DEATH_ASSERT(size < std::size_t{1} << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
#endif
DEATH_ASSERT(data || size == 0, , "Containers::String: Received a null string of size %zu", size);
@@ -193,7 +193,7 @@ namespace Death::Containers
// Compared to StringView construction which happens a lot this shouldn't, the chance of strings > 1 GB on 32-bit
// is rare but possible and thus worth checking even in release; but most importantly checking for null
// termination outweighs potential speed issues
- DEATH_ASSERT(size < std::size_t { 1 } << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
+ DEATH_ASSERT(size < std::size_t{1} << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
DEATH_ASSERT(data && !data[size], , "Containers::String: Can only take ownership of a non-null null-terminated array");
_large.data = data;
@@ -213,7 +213,7 @@ namespace Death::Containers
{
// Compared to StringView construction which happens a lot this shouldn't, and the chance of strings > 1 GB on 32-bit
// is rare but possible and thus worth checking even in release
- DEATH_ASSERT(size < std::size_t { 1 } << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
+ DEATH_ASSERT(size < std::size_t{1} << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
if (size < Implementation::SmallStringSize) {
// Everything already zero-init'd in the constructor init list
@@ -229,7 +229,7 @@ namespace Death::Containers
{
// Compared to StringView construction which happens a lot this shouldn't, and the chance of strings > 1 GB on 32-bit
// is rare but possible and thus worth checking even in release
- DEATH_ASSERT(size < std::size_t { 1 } << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
+ DEATH_ASSERT(size < std::size_t{1} << (sizeof(std::size_t) * 8 - 2), , "Containers::String: String expected to be smaller than 2^%zu bytes, got %zu", sizeof(std::size_t) * 8 - 2, size);
construct(NoInit, size);
}
@@ -299,11 +299,11 @@ namespace Death::Containers
if (_small.size & Implementation::SmallStringBit) {
const std::size_t size = _small.size & ~SmallSizeMask;
// Allocate the output including a null terminator at the end, but don't include it in the size
- out = Array { Array{NoInit, size + 1}.release(), size };
+ out = Array{Array{NoInit, size + 1}.release(), size};
out[size] = '\0';
std::memcpy(out.data(), _small.data, size);
} else {
- out = Array { _large.data, _large.size & ~LargeSizeMask, deleter() };
+ out = Array{_large.data, _large.size & ~LargeSizeMask, deleter()};
}
// Same as in release(). Create a zero-size small string to fullfil the guarantee of data() being always non-null
@@ -461,119 +461,119 @@ namespace Death::Containers
}
MutableStringView String::slice(char* const begin, char* const end) {
- return MutableStringView { *this }.slice(begin, end);
+ return MutableStringView{*this}.slice(begin, end);
}
StringView String::slice(const char* const begin, const char* const end) const {
- return StringView { *this }.slice(begin, end);
+ return StringView{*this}.slice(begin, end);
}
MutableStringView String::slice(const std::size_t begin, const std::size_t end) {
- return MutableStringView { *this }.slice(begin, end);
+ return MutableStringView{*this}.slice(begin, end);
}
StringView String::slice(const std::size_t begin, const std::size_t end) const {
- return StringView { *this }.slice(begin, end);
+ return StringView{*this}.slice(begin, end);
}
MutableStringView String::prefix(char* const end) {
- return MutableStringView { *this }.prefix(end);
+ return MutableStringView{*this}.prefix(end);
}
StringView String::prefix(const char* const end) const {
- return StringView { *this }.prefix(end);
+ return StringView{*this}.prefix(end);
}
MutableStringView String::suffix(char* const begin) {
- return MutableStringView { *this }.suffix(begin);
+ return MutableStringView{*this}.suffix(begin);
}
StringView String::suffix(const char* const begin) const {
- return StringView { *this }.suffix(begin);
+ return StringView{*this}.suffix(begin);
}
MutableStringView String::prefix(const std::size_t count) {
- return MutableStringView { *this }.prefix(count);
+ return MutableStringView{*this}.prefix(count);
}
StringView String::prefix(const std::size_t count) const {
- return StringView { *this }.prefix(count);
+ return StringView{*this}.prefix(count);
}
MutableStringView String::exceptPrefix(const std::size_t count) {
- return MutableStringView { *this }.exceptPrefix(count);
+ return MutableStringView{*this}.exceptPrefix(count);
}
StringView String::exceptPrefix(const std::size_t count) const {
- return StringView { *this }.exceptPrefix(count);
+ return StringView{*this}.exceptPrefix(count);
}
MutableStringView String::exceptSuffix(const std::size_t count) {
- return MutableStringView { *this }.exceptSuffix(count);
+ return MutableStringView{*this}.exceptSuffix(count);
}
StringView String::exceptSuffix(const std::size_t count) const {
- return StringView { *this }.exceptSuffix(count);
+ return StringView{*this}.exceptSuffix(count);
}
Array String::split(const char delimiter) {
- return MutableStringView { *this }.split(delimiter);
+ return MutableStringView{*this}.split(delimiter);
}
Array String::split(const char delimiter) const {
- return StringView { *this }.split(delimiter);
+ return StringView{*this}.split(delimiter);
}
Array String::split(const StringView delimiter) {
- return MutableStringView { *this }.split(delimiter);
+ return MutableStringView{*this}.split(delimiter);
}
Array String::split(const StringView delimiter) const {
- return StringView { *this }.split(delimiter);
+ return StringView{*this}.split(delimiter);
}
Array String::splitWithoutEmptyParts(const char delimiter) {
- return MutableStringView { *this }.splitWithoutEmptyParts(delimiter);
+ return MutableStringView{*this}.splitWithoutEmptyParts(delimiter);
}
Array String::splitWithoutEmptyParts(const char delimiter) const {
- return StringView { *this }.splitWithoutEmptyParts(delimiter);
+ return StringView{*this}.splitWithoutEmptyParts(delimiter);
}
Array String::splitOnAnyWithoutEmptyParts(const StringView delimiters) {
- return MutableStringView { *this }.splitOnAnyWithoutEmptyParts(delimiters);
+ return MutableStringView{*this}.splitOnAnyWithoutEmptyParts(delimiters);
}
Array String::splitOnAnyWithoutEmptyParts(const StringView delimiters) const {
- return StringView { *this }.splitOnAnyWithoutEmptyParts(delimiters);
+ return StringView{*this}.splitOnAnyWithoutEmptyParts(delimiters);
}
Array String::splitOnWhitespaceWithoutEmptyParts() {
- return MutableStringView { *this }.splitOnWhitespaceWithoutEmptyParts();
+ return MutableStringView{*this}.splitOnWhitespaceWithoutEmptyParts();
}
Array String::splitOnWhitespaceWithoutEmptyParts() const {
- return StringView { *this }.splitOnWhitespaceWithoutEmptyParts();
+ return StringView{*this}.splitOnWhitespaceWithoutEmptyParts();
}
StaticArray<3, MutableStringView> String::partition(const char separator) {
- return MutableStringView { *this }.partition(separator);
+ return MutableStringView{*this}.partition(separator);
}
StaticArray<3, StringView> String::partition(const char separator) const {
- return StringView { *this }.partition(separator);
+ return StringView{*this}.partition(separator);
}
StaticArray<3, MutableStringView> String::partition(const StringView separator) {
- return MutableStringView { *this }.partition(separator);
+ return MutableStringView{*this}.partition(separator);
}
StaticArray<3, StringView> String::partition(const StringView separator) const {
- return StringView { *this }.partition(separator);
+ return StringView{*this}.partition(separator);
}
String String::join(const ArrayView strings) const {
- return StringView { *this }.join(strings);
+ return StringView{*this}.join(strings);
}
String String::join(const std::initializer_list strings) const {
@@ -582,7 +582,7 @@ namespace Death::Containers
}
String String::joinWithoutEmptyParts(const ArrayView strings) const {
- return StringView { *this }.joinWithoutEmptyParts(strings);
+ return StringView{*this}.joinWithoutEmptyParts(strings);
}
String String::joinWithoutEmptyParts(const std::initializer_list strings) const {
@@ -591,199 +591,199 @@ namespace Death::Containers
}
bool String::hasPrefix(const StringView prefix) const {
- return StringView { *this }.hasPrefix(prefix);
+ return StringView{*this}.hasPrefix(prefix);
}
bool String::hasPrefix(const char prefix) const {
- return StringView { *this }.hasPrefix(prefix);
+ return StringView{*this}.hasPrefix(prefix);
}
bool String::hasSuffix(const StringView suffix) const {
- return StringView { *this }.hasSuffix(suffix);
+ return StringView{*this}.hasSuffix(suffix);
}
bool String::hasSuffix(const char suffix) const {
- return StringView { *this }.hasSuffix(suffix);
+ return StringView{*this}.hasSuffix(suffix);
}
MutableStringView String::exceptPrefix(const StringView prefix) {
- return MutableStringView { *this }.exceptPrefix(prefix);
+ return MutableStringView{*this}.exceptPrefix(prefix);
}
StringView String::exceptPrefix(const StringView prefix) const {
- return StringView { *this }.exceptPrefix(prefix);
+ return StringView{*this}.exceptPrefix(prefix);
}
MutableStringView String::exceptSuffix(const StringView suffix) {
- return MutableStringView { *this }.exceptSuffix(suffix);
+ return MutableStringView{*this}.exceptSuffix(suffix);
}
StringView String::exceptSuffix(const StringView suffix) const {
- return StringView { *this }.exceptSuffix(suffix);
+ return StringView{*this}.exceptSuffix(suffix);
}
MutableStringView String::trimmed(const StringView characters) {
- return MutableStringView { *this }.trimmed(characters);
+ return MutableStringView{*this}.trimmed(characters);
}
StringView String::trimmed(const StringView characters) const {
- return StringView { *this }.trimmed(characters);
+ return StringView{*this}.trimmed(characters);
}
MutableStringView String::trimmed() {
- return MutableStringView { *this }.trimmed();
+ return MutableStringView{*this}.trimmed();
}
StringView String::trimmed() const {
- return StringView { *this }.trimmed();
+ return StringView{*this}.trimmed();
}
MutableStringView String::trimmedPrefix(const StringView characters) {
- return MutableStringView { *this }.trimmedPrefix(characters);
+ return MutableStringView{*this}.trimmedPrefix(characters);
}
StringView String::trimmedPrefix(const StringView characters) const {
- return StringView { *this }.trimmedPrefix(characters);
+ return StringView{*this}.trimmedPrefix(characters);
}
MutableStringView String::trimmedPrefix() {
- return MutableStringView { *this }.trimmedPrefix();
+ return MutableStringView{*this}.trimmedPrefix();
}
StringView String::trimmedPrefix() const {
- return StringView { *this }.trimmedPrefix();
+ return StringView{*this}.trimmedPrefix();
}
MutableStringView String::trimmedSuffix(const StringView characters) {
- return MutableStringView { *this }.trimmedSuffix(characters);
+ return MutableStringView{*this}.trimmedSuffix(characters);
}
StringView String::trimmedSuffix(const StringView characters) const {
- return StringView { *this }.trimmedSuffix(characters);
+ return StringView{*this}.trimmedSuffix(characters);
}
MutableStringView String::trimmedSuffix() {
- return MutableStringView { *this }.trimmedSuffix();
+ return MutableStringView{*this}.trimmedSuffix();
}
StringView String::trimmedSuffix() const {
- return StringView { *this }.trimmedSuffix();
+ return StringView{*this}.trimmedSuffix();
}
MutableStringView String::find(const StringView substring) {
// Calling straight into the concrete implementation to reduce call stack depth
- return MutableStringView { *this }.findOr(substring, nullptr);
+ return MutableStringView{*this}.findOr(substring, nullptr);
}
StringView String::find(const StringView substring) const {
// Calling straight into the concrete implementation to reduce call stack depth
- return StringView { *this }.findOr(substring, nullptr);
+ return StringView{*this}.findOr(substring, nullptr);
}
MutableStringView String::find(const char character) {
// Calling straight into the concrete implementation to reduce call stack depth
- return MutableStringView { *this }.findOr(character, nullptr);
+ return MutableStringView{*this}.findOr(character, nullptr);
}
StringView String::find(const char character) const {
// Calling straight into the concrete implementation to reduce call stack depth
- return StringView { *this }.findOr(character, nullptr);
+ return StringView{*this}.findOr(character, nullptr);
}
MutableStringView String::findOr(const StringView substring, char* const fail) {
- return MutableStringView { *this }.findOr(substring, fail);
+ return MutableStringView{*this}.findOr(substring, fail);
}
StringView String::findOr(const StringView substring, const char* const fail) const {
- return StringView { *this }.findOr(substring, fail);
+ return StringView{*this}.findOr(substring, fail);
}
MutableStringView String::findOr(const char character, char* const fail) {
- return MutableStringView { *this }.findOr(character, fail);
+ return MutableStringView{*this}.findOr(character, fail);
}
StringView String::findOr(const char character, const char* const fail) const {
- return StringView { *this }.findOr(character, fail);
+ return StringView{*this}.findOr(character, fail);
}
MutableStringView String::findLast(const StringView substring) {
// Calling straight into the concrete implementation to reduce call stack depth
- return MutableStringView { *this }.findLastOr(substring, nullptr);
+ return MutableStringView{*this}.findLastOr(substring, nullptr);
}
StringView String::findLast(const StringView substring) const {
// Calling straight into the concrete implementation to reduce call stack depth
- return StringView { *this }.findLastOr(substring, nullptr);
+ return StringView{*this}.findLastOr(substring, nullptr);
}
MutableStringView String::findLast(const char character) {
// Calling straight into the concrete implementation to reduce call stack depth
- return MutableStringView { *this }.findLastOr(character, nullptr);
+ return MutableStringView{*this}.findLastOr(character, nullptr);
}
StringView String::findLast(const char character) const {
/* Calling straight into the concrete implementation to reduce call stack depth */
- return StringView { *this }.findLastOr(character, nullptr);
+ return StringView{*this}.findLastOr(character, nullptr);
}
MutableStringView String::findLastOr(const StringView substring, char* const fail) {
- return MutableStringView { *this }.findLastOr(substring, fail);
+ return MutableStringView{*this}.findLastOr(substring, fail);
}
StringView String::findLastOr(const StringView substring, const char* const fail) const {
- return StringView { *this }.findLastOr(substring, fail);
+ return StringView{*this}.findLastOr(substring, fail);
}
MutableStringView String::findLastOr(const char character, char* const fail) {
- return MutableStringView { *this }.findLastOr(character, fail);
+ return MutableStringView{*this}.findLastOr(character, fail);
}
StringView String::findLastOr(const char character, const char* const fail) const {
- return StringView { *this }.findLastOr(character, fail);
+ return StringView{*this}.findLastOr(character, fail);
}
bool String::contains(const StringView substring) const {
- return StringView { *this }.contains(substring);
+ return StringView{*this}.contains(substring);
}
bool String::contains(const char character) const {
- return StringView { *this }.contains(character);
+ return StringView{*this}.contains(character);
}
MutableStringView String::findAny(const StringView characters) {
- return MutableStringView { *this }.findAny(characters);
+ return MutableStringView{*this}.findAny(characters);
}
StringView String::findAny(const StringView characters) const {
- return StringView { *this }.findAny(characters);
+ return StringView{*this}.findAny(characters);
}
MutableStringView String::findAnyOr(const StringView characters, char* fail) {
- return MutableStringView { *this }.findAnyOr(characters, fail);
+ return MutableStringView{*this}.findAnyOr(characters, fail);
}
StringView String::findAnyOr(const StringView characters, const char* fail) const {
- return StringView { *this }.findAnyOr(characters, fail);
+ return StringView{*this}.findAnyOr(characters, fail);
}
MutableStringView String::findLastAny(const StringView characters) {
- return MutableStringView { *this }.findLastAny(characters);
+ return MutableStringView{*this}.findLastAny(characters);
}
StringView String::findLastAny(const StringView characters) const {
- return StringView { *this }.findLastAny(characters);
+ return StringView{*this}.findLastAny(characters);
}
MutableStringView String::findLastAnyOr(const StringView characters, char* fail) {
- return MutableStringView { *this }.findLastAnyOr(characters, fail);
+ return MutableStringView{*this}.findLastAnyOr(characters, fail);
}
StringView String::findLastAnyOr(const StringView characters, const char* fail) const {
- return StringView { *this }.findLastAnyOr(characters, fail);
+ return StringView{*this}.findLastAnyOr(characters, fail);
}
bool String::containsAny(const StringView substring) const {
- return StringView { *this }.containsAny(substring);
+ return StringView{*this}.containsAny(substring);
}
char* String::release() {
@@ -802,11 +802,11 @@ namespace Death::Containers
namespace Implementation
{
String StringConverter::from(const std::string& other) {
- return String { other.data(), other.size() };
+ return String{other.data(), other.size()};
}
std::string StringConverter::to(const String& other) {
- return std::string { other.data(), other.size() };
+ return std::string{other.data(), other.size()};
}
}
}
\ No newline at end of file
diff --git a/Sources/Shared/Containers/String.h b/Sources/Shared/Containers/String.h
index 5174e0a0..d3bf6b1e 100644
--- a/Sources/Shared/Containers/String.h
+++ b/Sources/Shared/Containers/String.h
@@ -165,7 +165,8 @@ namespace Death::Containers
* @cpp nullptr @ce --- in that case an empty string is constructed.
* Depending on the size, it's either stored allocated or in a SSO.
*/
- /*implicit*/ String(const char* data);
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a String or std::size_t */
+ template::value && !std::is_convertible::value>::type> /*implicit*/ String(T data) : String{nullptr, nullptr, nullptr, data} {}
/**
* @brief Construct from a sized C string
@@ -860,6 +861,8 @@ namespace Death::Containers
char* release();
private:
+ // Delegated to from the (templated) String(const char*). THREE extra nullptr arguments to avoid accidental ambiguous overloads.
+ explicit String(std::nullptr_t, std::nullptr_t, std::nullptr_t, const char* data);
// Delegated to from the (templated) String(char*, Deleter). Argument order shuffled together with a null parameter to avoid accidental ambiguous overloads.
explicit String(Deleter deleter, std::nullptr_t, char* data) noexcept;
diff --git a/Sources/Shared/Containers/StringView.h b/Sources/Shared/Containers/StringView.h
index b9791c87..76b92ebd 100644
--- a/Sources/Shared/Containers/StringView.h
+++ b/Sources/Shared/Containers/StringView.h
@@ -47,14 +47,14 @@ namespace Death::Containers
* string view with this flag set doesn't need to have a copy allocated in
* order to ensure it stays in scope.
*/
- Global = std::size_t { 1 } << (sizeof(std::size_t) * 8 - 1),
+ Global = std::size_t{1} << (sizeof(std::size_t) * 8 - 1),
/**
* The referenced string is null-terminated. A string view with this flag
* set doesn't need to have a null-terminated copy allocated in order to
* pass to an API that expects only null-terminated strings.
*/
- NullTerminated = std::size_t { 1 } << (sizeof(std::size_t) * 8 - 2)
+ NullTerminated = std::size_t{1} << (sizeof(std::size_t) * 8 - 2)
};
DEFINE_ENUM_OPERATORS(StringViewFlags);
@@ -75,12 +75,15 @@ namespace Death::Containers
template class BasicStringView
{
public:
+ /* To avoid ambiguity in certain cases of passing 0 to overloads that take either a StringView or std::size_t */
+ template::value>::type> constexpr /*implicit*/ BasicStringView(U) noexcept : _data{}, _sizePlusFlags{std::size_t(StringViewFlags::Global)} {}
+
/**
* @brief Default constructor
*
* A default-constructed instance has @ref StringViewFlags::Global set.
*/
- constexpr /*implicit*/ BasicStringView(std::nullptr_t = nullptr) noexcept : _data{}, _sizePlusFlags{std::size_t(StringViewFlags::Global)} {}
+ constexpr /*implicit*/ BasicStringView() noexcept : _data{}, _sizePlusFlags{std::size_t(StringViewFlags::Global)} {}
/**
* @brief Construct from a C string of known size
@@ -104,7 +107,7 @@ namespace Death::Containers
constexpr /*implicit*/ BasicStringView(T* data, std::size_t size, StringViewFlags flags = {}) noexcept : _data{data}, _sizePlusFlags{
// This ends up being called from BasicStringView(T*, Flags), so basically on every implicit conversion
// from a C string, thus the release build perf aspect wins over safety
- (size | (std::size_t(flags) & Implementation::StringViewSizeMask))} { }
+ (size | (std::size_t(flags) & Implementation::StringViewSizeMask))} {}
/**
* @brief Construct from a @ref String
@@ -141,7 +144,7 @@ namespace Death::Containers
// It's also explicitly disallowing T[] arguments (which are implicitly convertible to an ArrayView), because those should be picking the T*
// overload and rely on strlen(), consistently with how C string literals work; and disallowing construction from a StringView
// because it'd get preferred over the implicit copy constructor.
- template::type>::value && !std::is_same::type, BasicStringView>::value, decltype(ArrayView{std::declval()})>::type> constexpr /*implicit*/ BasicStringView(U&& data, StringViewFlags flags = { }) noexcept : BasicStringView{flags, ArrayView(data)} {}
+ template::type>::value && !std::is_same::type, BasicStringView>::value && !std::is_same::type, std::nullptr_t>::value, decltype(ArrayView{std::declval()})>::type> constexpr /*implicit*/ BasicStringView(U&& data, StringViewFlags flags = {}) noexcept: BasicStringView{flags, ArrayView(data)} {}
/** @brief Construct a @ref StringView from a @ref MutableStringView */
template::value>::type> constexpr /*implicit*/ BasicStringView(BasicStringView mutable_) noexcept : _data{mutable_._data}, _sizePlusFlags{mutable_._sizePlusFlags} {}
@@ -162,7 +165,7 @@ namespace Death::Containers
* The @ref BasicStringView(std::nullptr_t) overload (which is a
* default constructor) is additionally @cpp constexpr @ce.
*/
- DEATH_CONSTEXPR14 /*implicit*/ BasicStringView(T* data, StringViewFlags extraFlags = { }) noexcept : BasicStringView {data, extraFlags, nullptr} {}
+ template::value && std::is_convertible::value>::type> /*implicit*/ BasicStringView(U data, StringViewFlags extraFlags = {}) noexcept : BasicStringView{data, extraFlags, nullptr} {}
/**
* @brief Construct a view on an external type / from an external representation
@@ -283,7 +286,7 @@ namespace Death::Containers
* is @cpp nullptr @ce, returns zero-sized @cpp nullptr @ce view.
*/
constexpr BasicStringView prefix(T* end) const {
- return end ? slice(_data, end) : BasicStringView {};
+ return end ? slice(_data, end) : BasicStringView{};
}
/**
@@ -761,13 +764,13 @@ namespace Death::Containers
// Called from BasicStringView(U&&, StringViewFlags), see its comment for details; arguments in a flipped order to avoid accidental
// ambiguity. The ArrayView type is a template to avoid having to include ArrayView.h.
- template::value>::type> constexpr explicit BasicStringView(StringViewFlags flags, ArrayView data) noexcept : BasicStringView { data.data(), data.size(), flags } {}
+ template::value>::type> constexpr explicit BasicStringView(StringViewFlags flags, ArrayView data) noexcept : BasicStringView{data.data(), data.size(), flags} {}
// Used by the char* constructor, delinlined because it calls into std::strlen()
explicit BasicStringView(T* data, StringViewFlags flags, std::nullptr_t) noexcept;
// Used by slice() to skip unneeded checks in the public constexpr constructor
- constexpr explicit BasicStringView(T* data, std::size_t sizePlusFlags, std::nullptr_t) noexcept : _data { data }, _sizePlusFlags { sizePlusFlags } {}
+ constexpr explicit BasicStringView(T* data, std::size_t sizePlusFlags, std::nullptr_t) noexcept : _data{data}, _sizePlusFlags{sizePlusFlags} {}
T* _data;
std::size_t _sizePlusFlags;
@@ -825,7 +828,7 @@ namespace Death::Containers
*/
constexpr StringView operator"" _s(const char* data, std::size_t size) {
// Using plain bit ops instead of EnumSet to speed up debug builds
- return StringView { data, size, StringViewFlags(std::size_t(StringViewFlags::Global) | std::size_t(StringViewFlags::NullTerminated)) };
+ return StringView{data, size, StringViewFlags(std::size_t(StringViewFlags::Global) | std::size_t(StringViewFlags::NullTerminated))};
}
}
@@ -966,7 +969,7 @@ namespace Death::Containers
template<> struct ArrayViewConverter> {
static ArrayView from(const BasicStringView& other);
};
- template struct ErasedArrayViewConverter> : ArrayViewConverter> { };
- template struct ErasedArrayViewConverter> : ArrayViewConverter> { };
+ template struct ErasedArrayViewConverter> : ArrayViewConverter> {};
+ template struct ErasedArrayViewConverter> : ArrayViewConverter> {};
}
}
\ No newline at end of file
diff --git a/Sources/Shared/IO/Stream.h b/Sources/Shared/IO/Stream.h
index 33ade27b..12ffcb5d 100644
--- a/Sources/Shared/IO/Stream.h
+++ b/Sources/Shared/IO/Stream.h
@@ -75,7 +75,7 @@ namespace Death::IO
template::value>::type* = nullptr>
DEATH_ALWAYS_INLINE T ReadValue()
{
- T buffer;
+ T buffer = { };
Read(&buffer, sizeof(T));
return buffer;
}
diff --git a/Sources/nCine/AppConfiguration.cpp b/Sources/nCine/AppConfiguration.cpp
index 7be38f51..42e37842 100644
--- a/Sources/nCine/AppConfiguration.cpp
+++ b/Sources/nCine/AppConfiguration.cpp
@@ -52,7 +52,6 @@ namespace nCine
glMajorVersion_(3),
glMinorVersion_(3),
#endif
- argc_(0),
argv_(nullptr)
{
#if defined(DEATH_TARGET_ANDROID)
@@ -81,21 +80,8 @@ namespace nCine
return dataPath_;
}
-#if defined(DEATH_TARGET_WINDOWS)
- const String AppConfiguration::argv(int index) const
- {
- if (index < argc_) {
- return Death::Utf8::FromUtf16(argv_[index]);
- }
- return { };
- }
-#else
const StringView AppConfiguration::argv(int index) const
{
- if (index < argc_) {
- return argv_[index];
- }
- return { };
+ return argv_[index];
}
-#endif
}
diff --git a/Sources/nCine/AppConfiguration.h b/Sources/nCine/AppConfiguration.h
index c9ee5cba..cd8c631c 100644
--- a/Sources/nCine/AppConfiguration.h
+++ b/Sources/nCine/AppConfiguration.h
@@ -9,12 +9,6 @@ using namespace Death::Containers;
namespace nCine
{
-#if defined(DEATH_TARGET_WINDOWS)
- typedef wchar_t* NativeArgument;
-#else
- typedef char* NativeArgument;
-#endif
-
/// The class storing initialization settings for an nCine application
class AppConfiguration
{
@@ -96,15 +90,11 @@ namespace nCine
}
/// \returns The number of arguments passed on the command-line
- inline int argc() const {
- return argc_;
+ inline std::size_t argc() const {
+ return argv_.size();
}
/// \returns The selected argument from the ones passed on the command-line
-#if defined(DEATH_TARGET_WINDOWS)
- const String argv(int index) const;
-#else
const StringView argv(int index) const;
-#endif
private:
// Pre-configured compile-time variables
@@ -113,8 +103,11 @@ namespace nCine
const unsigned int glMajorVersion_;
const unsigned int glMinorVersion_;
- int argc_;
- NativeArgument* argv_;
+#if defined(DEATH_TARGET_WINDOWS)
+ Array argv_;
+#else
+ Array argv_;
+#endif
String dataPath_;
friend class MainApplication;
diff --git a/Sources/nCine/Application.cpp b/Sources/nCine/Application.cpp
index 12e121da..3df36e99 100644
--- a/Sources/nCine/Application.cpp
+++ b/Sources/nCine/Application.cpp
@@ -1,5 +1,13 @@
#include "../Common.h"
+#if defined(DEATH_TARGET_WINDOWS)
+extern "C"
+{
+ _declspec(dllexport) unsigned long int NvOptimusEnablement = 0x00000001;
+ _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 0x00000001;
+};
+#endif
+
#if defined(DEATH_TARGET_WINDOWS) && !defined(CMAKE_BUILD)
# pragma comment(lib, "opengl32.lib")
# if defined(_M_X64)
@@ -33,13 +41,6 @@
# else
# error Unsupported architecture
# endif
-
-extern "C"
-{
- _declspec(dllexport) unsigned long int NvOptimusEnablement = 0x00000001;
- _declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
-};
-
#endif
#include "Application.h"
diff --git a/Sources/nCine/Base/Algorithms.cpp b/Sources/nCine/Base/Algorithms.cpp
index 10aefa5a..adbc3391 100644
--- a/Sources/nCine/Base/Algorithms.cpp
+++ b/Sources/nCine/Base/Algorithms.cpp
@@ -12,7 +12,7 @@
namespace nCine
{
- int copyStringFirst(char* dest, int destSize, const char* source, int count)
+ std::int32_t copyStringFirst(char* dest, std::int32_t destSize, const char* source, std::int32_t count)
{
if (destSize == 0) {
return 0;
@@ -44,24 +44,24 @@ namespace nCine
return n;
}
- int formatString(char* buffer, size_t maxLen, const char* format, ...)
+ std::int32_t formatString(char* buffer, std::size_t maxLen, const char* format, ...)
{
va_list args;
va_start(args, format);
#if defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_MINGW)
- const int writtenChars = vsnprintf_s(buffer, maxLen, maxLen - 1, format, args);
- const int result = (writtenChars > -1 ? writtenChars : maxLen - 1);
+ const std::int32_t writtenChars = vsnprintf_s(buffer, maxLen, maxLen - 1, format, args);
+ const std::int32_t result = (writtenChars > -1 ? writtenChars : maxLen - 1);
#else
- const int result = ::vsnprintf(buffer, maxLen, format, args);
+ const std::int32_t result = ::vsnprintf(buffer, maxLen, format, args);
#endif
va_end(args);
return result;
}
- inline unsigned CountDecimalDigit32(uint32_t n)
+ inline std::uint32_t CountDecimalDigit32(std::uint32_t n)
{
#if defined(DEATH_TARGET_MSVC) || defined(DEATH_TARGET_GCC)
- static constexpr uint32_t powers_of_10[] = {
+ static constexpr std::uint32_t powers_of_10[] = {
0,
10,
100,
@@ -77,9 +77,9 @@ namespace nCine
# if defined(DEATH_TARGET_MSVC)
unsigned long i = 0;
_BitScanReverse(&i, n | 1);
- uint32_t t = (i + 1) * 1233 >> 12;
+ std::uint32_t t = (i + 1) * 1233 >> 12;
# elif defined(DEATH_TARGET_GCC)
- uint32_t t = (32 - __builtin_clz(n | 1)) * 1233 >> 12;
+ std::uint32_t t = (32 - __builtin_clz(n | 1)) * 1233 >> 12;
# endif
return t - (n < powers_of_10[t]) + 1;
#else
@@ -97,10 +97,10 @@ namespace nCine
#endif
}
- inline unsigned CountDecimalDigit64(uint64_t n)
+ inline std::uint32_t CountDecimalDigit64(std::uint64_t n)
{
#if defined(DEATH_TARGET_MSVC) || defined(DEATH_TARGET_GCC)
- static constexpr uint64_t powers_of_10[] = {
+ static constexpr std::uint64_t powers_of_10[] = {
0,
10,
100,
@@ -124,20 +124,20 @@ namespace nCine
};
# if defined(DEATH_TARGET_GCC)
- uint32_t t = (64 - __builtin_clzll(n | 1)) * 1233 >> 12;
+ std::uint32_t t = (64 - __builtin_clzll(n | 1)) * 1233 >> 12;
# elif defined(DEATH_TARGET_32BIT)
unsigned long i = 0;
- uint64_t m = n | 1;
+ std::uint64_t m = n | 1;
if (_BitScanReverse(&i, m >> 32)) {
i += 32;
} else {
_BitScanReverse(&i, m & 0xFFFFFFFF);
}
- uint32_t t = (i + 1) * 1233 >> 12;
+ std::uint32_t t = (i + 1) * 1233 >> 12;
# else
unsigned long i = 0;
_BitScanReverse64(&i, n | 1);
- uint32_t t = (i + 1) * 1233 >> 12;
+ std::uint32_t t = (i + 1) * 1233 >> 12;
# endif
return t - (n < powers_of_10[t]) + 1;
#else
@@ -165,9 +165,9 @@ namespace nCine
#endif
}
- void u32tos(uint32_t value, char* buffer)
+ void u32tos(std::uint32_t value, char* buffer)
{
- unsigned digit = CountDecimalDigit32(value);
+ std::uint32_t digit = CountDecimalDigit32(value);
buffer += digit;
*buffer = '\0';
@@ -177,9 +177,9 @@ namespace nCine
} while (value > 0);
}
- void i32tos(int32_t value, char* buffer)
+ void i32tos(std::int32_t value, char* buffer)
{
- uint32_t u = static_cast(value);
+ std::uint32_t u = static_cast(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
@@ -187,9 +187,9 @@ namespace nCine
u32tos(u, buffer);
}
- void u64tos(uint64_t value, char* buffer)
+ void u64tos(std::uint64_t value, char* buffer)
{
- unsigned digit = CountDecimalDigit64(value);
+ std::uint32_t digit = CountDecimalDigit64(value);
buffer += digit;
*buffer = '\0';
@@ -199,9 +199,9 @@ namespace nCine
} while (value > 0);
}
- void i64tos(int64_t value, char* buffer)
+ void i64tos(std::int64_t value, char* buffer)
{
- uint64_t u = static_cast(value);
+ std::uint64_t u = static_cast(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
@@ -209,26 +209,26 @@ namespace nCine
u64tos(u, buffer);
}
- void ftos(double value, char* buffer, int bufferSize)
+ void ftos(double value, char* buffer, std::int32_t bufferSize)
{
#if defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_MINGW)
- int length = sprintf_s(buffer, bufferSize, "%f", value);
+ std::int32_t length = sprintf_s(buffer, bufferSize, "%f", value);
#else
- int length = snprintf(buffer, bufferSize, "%f", value);
+ std::int32_t length = snprintf(buffer, bufferSize, "%f", value);
#endif
if (length <= 0) {
buffer[0] = '\0';
return;
}
- int n = length - 1;
+ std::int32_t n = length - 1;
while (n >= 0 && buffer[n] == '0') {
n--;
}
n++;
bool separatorFound = false;
- for (int i = 0; i < n; i++) {
+ for (std::int32_t i = 0; i < n; i++) {
if (buffer[i] == '.' || buffer[i] == ',') {
separatorFound = true;
break;
@@ -244,42 +244,4 @@ namespace nCine
buffer[n] = '\0';
}
}
-
- uint64_t parseVersion(const Containers::StringView version)
- {
- auto parts = version.split('.');
- size_t partsCount = parts.size();
- if (partsCount == 0) {
- return 0;
- }
-
- uint64_t major = 0, minor = 0, patch = 0;
- size_t partSize;
- char stringBuffer[32];
-
- partSize = std::min(parts[0].size(), sizeof(stringBuffer) - 1);
- std::memcpy(stringBuffer, parts[0].data(), partSize);
- stringBuffer[partSize] = '\0';
- major = strtol(stringBuffer, nullptr, 10);
-
- if (partsCount >= 2 && !parts[1].empty()) {
- partSize = std::min(parts[1].size(), sizeof(stringBuffer) - 1);
- std::memcpy(stringBuffer, parts[1].data(), partSize);
- stringBuffer[partSize] = '\0';
- minor = strtol(stringBuffer, nullptr, 10);
- }
- if (partsCount >= 3 && !parts[2].empty()) {
- if (parts[2][0] == 'r') {
- // GIT Revision - always the latest
- patch = 0x0FFFFFFFull;
- } else {
- partSize = std::min(parts[2].size(), sizeof(stringBuffer) - 1);
- std::memcpy(stringBuffer, parts[2].data(), partSize);
- stringBuffer[partSize] = '\0';
- patch = strtol(stringBuffer, nullptr, 10);
- }
- }
-
- return (patch & 0xFFFFFFFFull) | ((minor & 0xFFFFull) << 32) | ((major & 0xFFFFull) << 48);
- }
}
diff --git a/Sources/nCine/Base/Algorithms.h b/Sources/nCine/Base/Algorithms.h
index ee007803..df9ab338 100644
--- a/Sources/nCine/Base/Algorithms.h
+++ b/Sources/nCine/Base/Algorithms.h
@@ -13,32 +13,32 @@ using namespace Death;
namespace nCine
{
// Traits
- template
+ template
struct isIntegral
{
static constexpr bool value = false;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
@@ -48,46 +48,46 @@ namespace nCine
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template <>
+ template<>
struct isIntegral
{
static constexpr bool value = true;
};
- template
+ template
inline bool IsLess(const T &a, const T &b)
{
return a < b;
}
- template
+ template
inline bool IsNotLess(const T &a, const T &b)
{
return !(a < b);
}
/// Returns true if the range is sorted into ascending order
- template
+ template
inline bool isSorted(Iterator first, const Iterator last)
{
if (first == last)
@@ -104,7 +104,7 @@ namespace nCine
}
/// Returns true if the range is sorted, using a custom comparison
- template
+ template
inline bool isSorted(Iterator first, const Iterator last, Compare comp)
{
if (first == last)
@@ -121,7 +121,7 @@ namespace nCine
}
/// Returns an iterator to the first element in the range which does not follow an ascending order, or last if sorted
- template
+ template
inline const Iterator isSortedUntil(Iterator first, const Iterator last)
{
if (first == last)
@@ -138,7 +138,7 @@ namespace nCine
}
/// Returns an iterator to the first element in the range which does not follow the custom comparison, or last if sorted
- template
+ template
inline const Iterator isSortedUntil(Iterator first, const Iterator last, Compare comp)
{
if (first == last)
@@ -155,7 +155,7 @@ namespace nCine
}
/// Partition function for quicksort with iterators
- template
+ template
inline Iterator partition(Iterator first, Iterator last, Compare comp)
{
Iterator pivot = last;
@@ -183,10 +183,10 @@ namespace nCine
namespace
{
/// Quicksort implementation with random access iterators and custom compare function
- template
+ template
inline void quicksort(Iterator first, Iterator last, RandomAccessIteratorTag, Compare comp)
{
- int size = distance(first, last, RandomAccessIteratorTag());
+ std::int32_t size = distance(first, last, RandomAccessIteratorTag());
if (size > 1) {
Iterator p = prev(last);
std::swap(*next(first, size / 2), *p);
@@ -198,7 +198,7 @@ namespace nCine
}
/// Quicksort implementation with bidirectional iterators and custom compare function
- template
+ template
inline void quicksort(Iterator first, Iterator last, BidirectionalIteratorTag, Compare comp)
{
if (first != last) {
@@ -214,28 +214,28 @@ namespace nCine
}
/// Quicksort implementation with iterators and custom compare function
- template
+ template
inline void quicksort(Iterator first, Iterator last, Compare comp)
{
quicksort(first, last, IteratorTraits::IteratorCategory(), comp);
}
/// Quicksort implementation with iterators, ascending order
- template
+ template
inline void quicksort(Iterator first, Iterator last)
{
quicksort(first, last, IteratorTraits::IteratorCategory(), IsLess::ValueType>);
}
/// Quicksort implementation with iterators, descending order
- template
+ template
inline void quicksortDesc(Iterator first, Iterator last)
{
quicksort(first, last, IteratorTraits::IteratorCategory(), IsNotLess::ValueType>);
}
/// A container for functions to destruct objects and arrays of objects
- template
+ template
struct destructHelpers
{
template
@@ -245,16 +245,16 @@ namespace nCine
}
template
- inline static void destructArray(T* ptr, unsigned int numElements)
+ inline static void destructArray(T* ptr, std::uint32_t numElements)
{
- for (unsigned int i = 0; i < numElements; i++)
+ for (std::uint32_t i = 0; i < numElements; i++)
ptr[numElements - i - 1].~T();
}
};
namespace detail
{
- template
+ template
struct typeIdentity
{
using type = T;
@@ -266,18 +266,18 @@ namespace nCine
auto tryAddRValueReference(...)->typeIdentity;
}
- template
+ template
struct addRValueReference : decltype(detail::tryAddRValueReference(0)) {};
- template
+ template
typename addRValueReference::type declVal();
/// Specialization for trivially destructible types
- template
+ template
struct isDestructible
{
static constexpr bool value = false;
};
- template
+ template
struct isDestructible().~T())>
{
static constexpr bool value = (true && !__is_union(T));
@@ -285,47 +285,47 @@ namespace nCine
// Use `__has_trivial_destructor()` only on GCC
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
- template
+ template
struct hasTrivialDestructor
{
static constexpr bool value = __has_trivial_destructor(T);
};
- template
+ template
struct isTriviallyDestructible
{
static constexpr bool value = isDestructible::value && hasTrivialDestructor::value;
};
#else
- template
+ template
struct isTriviallyDestructible
{
static constexpr bool value = __is_trivially_destructible(T);
};
#endif
- template <>
+ template<>
struct destructHelpers
{
- template
+ template
inline static void destructObject(T* ptr)
{
}
- template
+ template
inline static void destructArray(T* ptr, unsigned int numElements)
{
}
};
- template
+ template
void destructObject(T* ptr)
{
destructHelpers::value>::destructObject(ptr);
}
- template
- void destructArray(T* ptr, unsigned int numElements)
+ template
+ void destructArray(T* ptr, std::uint32_t numElements)
{
destructHelpers::value>::destructArray(ptr, numElements);
}
@@ -335,9 +335,9 @@ namespace nCine
return a + ratio * (b - a);
}
- inline int lerp(int a, int b, float ratio)
+ inline std::int32_t lerp(std::int32_t a, std::int32_t b, float ratio)
{
- return (int)std::round(a + ratio * (float)(b - a));
+ return (std::int32_t)std::round(a + ratio * (float)(b - a));
}
inline void lowercaseInPlace(const Containers::MutableStringView string)
@@ -363,20 +363,80 @@ namespace nCine
}
}
- int copyStringFirst(char* dest, int destSize, const char* source, int count = -1);
+ std::int32_t copyStringFirst(char* dest, std::int32_t destSize, const char* source, std::int32_t count = -1);
+
+ template
+ inline std::int32_t copyStringFirst(char(&dest)[size], const char* source, std::int32_t count = -1) {
+ return copyStringFirst(dest, size, source, count);
+ }
+
+ int formatString(char* buffer, std::size_t maxLen, const char* format, ...);
- template
- inline int copyStringFirst(char(&dest)[N], const char* source, int count = -1) {
- return copyStringFirst(dest, N, source, count);
+ void u32tos(std::uint32_t value, char* buffer);
+ void i32tos(std::int32_t value, char* buffer);
+ void u64tos(std::uint64_t value, char* buffer);
+ void i64tos(std::int64_t value, char* buffer);
+ void ftos(double value, char* buffer, std::int32_t bufferSize);
+
+ constexpr bool isDigit(char c)
+ {
+ return (c >= '0' && c <= '9');
+ }
+
+ constexpr std::uint32_t stou32(const char* str, std::size_t length)
+ {
+ std::uint32_t n = 0;
+ while (length > 0) {
+ if (!isDigit(*str)) {
+ break;
+ }
+ n *= 10;
+ n += (*str++ - '0');
+ length--;
+ }
+ return n;
+ }
+
+ constexpr std::uint64_t stou64(const char* str, std::size_t length)
+ {
+ std::uint64_t n = 0;
+ while (length > 0) {
+ if (!isDigit(*str)) {
+ break;
+ }
+ n *= 10;
+ n += (*str++ - '0');
+ length--;
+ }
+ return n;
}
- int formatString(char* buffer, size_t maxLen, const char* format, ...);
+ constexpr std::uint64_t parseVersion(const Containers::StringView& version)
+ {
+ std::size_t versionLength = version.size();
+ std::size_t dotIndices[3] { };
+ std::size_t foundCount = 0;
- void u32tos(uint32_t value, char* buffer);
- void i32tos(int32_t value, char* buffer);
- void u64tos(uint64_t value, char* buffer);
- void i64tos(int64_t value, char* buffer);
- void ftos(double value, char* buffer, int bufferSize);
+ for (std::size_t i = 0; i < versionLength; i++) {
+ if (version[i] == '.') {
+ dotIndices[foundCount++] = i;
+ if (foundCount >= countof(dotIndices) - 1) {
+ // Save only indices of the first 2 dots and keep the last index for string length
+ break;
+ }
+ }
+ }
+
+ dotIndices[foundCount] = versionLength;
- uint64_t parseVersion(const Containers::StringView version);
+ std::uint64_t major = stou32(&version[0], dotIndices[0]);
+ std::uint64_t minor = (foundCount >= 1 ? stou32(&version[dotIndices[0] + 1], dotIndices[1] - dotIndices[0] - 1) : 0);
+ std::uint64_t patch = (foundCount >= 2
+ ? (version[dotIndices[1] + 1] != 'r'
+ ? stou32(&version[dotIndices[1] + 1], dotIndices[2] - dotIndices[1] - 1)
+ : 0x0FFFFFFFULL) // GIT Revision - use special value, so it's always the latest (without upper 4 bits)
+ : 0);
+
+ return (patch & 0xFFFFFFFFULL) | ((minor & 0xFFFFULL) << 32) | ((major & 0xFFFFULL) << 48);
+ }
}
diff --git a/Sources/nCine/Graphics/BinaryShaderCache.cpp b/Sources/nCine/Graphics/BinaryShaderCache.cpp
index e22b7859..2bf2d10c 100644
--- a/Sources/nCine/Graphics/BinaryShaderCache.cpp
+++ b/Sources/nCine/Graphics/BinaryShaderCache.cpp
@@ -179,6 +179,10 @@ namespace nCine
void BinaryShaderCache::prune()
{
+ if (path_.empty()) {
+ return;
+ }
+
fs::Directory dir(path_);
while (const StringView shaderPath = dir.GetNext()) {
if (fs::GetExtension(shaderPath) != "shader"_s) {
@@ -186,17 +190,20 @@ namespace nCine
}
StringView filename = fs::GetFileNameWithoutExtension(shaderPath);
+
+ bool shouldRemove;
if (filename.size() != 32) {
- fs::RemoveFile(shaderPath);
- continue;
+ shouldRemove = true;
+ } else {
+ char componentString[17];
+ std::memcpy(componentString, &filename[16], 16);
+ componentString[16] = '\0';
+
+ std::uint64_t platformHash = strtoull(componentString, nullptr, 16);
+ shouldRemove = (platformHash != platformHash_);
}
- char componentString[17];
- std::memcpy(componentString, &filename[16], 16);
- componentString[16] = '\0';
-
- std::uint64_t platformHash = strtoull(componentString, nullptr, 16);
- if (platformHash != platformHash_) {
+ if (shouldRemove) {
fs::RemoveFile(shaderPath);
}
}
@@ -204,8 +211,16 @@ namespace nCine
void BinaryShaderCache::clear()
{
+ if (path_.empty()) {
+ return;
+ }
+
fs::Directory dir(path_);
while (const StringView shaderPath = dir.GetNext()) {
+ if (fs::GetExtension(shaderPath) != "shader"_s) {
+ continue;
+ }
+
fs::RemoveFile(shaderPath);
}
}
diff --git a/Sources/nCine/Input/JoyMapping.cpp b/Sources/nCine/Input/JoyMapping.cpp
index 80c8bb4d..8b3af82b 100644
--- a/Sources/nCine/Input/JoyMapping.cpp
+++ b/Sources/nCine/Input/JoyMapping.cpp
@@ -4,7 +4,6 @@
#include "../Primitives/Vector2.h"
#include // for memcpy()
-#include // for strtoul()
#include
#include
diff --git a/Sources/nCine/MainApplication.cpp b/Sources/nCine/MainApplication.cpp
index 8ac9915a..29fd4c55 100644
--- a/Sources/nCine/MainApplication.cpp
+++ b/Sources/nCine/MainApplication.cpp
@@ -17,13 +17,15 @@
#if defined(DEATH_TARGET_EMSCRIPTEN)
# include
-#endif
-#if defined(DEATH_TARGET_SWITCH)
+#elif defined(DEATH_TARGET_SWITCH)
# include
+#elif defined(DEATH_TARGET_WINDOWS)
+# include
#endif
#include "tracy.h"
+using namespace Death;
using namespace Death::Containers::Literals;
using namespace Death::IO;
@@ -44,6 +46,7 @@ extern "C" IMAGE_DOS_HEADER __ImageBase;
bool __showLogConsole;
bool __hasVirtualTerminal;
+Array __consolePrompt;
static bool CreateLogConsole(const StringView& title)
{
@@ -68,6 +71,51 @@ static bool CreateLogConsole(const StringView& title)
::setvbuf(stdin, NULL, _IONBF, 0);
}
+ // Try to get command prompt to be able to reprint it when the game exits
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (::GetConsoleScreenBufferInfo(consoleHandleOut, &csbi)) {
+ DWORD dwConsoleColumnWidth = (DWORD)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
+ SHORT xEnd = csbi.dwCursorPosition.X;
+ SHORT yEnd = csbi.dwCursorPosition.Y;
+ if (xEnd != 0 || yEnd != 0) {
+ DWORD dwNumberOfChars;
+ SHORT yBegin = yEnd;
+ if (dwConsoleColumnWidth > 16) {
+ Array tmp(NoInit, dwConsoleColumnWidth);
+ while (yBegin > 0) {
+ COORD dwReadCoord = { 0, yBegin };
+ if (!::ReadConsoleOutputCharacter(consoleHandleOut, tmp.data(), dwConsoleColumnWidth, dwReadCoord, &dwNumberOfChars)) {
+ break;
+ }
+
+ for (DWORD i = dwNumberOfChars - 8; i < dwNumberOfChars; i++) {
+ wchar_t wchar = tmp[i];
+ if (wchar != L' ') {
+ yBegin--;
+ continue;
+ }
+ }
+
+ if (yBegin < yEnd) {
+ yBegin++;
+ }
+ break;
+ }
+ }
+
+ DWORD promptLength = (yEnd - yBegin) * dwConsoleColumnWidth + xEnd;
+ __consolePrompt = Array(NoInit, promptLength);
+ COORD dwPromptCoord = { 0, yEnd };
+ if (::ReadConsoleOutputCharacter(consoleHandleOut, __consolePrompt.data(), promptLength, dwPromptCoord, &dwNumberOfChars)) {
+ if (::SetConsoleCursorPosition(consoleHandleOut, dwPromptCoord)) {
+ ::FillConsoleOutputCharacter(consoleHandleOut, L' ', promptLength, dwPromptCoord, &dwNumberOfChars);
+ }
+ } else {
+ __consolePrompt = {};
+ }
+ }
+ }
+
return true;
} else if (::AllocConsole()) {
::freopen_s(&fDummy, "CONOUT$", "w", stdout);
@@ -98,21 +146,20 @@ static bool CreateLogConsole(const StringView& title)
static void DestroyLogConsole()
{
- // The "Enter" key is only sent if the console window is in focus
- if (::GetConsoleWindow() == ::GetForegroundWindow()) {
- // Send the "Enter" key to the console to release the command prompt
- INPUT ip;
- ip.type = INPUT_KEYBOARD;
- ip.ki.wScan = 0;
- ip.ki.time = 0;
- ip.ki.dwExtraInfo = 0;
-
- ip.ki.wVk = 0x0D; // virtual-key code for the "Enter" key
- ip.ki.dwFlags = 0; // 0 for key press
- ::SendInput(1, &ip, sizeof(INPUT));
-
- ip.ki.dwFlags = KEYEVENTF_KEYUP; // `KEYEVENTF_KEYUP` for key release
- ::SendInput(1, &ip, sizeof(INPUT));
+ if (!__consolePrompt.empty()) {
+ HANDLE consoleHandleOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ if (consoleHandleOut != INVALID_HANDLE_VALUE) {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (::GetConsoleScreenBufferInfo(consoleHandleOut, &csbi)) {
+ DWORD xEnd = csbi.dwCursorPosition.X;
+ DWORD yEnd = csbi.dwCursorPosition.Y;
+ if (xEnd != 0 || yEnd != 0) {
+ DWORD dwNumberOfCharsWritten;
+ ::WriteConsole(consoleHandleOut, L"\r\n", countof(L"\r\n") - 1, &dwNumberOfCharsWritten, NULL);
+ ::WriteConsole(consoleHandleOut, __consolePrompt.data(), (DWORD)__consolePrompt.size(), &dwNumberOfCharsWritten, NULL);
+ }
+ }
+ }
}
::FreeConsole();
@@ -120,18 +167,14 @@ static void DestroyLogConsole()
static bool EnableVirtualTerminalProcessing()
{
- HANDLE hOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
- if (hOut == INVALID_HANDLE_VALUE) {
+ HANDLE consoleHandleOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
+ if (consoleHandleOut == INVALID_HANDLE_VALUE) {
return false;
}
+
DWORD dwMode = 0;
- if (!::GetConsoleMode(hOut, &dwMode)) {
- return false;
- }
- if (!::SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
- return false;
- }
- return true;
+ return (::GetConsoleMode(consoleHandleOut, &dwMode) &&
+ ::SetConsoleMode(consoleHandleOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING));
}
#elif defined(DEATH_TRACE) && (defined(DEATH_TARGET_APPLE) || defined(DEATH_TARGET_EMSCRIPTEN) || defined(DEATH_TARGET_UNIX))
@@ -228,7 +271,8 @@ namespace nCine
#if defined(DEATH_TRACE)
# if defined(DEATH_TARGET_APPLE)
- __hasVirtualTerminal = isatty(1);
+ // Xcode's console reports that it is a TTY, but it doesn't support colors, TERM is not defined in this case
+ __hasVirtualTerminal = isatty(1) && ::getenv("TERM");
# elif defined(DEATH_TARGET_EMSCRIPTEN)
char* userAgent = (char*)EM_ASM_PTR({
return (typeof navigator !== 'undefined' && navigator !== null &&
@@ -244,14 +288,14 @@ namespace nCine
}
# elif defined(DEATH_TARGET_WINDOWS) && !defined(DEATH_TARGET_WINDOWS_RT)
__showLogConsole = false;
- for (int i = 0; i < argc; i++) {
+ for (std::int32_t i = 0; i < argc; i++) {
if (wcscmp(argv[i], L"/log") == 0) {
__showLogConsole = true;
break;
}
}
if (__showLogConsole) {
- CreateLogConsole(NCINE_APP_NAME " Console");
+ CreateLogConsole(NCINE_APP_NAME " [Console]");
__hasVirtualTerminal = EnableVirtualTerminalProcessing();
} else {
__hasVirtualTerminal = false;
@@ -259,17 +303,24 @@ namespace nCine
# elif defined(DEATH_TARGET_UNIX)
::setvbuf(stdout, nullptr, _IONBF, 0);
::setvbuf(stderr, nullptr, _IONBF, 0);
-
- // Xcode's console reports that it is a TTY, but it doesn't support colors, but TERM is not defined
- __hasVirtualTerminal = isatty(1) && std::getenv("TERM");
+ __hasVirtualTerminal = isatty(1);
# endif
#endif
appEventHandler_ = createAppEventHandler();
// Only `OnPreInit()` can modify the application configuration
- appCfg_.argc_ = argc;
- appCfg_.argv_ = argv;
+#if defined(DEATH_TARGET_WINDOWS)
+ appCfg_.argv_ = Array(argc - 1);
+ for (std::int32_t i = 1; i < argc; i++) {
+ appCfg_.argv_[i - 1] = Utf8::FromUtf16(argv[i]);
+ }
+#else
+ appCfg_.argv_ = Array(argc - 1);
+ for (std::int32_t i = 1; i < argc; i++) {
+ appCfg_.argv_[i - 1] = argv[i];
+ }
+#endif
appEventHandler_->OnPreInit(appCfg_);
LOGI("IAppEventHandler::OnPreInit() invoked");
diff --git a/Sources/nCine/MainApplication.h b/Sources/nCine/MainApplication.h
index 78ec3201..1993d101 100644
--- a/Sources/nCine/MainApplication.h
+++ b/Sources/nCine/MainApplication.h
@@ -4,6 +4,12 @@
namespace nCine
{
+#if defined(DEATH_TARGET_WINDOWS)
+ typedef wchar_t* NativeArgument;
+#else
+ typedef char* NativeArgument;
+#endif
+
#if defined(WITH_QT5)
class Qt5Widget;
#endif
diff --git a/cmake/ncine_headers.cmake b/cmake/ncine_headers.cmake
index fa7c4ee9..e44b701c 100644
--- a/cmake/ncine_headers.cmake
+++ b/cmake/ncine_headers.cmake
@@ -331,6 +331,7 @@ list(APPEND HEADERS
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/InGameMenu.h
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/InputDiagnosticsSection.h
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/LanguageSelectSection.h
+ ${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/LoadingSection.h
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/MainMenu.h
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/OptionsSection.h
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/PauseSection.h
diff --git a/cmake/ncine_sources.cmake b/cmake/ncine_sources.cmake
index 200ac429..e6fcbfbe 100644
--- a/cmake/ncine_sources.cmake
+++ b/cmake/ncine_sources.cmake
@@ -256,6 +256,7 @@ list(APPEND SOURCES
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/InGameMenu.cpp
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/InputDiagnosticsSection.cpp
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/LanguageSelectSection.cpp
+ ${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/LoadingSection.cpp
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/MainMenu.cpp
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/OptionsSection.cpp
${NCINE_SOURCE_DIR}/Jazz2/UI/Menu/PauseSection.cpp