darknesswind / nutcracker Goto Github PK
View Code? Open in Web Editor NEWfork from DamianXVI's squirrel decompiler
fork from DamianXVI's squirrel decompiler
If a while
loop contains a continue
statement, NutCracker may misinterpret the loop as a do/while loop. For example, take this code:
local i,x
while(true)
{
if(x) continue;
i++;
}
If you compile and decompile it, you get this:
do
{
local i;
local x;
// [002] OP_JZ 4 4 0 0
}
while (x);
i++;
The problem is that PreprocessDoWhileInfo
sees the backwards jump from the conditional continue
statement and thinks that's the end of a do/while loop and it gets decompiled that way even though later instructions wouldn't make sense if it was a do/while loop.
The right fix is probably to rewrite NutCracker to do a proper basic block analysis rather than just looking for patterns in the instruction stream, but a hacky fix could be to abort do/while loop decompilation and retry as a non-do/while loop if NutCracker gets into a situation it doesn't understand.
Compile this code:
i = 1
do j = 0 while (j)
Then decompile it with NutCracker. The result is:
do
{
this.i = 1;
this.j = 0;
}
while (this.j);
In general, one operation before a do/while loop is included in the loop. I believe the reason is that the beginning of the loop is calculated incorrectly. In PreprocessDoWhileInfo
there's this line:
int beginPos = endPos + curInst.arg1;
I think it should be replaced with:
int beginPos = endPos + 1 + curInst.arg1;
新的DLC野兽和探索出了,我们目前在做战场兄弟新版本的汉化,但是没法用现在的项目代码解开加密的cnut文件,我们也缺少能做C++编码工作的人。
请问大佬能帮个忙吗?
Try compiling this function.
function foo() {
foreach(o in list) {
if (a) { continue; }
b.push();
}
}
Then decompile it. You get this result:
function foo() {
foreach(o in list) {
if (a) { continue; }
a.push();
}
}
In fact, whatever the condition of the 'if' is will be duplicated on the next line. E.g.
if (a.query(1,2,3)) { continue; }
b.push();
becomes:
if (a.query(1,2,3)) { continue; }
a.query(1,2,3).push();
when it's placed inside the foreach loop.
When new state blocks are pushed onto the state stack, the loop state is not reset. This causes failures processing nested loops. Try compiling and decompiling this code:
while(true)
{
if (true)
{
if (true)
{
continue;
}
}
for(local i = 0; i <= 10; i++)
{
if (true)
{
continue;
}
else if (true)
{
if (true)
{
}
else
{
}
}
}
}
The output is:
while (true)
{
if (true)
{
if (true)
{
continue;
}
}
local i = 0;
while (i <= 10)
{
if (true)
{
continue;
}
else if (true)
{
if (true)
{
}
else
{
}
}
i++;
}
}
The 'for' loop has changed into an infinite loop. I think when pushing states onto the state stack, the loop flags should be cleared:
// While block found - push loop block
BlockState prevBlockState = state.m_BlockState;
state.m_BlockState.inLoop = BlockState::WhileLoop;
state.m_BlockState.loopFlags = 0; // CLEAR THE LOOP FLAGS
Consider this small script:
while(true) {
if(!this.condition) {
if(--n <= 0) { break; }
}
}
When compiled and decompiled, the output is:
while (true) {
if (!this.condition) {
if (--this.n <= 0) {
}
}
}
The 'break' statement is missing, resulting in an infinite loop.
NutCracker loses precision when outputting floating point numbers. For example, the script print(1e-16);
is decompiled as this.print(0.00000000);
which is clearly not the same thing. The script print(0.9);
is decompiled as this.print(0.89999998);
which might be the same thing but also might not be, and definitely looks bad.
Also, the output is ugly. print(1.0);
is decompiled as this.print(1.00000000);
. This gets especially noticeable with math-heavy code.
Basically, the floating-point number printer should be fixed to print in a round-trippable format that's no longer than necessary to convey the correct value.
In DecompileLoopJumpInstruction
, if the jump is backwards to the start of the loop it may decompile it as a continue statement, but it doesn't set the UsedBackwardJumpContinue
flag, so the postprocessing step may convert it to a for loop improperly. See this code:
local i,x
while(true)
{
if(x) continue;
i++;
}
When compiled and decompiled (assuming some other bugs are fixed), the result is:
local i;
for( local x; true; i++ )
{
if (x)
{
continue;
}
}
This is not equivalent. In the first example, the increment is skipped if x
is true but in the second example it is not.
The OP_NEWOBJ opcode is missing a break statement in the switch:
case NOT_CLASS:
{
ExpressionPtr attributes;
ExpressionPtr baseClass;
if (arg1 != -1)
baseClass = state.GetVar(arg1);
if (arg2 != 0xff)
attributes = state.GetVar(arg2);
state.SetVar(arg0, ExpressionPtr(new NewClassExpression(baseClass, attributes)));
// MISSING BREAK HERE!
}
default:
assert(0);
break;
This causes a crash when it falls into the assert(0).
It looks like the for loop postprocessing code can drop the increment statement in one case:
StatementPtr incrementStatement = GetForIncrementStatement();
if ((prevStatement->GetType() != Stat_LocalVar) && !incrementStatement)
return StatementPtr();
GetForIncrementStatement
actually removes the increment statement from the block, and then if it doesn't get used (because GetType() == Stat_LocalVar), I believe it just disappears.
Compile the following code:
for(;;) i++;
and then decompile it. The result is:
local i;
i++;
// [002] OP_JMP 0 -2 0 0
NutCracker doesn't realize that it's in a loop.
Good time of day.
How do I change the font location?
There are no values in nut files.
你好 请问这个源码应该用什么版本的C++编译 我用2013编译后运行总是报错
I was looking at some .nut file and found NutCracker, as its advised on the readme, it doesn't work, luckily i have some experience on lua so i quickly fixed it to dump the files.
First thing to add NutScript.cpp > LoadFromStream
if (reader.ReadInt32() != sizeof(uint16_t))...
using wide string you will use 2 bytes for each char
BinaryParser.h > ReadSQString
static char* buff;
static std::vector<char> buf;
int len = ReadInt32()*2;
if (len < 0)
len = 0;
if (buf.size() < (size_t)len)
buf.resize(len);
buff = (char*)malloc(len/2);
Read((void*)buf.data(), len, true);
WideCharToMultiByte(/*CP_ACP*/0, 0, (wchar_t*)buf.data(), len, buff, len/2, 0, 0);
str.assign(buff, len/2);
delete buff;
dirty code, note len is multiplied by 2 and then WideCharToMultiByte is used to convert text to ansi representation. You should include windows.h on BinaryParser.h in order to use encoding api.
NutCracker uses the .OCP culture, which prints numbers according to the user's locale. So in countries that use the comma as the decimal separator rather than the period, a number like 1.25 gets printed as 1,250000. Then, NutCracker tries to find a period in it:
if (m_text.indexOf('.') == std::string::npos)
m_text.append(L".0");
Since it doesn't find one, it adds ".0" to the end, resulting in "1,250000.0", which is not a valid Squirrel number.
NutCracker should use a fixed culture that always uses the period as the decimal separator.
Sorry to trouble you, but every time I try to decompile this particular cnut, I keep getting the "accessing non valid stack position" error. I have tried to debug the code, and it looks like the BinaryReader is mangling some of the int32 reads, but I'm not advanced enough with C++ to be able to figure out why.
If you're still working at all on this repo, could you please take a look? Would greatly appreciate it.
In some cases void NutFunction::DecompileDoWhileLoop( VMState& state, int endPos) const
endPos > state.IP()
which causes ExpressionPtr condition;
to be null and crash later when printing DoWhileStatement.
Tested on: script.zip
Here is another sample that causes NutCracker to crash: script.zip
Now it's something to do with foreach being inside a loop. At least that is what a quick look in debugger gives.
It starts decompiling statements inside a loop and goes outside that loop when processing OP_FOREACH instruction in that sample (foreach loopEndIp
ends up being higher that the end of a parent loop).
Maybe FOREACH loop decompiling is not up-to-date with the recent changes to loops or determination of a loop block is incorrect in some cases.
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.