unrpyc icon indicating copy to clipboard operation
unrpyc copied to clipboard

AttributeError: 'RawBlock' object has no attribute 'statements'

Open zorbathut opened this issue 1 year ago • 11 comments
trafficstars

Ran into an error when decompiling a free game. The full stacktrace:

Traceback (most recent call last):
  File "/data/bulky/games/x/util/unrpyc/unrpyc.py", line 260, in worker
    return decompile_rpyc(filename, args.clobber, args.dump, no_pyexpr=args.no_pyexpr,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/bulky/games/x/util/unrpyc/unrpyc.py", line 202, in decompile_rpyc
    decompiler.pprint(out_file, ast, options)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 54, in pprint
    Decompiler(out_file, options).dump(ast)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 109, in dump
    super(Decompiler, self).dump(ast, skip_indent_until_write=True)
  File "/data/bulky/games/x/util/unrpyc/decompiler/util.py", line 49, in dump
    self.print_nodes(ast)
  File "/data/bulky/games/x/util/unrpyc/decompiler/util.py", line 140, in print_nodes
    self.print_node(node)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 122, in print_node
    self.dispatch.get(type(ast), type(self).print_unknown)(self, ast)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 499, in print_init
    self.print_nodes(ast.block)
  File "/data/bulky/games/x/util/unrpyc/decompiler/util.py", line 140, in print_nodes
    self.print_node(node)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 122, in print_node
    self.dispatch.get(type(ast), type(self).print_unknown)(self, ast)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 170, in print_image
    self.print_atl(ast.atl)
  File "/data/bulky/games/x/util/unrpyc/decompiler/__init__.py", line 127, in print_atl
    self.linenumber = atldecompiler.pprint(
                      ^^^^^^^^^^^^^^^^^^^^^
  File "/data/bulky/games/x/util/unrpyc/decompiler/atldecompiler.py", line 7, in pprint
    return ATLDecompiler(out_file, options).dump(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/bulky/games/x/util/unrpyc/decompiler/atldecompiler.py", line 26, in dump
    self.print_block(ast)
  File "/data/bulky/games/x/util/unrpyc/decompiler/atldecompiler.py", line 45, in print_block
    if block.statements:
       ^^^^^^^^^^^^^^^^
AttributeError: 'RawBlock' object has no attribute 'statements'

If you'd like to reproduce it, grab https://hatchet-games.itch.io/maeves-academy and try decompiling the file goth_wardrobe.rpyc.

I'm using v2.0.0. Worked fine on all the other files, so it's probably not my environment.

(Thanks for the Py3 update!)

zorbathut avatar Mar 06 '24 05:03 zorbathut

~Ok, this was a breeze. I got this already and can make later a pull req for it.~

Basically "RawBlock" can be without attrs(fix this and more will come) and we need now to check against it. Like in many other places in the last years. I have the feeling that's not last we've seen of this type of bug.

Edit: A little bit too fast... With the fix (dev_fix_ish197) it decompiles without error but the resulting .rpy is "strange". A lot of image statement blocks without content. goth_wardrobe.zip

madeddy avatar Mar 06 '24 09:03 madeddy

Basically "RawBlock" can be without attrs(fix this and more will come) and we need now to check against it. Like in many other places in the last years.

Huh, I wonder what causes that. renpy should unconditionally assign self.statements in RawBlock.__init__.

I have a feeling more is afoot there.

CensoredUsername avatar Mar 06 '24 11:03 CensoredUsername

Try decompiling that single file with legacy ;)

CensoredUsername avatar Mar 06 '24 12:03 CensoredUsername

Damn. I had the thought to try this, so why didn't i? 😩 So my other thought could very well also fit: A legacy game put on v8 without forced recompile, huh?

@zorbathut

  • Could you look in which Ren'Py version the previous game versions run? I'm curious.
  • To decompile this app you need to run the unrpyc legacy (v1.3 with python2).

madeddy avatar Mar 06 '24 13:03 madeddy

To decompile this app you need to run the unrpyc legacy (v1.3 with python2).

No, only to decompile that single file. The rest was compiled with 8.1, go figure why.

CensoredUsername avatar Mar 06 '24 15:03 CensoredUsername

@zorbathut

* Could you look in which Ren'Py version the previous game versions run? I'm curious.

I'm afraid I don't actually have any older versions, nor do I see a way to get them off Itch.

* To decompile this app you need to run the unrpyc legacy (v1.3 with python2).

Aha, wild. Not going to worry about this for now then! Thanks for looking into it :)

zorbathut avatar Mar 06 '24 16:03 zorbathut

No, only to decompile that single file.

Strange. I decompiled and astdumped everything error free with v1.3.0. Tested the game even a few minutes(fast skip) and encountered no issues. Very curious.

The rest was compiled with 8.1, go figure why.

Ah no. I found a few more in there from different dates all the way back to 2022, but the most are as you said. 🤷🏻 More riddles...

madeddy avatar Mar 06 '24 17:03 madeddy

Strange. I decompiled and astdumped everything error free with v1.3.0. Tested the game even a few minutes(fast skip) and encountered no issues. Very curious.

Seems for everything I ripped out of either, both legacy and master are still pretty decent at decompiling stuff from either py2 or py3 based renpy. Honestly pretty surprising.

CensoredUsername avatar Mar 07 '24 03:03 CensoredUsername

Just FYI, I'm fine with this being either closed or kept open for discussion. If possible, though, I'd request some check to ensure you're using the right version - I'm certain I won't be the last person to report something that ends up being "wrong renpy version", and a more descriptive error message would go a long way to stop that.

This assumes there's a good way for code to tell which version the .rpyc is, though.

. . . although, given the discussion here, maybe with an optional flag to try anyway. Or do the check only on error.

zorbathut avatar Mar 07 '24 07:03 zorbathut

I'm currently of the opinion that doing both is a good idea. I've added some detection, but the format makes it hard to detect all. That said, if we can't detect it, we might just be able to decompile it decently. The detection just prints a warning, and I'll add back a few pre ren'py 8 features (basically just ubiquitous parameter / argument handling). That will likely be enough to support basically every ren'py 7 game as well. Not sure how much more it'd be able to decently handle. 6.17 is the first I know it definitely won't as that requires old-style screenlang handling. Rest is some much more minor backwards compatibility hazards.

CensoredUsername avatar Mar 09 '24 00:03 CensoredUsername

See #199

CensoredUsername avatar Mar 10 '24 02:03 CensoredUsername

This has all been implemented.

CensoredUsername avatar Apr 16 '24 22:04 CensoredUsername