rofl0r / agsutils

contains utils for AGS: game extractor, repacker, disassembler and assembler
44 stars 14 forks source link

Random bug in kathy rain if you're interested #3

Open i30817 opened 5 years ago

i30817 commented 5 years ago

In the start of chapter 2, it's possible to, after talking to the guy that wants his internet back and opening the envelope to get a 'boot disk' without the bottom 'Read Instructions on floppy disk' context button, which is a dead end because it has the phone number of the IT guy Clyde.

You can't even press it (555-8181) manually without the number in the notebook (with it, you can on).

This is apparently very random because it happened to me now, then i restarted the game and it didn't again. It's very strange and i have no clue how to make it happen.

i30817 commented 5 years ago

I think this comes from restoring save games after starting a new game.

I think the game is storing 'stuff' in savegames and reacts badly in some conditions:

https://github.com/onitake/ags/pull/2/files#diff-5a305e749373fc1ae503198497ac396cL505

Maybe i screwed up and deleted the agssave.999 autosave? Or maybe i used a later autosave on the dir when i was restoring it. Whatever, i'm tired of thinking about this, just documentation that this bug can happen and it can be avoided by (probably) deleting all the saves and restarting....

i30817 commented 5 years ago

I found something interesting now on the current version of AGS 3.5 with a tentative fix for https://github.com/adventuregamestudio/ags/issues/799. If you run the game once (with the right libagsspritefont.so) you'll start the game, but if you retry, it'll crash on a script inside. Then if you delete the copy of acsetup.cfg on ~/.local/share/ags/Kathy Rain it starts booting again. I'd expect the problem to be on the automatic agssave.999 (from trying to read the extra data from my previous post) but no.

For some reason this game really really hates the write redirection to the savegame dir i was trying. Setting user_data_dir=. 'fixes' it at the cost of making the game not work on readonly dir again.

gdb doesn't say anything about the return values of the function that calculates those paths, so i'm at a loss:

Thread 1 "ags" hit Breakpoint 1, ResolveScriptPath (orig_sc_path=..., 
    read_only=true, rp=...)
    at /media/i30817/Huginn/Documents/projects/ags/Engine/ac/file.cpp:361
warning: Source file is more recent than executable.
361     rp.BaseDir = parent_dir;
(gdb) n
362     rp.FullPath = full_path;
(gdb) n
363     rp.AltPath = alt_path;
(gdb) n
364     return true;
(gdb) printf "%s\n%s\n%s\n", parent_dir.GetCStr(), full_path.GetCStr(), alt_path.GetCStr()
/home/i30817/.local/share/ags/Kathy Rain/
/home/i30817/.local/share/ags/Kathy Rain/acsetup.cfg
./acsetup.cfg
(gdb) c
Continuing.
[Thread 0x7fffdf7fe700 (LWP 18102) exited]
An error has occurred. Please contact the game author for support, as this is likely to be a scripting error and not a bug in AGS.
(ACI version 3.5.0.15)

in "ConfigHelper.asc", line 61
from "ConfigHelper.asc", line 145
from "GlobalScript.asc", line 2117

Error: Error running function 'game_start':
Error: Null pointer referenced

[Thread 0x7fffeffff700 (LWP 18100) exited]
[Thread 0x7fffdd10d700 (LWP 18104) exited]
[Thread 0x7fffdda4f700 (LWP 18103) exited]
***** ENGINE HAS SHUTDOWN
[Thread 0x7ffff59da700 (LWP 18097) exited]
[Thread 0x7ffff59de240 (LWP 18093) exited]
[Inferior 1 (process 18093) exited with code 0133]
ghost commented 5 years ago

A random idea: they have something important in the config file that comes with a game and are reading it in script. Maybe they don't expect that wanted data may not be available.

i30817 commented 5 years ago

Yeah, that was what i was thinking (first for the zip because of the code for it in this pull request above has something to add 'extras' to saves). But since it's crashing after reading the location of acsetup and before reading the save, and removing that acsetup copy makes it boot, maybe there is something to what you're saying.

However, one problem: i already modified the file so it's not even near the original and it boots the first time, which makes me think the path itself is the problem:

[override]
os=win
[sound]
speechvol=50
musicvol=50
mastervol=100
midiid=NONE
[graphics]
game_scale_fs=max_round
windowed=0
[language]
translation=
[mouse]
auto_lock=0
speech_hold=0
speed=2,0
[misc]
//user_data_dir=.

