orvid / caprica Goto Github PK
View Code? Open in Web Editor NEWA compiler for the Papyrus scripting language used by the Creation Engine.
License: MIT License
A compiler for the Papyrus scripting language used by the Creation Engine.
License: MIT License
Orvid, logical statement evaluation seems to be broken in Caprica v0.2.0 when using optimization.
I put the following debug code in a script and compiled it with Caprica using the -O optimization option.
Debug.Trace("!TRUE && TRUE = " + (!TRUE && TRUE), 1)
Debug.Trace("!TRUE && FALSE = " + (!TRUE && FALSE), 1)
Debug.Trace("!FALSE && TRUE = " + (!FALSE && TRUE), 1)
Debug.Trace("!FALSE && FALSE = " + (!FALSE && FALSE), 1)
Debug.Trace("!TRUE || TRUE = " + (!TRUE || TRUE), 1)
Debug.Trace("!TRUE || FALSE = " + (!TRUE || FALSE), 1)
Debug.Trace("!FALSE || TRUE = " + (!FALSE || TRUE), 1)
Debug.Trace("!FALSE || FALSE = " + (!FALSE || FALSE), 1)
RESULT:
[06/21/2016 - 10:52:53PM] !TRUE && TRUE = True (this should be FALSE)
[06/21/2016 - 10:52:53PM] !TRUE && FALSE = True (this should be FALSE)
[06/21/2016 - 10:52:53PM] !FALSE && TRUE = True (correct result)
[06/21/2016 - 10:52:53PM] !FALSE && FALSE = False (correct result)
[06/21/2016 - 10:52:53PM] !TRUE || TRUE = True (correct result)
[06/21/2016 - 10:52:53PM] !TRUE || FALSE = False (correct result)
[06/21/2016 - 10:52:53PM] !FALSE || TRUE = False (this should be TRUE)
[06/21/2016 - 10:52:53PM] !FALSE || FALSE = False (this should be TRUE)
As you can see, some of the results are incorrect compared with what I would expect from the CK compiler.
Edit: HOWEVER, it does work correctly if the -O option is not used, so it is an issue with the optimization. Cheers
This issue came up a few times for me so I thought I'd make note of it. It's not a huge deal, it can just be a little bit tricky when working with larger mods with similarly named functions. Haven't looked into the compilation process used, but it should be a fairly trivial fix to look for 2 names or checking the symbol table. (Low priority / QoL fix)
Specific example:
When defining multiple global functions with the name "LandShip" inside the same namespace of a script, the compiler will give the same message as success (press any key to continue...) without any warnings. I actually noticed it was failing because it did not produce a file and the usual warning message about experimental features (the guard ones) was lacking.
dubhAutoLootFunctions.psc(28,53): Fatal Error: Unresolved function name 'GetCount'!
ObjectReference[] Function FilterLootArrayByKeywords(Formlist akKeywords, ObjectReference akActor, Float afRadius)
ObjectReference[] akResult = new ObjectReference[akKeywords.GetSize()]
ForEach Form akKeyword In akKeywords
; empty
EndForEach
Return akResult
EndFunction
The ForEach statement may be used to iterate over arrays or collections. A collection is defined as any Object which implements a method named GetCount
that accepts no parameters and returns an Int
, and also implements a method named GetAt
which accepts a single Int
argument and returns the value at that index. <expression>
is evaluated exactly once before the loop begins. FormList
and RefCollectionAlias
are two examples of objects in Fallout 4 that are considered collections.
<foreach> ::= 'ForEach' (<type>|'Auto') <identifier> 'In' <expression>
<statement>*
'EndForEach'
Formlist
uses GetSize()
, not GetCount()
. Caprica might need to take this into account.
I'm trying to compile the script Fragments\TopicInfos\tif_ffclinicr02_001c2c28.psc for starfield
the scriptname is
ScriptName Fragments:TopicInfos:TIF_FFClinicR02_001C2C28 Extends TopicInfo Const hidden
--help doesnt have any info on changing the namespace
not sure how to compile these scripts with the newer fallout 4 style scriptnames
edit: not sure how possible this is but if no namespace is provided, it would be great if it checked the scriptname for : and automatically set the namespace if found or have an option to create the name space it expected instead of just erroring out
Hi, I have some issues compiling a Skyrim script as it doesn't detect some function names. Here is an example to reproduce.
Thank you for this project. Looks very cool!
My script:
Scriptname Test extends Actor
Function Test()
GetActorBase()
EndFunction
Calling Caprica
.\Caprica.exe Test.psc -g skyrim -i "D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source" -f "D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source\TESV_Papyrus_Flags.flg"
Adding current working directory to import list: D:\Steam\steamapps\common\Skyrim Special Edition\Papyrus Compiler\Input
Adding import directory: D:\Steam\steamapps\common\Skyrim Special Edition\Data\Scripts\Source
Importing files...
Imported 13938 files.
Adding file 'Test.psc' to namespace ''.
Compiling Test.psc
Test.psc (4, 2:14): Fatal Error: Unresolved function name 'GetActorBase'!```
Hi. Thank you for this build!
Fatal Error: Unable to resolve type 'Quest'!
I'm getting the error when trying to create the pex using a psc created by Champollion for an existing game script.
I'm using the latest Champollion from here: https://www.nexusmods.com/starfield/mods/4528
I'm also using the Papyrus decompile and Compile Command Files from here: https://www.nexusmods.com/starfield/mods/3921
Caprica Papyrus Compiler v0.3.0
Compiling SKK_ShipToolsQuestScript.psc
SKK_ShipToolsQuestScript.psc (8, 7:21): Warning W4007: The script variable 'fScriptVersion' is initialized but is never used.
;*******************************************
Scriptname SKK_ShipToolsQuestScript extends Quest
Float fScriptVersion = 0.0
Event OnQuestInit()
fScriptVersion = 1.0 ;this would beg to differ
EndEvent
;*******************************************
D:\Steam\steamapps\common\Fallout 4\Data\scripts\source\LegendaryItemQuestScript.psc(52,7):
Fatal Error: Expected 'Identifier' got 'Continue'!
Looks like the error is related to:
bool continue = ListOfSpecificModsToDisallow.Find(ModToConsider as Form) <= -1
Does Caprica's Find() syntax match Champollion's?
CAPRICA.EXE (Cmake#17 Master:6346694835) unhandled syntax error:
Debug.Trace("Scriptname.FunctionName.Content", aiSeverity=0, aiSeverity=0)
Importing files...
Imported 4560 files.
WARNING: Loose input files are assumed as being in the root namespace.
Adding file 'SKK_ConsoleUtilityScript.psc' to namespace ''.
Compiling SKK_ConsoleUtilityScript.psc
Press any key to continue . . .
No compile warnings or unexpected token errors.
So, I ran into a confusing thing earlier, where one line of code in one of my scripts wasn't working as expected. I investigated, and here's some test code that demonstrates the bug:
scriptname boolTestB
struct testStruct
bool b
int i
float f
ObjectReference O
endStruct
Function testBoolStruct() Global
testStruct structA = new testStruct
testStruct structB = new testStruct
structA.b = false
structA.i = 0
structA.f = 0
structA.O = Game.GetForm(0x14) as ObjectReference ; PlayerRef
structB.b = true
structB.i = 1
structB.f = 1
structB.O = Game.GetForm(0x22B94) as ObjectReference ; some arbitrary rock somewhere
bool bFalse = false
bool bTrue = true
int i0 = 0
int i1 = 1
float f0 = 0
float f1 = 1
ObjectReference PlayerRef = Game.GetForm(0x14) as ObjectReference
ObjectReference Rock = Game.GetForm(0x22B94) as ObjectReference
Debug.Trace("##############################")
; these work properly
Debug.Trace("structA: " + structA.b)
Debug.Trace("structB: " + structB.b)
Debug.Trace("structA.b || structB.b: " + (structA.b || structB.b))
Debug.Trace("structB.b || structA.b: " + (structB.b || structA.b))
Debug.Trace("false || (bTrue != bFalse): " + (false || (bTrue != bFalse)))
Debug.Trace("false || (i0 != i1): " + (false || (i0 != i1)))
Debug.Trace("false || (f0 != f1): " + (false || (f0 != f1)))
Debug.Trace("false || (PlayerRef != Rock): " + (false || (PlayerRef != Rock)))
Debug.Trace("false || (structA.i != structB.i): " + (false || (structA.i != structB.i)))
Debug.Trace("false || (structA.f != structB.f): " + (false || (structA.f != structB.f)))
Debug.Trace("false || (structA.O != structB.O): " + (false || (structA.O != structB.O)))
; this one doesn't, with Caprica
Debug.Trace("false || (structA.b != structB.b): " + (false || (structA.b != structB.b)))
Debug.Trace("##############################")
EndFunction
I compiled two separate scripts, identical except that one was compiled with the Bethesda compiler, and the other with Caprica (boolTestB and boolTestC respectively), and here's the log output, again in the same order:
[04/17/2018 - 07:46:57PM] ##############################
[04/17/2018 - 07:46:57PM] structA: False
[04/17/2018 - 07:46:57PM] structB: True
[04/17/2018 - 07:46:57PM] structA.b || structB.b: True
[04/17/2018 - 07:46:57PM] structB.b || structA.b: True
[04/17/2018 - 07:46:57PM] false || (bTrue != bFalse): True
[04/17/2018 - 07:46:57PM] false || (i0 != i1): True
[04/17/2018 - 07:46:57PM] false || (f0 != f1): True
[04/17/2018 - 07:46:57PM] false || (PlayerRef != Rock): True
[04/17/2018 - 07:46:57PM] false || (structA.i != structB.i): True
[04/17/2018 - 07:46:57PM] false || (structA.f != structB.f): True
[04/17/2018 - 07:46:57PM] false || (structA.O != structB.O): True
[04/17/2018 - 07:46:57PM] false || (structA.b != structB.b): True
[04/17/2018 - 07:46:57PM] ##############################
[04/17/2018 - 07:47:00PM] ##############################
[04/17/2018 - 07:47:00PM] structA: False
[04/17/2018 - 07:47:00PM] structB: True
[04/17/2018 - 07:47:00PM] structA.b || structB.b: True
[04/17/2018 - 07:47:00PM] structB.b || structA.b: True
[04/17/2018 - 07:47:00PM] false || (bTrue != bFalse): True
[04/17/2018 - 07:47:00PM] false || (i0 != i1): True
[04/17/2018 - 07:47:00PM] false || (f0 != f1): True
[04/17/2018 - 07:47:00PM] false || (PlayerRef != Rock): True
[04/17/2018 - 07:47:00PM] false || (structA.i != structB.i): True
[04/17/2018 - 07:47:00PM] false || (structA.f != structB.f): True
[04/17/2018 - 07:47:00PM] false || (structA.O != structB.O): True
[04/17/2018 - 07:47:00PM] false || (structA.b != structB.b): False
[04/17/2018 - 07:47:00PM] ##############################
Also, here's the assembly after running each through Bethesda's disassembler:
JumpT ::temp15 _label9 ;@line 51
StructGet ::temp13 structA b ;@line 51
StructGet ::temp2 structB b ;@line 51
CompareEQ ::temp12 ::temp13 ::temp2 ;@line 51
Not ::temp12 ::temp12 ;@line 51
Cast ::temp15 ::temp12 ;@line 51
_label9:
Cast ::temp10 ::temp15 ;@line 51
StrCat ::temp11 "false || (structA.b != structB.b): " ::temp10 ;@line 51
CallStatic debug Trace ::nonevar ::temp11 0 ;@line 51
JumpT ::temp3 _label9 ;@line 51
StructGet ::temp3 structA b ;@line 51
StructGet ::temp3 structB b ;@line 51
CompareEQ ::temp3 ::temp3 ::temp3 ;@line 51
Not ::temp3 ::temp3 ;@line 51
Assign ::temp3 ::temp3 ;@line 51
_label9:
Cast ::temp4 ::temp3 ;@line 51
StrCat ::temp4 "false || (structA.b != structB.b): " ::temp4 ;@line 51
CallStatic debug Trace ::nonevar ::temp4 0 ;@line 51
It appears that Caprica gets really confused and starts assigning everything to ::temp3 for some reason. This also makes Champollion fail miserably on the Caprica script, it just gives up when it gets to that section. This was compiled with the now 2-year-old v0.2.0 release binary from the Nexus, without the -O flag since I know it's borked in that version.
Also, here's a link to an archive with the sources, compiled scripts, Caprica .pas output (for boolTestC), and disassembled .pas outputs for both, in case it helps any.
This is easy enough to work around, but really confusing if you run into it like I did. Any ideas?
It appears that using script wide variables causes the script to not function. It compiles without error but when called in game will not perform any action.
You can actually de-compile a working vanilla script that contains a script wide variable then recompile it without making any changes and it will no longer function.
Here is an example that compiles but does not function
const ScriptName Broken extends ObjectReference
;-- Script Wide Variables ---------------------------------------
int test = 0
Function OnInit()
test = 5
Debug.Notification("This text never appears")
EndFunction
Remove the script wide variable and suddenly it does work
const ScriptName Broken extends ObjectReference
Function OnInit()
Debug.Notification("This text does appears")
EndFunction
It seems that if there's a G:\ drive with no disk in it (in my case, a card reader), this error pops up endlessly until I stop Caprica with Ctrl-C/
I'm simply dropping a script on Caprica.exe (version 0.1.5) after having set the import folder containing the extracted Fallout 4 scripts in caprica.cfg
I've tried this with three different computers, and they all do this if there's an empty G drive. If there's a disk in G, or if it's removed or disabled, it works fine.
All the stock Fallout 4 scripts reference a G drive as their source. Perhaps that has something to do with it.
The latest build of Caprica fails early with this error:
Fatal Error: Unable to locate line at offset 481
The issue seems related to FO4_Papyrus_Flags.flg
from 0.2.
The issue does not occur with Institute_Papyrus_Flags.flg
but it also does not compile with the base game flags file.
Scripts SET confitional variables to manage terminal and message display options, example:
Bool bQuestObjectivesHidden = false Conditional ; GetVMQuestVariable condition SGMenu display options
Scripts may never GET the values as they are purely to condition UI elements which triggers a Caprica compile warning:
WARNING W4007: The script variable ABC is initalized byt is never used.
This is totally unnecessary and disrupts compiler output error checking, laeding to un-natural aces as workarounds:
Function BogusCapricaFunction() ; Unnecessary calls on conditional GetVMQuestVariables to clear caprica compiler warnings
Debug.Trace("SKK_CSNGQuestScript " + bQuestObjectivesHidden, aiSeverity=0)
EndFunction
try to compile a script that i have befor decompiled.. nothing big..
i have no idear, do i missed something.. please help me..
v0.3 errors on compiling workshopnpcscript as output by the latest Champollion- it includes the construct If (Self as Actor is companionactorscript)
.
It compiles fine if you add brackets to produce If ((Self as Actor) is companionactorscript)
It seems that Caprica doesn't support Structs yet. Can you add this? I like Caprica's language extensions, but it's unfortunate that it doesn't support structs like Beth's compiler.
Regardless of a script property or variable, Caprica v0.3.0 is not parsing decimal integer values with lead zeros as initializers correctly.
Papyrus Script:
Scriptname TestInitializer Extends Form
Int i7dc = 0007 Const
Int i8dc = 0008 Const
Int i124dc = 0124 Const
Int i7hc = 0x0007 Const
Int i8hc = 0x0008 Const
Int i124hc = 0x007C Const
ASM output from Caprica v0.3.0:
.info
.source "TestInitializer.psc"
.modifyTime 0
.compileTime 1700096266
.user "REDACTED"
.computer "REDACTED"
.endInfo
.userFlagsRef
.endUserFlagsRef
.objectTable
.object TestInitializer Form
.userFlags 0
.docString ""
.autoState
.structTable
.endStructTable
.variableTable
.variable i7dc Int const
.userFlags 0
.initialValue 7
.endVariable
.variable i8dc Int const
.userFlags 0
.initialValue 0
.endVariable
.variable i124dc Int const
.userFlags 0
.initialValue 84
.endVariable
.variable i7hc Int const
.userFlags 0
.initialValue 7
.endVariable
.variable i8hc Int const
.userFlags 0
.initialValue 8
.endVariable
.variable i124hc Int const
.userFlags 0
.initialValue 124
.endVariable
.endVariableTable
.guardTable
.endGuardTable
.propertyTable
.endPropertyTable
.propertyGroupTable
.endPropertyGroupTable
.stateTable
.state
.endState
.endStateTable
.endObject
.endObjectTable
NT
Decompiling a script with Champollion and recompiling it with Caprica inserts forced casting (explicit coercion) in a bunch of places (variable as bool, variable as float, etc...). Just noticed you mentioned this already, so editing this ticket. The explicit coercion appears to be generating errors.
Enable logging (so can see the stack traces)
in Fallout.ini, under [Papyrus]:
bEnableLogging=1
bEnableTrace=1
bLoadDebugInformation=1
Decompile workshopscript.pex in Champollion and recompile the resulting workshopscript.psc
Go to a Settlement, and move a Settler to another Settlement
or
Find an idle companion at a Settlement, ask him/her to join you, then dismiss him/her to a different Settlement
errors are generated in \Documents\My Games\Fallout 4\Logs\Script\
(note that the vanilla script generates a couple of warnings and errors on its own, but recompiling generates a bunch of new ones that appear to be casting related)
If you decompile a .pex with Champollion and recompile the .psc with Caprica, properties generate duplicate modifiers:
Before:
bool Property OwnedByPlayer = False auto conditional
bool Property PlayerHasVisited auto conditional hidden
After:
bool Property OwnedByPlayer = False auto conditional conditional
bool Property PlayerHasVisited auto conditional hidden conditional hidden
The only reason there is the restriction is because of bad coders and lazy GC. However, I'd like there to be an option to explicitly allow this. Certain things simply cannot be accomplished due to actual VM limitations but can be bypassed by encapsulating data.
One cannot do this:
function doFoo( int[] bar )
; blah
endfunction
function threadFoo( int[] bar )
var[] varpar = new var[ 1 ]
varpar[ 0 ] = bar
callfunctionnowait( "doFoo", varpar )
endfunction
However, one could encapsulate that:
struct __FooParams
int[] bar
endstruct
function __FooParamsDispose( __FooParams params )
params.bar = None
endfunction
function __doFoo( __FooParams params )
; blah
__FooParamsDispose( params )
endfunction
function doFoo( int[] bar )
__FooParams params = new __FooParams
params.bar = bar
__doFoo( params )
endfunction
function threadFoo( int[] bar )
__FooParams params = new __FooParams
params.bar = bar
var[] varpar = new var[ 1 ]
varpar [ 0 ] = params
callfunctionnowait( "__doFoo", varpar )
endfunction
Was able to do this with Fallout 4 and the CK Compiler Patch - would be great if Caprica supported this too. The VM doesn't care, it's just doing instructions, as I said - this restriction is just due to bad coders and lazy GC.
Edit: fixed typos and added code tags
The vanilla compiler can compare strings. It behaves like you would expect from any sensible language, returning the alphabetical order.
However, Caprica doesn't allow you to compile a script which attempts to compare two strings.
Please note that this is developed against MSVC 2015, so other compilers may not work, and earlier versions of MSVC almost certainly won't due to C++11 support.
That's what the readme says, but Caprica.vcxproj is configured for ToolsVersion="15.0"
and <PlatformToolset>v141</PlatformToolset>
.
The project also contains paths for boost_1_58_0
, but CMakeLists.txt
is looking for:
find_package(Boost 1.52.0 COMPONENTS filesystem program_options REQUIRED)
Just trying to open the project in VS2015 was a pain, but opening the project in VS2017 was a breeze. That said, I can't get Caprica to compile due to a wide assortment of errors. I was wondering if you could fix this project up so that it can be compiled, as well as some build instructions.
I'm not sure if this project is still active, but I still use Caprica constantly because I appreciate the faster compile speed and language extensions, so I'll leave this here anyway.
I was profiling a bit of code that looks like this, tweaking a mediocre random number generator I wrote to be "good enough", but more importantly is like 200x faster than Utility.RandomInt/Float because it isn't a delayed function.
LCG n = GetLCG()
float[] weights = new float[10]
float totalWeight = 0
for int i = 0 to 9
weights[i] = (i+1)*0.1
totalWeight += weights[i]
endfor
int[] bins = new int[10]
for int i = 0 to 10000
bins[WeightedRandom(n, weights, 5.5)] += 1
endfor
So I noticed that bins[] was coming out with a uniform distribution, which is wrong, and that WeightedRandom() was getting run 20k times instead of the intended 10k. Obviously that means that that line of code is equivalent to
bins[WeightedRandom(n, weights, 5.5)] = bins[WeightedRandom(n, weights, 5.5)] + 1
Tweaking it just slightly to pre-calculate the index fixed it, e.g.
int index = WeightedRandom(n, weights, 5.5)
bins[index] += 1
I suppose this makes sense in a way, but it's something to be aware of in case you're trying to write lazy test code like me.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.