Initial push to repo

This commit is contained in:
2026-07-03 19:56:31 +02:00
commit 4cf4176d57
1305 changed files with 43455 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleInterface.h"
class FSUDSScriptActions;
class FSlateStyleSet;
DECLARE_LOG_CATEGORY_EXTERN(LogSUDSEditor, Verbose, All);
class FSUDSEditorModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
protected:
TSharedPtr<FSUDSScriptActions> ScriptActions;
TSharedPtr<FSlateStyleSet> StyleSet;
};

View File

@@ -0,0 +1,22 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
class USUDSScript;
class USUDSScriptNode;
struct FSUDSMessageLogger;
class FSUDSEditorScriptTools
{
public:
static void WriteBackTextIDs(USUDSScript* Script, FSUDSMessageLogger& Logger);
static bool WriteBackTextIDsFromNodes(const TArray<USUDSScriptNode*> Nodes, TArray<FString>& Lines, const FString& NameForErrors, FSUDSMessageLogger& Logger);
static bool WriteBackTextID(const FText& AssetText, int LineNo, TArray<FString>& Lines, const FString& NameForErrors, FSUDSMessageLogger& Logger);
static bool WriteBackGosubID(const FString& GosubID, int LineNo, TArray<FString>& Lines, const FString& NameForErrors, FSUDSMessageLogger& Logger);
static bool TextIDCheckMatch(const FText& AssetText, const FString& SourceLine);
};

View File

@@ -0,0 +1,73 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "SUDSEditorSettings.generated.h"
UENUM(BlueprintType)
enum class ESUDSAssetLocation : uint8
{
/// Use a single flat shared directory
SharedDirectory,
/// Use a shared base directory, but create subfolders based on the script asset name
SharedDirectorySubdir,
/// Place asset alongside the script that originated it
ScriptDirectory,
/// Place asset in a subfolder of the folder containing the script that generated it, named the same as the script
ScriptDirectorySubdir
};
class USUDSScript;
/**
* Settings for editor-specific aspects of SUDS (no effect at runtime)
*/
UCLASS(config = Editor, defaultconfig, meta=(DisplayName="SUDS Editor"))
class SUDSEDITOR_API USUDSEditorSettings : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Where to place Dialogue Voice assets for speakers in scripts when generated", RelativeToGameContentDir, LongPackageName))
ESUDSAssetLocation DialogueVoiceAssetLocation = ESUDSAssetLocation::SharedDirectory;
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Shared directory for Dialogue Voice assets, if using a shared directory", RelativeToGameContentDir, LongPackageName))
FDirectoryPath DialogueVoiceAssetSharedDir;
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Where to place Dialogue Wave assets for speaker lines in scripts", RelativeToGameContentDir, LongPackageName))
ESUDSAssetLocation DialogueWaveAssetLocation = ESUDSAssetLocation::ScriptDirectorySubdir;
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Shared directory for Dialogue Wave assets, if using a shared directory", RelativeToGameContentDir, LongPackageName))
FDirectoryPath DialogueWaveAssetSharedDir;
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Prefix to give Dialogue Voice assets in front of their SpeakerID", RelativeToGameContentDir, LongPackageName))
FString DialogueVoiceAssetPrefix = "DV_";
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "Prefix to give Dialogue Wave assets in front of their SpeakerID", RelativeToGameContentDir, LongPackageName))
FString DialogueWaveAssetPrefix = "DW_";
UPROPERTY(config, EditAnywhere, Category = "Voice", meta = (Tooltip = "When generating subdirectories and wave asset names from script names, whether to strip characters before the first '_' to avoid including script prefix", RelativeToGameContentDir, LongPackageName))
bool StripScriptPrefixesWhenGeneratingNames = true;
UPROPERTY(config, EditAnywhere, Category = "Voice", AdvancedDisplay, meta = (Tooltip = "Whether to auto-generate Dialogue Voice/Wave assets for ALL dialogue scripts on import. Note: you can always generate VO assets manually."))
bool AlwaysAutoGenerateVoiceOverAssetsOnImport = false;
UPROPERTY(config, EditAnywhere, Category = "Voice", AdvancedDisplay, meta = (Tooltip = "Auto-generate Dialogue Voice/Wave assets for scripts in these directories (and subdirectories) on import. Note: you can always generate VO assets manually.", RelativeToGameContentDir, LongPackageName))
TArray<FDirectoryPath> DirectoriesToAutoGenerateVoiceOverAssetsOnImport;
UPROPERTY(config, EditAnywhere, Category = "Choices", meta = (Tooltip = "Whether to generate a spoken line for choices (default false)."))
bool AlwaysGenerateSpeakerLinesFromChoices = false;
UPROPERTY(config, EditAnywhere, Category = "Choices", meta = (Tooltip = "The SpeakerID to use for speaker lines generated from choices"))
FString SpeakerIdForGeneratedLinesFromChoices = "Player";
UPROPERTY(config, EditAnywhere, Category = "Assets", AdvancedDisplay, meta = (Tooltip = "Whether to create string tables as a separate package (.uasset) from the SUDS Script, which will cause them to appear separately in the Content Browser (requires script re-import)"))
bool bCreateStringTablesAsSeparatePackages = true;
USUDSEditorSettings() {}
bool ShouldGenerateVoiceAssets(const FString& PackagePath) const;
FString GetVoiceOutputDir(const FString& PackagePath, const FString& ScriptName) const;
FString GetWaveOutputDir(const FString& PackagePath, const FString& ScriptName) const;
static FString GetOutputDir(ESUDSAssetLocation Location, const FString& SharedPath, const FString& PackagePath, const FString& ScriptName);
};