Maybe the first run of the original exec is supposed to add a line to the cfg...

os=win btw, works around the scrolling bug you mention in the wiki because it's a older version of a 'smooth scrolling' script being stupid about !windows.

I'll revert my modifications for that other RO filesystem bug and try again to see if the behavior is the same in the debugger.

edit: reverting the modifications, fallsback to '.' as the main parent dir. Makes sense because linux is using parent_dir = MakeAppDataPath(); as the fallback and in linux that function is not defined in 3.5...

i30817 commented 5 years ago

Ahah, the copy is very different from the original

[graphics]
render_at_screenres=0
windowed=0
[language]
translation=
[mouse]
control_enabled=1
speed=2,000000

edit: apparently it's kind of normal, at least according to Heroine's quest.

i30817 commented 5 years ago

It's like this game in particular can't abide the partial file... I'll try to reintroduce the modifications and copy the complete acsetup.cfg file to the save dir after the first run. If it starts, the game is manually reading the config and fails at considering it a onion...

Yeah... that worked. Pretty sad. I suspect this will need a script patch to figure out what the game is doing and fix clearly.

i30817 commented 5 years ago

Weird. Deleting the game save dir (and any saves inside the game dir), making the game directory read only an making the acsetup include:

[misc]
shared_data_dir=.
user_data_dir=.

Makes the saves save and load on the ~/.local/share/ags/Kathy Rain, but the acsetup.cfg load from the game dir. I suppose there is a further fallback somewhere.

The debug session in this case is quite mysterious:

Thread 1 "ags" hit Breakpoint 1, ResolveScriptPath (orig_sc_path=..., 
    read_only=true, rp=...)
    at /media/i30817/Huginn/Documents/projects/ags/Engine/ac/file.cpp:361
361     rp.BaseDir = parent_dir;
(gdb) n
362     rp.FullPath = full_path;
(gdb) n
363     rp.AltPath = alt_path;
(gdb) n
364     return true;
(gdb) printf "%s\n%s\n%s\n", parent_dir.GetCStr(), full_path.GetCStr(), alt_path.GetCStr()
./
./acsetup.cfg
./acsetup.cfg
(gdb) c
Continuing.

Thread 1 "ags" hit Breakpoint 1, ResolveScriptPath (orig_sc_path=..., 
    read_only=true, rp=...)
    at /media/i30817/Huginn/Documents/projects/ags/Engine/ac/file.cpp:361
