From 29ffaddb3eddc28ee19298834e70d28fa9505a15 Mon Sep 17 00:00:00 2001 From: Julian Benda Date: Wed, 15 Apr 2026 14:05:09 +0200 Subject: [PATCH] fix(UE): use UTF8 instead of ANSI encoding switch ANSI_TO_TCHAR and TCHAR_TO_ANSI to the UTF8 equivalents to support UTF8 in UE --- inkcpp/runner_impl.cpp | 2 +- .../Source/inkcpp/Private/InkChoice.cpp | 23 +-- .../inkcpp/Source/inkcpp/Private/InkList.cpp | 12 +- .../Source/inkcpp/Private/InkRuntime.cpp | 178 ++++++++++-------- .../Source/inkcpp/Private/InkThread.cpp | 6 +- .../inkcpp_editor/Private/InkAssetFactory.cpp | 6 +- 6 files changed, 120 insertions(+), 107 deletions(-) diff --git a/inkcpp/runner_impl.cpp b/inkcpp/runner_impl.cpp index b39f91f5..01cccd2a 100644 --- a/inkcpp/runner_impl.cpp +++ b/inkcpp/runner_impl.cpp @@ -538,7 +538,7 @@ runner_impl::line_type runner_impl::getline() # ifdef INK_ENABLE_STL line_type result{_output.get()}; # elif defined(INK_ENABLE_UNREAL) - line_type result{ANSI_TO_TCHAR(_output.get_alloc(_globals->strings(), _globals->lists()))}; + line_type result{UTF8_TO_TCHAR(_output.get_alloc(_globals->strings(), _globals->lists()))}; # else # error unsupported constraints for getline # endif diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp index 2b3cda39..365d2b4f 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkChoice.cpp @@ -8,32 +8,21 @@ #include "ink/choice.h" -FString UInkChoice::GetText() const -{ - return data->text(); -} +FString UInkChoice::GetText() const { return data->text(); } -UInkChoice::UInkChoice() { - tags = NewObject(); -} +UInkChoice::UInkChoice() { tags = NewObject(); } -int UInkChoice::GetIndex() const -{ - return data->index(); -} +int UInkChoice::GetIndex() const { return data->index(); } -const UTagList* UInkChoice::GetTags() const -{ - return tags; -} +const UTagList* UInkChoice::GetTags() const { return tags; } void UInkChoice::Initialize(const ink::runtime::choice* c) { data = c; if (c->has_tags()) { TArray fstring_tags{}; - for(unsigned i = 0; i < c->num_tags(); ++i) { - fstring_tags.Add(FString(ANSI_TO_TCHAR(c->get_tag(i)))); + for (unsigned i = 0; i < c->num_tags(); ++i) { + fstring_tags.Add(FString(UTF8_TO_TCHAR(c->get_tag(i)))); } tags->Initialize(fstring_tags); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp index aedb948b..17ec5680 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkList.cpp @@ -12,7 +12,7 @@ bool UInkList::ContainsFlag(const FString& flag_name) const { - return list_data->contains(TCHAR_TO_ANSI(*flag_name)); + return list_data->contains(TCHAR_TO_UTF8(*flag_name)); } bool UInkList::ContainsEnum(const UEnum* Enum, const uint8& value) const @@ -25,7 +25,7 @@ bool UInkList::ContainsEnum(const UEnum* Enum, const uint8& value) const return false; } - return list_data->contains(TCHAR_TO_ANSI(*Enum->GetDisplayNameTextByValue(value).ToString())); + return list_data->contains(TCHAR_TO_UTF8(*Enum->GetDisplayNameTextByValue(value).ToString())); } TArray UInkList::ElementsOf(const UEnum* Enum) const @@ -38,10 +38,10 @@ TArray UInkList::ElementsOf(const UEnum* Enum) const FString enumName = Enum->GetFName().ToString(); int num = Enum->NumEnums(); - std::string str(TCHAR_TO_ANSI(*enumName)); + std::string str(TCHAR_TO_UTF8(*enumName)); for (auto itr = list_data->begin(str.c_str()); itr != list_data->end(); ++itr) { bool hit = false; - const FString flag(ANSI_TO_TCHAR((*itr).flag_name)); + const FString flag(UTF8_TO_TCHAR((*itr).flag_name)); for (int i = 0; i < num; ++i) { FString enumStr = Enum->GetDisplayNameTextByIndex(i).ToString(); if (enumStr.EndsWith(flag)) { @@ -65,7 +65,7 @@ TArray UInkList::ElementsOfAsString(const UEnum* Enum) const TArray ret; FString EnumName = Enum->GetFName().ToString(); - for (auto itr = list_data->begin(TCHAR_TO_ANSI(*EnumName)); itr != list_data->end(); ++itr) { + for (auto itr = list_data->begin(TCHAR_TO_UTF8(*EnumName)); itr != list_data->end(); ++itr) { ret.Add(FString((*itr).flag_name)); } return ret; @@ -85,5 +85,5 @@ TArray UInkList::Elements() const bool UInkList::ContainsList(const FString& name) const { - return list_data->begin(TCHAR_TO_ANSI(*name)) != list_data->end(); + return list_data->begin(TCHAR_TO_UTF8(*name)) != list_data->end(); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp index 1c2ea10c..b549a643 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkRuntime.cpp @@ -20,18 +20,25 @@ #include "system.h" #include "types.h" -namespace ink { using value = runtime::value; } +namespace ink +{ +using value = runtime::value; +} // namespace ink // Sets default values -AInkRuntime::AInkRuntime() : mpRuntime(nullptr) +AInkRuntime::AInkRuntime() + : mpRuntime(nullptr) { - // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you + // don't need it. PrimaryActorTick.bCanEverTick = true; } AInkRuntime::~AInkRuntime() { - if(mSnapshot) { delete mpSnapshot; } + if (mSnapshot) { + delete mpSnapshot; + } mSnapshot.Reset(); } @@ -39,10 +46,12 @@ AInkRuntime::~AInkRuntime() void AInkRuntime::BeginPlay() { // Create the CPU for the story - if (InkAsset != nullptr) - { + if (InkAsset != nullptr) { // TODO: Better error handling? What if load fails. - mpRuntime = ink::runtime::story::from_binary(reinterpret_cast(InkAsset->CompiledStory.GetData()), InkAsset->CompiledStory.Num(), false); + mpRuntime = ink::runtime::story::from_binary( + reinterpret_cast(InkAsset->CompiledStory.GetData()), + InkAsset->CompiledStory.Num(), false + ); UE_LOG(InkCpp, Display, TEXT("Loaded Ink asset")); // create globals @@ -53,12 +62,10 @@ void AInkRuntime::BeginPlay() } // initialize globals mpRuntime->new_runner(mpGlobals); - } - else - { + } else { UE_LOG(InkCpp, Warning, TEXT("No story asset assigned.")); } - + Super::BeginPlay(); } @@ -71,43 +78,39 @@ void AInkRuntime::Tick(float DeltaTime) if (mThreads.Num() == 0) return; - // Special: If we're in exclusive mode, only execute the thread at the + // Special: If we're in exclusive mode, only execute the thread at the // top of the exclusive stack - while (mExclusiveStack.Num() > 0) - { + while (mExclusiveStack.Num() > 0) { // Make sure top thread is active UInkThread* top = mExclusiveStack.Top(); // If it can't execute, we're not doing anything else - if (!top->CanExecute()) + if (! top->CanExecute()) return; // Execute it - if (top->Execute()) - { + if (top->Execute()) { mExclusiveStack.Remove(top); mThreads.Remove(top); // execute next exclusive thread continue; } - + // Nothing more to do return; } // Execute other available threads - for (auto iter = mThreads.CreateIterator(); iter; iter++) - { + for (auto iter = mThreads.CreateIterator(); iter; iter++) { UInkThread* pNextThread = *iter; // Ignore threads that aren't eligible for execution - if (!pNextThread->CanExecute()) + if (! pNextThread->CanExecute()) continue; // Execute - if (pNextThread->Execute()) - { + if (pNextThread->Execute()) { // If the thread has finished, destroy it iter.RemoveCurrent(); mExclusiveStack.Remove(pNextThread); @@ -117,25 +120,24 @@ void AInkRuntime::Tick(float DeltaTime) void AInkRuntime::HandleTagFunction(UInkThread* Caller, const TArray& Params) { - // Look for method and execute with parameters - FGlobalTagFunctionMulticastDelegate* function = mGlobalTagFunctions.Find(FName(*Params[0])); - if (function != nullptr) - { - function->Broadcast(Caller, Params); - } + // Look for method and execute with parameters + FGlobalTagFunctionMulticastDelegate* function = mGlobalTagFunctions.Find(FName(*Params[0])); + if (function != nullptr) { + function->Broadcast(Caller, Params); + } } -void AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate & function) +void AInkRuntime::RegisterTagFunction(FName functionName, const FTagFunctionDelegate& function) { // Register tag function mGlobalTagFunctions.FindOrAdd(functionName).Add(function); } -UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, bool startImmediately) +UInkThread* + AInkRuntime::Start(TSubclassOf type, FString path, bool startImmediately) { UE_LOG(InkCpp, Display, TEXT("Start")); - if (mpRuntime == nullptr || type == nullptr) - { + if (mpRuntime == nullptr || type == nullptr) { UE_LOG(InkCpp, Warning, TEXT("failed to start")); return nullptr; } @@ -150,34 +152,43 @@ UInkThread* AInkRuntime::Start(TSubclassOf type, FString path, FInkSnapshot AInkRuntime::Snapshot() { ink::runtime::snapshot* inkSnapshot = mpGlobals->create_snapshot(); - FInkSnapshot snapshot(reinterpret_cast(inkSnapshot->get_data()), inkSnapshot->get_data_len()); + FInkSnapshot snapshot( + reinterpret_cast(inkSnapshot->get_data()), inkSnapshot->get_data_len() + ); delete inkSnapshot; return snapshot; } -void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) { - mSnapshot = snapshot; - mpSnapshot = ink::runtime::snapshot::from_binary(reinterpret_cast(mSnapshot->data.GetData()), mSnapshot->data.Num(), false); +void AInkRuntime::LoadSnapshot(const FInkSnapshot& snapshot) +{ + mSnapshot = snapshot; + mpSnapshot = ink::runtime::snapshot::from_binary( + reinterpret_cast(mSnapshot->data.GetData()), mSnapshot->data.Num(), false + ); mpGlobals = mpRuntime->new_globals_from_snapshot(*mpSnapshot); } -UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) +UInkThread* + AInkRuntime::StartExisting(UInkThread* thread, FString path, bool startImmediately /*= true*/) { - if (mpRuntime == nullptr) - { + if (mpRuntime == nullptr) { UE_LOG(InkCpp, Warning, TEXT("Failed to start existing")); return nullptr; } - + // remove handle if it still exists mThreads.Remove(thread); mExclusiveStack.Remove(thread); // Initialize thread with new runner ink::runtime::runner runner; - if (mSnapshot && path.IsEmpty()) { + if (mSnapshot && path.IsEmpty()) { if (mpSnapshot->num_runners() == mThreads.Num()) { - UE_LOG(InkCpp, Warning, TEXT("Already created all Threads from Snapshot!, will not create more. You can Still create new Threads with entering the starting Path.")); + UE_LOG( + InkCpp, Warning, + TEXT("Already created all Threads from Snapshot!, will not create more. You can Still " + "create new Threads with entering the starting Path.") + ); return nullptr; } runner = mpRuntime->new_runner_from_snapshot(*mpSnapshot, mpGlobals, mThreads.Num()); @@ -187,17 +198,15 @@ UInkThread* AInkRuntime::StartExisting(UInkThread* thread, FString path, bool st thread->Initialize(path, this, runner); // If we're not starting immediately, just queue - if (!startImmediately || - // Even if we want to start immediately, don't if there's an exclusive thread and it's not us - (mExclusiveStack.Num() > 0 && mExclusiveStack.Top() != thread)) - { + if (! startImmediately || + // Even if we want to start immediately, don't if there's an exclusive thread and it's not us + (mExclusiveStack.Num() > 0 && mExclusiveStack.Top() != thread)) { mThreads.Add(thread); return thread; } // Execute the newly created thread - if (!thread->Execute()) - { + if (! thread->Execute()) { // If it hasn't finished immediately, add it to the threads list mThreads.Add(thread); } @@ -221,45 +230,60 @@ void AInkRuntime::PopExclusiveThread(UInkThread* Thread) mExclusiveStack.Remove(Thread); } -FInkVar AInkRuntime::GetGlobalVariable(const FString& name) { - ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); - if(var) { return FInkVar(*var); } - else { UE_LOG(InkCpp, Warning, TEXT("Failed to find global variable with name: %s"), *name); } +FInkVar AInkRuntime::GetGlobalVariable(const FString& name) +{ + ink::optional var = mpGlobals->get(TCHAR_TO_UTF8(*name)); + if (var) { + return FInkVar(*var); + } else { + UE_LOG(InkCpp, Warning, TEXT("Failed to find global variable with name: %s"), *name); + } return FInkVar{}; } -void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) { - bool success = mpGlobals->set(TCHAR_TO_ANSI(*name), value.to_value()); - if(!success) { +void AInkRuntime::SetGlobalVariable(const FString& name, const FInkVar& value) +{ + bool success = mpGlobals->set(TCHAR_TO_UTF8(*name), value.to_value()); + if (! success) { UE_LOG(InkCpp, Warning, TEXT("Filed to set global variable with name: %s"), *name); - ink::optional var = mpGlobals->get(TCHAR_TO_ANSI(*name)); - if(var) { - UE_LOG(InkCpp, Warning, - TEXT("Reason: wrong type!, got: %i, expected: %i"), - static_cast(value.to_value().type), - static_cast(var->type) ); + ink::optional var = mpGlobals->get(TCHAR_TO_UTF8(*name)); + if (var) { + UE_LOG( + InkCpp, Warning, TEXT("Reason: wrong type!, got: %i, expected: %i"), + static_cast(value.to_value().type), static_cast(var->type) + ); } else { - UE_LOG(InkCpp, Warning, TEXT("Reason: no variable with this name exists! '%s'"), - *name); + UE_LOG(InkCpp, Warning, TEXT("Reason: no variable with this name exists! '%s'"), *name); } } } -void AInkRuntime::ObserverVariable(const FString& name, const FVariableCallbackDelegate& callback) { - mpGlobals->observe(TCHAR_TO_ANSI(*name), [callback](){callback.Execute();}); + +void AInkRuntime::ObserverVariable(const FString& name, const FVariableCallbackDelegate& callback) +{ + mpGlobals->observe(TCHAR_TO_UTF8(*name), [callback]() { callback.Execute(); }); } -void AInkRuntime::ObserverVariableEvent(const FString& name, const FVariableCallbackDelegateNewValue& callback) { - mpGlobals->observe(TCHAR_TO_ANSI(*name), [callback](ink::runtime::value x){callback.Execute(FInkVar(x));}); +void AInkRuntime::ObserverVariableEvent( + const FString& name, const FVariableCallbackDelegateNewValue& callback +) +{ + mpGlobals->observe(TCHAR_TO_UTF8(*name), [callback](ink::runtime::value x) { + callback.Execute(FInkVar(x)); + }); } -void AInkRuntime::ObserverVariableChange(const FString& name, const FVariableCallbackDelegateNewOldValue& callback) { - mpGlobals->observe(TCHAR_TO_ANSI(*name), - [callback](ink::runtime::value x, ink::optional y){ - if (y.has_value()) { - callback.Execute(FInkVar(x), FInkVar(y.value())); - } else { - callback.Execute(FInkVar(x), FInkVar()); - } - } +void AInkRuntime::ObserverVariableChange( + const FString& name, const FVariableCallbackDelegateNewOldValue& callback +) +{ + mpGlobals->observe( + TCHAR_TO_UTF8(*name), + [callback](ink::runtime::value x, ink::optional y) { + if (y.has_value()) { + callback.Execute(FInkVar(x), FInkVar(y.value())); + } else { + callback.Execute(FInkVar(x), FInkVar()); + } + } ); } diff --git a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp index c9c76c6b..119ef2c9 100644 --- a/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp +++ b/unreal/inkcpp/Source/inkcpp/Private/InkThread.cpp @@ -64,14 +64,14 @@ void UInkThread::RegisterExternalFunction( const FString& functionName, const FExternalFunctionDelegate& function, bool lookaheadSafe ) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function, lookaheadSafe); + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_UTF8(*functionName)), function, lookaheadSafe); } void UInkThread::RegisterExternalEvent( const FString& functionName, const FExternalFunctionVoidDelegate& function, bool lookaheadSafe ) { - mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_ANSI(*functionName)), function, lookaheadSafe); + mpRunner->bind_delegate(ink::hash_string(TCHAR_TO_UTF8(*functionName)), function, lookaheadSafe); } void UInkThread::Initialize(FString path, AInkRuntime* runtime, ink::runtime::runner thread) @@ -108,7 +108,7 @@ bool UInkThread::ExecuteInternal() return true; mbHasRun = true; if (mStartPath.Len()) { - mpRunner->move_to(ink::hash_string(TCHAR_TO_ANSI(*mStartPath))); + mpRunner->move_to(ink::hash_string(TCHAR_TO_UTF8(*mStartPath))); } if (mbInChoice) { diff --git a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp index 43184e09..99479728 100644 --- a/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp +++ b/unreal/inkcpp/Source/inkcpp_editor/Private/InkAssetFactory.cpp @@ -81,7 +81,7 @@ void TraversImports( ); while (matcher.FindNext()) { std::filesystem::path match_file_path = filepath; - match_file_path.replace_filename(TCHAR_TO_ANSI(*matcher.GetCaptureGroup(2))); + match_file_path.replace_filename(TCHAR_TO_UTF8(*matcher.GetCaptureGroup(2))); TraversImports(AssetImportData, visited, match_file_path); } } @@ -97,7 +97,7 @@ UObject* UInkAssetFactory::FactoryCreateFile( static const std::string ink_suffix{".ink"}; try { using path = std::filesystem::path; - std::string cFilename = TCHAR_TO_ANSI(*Filename); + std::string cFilename = TCHAR_TO_UTF8(*Filename); path story_path(cFilename, path::format::generic_format); story_path.make_preferred(); bool use_temp_file = false; @@ -112,7 +112,7 @@ UObject* UInkAssetFactory::FactoryCreateFile( return nullptr; } path path_bin( - TCHAR_TO_ANSI(*IPluginManager::Get().FindPlugin(TEXT("InkCPP"))->GetBaseDir()), + TCHAR_TO_UTF8(*IPluginManager::Get().FindPlugin(TEXT("InkCPP"))->GetBaseDir()), path::format::generic_format ); path_bin.make_preferred();