View File

@@ -0,0 +1,34 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "SUDSScriptNodeText.h"
#include "Sound/DialogueVoice.h"
struct FSUDSMessageLogger;
class USUDSScript;
class SUDSEDITOR_API FSUDSEditorVoiceOverTools
{
public:
static void GenerateAssets(USUDSScript* Script, EObjectFlags Flags, FSUDSMessageLogger* Logger);
protected:
static void GenerateVoiceAssets(USUDSScript* Script,
EObjectFlags Flags,
FSUDSMessageLogger *Logger,
TMap<FString, UDialogueVoice*> &OutCreatedVoices);
static void GenerateWaveAssets(USUDSScript* Script, EObjectFlags Flags, TMap<FString, UDialogueVoice*>, FSUDSMessageLogger* Logger);
static bool GetSpeakerVoicePackageName(USUDSScript* Script, const FString& SpeakerID, FString& OutPackageName);
static bool GetSpeakerVoiceAssetNames(USUDSScript* Script,
const FString& SpeakerID,
FString& OutPackageName,
FString& OutAssetName);
static UDialogueVoice* FindSpeakerVoice(USUDSScript* Script, const FString& SpeakerID);
static FString GetScriptNameAsPrefix(USUDSScript* Script);
static FString GetVoiceOutputDir(USUDSScript* Script);
static FString GetWaveOutputDir(USUDSScript* Script);
};

View File

@@ -0,0 +1,60 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "Logging/TokenizedMessage.h"
class FTokenizedMessage;
struct SUDSEDITOR_API FSUDSMessageLogger
{
protected:
TArray<TSharedRef<FTokenizedMessage>> ErrorMessages;
bool bWriteToMessageLog = true;
public:
FSUDSMessageLogger() {}
FSUDSMessageLogger(bool bWriteToMsg) : bWriteToMessageLog(bWriteToMsg) {}
~FSUDSMessageLogger();
void SetWriteToMessageLog(bool bWrite) { bWriteToMessageLog = bWrite; }
bool HasErrors() const;
int NumErrors() const;
bool HasWarnings() const;
int NumWarnings() const;
bool GetWriteToMessageLog() const { return bWriteToMessageLog; }
void AddMessage(EMessageSeverity::Type Severity, const FText& Text);
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 6
template <typename... Types>
FORCEINLINE void Logf(ELogVerbosity::Type Verbosity, UE::Core::TCheckedFormatString<FString::FmtCharType, Types...> Fmt, Types... Args)
#else
template <typename FmtType, typename... Types>
FORCEINLINE void Logf(ELogVerbosity::Type Verbosity, const FmtType& Fmt, Types... Args)
#endif
{
EMessageSeverity::Type Sev = EMessageSeverity::Info;
switch(Verbosity)
{
case ELogVerbosity::Fatal:
case ELogVerbosity::Error:
Sev = EMessageSeverity::Error;
break;
case ELogVerbosity::Warning:
Sev = EMessageSeverity::Warning;
break;
default: ;
}
AddMessage(Sev, FText::FromString(FString::Printf(Fmt, Args...)));
}
const TArray<TSharedRef<FTokenizedMessage>>& GetErrorMessages() const { return ErrorMessages; }
/// Clear messages in preparation for an import
static void ClearMessages();
};