361     rp.BaseDir = parent_dir;
(gdb) n
362     rp.FullPath = full_path;
(gdb) n
363     rp.AltPath = alt_path;
(gdb) n
364     return true;
(gdb) printf "%s\n%s\n%s\n", parent_dir.GetCStr(), full_path.GetCStr(), alt_path.GetCStr()
./
./*.tra
./*.tra
(gdb) c
Continuing.

Thread 1 "ags" hit Breakpoint 1, ResolveScriptPath (orig_sc_path=..., 
    read_only=true, rp=...)
    at /media/i30817/Huginn/Documents/projects/ags/Engine/ac/file.cpp:361
361     rp.BaseDir = parent_dir;
(gdb) n
362     rp.FullPath = full_path;
(gdb) n
363     rp.AltPath = alt_path;
(gdb) n
364     return true;
(gdb) printf "%s\n%s\n%s\n", parent_dir.GetCStr(), full_path.GetCStr(), alt_path.GetCStr()
./
./*.tra
./*.tra
(gdb) c
Continuing.

Thread 1 "ags" hit Breakpoint 1, ResolveScriptPath (orig_sc_path=..., 
    read_only=true, rp=...)
    at /media/i30817/Huginn/Documents/projects/ags/Engine/ac/file.cpp:361
361     rp.BaseDir = parent_dir;
(gdb) n
362     rp.FullPath = full_path;
(gdb) n
363     rp.AltPath = alt_path;
(gdb) n
364     return true;
(gdb) printf "%s\n%s\n%s\n", parent_dir.GetCStr(), full_path.GetCStr(), alt_path.GetCStr()
./
./acsetup.cfg
./acsetup.cfg
(gdb) c
Continuing.

Since after the after the first two file searches it's the 'load game' and the last 'Continuing' is right after a the load is complete in ~/.local/share/ags/Kathy Rain. The engine is not using that fallback code to search for saves, just acsetup and translation files. Weird.

ghost commented 5 years ago

ResolveScriptPath is only called when a script searches for file, with File.Open, File.Exists, and so on.

Engine itself does not use it (because there's no need, it already knows the full path to saves before the game begins).

i30817 commented 5 years ago

I had expected that user_data_dir=. would nuke my chances of having working saves (it + the readonly squashfs dir is my hack for the weird crash with using the gamedir acsetup.cfg and still use the savegames by making the game think it can only read from the read-only dir for acsetup.cfg).

But that doesn't happen, so it sort of works. Maybe there is a fallback wherever that code is used.

I'm also 95% sure this will break in the future, if that other problem with the telephone guy on chapter 2 doesn't manifest again. Oh well, this game and whispers of a machine are barely working on the upstream ags anyway... I only bother because they're good games.

Moral of the story, ask game devs not to fork AGS pretty please.

i30817 commented 5 years ago

I found which property the game is trying to read on the acsetup.cfg on the save dir and crashes by process of elimination.

[sound]

If you add this it doesn't crash and in the next (3rd) run that acsetup.cfg has:

[sound]
speechvol=50
musicvol=50
mastervol=100

(those others are probably script defaults because i changed them on the original acsetup.cfg to test if it was from there)

to be clear: 1rst run: acsetup.cfg on savedir gets created, runs 2nd run: if acsetup.cfg on the save dir doesn't have the [sound] subsection, even if empty, crash, otherwise run and create defaults for that subsection. 3rd run and rest: keep running or crashing depending on results of 2nd.

So the game script is trying to read/create defaults for the sound volume and panics if the acsetup.cfg it's given doesn't have that tag. It was the only part of the 'menu' screen that wasn't already in the savedir acsetup.cfg.

Maybe worth it to ask for AGS upstream to copy.

i30817 commented 5 years ago

FYI, i found this bug can be worked around (if the path to writable files when the game dir is readonly in linux is fixed on upstream ags, which is the only time it manifests); if you add a [sound] section with midiid=NONE on the game dir acsetup.cfg.

You see, in spite of that being a no-op, since the game doesn't use midi, that's a property the ags Engine copies into the acsetup.cfg copy it does on the save dir, thus creating the [sound] section, which is enough to avoid the crash.

i30817 commented 5 years ago

Also this goddamn game original acsetup.cfg has a property named 'firstboot' which is probably the reason of this weirdness. Damn engine forks.

ghost commented 5 years ago

I do not think this is related to engine fork in any way. At least these custom options are not found in their engine code. Most probably they are reading and writing that config in script, and that is something anyone can do in official AGS right now.

EDIT: there's "first_load" string found in the exe, which seem to be a part of the script strings (surrounded by dialog texts)

i30817 commented 5 years ago

Yeah it's first_load, i was typing it out of memory from the day before.

You're right about this game in particular 'extra' options being saveable and remembering across restarts - though speech_hold=1 does a peculiar effect in the intro menu before it settles down, though maybe that's just my replacement libagsspritefont.so not liking it.

But for instance Lamplight City equivalent options (volumes and mouse hold) are all reset on load or restart, so that was the case i was thinking. I believe technobabylon does the same thing. It may be related to that utils dll from wadjet eye games. Their studio 'achievements' files do work, possibly because it's pure script again instead of a dll that is stubbed.

Meh, i got the game working, even if i doubt the average person to figure it out alone, so this was just mostly to add this info to the wiki here.

Though i'm worried about the original bug on chapter 2. I don't know what's broken there for sure, but i suspect the load save function on the fork serializing and deserializing extra 'stuff'. If I'm right this is the only case of this being used in this game, which is a bit annoying. I haven't played Whispers of a machine to see if there is a deadend bug like that there yet (and i probably won't without the bugged 'scanner' being fixed, i should probably clone the project and build that version of libagsspritefont.so to see if it fixes it).

If you want more games that do 'weird stuff', Until I have You takes 30 seconds to start up on a compressed drive because it decides to create savegames with 40 mb on startup for some reason. It's not even the compression or the game being 1.1 gb not counting the audio and music, it's the saves. edit: though i tried it just now on a non-debug build and it's 'only' 22mb and loads up faster, so apparently savegames are worse on debug, good to know.