From 080f01178cfabeeb2ee42b83de611c09c741ab63 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Fri, 15 Aug 2025 19:22:27 -0600 Subject: [PATCH 1/9] add pin attribute parser for serializeAsSinglePrecisionFloat --- src/data/pin/pin-property.ts | 1 + src/parser/pin-property.parser.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/data/pin/pin-property.ts b/src/data/pin/pin-property.ts index 7d8058b..ceb3659 100644 --- a/src/data/pin/pin-property.ts +++ b/src/data/pin/pin-property.ts @@ -27,6 +27,7 @@ export class PinProperty extends CustomProperty { isConst: boolean; isWeakPointer: boolean; isUObjectWrapper: boolean; + serializeAsSinglePrecisionFloat: boolean; linkedTo: PinLink[]; persistentGUID: string; diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 22e7f29..8a357f5 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -72,6 +72,9 @@ export class PinPropertyParser implements CustomPropertyParser { // console.log(`Found interesting attribute 'PinType.PinSubCategoryMemberReference' for which a value other than '()' was set. PinType.PinSubCategoryMemberReference='${value}' [pin-name: ${p.name}]`); // } }, + "PinType.bSerializeAsSinglePrecisionFloat": (p: PinProperty, value: string) => { + p.serializeAsSinglePrecisionFloat = value.toLowerCase() === "true"; + }, } parse(propertyData: string, nodeName: string): PinProperty { From 0827af7296d45b62d013d5f1a04e33fac7bad4ae Mon Sep 17 00:00:00 2001 From: dave-aa Date: Fri, 15 Aug 2025 19:22:54 -0600 Subject: [PATCH 2/9] add pin attribute parser for subPins --- src/data/pin/pin-property.ts | 1 + src/parser/pin-property.parser.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/data/pin/pin-property.ts b/src/data/pin/pin-property.ts index ceb3659..12e12c8 100644 --- a/src/data/pin/pin-property.ts +++ b/src/data/pin/pin-property.ts @@ -50,6 +50,7 @@ export class PinProperty extends CustomProperty { hideName: boolean; showInHead: boolean; + subPins: string; constructor(nodeName: string) { super(); diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 8a357f5..232eed4 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -75,6 +75,9 @@ export class PinPropertyParser implements CustomPropertyParser { "PinType.bSerializeAsSinglePrecisionFloat": (p: PinProperty, value: string) => { p.serializeAsSinglePrecisionFloat = value.toLowerCase() === "true"; }, + "SubPins": (p: PinProperty, value: string) => { + p.subPins = value; + }, } parse(propertyData: string, nodeName: string): PinProperty { From 3b1d989bd74ebffd3dbef9b2e4f766c4dc55a9e7 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Mon, 18 Aug 2025 09:01:00 -0600 Subject: [PATCH 3/9] add pin attribute parser for parentPin --- src/data/pin/pin-property.ts | 1 + src/parser/pin-property.parser.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/data/pin/pin-property.ts b/src/data/pin/pin-property.ts index 12e12c8..77cdbb5 100644 --- a/src/data/pin/pin-property.ts +++ b/src/data/pin/pin-property.ts @@ -51,6 +51,7 @@ export class PinProperty extends CustomProperty { hideName: boolean; showInHead: boolean; subPins: string; + parentPin: string; constructor(nodeName: string) { super(); diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 232eed4..6a37087 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -78,6 +78,9 @@ export class PinPropertyParser implements CustomPropertyParser { "SubPins": (p: PinProperty, value: string) => { p.subPins = value; }, + "ParentPin": (p: PinProperty, value: string) => { + p.parentPin = BlueprintParserUtils.parseString(value); + }, } parse(propertyData: string, nodeName: string): PinProperty { From 4bd76a4b8b352b981eb55471d5626a51fd2f7264 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Wed, 20 Aug 2025 11:02:27 -0600 Subject: [PATCH 4/9] add pin attribute parser for DesiredPinDirection --- src/parser/pin-property.parser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 6a37087..f942e8a 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -26,6 +26,7 @@ export class PinPropertyParser implements CustomPropertyParser { "PinFriendlyName": (p: PinProperty, value: string) => { p.friendlyName = prettifyText(PinPropertyParser.parsePinFriendlyName(value)); }, "PinType.PinCategory": (p: PinProperty, value: string) => { p.category = PinPropertyParser.parsePinCategory(value); }, "Direction": (p: PinProperty, value: string) => { p.direction = PinPropertyParser.parseDirection(value); }, + "DesiredPinDirection": (p: PinProperty, value: string) => { p.direction = PinPropertyParser.parseDirection(value); }, "PinToolTip": (p: PinProperty, value: string) => { p.toolTip = BlueprintParserUtils.parseString(value); }, "PinType.PinSubCategory": (p: PinProperty, value: string) => { p.subCategory = BlueprintParserUtils.parseString(value); }, "PinType.PinSubCategoryObject": (p: PinProperty, value: string) => { p.subCategoryObject = PinPropertyParser.parseSubCategoryObject(value); }, From 5b20d3a53c07677778f92f2fd18042a094e0d991 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Mon, 18 Aug 2025 09:01:48 -0600 Subject: [PATCH 5/9] add node parser for UserDefinedPin add some null checks for subCategoryObject --- src/controls/utils/color-utils.ts | 2 +- .../node-parsers/generic-node.parser.ts | 4 ++- src/parser/pin-property.parser.ts | 29 +++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/controls/utils/color-utils.ts b/src/controls/utils/color-utils.ts index 11d5d14..987f578 100644 --- a/src/controls/utils/color-utils.ts +++ b/src/controls/utils/color-utils.ts @@ -13,7 +13,7 @@ export enum StructClass { export class ColorUtils { public static getPinColor(pin: PinProperty): string { - return this.getPinColorByCategory(pin.category, pin.subCategoryObject.class); + return this.getPinColorByCategory(pin.category, pin.subCategoryObject?.class); } public static getPinColorByCategory(category: PinCategory, subCategoryObject?: string): string { diff --git a/src/parser/node-parsers/generic-node.parser.ts b/src/parser/node-parsers/generic-node.parser.ts index 49fb07b..0d1784b 100644 --- a/src/parser/node-parsers/generic-node.parser.ts +++ b/src/parser/node-parsers/generic-node.parser.ts @@ -84,6 +84,7 @@ export class GenericNodeParser extends NodeParser { [key: string]: () => CustomPropertyParser } = { "Pin": () => new PinPropertyParser(), + "UserDefinedPin": () => new PinPropertyParser(), } constructor() { @@ -162,7 +163,8 @@ export class GenericNodeParser extends NodeParser { data.node.customProperties.push(property); if (property instanceof PinProperty) { - if ((property as PinProperty).subCategoryObject.class === StructClass.LatentActionInfo) { + const pinProp = property as PinProperty; + if (pinProp.subCategoryObject && pinProp.subCategoryObject.class === StructClass.LatentActionInfo) { data.node.latent = true; } } diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index f942e8a..cb6096a 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -25,6 +25,13 @@ export class PinPropertyParser implements CustomPropertyParser { "PinName": (p: PinProperty, value: string) => { p.name = prettifyText(BlueprintParserUtils.parseString(value)); }, "PinFriendlyName": (p: PinProperty, value: string) => { p.friendlyName = prettifyText(PinPropertyParser.parsePinFriendlyName(value)); }, "PinType.PinCategory": (p: PinProperty, value: string) => { p.category = PinPropertyParser.parsePinCategory(value); }, + "PinType": (p: PinProperty, value: string) => { + // UserDefinedPin format: PinType=(PinCategory="bool") + const categoryMatch = value.match(/PinCategory="([^"]+)"/); + if (categoryMatch) { + p.category = PinPropertyParser.parsePinCategory(`"${categoryMatch[1]}"`); + } + }, "Direction": (p: PinProperty, value: string) => { p.direction = PinPropertyParser.parseDirection(value); }, "DesiredPinDirection": (p: PinProperty, value: string) => { p.direction = PinPropertyParser.parseDirection(value); }, "PinToolTip": (p: PinProperty, value: string) => { p.toolTip = BlueprintParserUtils.parseString(value); }, @@ -271,9 +278,11 @@ export class PinPropertyParser implements CustomPropertyParser { case PinCategory.bool: return { control: CheckBoxControl, data: (BlueprintParserUtils.parseString(value).toLowerCase() === "true") }; case PinCategory.struct: - return this.parseDefaultValueStruct(p.subCategoryObject.class, value); + if (p.subCategoryObject && p.subCategoryObject.class) { + return this.parseDefaultValueStruct(p.subCategoryObject.class, value); + } case PinCategory.byte: - if (p.subCategoryObject.type === "Enum") { + if (p.subCategoryObject && p.subCategoryObject.type === "Enum") { return { control: TextBoxControl, data: BlueprintParserUtils.parseEnumValue(p.subCategoryObject.class, value) }; } else { return { control: TextBoxControl, data: BlueprintParserUtils.parseString(value) }; @@ -366,13 +375,15 @@ export class PinPropertyParser implements CustomPropertyParser { p.defaultValue = " "; p.defaultValueControlClass = TextBoxControl; case PinCategory.struct: - switch (p.subCategoryObject.class) { - case StructClass.VECTOR2D: - p.defaultValue = [ - { key: 'X', value: '0.0' }, - { key: 'Y', value: '0.0' }]; - p.defaultValueControlClass = StructBoxControl; - break; + if (p.subCategoryObject && p.subCategoryObject.class) { + switch (p.subCategoryObject.class) { + case StructClass.VECTOR2D: + p.defaultValue = [ + { key: 'X', value: '0.0' }, + { key: 'Y', value: '0.0' }]; + p.defaultValueControlClass = StructBoxControl; + break; + } } break; } From db86743d8a4c1176bafc1e999065b62747cfa35d Mon Sep 17 00:00:00 2001 From: dave-aa Date: Wed, 20 Aug 2025 11:27:06 -0600 Subject: [PATCH 6/9] add color and rounding support for 'real' pin type --- src/controls/utils/color-utils.ts | 1 + src/data/pin/pin-category.ts | 1 + src/parser/pin-property.parser.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/controls/utils/color-utils.ts b/src/controls/utils/color-utils.ts index 987f578..39e87b0 100644 --- a/src/controls/utils/color-utils.ts +++ b/src/controls/utils/color-utils.ts @@ -23,6 +23,7 @@ export class ColorUtils { case PinCategory.bool: return 'rgb(146, 1, 1)'; case PinCategory.float: + case PinCategory.real: return 'rgb(158, 250, 68)'; case PinCategory.int: return 'rgb(30, 226, 174)'; diff --git a/src/data/pin/pin-category.ts b/src/data/pin/pin-category.ts index 73e8cc3..d8bd101 100644 --- a/src/data/pin/pin-category.ts +++ b/src/data/pin/pin-category.ts @@ -8,6 +8,7 @@ export enum PinCategory { string = "string", text = "text", float = "float", + real = "real", struct = "struct", class = "class", bool = "bool", diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index cb6096a..55a846e 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -274,6 +274,7 @@ export class PinPropertyParser implements CustomPropertyParser { switch (p.category) { case PinCategory.float: + case PinCategory.real: return { control: TextBoxControl, data: removeInsignificantTrailingZeros(BlueprintParserUtils.parseString(value)) }; case PinCategory.bool: return { control: CheckBoxControl, data: (BlueprintParserUtils.parseString(value).toLowerCase() === "true") }; From 2fe3809848b24529bae7db5b816ecd10920c5ac7 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Wed, 20 Aug 2025 11:19:05 -0600 Subject: [PATCH 7/9] handle PinFriendlyName INVTEXT --- src/parser/pin-property.parser.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 55a846e..8ac5c29 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -223,6 +223,11 @@ export class PinPropertyParser implements CustomPropertyParser { key = lastValue; args[key] = value; } + } else if (prop.startsWith("INVTEXT")) { + const invtextMatch = prop.match(/INVTEXT\("([^"]*)"\)/); + if (invtextMatch && (index + offset) % 2 == 1) { + args[namingkKey] = invtextMatch[1]; + } } else { if ((index + offset) % 2 == 0) { namingkKey = prop.replace(/"/g, ''); From fefdcfba633bbdf607bf9785275abd4d50caa2fe Mon Sep 17 00:00:00 2001 From: dave-aa Date: Mon, 8 Sep 2025 16:51:26 -0600 Subject: [PATCH 8/9] add support for bDefaultsToPureFunc in CallFunctionNodeParser --- src/parser/node-parsers/call-function-node.parser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser/node-parsers/call-function-node.parser.ts b/src/parser/node-parsers/call-function-node.parser.ts index 1e140f3..d0574fe 100644 --- a/src/parser/node-parsers/call-function-node.parser.ts +++ b/src/parser/node-parsers/call-function-node.parser.ts @@ -26,6 +26,7 @@ export class CallFunctionNodeParser extends NodeParser { constructor() { super({ "bIsPureFunc": (node: CallFunctionNode, value: string) => { node.isPureFunc = (value === "True"); }, + "bDefaultsToPureFunc": (node: CallFunctionNode, value: string) => { node.isPureFunc = (value === "True"); }, "bIsConstFunc": (node: CallFunctionNode, value: string) => { node.isConstFunc = (value === "True"); }, "FunctionReference": (node: CallFunctionNode, value: string) => { const parser = new NodeDataReferenceParser(); From 7c5f7866916d95c8749596803b6ac584057b27f1 Mon Sep 17 00:00:00 2001 From: dave-aa Date: Fri, 10 Oct 2025 10:17:28 -0600 Subject: [PATCH 9/9] add pasing support LinearColor boxes Unreal draws an square box filled with the color value - e.g. a white or red box. Currently klee just lists the rgba number values in a line. draw the colored box instead. --- src/parser/pin-property.parser.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/parser/pin-property.parser.ts b/src/parser/pin-property.parser.ts index 8ac5c29..9a6418e 100644 --- a/src/parser/pin-property.parser.ts +++ b/src/parser/pin-property.parser.ts @@ -317,6 +317,16 @@ export class PinPropertyParser implements CustomPropertyParser { color.applyGamma(); return { control: ColorBoxControl, data: color}; default: + if (subCategoryObject.includes('LinearColor')) { + const cParams = this.parseDefaultValueStructCommon(value).map(p => Number(p.value)); + const color = new Color( + (cParams[0] || 0) * 255, + (cParams[1] || 0) * 255, + (cParams[2] || 0) * 255, + cParams[3]); + color.applyGamma(); + return { control: ColorBoxControl, data: color}; + } return { control: StructBoxControl, data: this.parseDefaultValueStructCommon(value) }; } }