View File

@@ -0,0 +1,37 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "AssetTypeActions_Base.h"
#include "SUDSScriptNodeGosub.h"
#include "SUDSScriptNodeText.h"
class USUDSScriptNode;
class USUDSScript;
struct FSUDSMessageLogger;
/**
*
*/
class FSUDSScriptActions : public FAssetTypeActions_Base
{
public:
virtual FText GetName() const override;
virtual FString GetObjectDisplayName(UObject* Object) const override;
virtual UClass* GetSupportedClass() const override;
virtual FColor GetTypeColor() const override;
virtual uint32 GetCategories() override;
virtual void GetResolvedSourceFilePaths(const TArray<UObject*>& TypeAssets,
TArray<FString>& OutSourceFilePaths) const override;
virtual bool IsImportedAsset() const override { return true; }
virtual void GetActions(const TArray<UObject*>& InObjects, FToolMenuSection& Section) override;
virtual bool HasActions(const TArray<UObject*>& InObjects) const override;
virtual void OpenAssetEditor(const TArray<UObject*>& InObjects,
TSharedPtr<IToolkitHost> EditWithinLevelEditor) override;
protected:
void WriteBackTextIDs(TArray<TWeakObjectPtr<USUDSScript>> Scripts);
void GenerateVOAssets(TArray<TWeakObjectPtr<USUDSScript>> Scripts);
};

View File

@@ -0,0 +1,37 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "SUDSScriptImporter.h"
#include "Factories/Factory.h"
#include "SUDSScriptFactory.generated.h"
/**
*
*/
UCLASS()
class SUDSEDITOR_API USUDSScriptFactory : public UFactory
{
GENERATED_BODY()
public:
USUDSScriptFactory();
protected:
virtual UObject* FactoryCreateText(UClass* InClass,
UObject* InParent,
FName InName,
EObjectFlags Flags,
UObject* Context,
const TCHAR* Type,
const TCHAR*& Buffer,
const TCHAR* BufferEnd,
FFeedbackContext* Warn) override;
bool ShouldGenerateVoiceAssets(const FString& PackagePath) const;
FSUDSScriptImporter Importer;
void ForceDeleteAssets(const TArray<FAssetData>& Assets);
UStringTable* CreateStringTable(UObject* ScriptParent, FName InName, USUDSScript* Script, EObjectFlags Flags, FSUDSMessageLogger* Logger);
};

View File

