-
Notifications
You must be signed in to change notification settings - Fork 28
/
uecall.nim
147 lines (122 loc) · 5.74 KB
/
uecall.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
include ../unreal/prelude
import ../codegen/[modelconstructor, ueemit, uebind, models, uemeta]
import std/[json, jsonutils, sequtils, options, sugar, enumerate, tables]
type
UEFunc* = object #Light metadata we could ue UFunc but we dont want to pull all those types into the vm
name* : string
className* : string
UECall* = object
fn* : UEFunc
self* : int
value* : JsonNode #On uebind [name] = value
proc makeUEFunc*(name, className : string) : UEFunc =
result.name = name
result.className = className
proc makeUECall*(fn : UEFunc, self : int, value : JsonNode) : UECall =
result.fn = fn
result.self = self
result.value = value
proc makeUECall*(fn : UEFunc, self : UObjectPtr, value : JsonNode) : UECall =
result.fn = fn
result.self = cast[int](self)
result.value = value
proc setPropWithValueInMemoryBlock*(prop : FPropertyPtr, memoryRegion:ByteAddress, value : JsonNode, allocatedStrings : var TArray[pointer], propOffset:int32 = prop.getOffset()) =
let propSize = prop.getSize()
let memoryRegion = (memoryRegion) + propOffset
let memoryBlock = cast[pointer](memoryRegion)
if prop.isFString():
var fstringPtr = cast[ptr FString](alloc0(sizeof(FString)))
fstringPtr[] = (FString)value.getStr()
allocatedStrings.add fstringPtr
copyMem(memoryBlock, fstringPtr, propSize)
elif prop.isInt() or prop.isObjectBased():
var val = value.getInt()
copyMem(memoryBlock, addr val, propSize)
elif prop.isFloat(): #MAYBE we can just try to match against all floats here so it works for returns too
var val = value.getFloat()
copyMem(memoryBlock, addr val, propSize)
# elif prop.isTArray(): TODO: This is not working
# let elems = value.getElems()
# let arrProp = castField[FArrayProperty](prop)
# let innerProp = arrProp.getInnerProp()
# UE_Log "Array prop name: " & $prop.getName() & " inner prop name: " & $innerProp.getName() & innerProp.getCppType()
# UE_Log "Array prop size: " & $prop.getSize() & " inner prop size: " & $innerProp.getSize()
# var arrayMemoryBlock = alloc0(sizeof(arrProp.getSize()))
# var arrayMemoryRegion = cast[ByteAddress](arrayMemoryBlock)
# allocatedStrings.add arrayMemoryBlock
# for idx, elem in enumerate(elems):
# let offset = idx.int32 * innerProp.getSize()
# propWithValueToMemoryBlock(innerProp, arrayMemoryRegion, elem, allocatedStrings, offset)
# var test = cast[ptr TArray[int]](arrayMemoryBlock)
# UE_Log "Array " & $test[]
# copyMem(memoryBlock, arrayMemoryBlock, arrProp.getSize())
elif prop.isStruct():
let structProp = castField[FStructProperty](prop)
let scriptStruct = structProp.getScriptStruct()
let structProps = scriptStruct.getFPropsFromUStruct() #Lets just do this here before making it recursive
var structMemoryRegion = memoryRegion
# let structMemory
for paramProp in structProps:
let name = paramProp.getName()
var val : JsonNode
if name in value:
val = value[name]
else:
val = value[name.firstToLow()] #Nim vs UE discrepancies
setPropWithValueInMemoryBlock(paramProp, structMemoryRegion, val, allocatedStrings)
proc getValueFromPropMemoryBlock*(prop:FPropertyPtr, returnMemoryRegion : ByteAddress) : JsonNode =
result = newJNull()
let returnSize = prop.getSize()
let returnMemoryBlock = cast[pointer](returnMemoryRegion)
if prop.isFString():
var returnValue = f""
copyMem(addr returnValue, returnMemoryBlock, returnSize)
result = newJString(returnValue)
if prop.isInt() or prop.isObjectBased():
var returnValue = 0
copyMem(addr returnValue, returnMemoryBlock, returnSize)
result = newJInt(returnValue)
if prop.isFloat():
var returnValue = 0.0
copyMem(addr returnValue, returnMemoryBlock, returnSize)
result = newJFloat(returnValue)
if prop.isStruct():
let structProp = castField[FStructProperty](prop)
let scriptStruct = structProp.getScriptStruct()
let structProps = scriptStruct.getFPropsFromUStruct()
let structMemoryRegion = returnMemoryRegion #same but keeping it for clarity/simmetry
result = newJObject()
for paramProp in structProps:
let name = paramProp.getName().firstToLow() #So when we parse the type in the vm it matches
let value = getValueFromPropMemoryBlock(paramProp, structMemoryRegion + paramProp.getOffset())
result[name] = value
func isStatic*(fn : UFunctionPtr) : bool = FUNC_Static in fn.functionFlags
proc uCall*(call : UECall) : JsonNode =
result = newJNull()
let fn = getClassByName(call.fn.className.removeFirstLetter()).findFunctionByName(n call.fn.name)
let self =
if fn.isStatic():
getDefaultObjectFromClassName(call.fn.className.removeFirstLetter())
else:
cast[UObjectPtr](call.self)
let propParams = fn.getFPropsFromUStruct().filterIt(it != fn.getReturnProperty())
if propParams.any():
var memoryBlock = alloc0(fn.parmsSize)
let memoryBlockAddr = cast[ByteAddress](memoryBlock)
var allocatedStrings = makeTArray[pointer]()
#TODO check return param and out params
for paramProp in propParams:
let paramValue = call.value[paramProp.getName()]
setPropWithValueInMemoryBlock(paramProp, memoryBlockAddr, paramValue, allocatedStrings)
self.processEvent(fn, memoryBlock)
if fn.doesReturn():
# let returnProp = fn.getFPropsFromUStruct().filterIt(it.getName() == call.fn.getReturnProp.get.name).head().get()
let returnProp = fn.getReturnProperty()
let returnOffset = fn.returnValueOffset
var returnMemoryRegion = memoryBlockAddr + returnOffset.int
result = getValueFromPropMemoryBlock(returnProp, returnMemoryRegion)
dealloc(memoryBlock)
for str in allocatedStrings:
dealloc(str)
else: #no params no return
self.processEvent(fn, nil)