Time for an update!
Now that I've recovered from the nwnnsscomp-induced heart attack, I'm back to solving the last of the reverse compiler challenges. (When I complete the reverse compiler, I'll provide a detailed explanation of NCS, how NCS maps to NWScript, how the reverse compiler works, and why I'm so critical of nwnnsscomp.)
The current problem I'm working on looks rather simple:
RSADDx
JSR
JSR
We see a reservation on the stack, followed by two subroutines. NCS rules for subroutines are as follows:
Optionally, reserve one or more values on the stack
Optionally, place subroutine arguments on the stack
Call the subroutine
You can see the disaster coming: it's those optional bits. In the example, the first subroutine might return a value. If so, that value is the argument of the second subroutine.
But, RSADDx is also used to create uninitialized named variables in NWScript, for example:
int i; //RSADDI
float f; //RSADDF
string s; //RSADDS
You should never create uninitialized variables, but it was once a common practice. Numerous examples exist in NCS files, and that means the example code can also be read as an uninitialized variable, followed by two subroutines that do not take parameters and return void. In other words:
Sub1(Sub0());
//vs
int i;
Sub0();
Sub1();
So, how do we figure this out? Unfortunately, we have to look inside each of the JSR calls. Which means yet another rewrite of my reverse compiler. It's not the worst thing in the world since the purpose of a reverse compiler is to reconstruct JSR calls. But more work must be done to determine dependencies, change the order in which things are analyzed, and store the resulting information for later use.
I don't work this hard in my paying job...