@@ -0,0 +1,520 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "SUDSExpression.h"
struct FSUDSMessageLogger;
class USUDSScript;
DECLARE_LOG_CATEGORY_EXTERN(LogSUDSImporter, Verbose, All);
struct SUDSEDITOR_API FSUDSParsedEdge
{
public:
/// Text associated with this edge (if a player choice option)
FString Text;
/// Identifier of the text, for the string table
FString TextID;
/// Metadata associated with text, for translator comments
TMap<FName, FString> TextMetadata;
/// The line this edge was created on
int SourceLineNo;
/// Condition expression that applies to this edge (for select nodes)
FSUDSExpression ConditionExpression;
/// Custom metadata for a node which the user can set. Can be used to annotate choice lines
/// Could be used to disable a choice if you don't have the stats to take it, or anything else
/// where you need a bit of extra information about a line that doesn't suit setting a dialogue variable for.
TMap<FName, FSUDSExpression> UserMetadata;
int SourceNodeIdx = -1;
int TargetNodeIdx = -1;
FSUDSParsedEdge(int LineNo) : SourceLineNo(LineNo){}
FSUDSParsedEdge(int FromNodeIdx, int ToNodeIdx, int LineNo, const FString& InText, const FString& InTextID, const TMap<FName, FString>& Metadata, const TMap<FName, FSUDSExpression>& UserMeta)
: Text(InText),
TextID(InTextID),
TextMetadata(Metadata),
SourceLineNo(LineNo),
UserMetadata(UserMeta),
SourceNodeIdx(FromNodeIdx),
TargetNodeIdx(ToNodeIdx)
{
}
FSUDSParsedEdge(int FromNodeIdx, int ToNodeIdx, int LineNo)
: SourceLineNo(LineNo),
SourceNodeIdx(FromNodeIdx),
TargetNodeIdx(ToNodeIdx)
{
}
void Reset()
{
*this = FSUDSParsedEdge(-1);
}
};
enum class ESUDSParsedNodeType : uint8
{
/// Text node, displaying a line of dialogue
Text,
/// Choice node, displaying a series of user choices which navigate to other nodes
Choice,
/// Select node, automatically selecting one which navigates to another node based on state (also Random)
Select,
/// Goto node, redirects execution somewhere else
/// Gotos are only nodes in the parsing structure, because they need to be discoverable as a fallthrough destination
/// When converting to runtime use they just become edges
Goto,
/// Gosub node, acts like goto except stores return location
Gosub,
/// Return node, returns from a gosub
Return,
/// Set variable node
SetVariable,
/// Event node
Event
};
/// Intermediate parsed node from script text
/// This will be converted into a final asset later
struct SUDSEDITOR_API FSUDSParsedNode
{
public:
ESUDSParsedNodeType NodeType;
int OriginalIndent;
/// Identifier is speaker ID, goto label, variable name etc
FString Identifier;
/// Text in native language
FString Text;
/// Identifier of the text, for the string table
FString TextID;
/// Metadata associated with text, for translator comments
TMap<FName, FString> TextMetadata;
/// Expression, for nodes that use it (e.g. set)
FSUDSExpression Expression;
/// Event arguments, for event nodes
TArray<FSUDSExpression> EventArgs;
/// Labels which lead to this node
TArray<FString> Labels;
/// Edges leading to other nodes
TArray<FSUDSParsedEdge> Edges;
/// The line this node was created on
int SourceLineNo;
/// Whether this is a valid fall-through target
bool AllowFallthrough = true;
/// Custom metadata for a node which the user can set. Can be used to annotate speaker lines
TMap<FName, FSUDSExpression> UserMetadata;
// Path hierarchy of choice nodes leading to this node, of the form "/C002/C006" etc, not including this node index
// This helps us identify valid fallthroughs
FString ChoicePath;
// Path hierarchy of select nodes leading to this node, of the form "/S002/S006" etc, not including this node index
// This helps us identify valid fallthroughs
FString ConditionalPath;
/// Although multiple edges can lead here, this index is for the auto-connected parent (may be nothing)
int ParentNodeIdx = -1;
FSUDSParsedNode(ESUDSParsedNodeType InNodeType, int Indent, int LineNo) : NodeType(InNodeType),
OriginalIndent(Indent),
SourceLineNo(LineNo)
{
}
FSUDSParsedNode(const FString& Label,
const FString& GosubID,
int Indent,
int LineNo) : NodeType(ESUDSParsedNodeType::Gosub),
OriginalIndent(Indent),
Identifier(Label),
TextID(GosubID),
SourceLineNo(LineNo)
{
}
FSUDSParsedNode(const FString& InSpeaker, const FString& InText, const FString& InTextID, const TMap<FName, FString>& Metadata, const TMap<FName, FSUDSExpression>& UserMeta, int Indent, int LineNo)
: NodeType(ESUDSParsedNodeType::Text),
OriginalIndent(Indent),
Identifier(InSpeaker),
Text(InText),
TextID(InTextID),
TextMetadata(Metadata),
SourceLineNo(LineNo),
UserMetadata(UserMeta)
{
}
FSUDSParsedNode(const FString& GotoLabel, int Indent, int LineNo)
: NodeType(ESUDSParsedNodeType::Goto), OriginalIndent(Indent), Identifier(GotoLabel), SourceLineNo(LineNo)
{
}
FSUDSParsedNode(const FString& VariableName, const FSUDSExpression& InExpr, int Indent, int LineNo)
: NodeType(ESUDSParsedNodeType::SetVariable),
OriginalIndent(Indent),
Identifier(VariableName),
Expression(InExpr),
SourceLineNo(LineNo)
{
}
FSUDSParsedNode(const FString& VariableName,
const FSUDSExpression& InExpr,
const FString& InTextID,
int Indent,
int LineNo)
: NodeType(ESUDSParsedNodeType::SetVariable),
OriginalIndent(Indent),
Identifier(VariableName),
TextID(InTextID),
Expression(InExpr),
SourceLineNo(LineNo)
{
}
};
class SUDSEDITOR_API FSUDSScriptImporter
{
public:
bool ImportFromBuffer(const TCHAR* Buffer, int32 Len, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
void PopulateAsset(USUDSScript* Asset, UStringTable* StringTable);
static FMD5Hash CalculateHash(const TCHAR* Buffer, int32 Len);
static const FString EndGotoLabel;
protected:
static const FString TreePathSeparator;
enum class EConditionalStage : uint8
{
IfStage,
ElseIfStage,
ElseStage,
RandomStage,
RandomOptionStage
};
enum class EConditionalParent : uint8
{
Select,
ElseIfStage,
ElseStage
};
// Struct for tracking if/elseif blocks
struct ConditionalContext
{
/// Index of parent select node where else will be added
int SelectNodeIdx = -1;
/// Previous block index (parent for nesting)
int PreviousBlockIdx = -1;
/// Track whether we're in if/elseif/else
EConditionalStage Stage;
/// String identifying the current condition; for elseif or else contains original "if" context
FString ConditionPathElement;
ConditionalContext(int InSelectNodeIdx, int InPrevBlockIdx, EConditionalStage InStage, const FString& InCondStr) :
SelectNodeIdx(InSelectNodeIdx),
PreviousBlockIdx(InPrevBlockIdx),
Stage(InStage),
ConditionPathElement(InCondStr)
{
}
};
/// Struct for tracking indents
struct IndentContext
{
public:
// The index of the Node which is the parent of this context
// This potentially changes every time a sequential text node is encountered in the same context, so it's
// always pointing to the last node encountered at this level, for connection
int LastNodeIdx = -1;
/// The outermost indent level where this context lives
/// You can indent things that don't create a new context, e.g.
/// 1. Indent a text line under another text line: this is the same as no indent, just a continuation
/// 2. Indent choices or conditions under a text line
/// This is just good for readability, but does not create a new context, it's just a linear sequence
/// Therefore the ThresholdIndent tracks the outermost indent relating to the current linear sequence, to know
/// when you do in fact need to pop the current context off the stack.
int ThresholdIndent = 0;
int LastTextNodeIdx = -1;
/// The path entry for this indent, to be combined with all previous levels to provide full path context
FString PathEntry;
IndentContext(int NodeIdx, int Indent, const FString& Path) : LastNodeIdx(NodeIdx), ThresholdIndent(Indent), LastTextNodeIdx(-1), PathEntry(Path) {}
};
/// A tree of nodes. Contained to separate header nodes from body nodes
struct ParsedTree
{
public:
/// The indent context stack representing where we are in the indentation tree while parsing
/// There must always be 1 level (root)
TArray<IndentContext> IndentLevelStack;
/// When encountering conditions and choice lines, we are building up details for an edge to another node, but
/// we currently don't know the target node. We keep these pending details here
int EdgeInProgressNodeIdx = -1;
int EdgeInProgressEdgeIdx = -1;
/// List of all nodes, appended to as parsing progresses
/// Ordering is important, these nodes must be in the order encountered in the file
TArray<FSUDSParsedNode> Nodes;
/// Record of goto labels to node index, built up during parsing (forward refs are OK so not complete until end of parsing)
TMap<FString, int> GotoLabelList;
/// Goto labels which have been encountered but we haven't found a destination yet
TArray<FString> PendingGotoLabels;
/// Goto labels that lead directly to another goto and thus are just aliases
TMap<FString, FString> AliasedGotoLabels;
/// Conditional blocks
/// "if" creates a new context, uses current as parent
/// "elseif" and "else" also create new contexts, but copies parent from current (sibling)
/// "endif" ends the context
/// You cannot have conditionals that were started in an indent context ending outside it
TArray<ConditionalContext> ConditionalBlocks;
/// Index of the current conditional block, if any
int CurrentConditionalBlockIdx = -1;
void Reset()
{
IndentLevelStack.Reset();
EdgeInProgressNodeIdx = EdgeInProgressEdgeIdx -1;
Nodes.Reset();
GotoLabelList.Reset();
PendingGotoLabels.Reset();
AliasedGotoLabels.Reset();
ConditionalBlocks.Reset();
CurrentConditionalBlockIdx = -1;
}
};
ParsedTree HeaderTree;
ParsedTree BodyTree;
struct ParsedMetadata
{
public:
FName Key;
FString Value;
int IndentLevel;
ParsedMetadata(const FName& InKey, const FString& InValue, int InIndentLevel)
: Key(InKey),
Value(InValue),
IndentLevel(InIndentLevel)
{
}
};
/// Text Metadata applied to speaker lines / choices until reset
/// For each key there's a stack of metadata, with most indented at the top
TMap<FName, TArray<ParsedMetadata>> PersistentMetadata;
/// Text Metadata applied just to the next speaker line or choice
TMap<FName, ParsedMetadata> TransientMetadata;
/// User metadata for the next speaker line or choice
TMap<FName, FSUDSExpression> UserMetadata;
/// List of speakers, detected during parsing of lines of text
TArray<FString> ReferencedSpeakers;
const int TabIndentValue = 4;
bool bHeaderDone = false;
bool bTooLateForHeader = false;
bool bHeaderInProgress = false;
TOptional<bool> bOverrideGenerateSpeakerLineForChoice;
TOptional<FString> OverrideChoiceSpeakerID;
bool bTextInProgress = false;
int ChoiceUniqueId = 0;
/// For generating text IDs
int TextIDHighestNumber = 0;
/// For generating gosub IDs
int GosubIDHighestNumber = 0;
/// Parse a single line
bool ParseLine(const FStringView& Line, int LineNo, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool ParseHeaderLine(const FStringView& Line, int IndentLevel, int LineNo, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool ParseBodyLine(const FStringView& Line, int IndentLevel, int LineNo, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool ParseCommentMetadataLine(const FStringView& Line, int IndentLevel, int LineNo, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool IsLastNodeOfType(const ParsedTree& Tree, ESUDSParsedNodeType Type);
bool ParseChoiceLine(const FStringView& Line, ParsedTree& Tree, int IndentLevel, int LineNo, const FString& NameForErrors, FSUDSMessageLogger*
Logger,
bool bSilent);
FSUDSParsedEdge* GetEdgeInProgress(ParsedTree& Tree);
void EnsureChoiceNodeExistsAboveSelect(ParsedTree& Tree, int IndentLevel, int LineNo);
static bool IsConditionalLine(const FStringView& Line);
bool ParseConditionalLine(const FStringView& Line, ParsedTree& Tree, int IndentLevel, int LineNo, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool ParseIfLine(const FStringView& Line,
ParsedTree& Tree,
const FString& ConditionStr,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseElseIfLine(const FStringView& Line,
ParsedTree& Tree,
const FString& ConditionStr,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseElseLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseEndIfLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger*
Logger,
bool bSilent);
static bool IsRandomLine(const FStringView& Line);
bool ParseRandomLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseBeginRandomLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseRandomOptionLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseEndRandomLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseGotoLabelLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseGotoLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseGosubLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseReturnLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseSetLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseEventLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseTextLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool ParseImportSettingLine(const FStringView& Line,
ParsedTree& Tree,
int IndentLevel,
int LineNo,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
TMap<FName, FString> GetTextMetadataForNextEntry(int CurrentLineIndent);
TMap<FName, FSUDSExpression> ConsumeUserMetadata();
bool IsCommentLine(const FStringView& TrimmedLine);
FStringView TrimLine(const FStringView& Line, int& OutIndentLevel) const;
int FindChoiceAfterTextNode(const FSUDSScriptImporter::ParsedTree& Tree, int TextNodeIdx, const FString& ConditionalPath);
int RecurseChoiceNodeFindDeepest(const ParsedTree& Tree, int FromChoiceIdx, const FString& ConditionalPath);
int RecurseSelectNodeFindDeepestChoice(const ParsedTree& Tree, int FromSelectNode, const FString& ConditionalPath);
int FindEdge(const FSUDSScriptImporter::ParsedTree& Tree, int ParentNodeIdx, int TargetNodeIndex);
FString MakeIfConditionPathElement(int SelectNodeIdx, const FString& ConditionStr);
FString MakeElseIfConditionPathElement(int SelectNodeIdx, const FString& ConditionStr);
FString MakeElseConditionPathElement(int SelectNodeIdx);
int FindLastChoiceNode(const ParsedTree& Tree, int IndentLevel);
int FindLastChoiceNode(const ParsedTree& Tree, int IndentLevel, int FromIndex, const FString& ConditionPath);
void PopIndent(ParsedTree& Tree);
void PushIndent(ParsedTree& Tree, int NodeIdx, int Indent, const FString& Path);
FString GetCurrentTreePath(const FSUDSScriptImporter::ParsedTree& Tree);
FString GetCurrentTreeConditionalPath(const FSUDSScriptImporter::ParsedTree& Tree);
FString GetTreeConditionalPath(const ParsedTree& Tree, int NodeIndex);
void SetFallthroughForNewNode(FSUDSScriptImporter::ParsedTree& Tree, FSUDSParsedNode& NewNode);
int AppendNode(ParsedTree& Tree, const FSUDSParsedNode& InNode);
bool SelectNodeIsMissingElsePath(const FSUDSScriptImporter::ParsedTree& Tree, const FSUDSParsedNode& Node);
bool PostImportSanityCheck(const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool ChoiceNodeCheckPaths(const FSUDSParsedNode& ChoiceNode,
const FString& NameForErrors,
FSUDSMessageLogger* Logger,
bool bSilent);
bool RecurseChoiceNodeCheckPaths(const FSUDSParsedNode& OrigChoiceNode, const FSUDSParsedNode& CurrChoiceNode, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
bool RecurseChoiceNodeCheckPaths(const FSUDSParsedNode& ChoiceNode, const FSUDSParsedEdge& Edge, const FString& NameForErrors, FSUDSMessageLogger*
Logger,
bool bSilent);
void ConnectRemainingNodes(ParsedTree& Tree, const FString& NameForErrors, FSUDSMessageLogger* Logger, bool bSilent);
void GenerateTextIDs(ParsedTree& BodyTree);
int FindFallthroughNodeIndex(ParsedTree& Tree, int StartNodeIndex, const FString& FromChoicePath, const FString& FromConditionalPath);
bool RetrieveAndRemoveTextID(FStringView& InOutLine, FString& OutTextID);
bool RetrieveAndRemoveGosubID(FStringView& InOutLine, FString& OutTextID);
FString GenerateTextID();
const FSUDSParsedNode* GetNode(const ParsedTree& Tree, int Index = 0);
int GetGotoTargetNodeIndex(const ParsedTree& Tree, const FString& InLabel);
void PopulateAssetFromTree(USUDSScript* Asset,
const ParsedTree& Tree,
TArray<TObjectPtr<class USUDSScriptNode>>* pOutNodes,
TMap<FName, int>* pOutLabels,
UStringTable* StringTable);
public:
const FSUDSParsedNode* GetNode(int Index = 0);
const FSUDSParsedNode* GetHeaderNode(int Index = 0);
/// Resolve a goto label to a target index (after import), or -1 if not resolvable
int GetGotoTargetNodeIndex(const FString& Label);
static bool RetrieveTextIDFromLine(FStringView& InOutLine, FString& OutTextID, int& OutNumber);
static bool RetrieveGosubIDFromLine(FStringView& InOutLine, FString& OutID, int& OutNumber);
};

View File

@@ -0,0 +1,27 @@
// Copyright Steve Streeting 2022
// Released under the MIT license https://opensource.org/license/MIT/
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "EditorReimportHandler.h"
#include "SUDSScriptFactory.h"
#include "SUDSScriptReimportFactory.generated.h"
// Reimports a USUDSScriptFactory asset
// Necessary to get the import pop-up
UCLASS()
class USUDSScriptReimportFactory : public USUDSScriptFactory, public FReimportHandler
{
GENERATED_BODY()
public:
USUDSScriptReimportFactory();
//~ Begin FReimportHandler Interface
virtual bool CanReimport(UObject* Obj, TArray<FString>& OutFilenames) override;
virtual void SetReimportPaths(UObject* Obj, const TArray<FString>& NewReimportPaths) override;
virtual EReimportResult::Type Reimport(UObject* Obj) override;
virtual int32 GetPriority() const override;
//~ End FReimportHandler Interface
};