Community

SCI Programming => SCI Development Tools => Topic started by: Charles on June 12, 2016, 11:05:00 PM

Title: Recreating complete QFG1 EGA source code
Post by: Charles on June 12, 2016, 11:05:00 PM
So I've decompiled QFG1 EGA, and am going through trying to replace any proc# or localproc# with actual descriptive names (same for global variables, etc), and it's going fairly well. I'd say I'm learning a lot about SCI scripting in the process, but there are a couple of items/questions that have come up that I'm not sure how to interpret.

1) in script 968 (SmoothLooper) the doit method of the SmoothLooper class decompiles into this:
Code: [Select]
(method (doit &tmp [temp0 2])
CorruptFunction_CantDetermineCodeBounds
)
So clearly something unexpected is going on with the byte-code. I'm not really too keen on understanding the raw byte-code, but at the very least is there a way to force SCICompanion to try converting it to asm?  Unless anybody has an other suggestion?

2) kernel_113 is called in several scripts (1, 255, 202, 32).  Is this an undocumented kernel call?  No clue what it is supposed to do.

3) DoSound. The documentation (http://scicompanion.com/Documentation/Kernels/DoSound.html?highlight=sndCHECK_DRIVER) says that DoSound(sndCHECK_DRIVER) only returns TRUE or FALSE, depending if the sound driver is installed, however there is code in QFG1 to the effect of (DoSound(sndCHECK_DRIVER) == 1), (DoSound(sndCHECK_DRIVER) <= 4), (DoSound(sndCHECK_DRIVER) > 4), implying it is more along the lines of the number of music channels present.

That's about all I have for now. Thanks for reading.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 13, 2016, 12:02:42 AM
1) Companion currently determines the boundaries of a function by getting the start point (which is known). Then, it walks the code from that point, looking for a ret (return) opcode that is beyond the target of any branch instructions encountered yet. The problem is, the script in question is corrupt (using SV.exe, look at the bnt instruction just after code_01b7) - there is a branch that goes way off into space (clearly it must be in code that is never actually executed at runtime - I wonder how Sierra's compiler generated this byte code though). This confuses Companion, so it can't determine where the function ends (hence not even being able to fall back to asm). You can see a little message about this in the decompiler output window (ERROR: Invalid branch target).

Let me think what I can do about this. I can probably use the starts of other functions to at least know that that branch goes out of bounds. I maybe not be able to produce readable code, but at least it could fall back to asm.

2) That might be Joystick. From ScummVM (unimplemented): // Subfunction 12 sets/gets joystick repeat rate

3) It probably is. Much of the SCI0 documentation was copied from the SCI Studio docs. Indeed, ScummVM calls it "DoSoundGetPolyphony".
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 13, 2016, 12:59:11 AM
I got it to output some source code (attached).

The bad branch instruction got replaced by -17747 in the source code ($baad in hex). So it's not "correct", but that code must never get hit anyway (as it would crash)

Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 13, 2016, 03:06:11 AM
So I've decompiled QFG1 EGA, and am going through trying to replace any proc# or localproc# with actual descriptive names (same for global variables, etc), and it's going fairly well.

That's a cool project!
Would you be sharing your results when you're done? We could all learn from your work.

2) kernel_113 is called in several scripts (1, 255, 202, 32).  Is this an undocumented kernel call?  No clue what it is supposed to do.

2) That might be Joystick. From ScummVM (unimplemented): // Subfunction 12 sets/gets joystick repeat rate

Actually, kernel 113 is "Intersections (https://github.com/scummvm/scummvm/blob/0a5a722b027303e29b486e146a580f7881ca2a2d/engines/sci/engine/kpathing.cpp#L849)":
Code: [Select]
Computes the nearest intersection point of a line segment and the polygon
set. Intersection points that are reached from the inside of a polygon
are ignored as are improper intersections which do not obstruct
visibility
Parameters: (PathfindingState *) s: The pathfinding state
            (const Common::Point &) p, q: The line segment (p, q)
Returns   : (int) PF_OK on success, PF_ERROR when no intersections were
                  found, PF_FATAL otherwise
            (Common::Point) *ret: On success, the closest intersection point
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 13, 2016, 03:46:45 AM
I think that's for SCI1 - this is SCI0, where Joystick is 113 (the parameters also match the signature). Open the game in Scumm, go to the debugger and type "functions" and you'll see.

Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 13, 2016, 03:56:54 AM
Sorry - you are correct!
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 13, 2016, 12:55:28 PM
I was just looking at the script from Hero's Quest and it seems to be different from that of QfG1EGA.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 13, 2016, 02:51:32 PM
2) That might be Joystick. From ScummVM (unimplemented): // Subfunction 12 sets/gets joystick repeat rate
Oh yeah, that makes sense... they're setting the joystick repeat rate to 0 before disposing... so that'd make the 12 actually jsCALL_DRIVER.

I got it to output some source code (attached).
Oh, that's awesome!  I've put that code in place.

I was just looking at the script from Hero's Quest and it seems to be different from that of QfG1EGA.
Yeah, I noticed the same thing.  At least HQ v1.000 does.  HQ v1.102 looks more or less identical to QFG1 v1.2.

I just assumed that's the same style as the earlier KQ4 and LSL2 scripts. I haven't looked at those myself.  There's a bunch of differences in HQ v1.000. Like each script starts off with the line (version 2), Includes and Uses have quotes around the filenames, the script number declaration doesn't use the # sign, there are no temp variables created with a procedure (instead there's a var declaration at the start of applicable procedures, public is called exports and its index then name, instead of the other way around.  Oh and it looks like {} are not used on embedded strings.  Dunno if there are any other differences... or what it really means.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 13, 2016, 03:07:35 PM
I just assumed that's the same style as the earlier KQ4 and LSL2 scripts. I haven't looked at those myself.  There's a bunch of differences in HQ v1.000. Like each script starts off with the line (version 2), Includes and Uses have quotes around the filenames, the script number declaration doesn't use the # sign, there are no temp variables created with a procedure (instead there's a var declaration at the start of applicable procedures, public is called exports and its index then name, instead of the other way around.  Oh and it looks like {} are not used on embedded strings.  Dunno if there are any other differences... or what it really means.

None of this has anything to do with the game itself. It just means you have set the game language to "Sierra Script" as opposed to "SCI Studio" when decompiling (set in Game->Properties).

Things should default to Sierra Script, unless Companion thought you had been editing it in SCI Studio (I think if it detects a game.ini in your game folder that doesn't have an explicit language setting, it might do that).

Some relevant documentation links:
http://scicompanion.com/Documentation/scripts.html#sierra-script
http://scicompanion.com/Documentation/sci_compiler.html
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 13, 2016, 05:39:00 PM
Oh. That explains why the syntax is like a slightly distorted version to what I'm familiar with. Not sure on why it defaulted to that though. I copied the game.ini file from my QFG1-EGA decompile, so I could keep the filenames I'd settled on.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 13, 2016, 07:11:47 PM
I might be able to be more aggressive about defaulting to Sierra script. I guess I was trying to avoid problems for people with existing games that didn't want to convert their stuff over to the new Sierra script syntax.  Maybe if the language is "unspecified", then I can check and see if there is a Main.sc that is in the old syntax. If not, then I will assume Sierra script.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 14, 2016, 01:07:27 AM
Let me know if you're interested in trying out a build of Companion that includes a bunch of decompiler fixes. I've been slowly plugging away at decompiler bugs - mainly the insidious ones that silently produce code that is incorrect. So that the "dream" of being able to "recompile all" and expect it to work perfectly is closer...

It's not quite there yet, but I've been able to decompile Laura Bow 1, and recompile all scripts (correcting a few errors that the decompiler can't possibly make sense of, like missing scripts), and it works well enough to to get through all the intro stuff, and walk around a few rooms and talk to people. I just tried QFG1, and there are still about 4 compile errors I get that I should be able to get rid of with a little more decompiler/compiler work.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 14, 2016, 02:24:22 AM
Thank you. On another note, I would love to setup a repository of the source for official games that will compile without error. Perhaps a repository on the Wiki.
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 14, 2016, 02:35:23 AM
Great idea!
But the code better be kept in some kind of source control repository like git or mercurial, so people could work on it in a continuous way.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 14, 2016, 03:21:58 AM
I use Bitbucket for my projects. It allows 5 users per account with unlimited repos. I would be happy to add a couple more users for this.
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 14, 2016, 03:48:32 AM
Why not GitHub?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 14, 2016, 09:09:28 AM
Let me know if you're interested in trying out a build of Companion that includes a bunch of decompiler fixes. I've been slowly plugging away at decompiler bugs - mainly the insidious ones that silently produce code that is incorrect. So that the "dream" of being able to "recompile all" and expect it to work perfectly is closer...

It's not quite there yet, but I've been able to decompile Laura Bow 1, and recompile all scripts (correcting a few errors that the decompiler can't possibly make sense of, like missing scripts), and it works well enough to to get through all the intro stuff, and walk around a few rooms and talk to people. I just tried QFG1, and there are still about 4 compile errors I get that I should be able to get rid of with a little more decompiler/compiler work.
Yeah, I'd be interested in that.  When you compile LB1, are you able to watch the Intro?  When I tried removing the Copy Protection (and replacing it with QFG1-style "Piracy hurts" dialog) I suddenly wasn't able to view the Intro no matter if I clicked yes or no.

Speaking of compiling QFG1, I hadn't done that before... I was just happily replacing global variable names and public procedures. When I tried to do that yesterday, I wasn't able to compile at all, and it felt like the biggest reason was because the new names had become decync'd with the .sco file.  Actually, speaking of your Manage Decompilation window says:
Quote
Decompiling a script will also generate a .sco file. The .sco file tracks procedure and variable names which are not present in the compiled script. By default they are given names such as "local4"
You may edit the names to make them more meaningful, and they will be picked up the next time you decompile the script.
How do you edit the names?  Or does that mean I just edit the .sc file and if I click decompile again, it will read in those names and use them?  That seems counter-intuitive to "decompile" when I've already got source code... Any way to edit the sco file directly? i.e. is there any documentation on the format?
I'd written q quick'n'dirty program to the more tedious editing... things like renaming script files and updating all use references to it... or doing global search/replace (with warnings if the new string already exists), or adding in include keys.sh and include game.sh if they're not already there.

That's a cool project!
Would you be sharing your results when you're done? We could all learn from your work.
Yeah, I don't really have a problem sharing when I'm done (or as I go along). I'm doing it all in a personal git server now (GitBlitGO, it's super easy to get up and running), so I can probably open up access to that to a few more people.  I think it might be better not putting it on something like GitHub though... it is technically copyrighted material.  I mean, sharing among a small community of people who I'm pretty sure already legally own copies of the games anyway is one thing...

Y'know, speaking of SCICompanion updates, there's a couple fixes (some relatively simple, some maybe not so) for issues I've come across that would greatly improve my workflow... the three big ones:
1) When doing a Compile All, showing the file that is causing an error, not just the line/col nums,
2) being able to middle-click (or wheel-click) on a tab to close it... or at the very least including a Close All option under the Window menu, and
3) having an option to compile as external patch files, rather than automatically rebuilding into resource.000... this one really hurts my source control... I'll compile a script to check I've not made any errors, and it will modify my resource.000 (which is about 2-3 MB in these early SCI0 games, but that whole 2MB gets added to my source control... In theory, it should only add the difference, but I've found difference checking to be spotty with binary files.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 14, 2016, 12:11:17 PM
Yeah, I'd be interested in that.  When you compile LB1, are you able to watch the Intro?  When I tried removing the Copy Protection (and replacing it with QFG1-style "Piracy hurts" dialog) I suddenly wasn't able to view the Intro no matter if I clicked yes or no.

Yes, I'm able to now. That was caused by a decompilation bug in proc255_0 (Print).

How do you edit the names?  Or does that mean I just edit the .sc file and if I click decompile again, it will read in those names and use them?  That seems counter-intuitive to "decompile" when I've already got source code... Any way to edit the sco file directly? i.e. is there any documentation on the format?

You edit them from within the decompiler dialog in the box that says "SCO". It doesn't pick anything up from the script files - that would be counter-intuitive. You need to do everything from the decompile dialog. Again, this is in the documentation:
http://scicompanion.com/Documentation/decompiler.html
(I do suggest you read the docs - some of the questions you've asked here before are answered in there)

Brian documented his .sco format. Should be on the wiki somewhere. At any rate, it doesn't really matter, .sco files are generated when you build too. So if you build things in the right order, there shouldn't be any issues. For example, change a public procedure name in in foobar.sc, and then foobar.sco will get that name. Then other scripts that depend on foobar will now compile correctly (assuming they're using the new name). I do have some script dependency tracking stuff in SCI Companion, but I forget if it's linked to "compile all". Not to mention, it won't work if the scripts don't parse because of syntax errors.


2) being able to middle-click (or wheel-click) on a tab to close it... or at the very least including a Close All option under the Window menu, and

Does clicking on the red X not work or something? Or you just want a bigger hit target? (Hmm, I just noticed VS does this... never used that functionality before).

3) having an option to compile as external patch files, rather than automatically rebuilding into resource.000... this one really hurts my source control...

I'd really like this too :P. I wish I'd broken from Brian's compile model earlier and had a chance to do this correctly. It seems like a big change, but I'll look into it, since I'm not the only one frustrated by it. Most of the groundwork is there to make this happen, so maybe it's not as bad as I think to make this change.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 14, 2016, 12:17:34 PM
Shouldn't it be just the sc/sco files and not the compiled resource package for your source control?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 14, 2016, 12:21:12 PM
Why not GitHub?

Last I looked GitHub did not have a free account and my Bitbucket account allows for unlimited repos.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 14, 2016, 12:29:41 PM
I used github for SCI Companion and I didn't pay anything (I think it's free for open source stuff only?). And AFAIK there are no limits on number of repos.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 14, 2016, 01:56:58 PM
Again, this is in the documentation:
http://scicompanion.com/Documentation/decompiler.html
(I do suggest you read the docs - some of the questions you've asked here before are answered in there)
I'll give that another read. I'd thought I'd read through most of the docs before, but that was before I'd started diving in and I could clearly use a refresher.

You edit them from within the decompiler dialog in the box that says "SCO".
I swear I'd tried that, but I'm trying it now, and I must not have been patient enough with it before.  I tried selecting the line items, I tried double-clicking on it, but I never tried selecting it then single-clicking it (or pressing F2). And now that I do, I see what appears to be a slight bug where you can also edit the label "Public procedures" and "Variables"

2) being able to middle-click (or wheel-click) on a tab to close it... or at the very least including a Close All option under the Window menu, and
Does clicking on the red X not work or something? Or you just want a bigger hit target? (Hmm, I just noticed VS does this... never used that functionality before).
do you mean the x on the tab itself?  If I have a dozen or more scripts open and I want to close them all, I generally want to  hover over one of the tabs, and middle-click rapid fire to close them all. Because the names of the scripts are variable length, the x is not always in the same spot.  But I hadn't thought until now of just using the MDI Child x in the upper right window. So nevermind about the wheel-click request.

Shouldn't it be just the sc/sco files and not the compiled resource package for your source control?
Yup, absolutely. Hindsight's 20/20.

Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 14, 2016, 07:59:35 PM
I can now decompiler QFG2 without error, and recompile all scripts and it appears to run (just walked around a few screens). :D
QFG1 too.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on June 14, 2016, 08:42:04 PM
Epic.

And you said you didn't want to mess with the decompiler :3
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 15, 2016, 12:16:17 AM
I can now decompiler QFG2 without error, and recompile all scripts and it appears to run (just walked around a few screens). :D
QFG1 too.
You can now decompile the SmoothLooper function and the --UNKNOWN-PROP-NAME-- in Feature.sc?  That's awesome!
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 15, 2016, 12:43:26 AM
I just signed up for GitHub. They seem to not want to tell you of the free options until after you sign up. I would really like to establish repos for the decompiled scripts of the official games. As I said I can set them up on my Bitbucket account, which has unlimited repos, and allows them to be private. Its drawback is the number of members is limited to just 5 per account. Do we need more than 5? GitHub has unlimited repos and members, but is higher profile and with no private repos with their free accounts Charles may have a point about needing a less conspicuous repository.

What do others say about where to host the repos?
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 15, 2016, 02:29:25 AM
You can now decompile the SmoothLooper function and the --UNKNOWN-PROP-NAME-- in Feature.sc?  That's awesome!

Oops, there's an asterisk beside my claims :P
UNKNOWN-PROP-NAME in canBeHere had to be fixed manually. SmoothLooper is fine though. And I think in both games there were missing scripts that were related to debugging, or development builds. Some of the game scripts reference them, so you have to comment those lines out. But all of that only takes 30 seconds or so. I'm not sure if I can do that automatically. I mean, I probably can, but it might be better to leave those errors in so it's obvious what's "missing". I'm not sure.

I'll probably add a list of tips on decompiling to the documentation that go over the common remaining errors like those and how to fix them/comment them out. Ideally the only errors will be corrupt scripts (like the UNKNOWN-PROP-NAME issue) and missing scripts - i.e. things that are beyond the capability of the decompiler, since they're essentially impossible to fix.

The real big deal is that the decompiler is getting decent enough that - as of now - I don't know of any incorrect code it generates (yes, it still falls back to asm for around 2% of the code). I'm sure there is some, but it's much better than before, where I had to warn people who were modding *just* to recompile the scripts they were changing, not all the scripts - or else something would break.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 15, 2016, 02:44:48 AM
Out of curiosity, what version of QfG2 is this?
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 15, 2016, 03:06:17 AM
Great news Phil!

About the remaining decompilation errors:
I think you should generate special code (inline assembly?) that would tell the compiler to ignore the error and generate as-is.
Same for missing (debug) scripts: the compiler should ignore it. The interpreter tries to load the scripts dynamically, so the user can drop-in the missing scripts and the game would use them.
If you force the user to comment out these references before compilation, this functionality would be gone. You can perhaps make it a compilation warning.

The ideal decompiler/compiler combo would allow to decompile and recompile the whole game and generate identical bytecode with no errors and human intervention.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 15, 2016, 11:27:16 AM
Out of curiosity, what version of QfG2 is this?

1.102

About the remaining decompilation errors:
I think you should generate special code (inline assembly?) that would tell the compiler to ignore the error and generate as-is.
Same for missing (debug) scripts: the compiler should ignore it. The interpreter tries to load the scripts dynamically, so the user can drop-in the missing scripts and the game would use them.
If you force the user to comment out these references before compilation, this functionality would be gone. You can perhaps make it a compilation warning.

I agree. I was trying to figure out how this code could be left in without producing an error (and without introducing new syntax). An asm block might be the way.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on June 15, 2016, 01:17:21 PM
The one version that I am missing. I guess this is one thing that any repository would have to take into account is different game versions, not that we should expect a decompilation of every version, but for any collaborative effort everyone would need to be working with the same version.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 15, 2016, 04:52:19 PM
I agree. I was trying to figure out how this code could be left in without producing an error (and without introducing new syntax). An asm block might be the way.

I've decided to go with special markup that lets you call a missing export without causing a compile error:

Code: [Select]
(__proc_341_0 "hello")  ; calls export 0 in script 341

Easier to implement than having the decompiler stick an asm block in there. I actually already do something similar with missing kernels (i.e. when I can't find a kernel name to map it to), so in some decompiles you might see:

Code: [Select]
(kernel_121 "blahblah") ; calls kernel 121
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 15, 2016, 07:00:40 PM
Sound great!
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 24, 2016, 10:01:09 AM
Hey Charles,
I found 2 scripts from QfG1. You might want to use them for reverse engineering the game's scripts.

CHARSAVE.SC
Code: [Select]
;charsave.sc   Hero's Quest...save character stats.

(script# CHARSAVE)

(public
   CharSave    0
)

;; Bits in svMiscEquip
(define  SWORD_BIT   1)
(define  CHAIN_BIT   2)
(define  PICK_BIT    4)
(define  TOOL_BIT    8)
(define  MIRROR_BIT  16)
(define  BABA_BIT    32)         
(define  SCORE_BIT   64)

(define  EXTRA_DATA  18)   ; Data items other than stats and name
(define  CHECK_DATA  10)   ; Data items that are in check sums

(local
;; local data for saving hero stats for next game
                         ;;;;;;;;;;;;;;;;;;start;;;;;;;;;;;;;;;;;;
   statsKey = $53        ;;;;;;;;order dependent variables;;;;;;;;
   svCharType            ;;;;;;;;order dependent variables;;;;;;;;
   svHighGold            ;;;;;;;;order dependent variables;;;;;;;;
   svLowGold             ;;;;;;;;order dependent variables;;;;;;;;
   svScore               ;;;;;;;;order dependent variables;;;;;;;;
   svMiscEquip           ;;;;;;;;order dependent variables;;;;;;;;
   [codedStats NUMSTATS] ;;;;;;;;order dependent variables;;;;;;;;
   svDaggers             ;;;;;;;;order dependent variables;;;;;;;;
   svHealing             ;;;;;;;;order dependent variables;;;;;;;;
   svMana                ;;;;;;;;order dependent variables;;;;;;;;
   svStamina             ;;;;;;;;order dependent variables;;;;;;;;
   svGhostOil            ;;;;;;;;order dependent variables;;;;;;;;
   bogus0   = $79        ;;;;;;;;order dependent variables;;;;;;;;
   bogus1   = $86        ;;;;;;;;order dependent variables;;;;;;;;
   checkSum1             ;;;;;;;;order dependent variables;;;;;;;;
   checkSum2             ;;;;;;;;order dependent variables;;;;;;;;
   bogus2   = $43        ;;;;;;;;order dependent variables;;;;;;;;
   bogus3   = $88        ;;;;;;;;order dependent variables;;;;;;;;
   bogus4   = $ad        ;;;;;;;;order dependent variables;;;;;;;;
   bogus5   = $f0        ;;;;;;;;order dependent variables;;;;;;;;
   checkSumKey = $ce     ;;;;;;;;order dependent variables;;;;;;;;
                         ;;;;;;;;;;;;;;;;;;;end;;;;;;;;;;;;;;;;;;;
   check1
   check2
   [YNSTR 5]
   [heroFileName 16]
   [bigStr 400]
   hasSaved              ;TRUE if hero saved
   [str 40]
)

(enum          ;states of saveHero Script
   askSave
   getInfoFileName
   getInfoFileName2
   openFile
   writeHeroInfo
   writeComplete
   tryAgain
   badAnswer
   saveDone
)

;(procedure
;  makeChar
;  makeZero
;  restoreHero
;  convWord
;  convByte
;)
;
;(procedure (makeZero &tmp whichSkill)
;  (HighPrint "make Zero")
;  (for  ((= whichSkill 0))
;        (< whichSkill NUMSTATS)
;        ((++ whichSkill))
;     (= [egoStats whichSkill] 0)
;  )
;  (= [invNum iGold] (= [invNum iSilver] (= score 0)))
;  (ego use: iSword)     
;  (ego use: iChainMail) 
;  (ego use: iLockPick)   
;  (ego use: iThiefKit)   
;  (ego use: iMagicMirror)
;  (Bclr fBabaFrog)     
;  (= [invNum iDagger] 0)
;  (= [invNum iHealingPotion] 0)
;  (= [invNum iManaPotion] 0)
;  (= [invNum iStaminaPotion] 0)
;  (= [invNum iGhostOil] 0)
;  (= score 0)
;  (= heroType 0)
;  (StrCpy @userName {xxxxxxxxxxy})
;  (for  ((= whichSkill 0))
;        (< whichSkill (+ NUMSTATS EXTRA_DATA))
;        ((++ whichSkill))
;     (= [statsKey (+ whichSkill 1)] 0)
;  )
;  (HighPrint "char Zeroed")
;  (return)
;)
;
;(procedure (makeChar &tmp whichSkill)
;  (HighPrint "make Char")
;  (for  ((= whichSkill 0))
;        (< whichSkill NUMSTATS)
;        ((++ whichSkill))
;     (= [egoStats whichSkill] whichSkill)
;  )
;  (= [invNum iDagger] 1)
;  (= [invNum iHealingPotion] 2)
;  (= [invNum iManaPotion] 3)
;  (= [invNum iStaminaPotion] 4)
;  (= [invNum iGhostOil] 5)
;  (= score 432)
;  (HighPrint "Char made")
;  (return)
;)
;
;(procedure (restoreHero &tmp whichSkill)
;  (HighPrint "restore Hero")
;  (if (not (heroinfo open: fRead))
;     (HighPrint "Failure in opening file")
;     (return)
;  )
;  (heroinfo readString: @userName 52)
;  (heroinfo readString: @bigStr 90)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  (for  ((= whichSkill 0))
;        (< whichSkill (+ NUMSTATS EXTRA_DATA))
;        ((++ whichSkill))
;
;     (= [statsKey (+ whichSkill 1)] (convWord [bigStr whichSkill]))
;
;  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  (for  ((= whichSkill (+ NUMSTATS EXTRA_DATA)))
;        (< 0 whichSkill)
;        ((-- whichSkill))
;
;     (^= [statsKey whichSkill] (& [statsKey (- whichSkill 1)] 127))
;
;  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  (= check1 checkSumKey)
;  (for  ((= whichSkill 0))
;        (< whichSkill (+ NUMSTATS CHECK_DATA))
;        ((+= whichSkill 2))
;     (= [statsKey (+ whichSkill 1)] (& [statsKey (+ whichSkill 1)] 127))
;     (+= check1 [statsKey (+ whichSkill 1)])
;  )
;  (= check2 0)
;  (for  ((= whichSkill 1))
;        (< whichSkill (+ NUMSTATS CHECK_DATA))
;        ((+= whichSkill 2))
;     (= [statsKey (+ whichSkill 1)] (& [statsKey (+ whichSkill 1)] 127))
;     (+= check2 [statsKey (+ whichSkill 1)])
;  )
;  (&= check1 127)
;  (&= check2 127)
;  (if (or (!= check1 checkSum1) (!= check2 checkSum2))
;     (HighPrint "CHECKSUM ERROR")
;  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  (for  ((= whichSkill 0))
;        (< whichSkill NUMSTATS)
;        ((++ whichSkill))
;     (= [egoStats whichSkill] [codedStats whichSkill])
;  )
;  (= [invNum iDagger] svDaggers) 
;  (= [invNum iHealingPotion] svHealing) 
;  (= [invNum iManaPotion] svMana)     
;  (= [invNum iStaminaPotion] svStamina) 
;  (= [invNum iGhostOil] svGhostOil)
;  (= [invNum iGold] (+ (* svHighGold 100) svLowGold))
;  (= score (+ svScore 128))
;  (if (& svMiscEquip SCORE_BIT) (+= score 256))
;  (if (& svMiscEquip SWORD_BIT) (= [invNum iSword] 1))
;  (if (& svMiscEquip CHAIN_BIT) (= [invNum iChainMail] 1))
;  (if (& svMiscEquip PICK_BIT)  (= [invNum iLockPick] 1)) 
;  (if (& svMiscEquip TOOL_BIT)  (= [invNum iThiefKit] 1)) 
;  (if (& svMiscEquip MIRROR_BIT) (= [invNum iMagicMirror] 1))
;  (HighPrint "Hero restored")
;  (return)
;)
;
;(procedure (convWord ascii)
;  (return (+ (convByte (>> ascii 8)) (* (convByte (& ascii 255)) 16)))
;)
;
;(procedure (convByte ascii)
;  (cond
;     ((== ascii 32)
;        (return 0)
;     )
;     ((<= 48 ascii 57)
;        (return (- ascii 48))
;     )
;     (else
;        (return (- ascii 87))
;     )
;  )
;)

(instance CharSave of Room
   (properties
      picture  pBlueSkyForCarpet
      horizon  0
      style    IRISOUT
   )
   
   (method (dispose)
      (StatusLine code: dftStatusCode)
      (super dispose:)
   )

   (method (init)
      (StatusLine
         code: endStatus,
         enable:)
      (super init: &rest)
      (cSound stop:)

      ; don't let'm control anything!
      (User canControl: FALSE, canInput: FALSE)
;;    (User canControl: FALSE, canInput: TRUE)

      (self setScript: saveHero)
   )

;  (method (handleEvent event)
;     (if (Said 'quit')
;        (= quit TRUE)
;     )
;     (if (Said 'make/zero')
;        (makeZero)
;     )
;     (if (Said 'make/hero')
;        (makeChar)
;     )
;     (if (and (Said 'restore/hero') hasSaved)
;        (restoreHero)
;     )
;  )
)


(instance heroinfo of File
   (properties
      name  {hq1_hero.sav})
)

(instance saveHero of Script
   (method (changeState newState &tmp whichSkill oldGold)
      (switch (= state newState)
         (askSave
;           (makeChar)
            (Format @heroFileName "a:hq1\_hero.sav")
            (if (>= score 500)
               (HighPrint "CONGRATULATIONS!!__You have successfully
                     completed \"Hero's Quest I:__So You Want To Be
                     A Hero\" with the maximum possible score, 500 of 500!!_
                     We welcome you to the ranks of the few, the proud,
                     the True Heroes!")
            else
               (HighPrint (Format @bigStr "Congratulations!__You have
                     successfully completed \"Hero's Quest I:__So You
                     Want To Be A Hero.\"__Your final score was %d of
                     500 possible Puzzle Points." score))
            )

            (HighPrint "If you have not already done so,
                  we encourage you to play \"Hero's Quest I\" again
                  with the other two character types; many of the
                  puzzles are different, or have alternate solutions.")

            (HighPrint "In the meantime, you are already a winner!__Please
                  insert a writeable disk in your floppy drive to
                  save your winning Hero for use in\n
                  \"Hero's Quest II:__Trial By Fire.\"")
            (self  cue:)
         )

         (getInfoFileName
            (= cycles 2)
         )

         (getInfoFileName2
            (if (GetInput @heroFileName 30
                     {Disk file in which to save your Hero.})
               (heroinfo name: @heroFileName)
               (= cycles 2)
            else
               (self changeState: tryAgain)
            )
         )

         (openFile
            (if (heroinfo open: fTrunc)
               (heroinfo close:)
               (= seconds 2)
            else
               (HighPrint (Format @bigStr "Could not create file -- %s."
                     (heroinfo name?)))
               (self changeState: tryAgain)
            )
         )               

         (writeHeroInfo
            (if (not (heroinfo open: fAppend))
               (self  changeState: tryAgain)
               (return)
            )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            (for  ((= whichSkill 0))
                  (< whichSkill NUMSTATS)
                  ((++ whichSkill))
               (= [codedStats whichSkill] [egoStats whichSkill])
            )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            (= oldGold (+ [invNum iGold] (/ [invNum iSilver] 10)))
            (= svCharType heroType)
            (= svHighGold ( /  oldGold 100))
            (= svLowGold  (mod oldGold 100))
            (= svScore score)
            (= svMiscEquip 0)
            (if (ego has: iSword)         (|= svMiscEquip SWORD_BIT))
            (if (ego has: iChainMail)     (|= svMiscEquip CHAIN_BIT))
            (if (ego has: iLockPick)      (|= svMiscEquip PICK_BIT))
            (if (ego has: iThiefKit)      (|= svMiscEquip TOOL_BIT))
            (if (ego has: iMagicMirror)   (|= svMiscEquip MIRROR_BIT))
            (if (Btst fBabaFrog)          (|= svMiscEquip BABA_BIT))
            (if (< 255 score)             (|= svMiscEquip SCORE_BIT))
            (= svDaggers  [invNum iDagger])
;           (= svHealing  [invNum iHealingPotion])
;           (= svMana     [invNum iManaPotion])
;           (= svStamina  [invNum iStaminaPotion])
            (= svGhostOil [invNum iGhostOil])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            (= checkSum1 checkSumKey)
            (for  ((= whichSkill 0))
                  (< whichSkill (+ NUMSTATS CHECK_DATA))
                  ((+= whichSkill 2))
               (= [statsKey (+ whichSkill 1)] (& [statsKey (+ whichSkill 1)] 127))
               (+= checkSum1 [statsKey (+ whichSkill 1)])
            )
            (= checkSum2 0)
            (for  ((= whichSkill 1))
                  (< whichSkill (+ NUMSTATS CHECK_DATA))
                  ((+= whichSkill 2))
               (= [statsKey (+ whichSkill 1)] (& [statsKey (+ whichSkill 1)] 127))
               (+= checkSum2 [statsKey (+ whichSkill 1)])
            )
            (&= checkSum1 127)
            (&= checkSum2 127)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            (for  ((= whichSkill 0))
                  (< whichSkill (+ NUMSTATS EXTRA_DATA))
                  ((++ whichSkill))
               (= [statsKey (+ whichSkill 1)] (& [statsKey (+ whichSkill 1)] 127))
               (^= [statsKey (+ whichSkill 1)] [statsKey whichSkill])
            )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            (heroinfo writeString: @userName)
            (heroinfo writeString: {\n})
            (for  ((= whichSkill 1))
                  ( < whichSkill (+ NUMSTATS EXTRA_DATA 1))
                  ((++ whichSkill))
               (Format @bigStr "%2x" [statsKey whichSkill])
               (heroinfo writeString: @bigStr)
            )
            (heroinfo writeString: {\n})
            (heroinfo close:)
            (= seconds 2)
         )

         (writeComplete
            (HighPrint "The save character file has been created.__Save
                  this disk for use with \"Hero's Quest II:__Trial By Fire\"
                  from Sierra On-Line.")
            (= hasSaved TRUE)
            (= cycles 2)
         )

         (tryAgain
            (Format @YNSTR "n")
            (if (GetInput @YNSTR 2
                     {If you wish to try saving your character again,
                        type \"y\", then ENTER.__Otherwise type \"n\",
                        then ENTER.}
                  )
               (if (StrCmp @YNSTR {y})
                  (self changeState: saveDone)
               else
                  (= bogus0 $79)
                  (= bogus1 $86)
                  (= bogus2 $43)
                  (= bogus3 $88)
                  (= bogus4 $ad)
                  (= bogus5 $f0)
                  (self changeState: getInfoFileName)
               )
            else
               (= cycles 2)
            )
         )

         (badAnswer
            (HighPrint "Please answer \"y\" or \"n\".")
            (self  changeState: tryAgain)
         )

         (saveDone
            (HighPrint "Thank you for playing \"Hero's Quest I:__So You
                  Want To Be A Hero,\" and congratulations again on
                  winning.__We'll see you again soon in\n
                  \"Hero's Quest II:__Trial By Fire.\"")
            (= quit TRUE)
         )
      )
   )
)

(instance endStatus of Code
   (method (doit strg)
      (Format strg "___Wow!__You're Really A Hero!__[score %d of 500]" score)
   )
)


MAZEBUG.SC
Code: [Select]
; MazeBug class to handle behavior of bugs in wizard's game (Mages' Mazes)

(script# MAZEBUG)

(include wizgame.sh)

(procedure
   setDeltaX
   setDeltaY
)


(class MazeBug kindof Actor
   (properties
      xStep    2
      yStep    2
      startX   0           ; Where to come back after dying
      startY   0
      deadTime 0           ; How long before we resurrect
      otherBug 0           ; I.D. of opponent
      form     smallBug    ; Which type (size) of creature
      smarts   5           ; How far bug thinks/looks ahead
      path     0           ; Which path we're following
   )

   (methods
      highPri        ; Switch to higher priority
      lowPri         ; Switch to lower priority
      changeForm     ; Trigger changes to next size/shape
      fixState       ; Set illegalBits and such based on priority/size
      die            ; Fall into the chasm and start game over
      nearRock       ; Are we close to a rock?
      nearBridge     ; Are we close to a bridge?
      nearLadder     ; Are we close to a ladder?
   )

   (method (init)
      (super  init: &rest)
      (self
         setPri: LowPri,
         fixState:
      )
   )

   (method (highPri)
      (self
         setPri: HighPri,
         fixState:
      )
   )

   (method (lowPri)
      (self
         setPri: LowPri,
         fixState:
      )
   )

   (method (changeForm  &tmp newLoop)
      (= newLoop (+ loop 1))
      (if (> (++ form) largeBug)
         ( = form smallBug)
         (-= newLoop 3)
      )

      (self
         setLoop: newLoop,
         fixState:
      )
      (if (and (!= form mediumBug) (== (self onControl: origin) cGREEN))
         (self  die:)            ; Changing form while on a ladder
      )
   )

   (method (fixState)            ; Account for current creature size
      (= illegalBits cWHITE)
      (if (<= priority LowPri)
         ( |= illegalBits cYELLOW)        ; On lower path
      else
         ( |= illegalBits cLMAGENTA)      ; On upper path
      )

      (switch form
         (smallBug
            ( |= illegalBits cGREEN)      ; Too small for ladders
            ( = moveSpeed (= cycleSpeed 0))
         )
         (mediumBug
            ; Too big to enter tunnels
;;          ( |= illegalBits (| cMAGENTA cLMAGENTA))
            ( |= illegalBits cLMAGENTA)
            ( = moveSpeed (= cycleSpeed 1))
         )
         (largeBug
            ; Too big to enter tunnels or climb ladders (clumsy)
;;          ( |= illegalBits ( | cMAGENTA cLMAGENTA cGREEN))
            ( |= illegalBits ( | cLMAGENTA cGREEN))
            ( = moveSpeed (= cycleSpeed 2))
         )
      )
   )

   (method (doit  &tmp whichControl)
      (cond
         (deadTime
            (if (not (-- deadTime))    ; Time to be resurrected
               (self
                  posn: startX startY,
                  setLoop: (- loop (- form smallBug)),
                  form: smallBug,
                  lowPri:,
                  fixState:
               )
            )
         )
         (else
            (super  doit:)
         )
      )
   )

   (method (canBeHere  &tmp canWe ctrls)
      (if (= canWe (super canBeHere:))
         (= ctrls (self onControl: 0))
         (if (or
               (and (& ctrls cLRED)  (self nearRock: 25))
               (and (& ctrls cLCYAN) (not (self nearBridge: 25)))
               (and (& ctrls cGREEN) (not (self nearLadder: 25)))
            )
            (= canWe FALSE)
         )
      )
      (return canWe)
   )

   (method (die)
      (= deadTime 20)            ; Disappear for 20 cycles
   )

   (method (nearRock dist)       ; Are we close to a rock?
      (return FALSE)             ; Implemented in instance
   )

   (method (nearBridge dist)     ; Are we close to a bridge?
      (return TRUE)              ; Implemented in instance
   )

   (method (nearLadder dist)     ; Are we close to a ladder?
      (return TRUE)              ; Implemented in instance
   )
)


(class MazeMove kindof Motion       ; Motion class for bugs in maze
   (properties
      client      0     ; Actor we are controlling
      caller      0     ; Object to cue when complete/blocked
      x           0     ; Eventual destination
      y           0
      b-moveCnt   0     ; Iterations of doit to skip
      curDir      mazeSW   ; Which way we're going (compass direction)
      prevDir     mazeSW   ; Which way we last went (compass direction)
      ditherTime  0     ; How long we should stand and dither
      completed   0     ; Set to TRUE when move is complete
      altMove     0     ; Kludge to make diagonal moves close to correct
      prevControl 0     ; Which control color we were on last doit cycle
   )

   (methods
      calcDir           ; Calculate compass direction from angle
      doMove            ; Do next step of move
      tryStep           ; Try to take a step
      chooseRoute       ; Find best path for next move(s)
      checkRoute        ; See if specified route works
      setHeading        ; Set client's heading based on compass direction
   )

   ;; Initialize mover -- do *not* do a (super init:)
   (method (init actor xTo yTo toCall &tmp cy theAngle)
      (if (>= argc 1)            (= client actor)
         (if (>= argc 2)         (= x xTo)
            (if (>= argc 3)      (= y yTo)
               (if (>= argc 4)   (= caller toCall)
               )
            )
         )
      )
     
      ;Set actor's heading.
      (client heading: (= theAngle (GetAngle (client x?) (client y?) x y)))
      (= prevDir (= curDir (self calcDir: theAngle)))
     
      (= b-moveCnt 0)
      (if (= cy (client cycler?))      ; Keep cycler in synch with mover
         (cy cycleCnt: 0)
      )
   )
   

   (method (calcDir theHeading)
      (return
         (cond
            (HEADING_N  mazeN)
            (HEADING_NE mazeNE)
            (HEADING_E  mazeE)
            (HEADING_SE mazeSE)
            (HEADING_S  mazeS)
            (HEADING_SW mazeSW)
            (HEADING_W  mazeW)
            (HEADING_NW mazeNW)
         )
      )
   )


   ;; Do next step -- do *not* do a (super doit:)
   (method (doit  &tmp clForm thisControl)
      (if ditherTime
         (-- ditherTime)
         (return)
      )

      (if (> (++ b-moveCnt) (client moveSpeed?))
         (= b-moveCnt 0)

         (= clForm (client form?))
         (= thisControl (client  onControl: origin))
         (if (and (!= thisControl prevControl) (< (client priority?) LowPri))
            (client  setPri: LowPri)      ; (Maybe) just left a tunnel
         )

         (switch thisControl
            (cGREY
               (client  lowPri:)
            )

            (cLBLUE
               (client  highPri:)
            )

            (cGREEN        ; On a ladder -- get off quickly
               (if (not (client  nearLadder: 25))
                  (client  die:)       ; Somebody yanked away our ladder
               )
            )

            (cLCYAN        ; On a bridge, or where one should be
               (if (not (client  nearBridge: 25))
                  (client  die:)       ; Fall into the chasm
               )
            )

            (cBROWN        ; At an intersection -- choose best path
               (self  chooseRoute: FALSE)
            )

            (cLMAGENTA
               (if (== (client priority?) LowPri)
                  (client setPri: (- LowPri 2))
               )
            )

            (cMAGENTA
               (if (== (client priority?) LowPri)
                  (client setPri: (- LowPri 2))
               )
            )
         )

         (= prevControl thisControl)
         (self  doMove:)         ; Make next move (if we can)
      )
     
      ;If our client is where he should be, we terminate.
      (if (and (<= (Abs (- x (client x?))) 2) (<= (Abs (- y (client y?))) 2))
         (self moveDone:)
         (return)
      )
   )


   ; Actually do next step of motion
   (method (doMove &tmp oldX oldY DX DY newDir newAngle mSpeed)
      (if (== (Random 1 10) 7)
         (self setHeading: (= newDir (Random 0 7)))   ; Monkey wrench
      )

      (= oldX (client x?))
      (= oldY (client y?))

      (if (self tryStep: oldX oldY curDir)
         (= prevDir curDir)      ; Don't care about previous bump anymore
      else
         (self  chooseRoute: TRUE)     ; Blocked -- get a new route
         (= mSpeed (client moveSpeed?))
         (= ditherTime (+ 1 mSpeed mSpeed))        ; Dither for a bit
         (client
            signal: (| (client signal?) blocked),
            forceUpd:
         )
         (return FALSE)
      )

      (client forceUpd:)

      (return TRUE)
   )


   (method (tryStep oldX oldY theDir  &tmp DX DY)
      (= DX (client xStep?))
      (= DY (client yStep?))

      (if (== (++ altMove) 2)
         (= altMove 0)
         (if (& curDir 1)        ; Moving at a diagonal
            (<<= DX 1)
            (<<= DY 1)
         )
      )

      (= DX (setDeltaX theDir DX))
      (= DY (setDeltaY theDir DY))

      (client
         x: (+ oldX DX),
         y: (+ oldY DY)
      )

      (BaseSetter client)
      (if (client canBeHere:)
         (return TRUE)
      else
         (if (== (++ altMove) 2)
            (= altMove 0)
         )

         (client
            x: oldX,
            y: oldY
         )

         (BaseSetter client)
         (client
            signal: (| (client signal?) blocked),
            forceUpd:
         )
         (return FALSE)
      )
   )


   (method (chooseRoute wasBlocked
         &tmp theAngle newDir incr aDir sm best cur bestDir other forms dist)

      (= newDir curDir)
      (if (== (Random 1 3) 2)
         (= theAngle (GetAngle (client x?) (client y?) x y))
         (= newDir (self  calcDir: theAngle))
      )

      (= other (client otherBug?))
      (= dist  (client distanceTo: other))
      (if (< dist ChaseRange)
         (= forms (- (client form?) (other form?)))
         (cond
            ((== forms -1)    ; Other bug wants to eat us -- run away!
               (= theAngle
                  (GetAngle (other x?) (other y?) (client x?) (client y?)))
               (= newDir (self  calcDir: theAngle))
            )

            ((and (< dist 12)
                  (== forms 1)
                  (== (client priority?) (other priority?))
               )
               ; Yum -- we caught dinner!
               (other die:)
            )

            ; Oh, what a cute/tasty little bug -- let's chase it!
            ; (if merely "cute", only a 1 in 4 of chasing it)
            ((or (== forms 1) (and (== forms 0) (not (Random 0 3))))
               (= theAngle
                  (GetAngle (client x?) (client y?) (other x?) (other y?)))
               (= newDir (self  calcDir: theAngle))
            )
         )
      )

      ;; Check routes, preferring one selected above.  If none of them
      ;;    work, stick with the pre-chosen one.
;;    (= sm (client smarts?))
      (= bestDir newDir)
      (for ((= incr (= best 0))) (<= incr 4) ((++ incr))
         (= cur (self  checkRoute: (= aDir (mod (+ newDir incr) 8))))
         (if (== aDir (mod (+ curDir 4) 8))
            (-= cur 4)        ; Try not to reverse directions
         )
         (if (> cur best)
            (= best cur)
            (= bestDir aDir)
         )

         (= cur (self  checkRoute: (= aDir (mod (- (+ newDir 8) incr) 8))))
         (if (== aDir (mod (+ curDir 4) 8))
            (-= cur 4)        ; Try not to reverse directions
         )
         (if (> cur best)
            (= best cur)
            (= bestDir aDir)
         )
      )

      (= newDir bestDir)
      (while (and wasBlocked (== curDir newDir))
         (= newDir (Random 0 7))
      )

      (self  setHeading: newDir)
      (client  forceUpd:)
      (return curDir)
   )


   ;; See if endpoint of potential route is legal place for creature to be
   ;;    (look-ahead distance determined by creature's smarts).
   (method (checkRoute theDir  &tmp oldX oldY curX curY sm index)
      (= curX (= oldX (client x?)))
      (= curY (= oldY (client y?)))
      (= sm (/ (client smarts?) 2))
;;    (= sm (- 11 (client smarts?)))      ; Shorter looks are smarter (??)

      (for ((= index 0)) (< index sm) ((++ index))
         (if (not (self tryStep: curX curY theDir))
            (break)
         )
      )

      (client
         x: oldX,
         y: oldY
      )

      (BaseSetter client)
      (return index)
   )


   (method (setHeading newHeading)
      (= prevDir curDir)
      (= curDir  newHeading)
      (client heading:
         (switch newHeading
            (mazeN   0)
            (mazeNE  45)
            (mazeE   90)
            (mazeSE  135)
            (mazeS   180)
            (mazeSW  225)
            (mazeW   270)
            (mazeNW  315)
         )
      )
   )
)


(procedure (setDeltaX theDir DX)
   (switch theDir
      (mazeN   (= DX 0))
      (mazeS   (= DX 0))
      (mazeSW  (= DX (- DX)))
      (mazeW   (= DX (- DX)))
      (mazeNW  (= DX (- DX)))
   )
   (return DX)
)

(procedure (setDeltaY theDir DY)
   (switch theDir
      (mazeN   (= DY (- DY)))
      (mazeNE  (= DY (- DY)))
      (mazeE   (= DY 0))
      (mazeW   (= DY 0))
      (mazeNW  (= DY (- DY)))
   )
   (return DY)
)


Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on June 24, 2016, 02:48:20 PM
I found 2 scripts from QfG1. You might want to use them for reverse engineering the game's scripts.
I knew about the first one... the second one is interesting. It abusesdoes some creative things with the animation system.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 27, 2016, 10:21:06 PM
Oooh, it's like Christmas. Those are both really helpful samples. The contents of both I'd already deciphered, especially the CHARSAVE.SC file, but learning the original names of some constants and procedures is extremely nice. I'm updating my sources to use those names now (except for Btst and Bclr... I prefer my names of getEventFlag and clearEventFlag).

Just to give a bit of status update, I had originally decompiled based on the v3.0.7 (I think?) SCICompanion, and had made a tonne of good progress renaming files, procedures and variables, but somewhere long the way, I made a mistake and nothing would compile. I tried backtracking, but had no joy. And then Phil updated the decompiled, so I figured I might as well just start over with decompiling based on the sco and game.ini files I'd already created. That's where I am now. Everything decompiled from scratch with my current variable names. My next step is to replace the constants for things like character class, or walk/run/sneak mode, or TimeOfDay (I.e. Dawn, Midday, sunset, Midnight, etc.) and so on and so forth. Now I'm compiling after each set of constants is replaced, and pushing to my local git server. I've tried allowing outside access, but there's a block somewhere along the chain (router, server, git, ??), and I haven't had a moment to sit down and troubleshoot.

A couple of things I've learned: I'm very impressed with the code for racking up puzzle points. There's a clever array that's significantly long, and they've made wrappers for it so you can toggle each bit in the array individually. At least a couple hundred... Very efficient, since each true/false flag only takes a single bit. So there's a bunch in there for game state stuff (like have you climbed up Henry's cliff side, or destroyed the nest outside the healer. Just everything like that. They also use these flags for puzzle points. So there's a specific procedure you call with the flag, the amount of points, and what character class it applies to (if it's only a specific class). I see a very easy potential to create a kid that lists exactly which points you missed at the end of the game, ala QFG5. Also I learned that if you're a Thief, and you get robbed in the alley, you lose 10 points. So that's fun.

Something else I found... Casting flame dart improves your throwing skill. I believe this is a bug, due to copy/pasta. The throwing file and the flame dart file are both very similar. I want to double check that one though, I didn't spend a lot of time reviewing it the 1st time through.
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 28, 2016, 02:46:55 AM
Thanks for the update - very interesting!

Btst and Bclr are used in many SCI games, and it's not limited to event flags, so you if you don't like the original names, you might want to use bitTest and bitClear etc. Names like getEventFlag is too specific.
EDIT: So I noticed that these functions are actually tied to a specific global flags array. Here's the version from QfG2:
Code: [Select]
(procedure (Btst flagEnum)
   (& [gameFlags (/ flagEnum 16)] (>> $8000 (mod flagEnum 16)))
;; (DisposeScript FLAGS)
)

(procedure (Bset flagEnum  &tmp oldState)
   (= oldState (Btst flagEnum))
   (|= [gameFlags (/ flagEnum 16)] (>> $8000 (mod flagEnum 16)))
   oldState
;; (DisposeScript FLAGS)
)

(procedure (Bclr flagEnum  &tmp oldState)
   (= oldState (Btst flagEnum))
   (&= [gameFlags (/ flagEnum 16)] (~ (>> $8000 (mod flagEnum 16))))
   oldState
;; (DisposeScript FLAGS)
)

The puzzle points management is nice and similar to other QfG games. It's a very good idea to add a patch for displaying the missing points! I say go for it.  :)

About the flame dart issue: you might find the discussion in the following ScummVM bug interesting:
https://bugs.scummvm.org/ticket/7133 (https://bugs.scummvm.org/ticket/7133)
It's about the problematic spell skill learning in QfG3 and even includes correspondence with Corey Cole.

Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on June 28, 2016, 12:54:38 PM
Btest, Bset and Bclear are part of the SCI1.1 template game too:

http://scicompanion.com/Documentation/sci11_procedures.html

http://scicompanion.com/Documentation/Tutorial/Score.html#flags


Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on June 29, 2016, 12:05:12 AM
All excellent points on keeping it as Btst, Bset and Bclr. Works for me.

I hadn't read anything on the template games documentation yet either, but I see those would be a good resource to use to learn some common conventions, too.

Edited to add, that's an interesting discussion about QFG3. I'd love to tackle some custom patches for that game, some time down the road. Classic problem: too much to do, to little time to do it.
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on June 29, 2016, 07:38:15 AM
I hadn't read anything on the template games documentation yet either, but I see those would be a good resource to use to learn some common conventions, too.

Yes, you should.
Or even better, go over Sierra's system scripts (the original "template" game): https://github.com/OmerMor/SCI16/tree/master/SYSTEM (https://github.com/OmerMor/SCI16/tree/master/SYSTEM).
This is their version for SCI16 from 1993 (1.001.099), but it should be pretty similar to the system scripts for QfG1EGA.

I also have earlier versions from SCI0. I'll upload them sometime as well.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on July 04, 2018, 01:50:14 PM
Okay, so after a bunch of restarts then dropping it for almost 2 years, I picked this project back up.

I'm using the latest SCICompanion (built myself from the github source), with Hero's Quest v1.000. My thought being that when this is fully commented, then I can then re-implement the differences between released versions, ending with the QFG1EGA available on gog and steam.

I've made some decisions that may ruffle some feathers. Whenever possible, I'm ditching the defines, variable naming, and file naming conventions originally created by Brian for SCIStudio in favour of those originally used by Sierra.  The SCI16 source code and documentation provided by OmerMor was a huge help in this regard.  Even though it was for a later version of SCI than's used in HQ1, I went with the same assumption OmerMor alluded to earlier... that (for the most part) Sierra wouldn't have completely ditched working code without a good reason... they were a business afterall, and time costs money.  So, I believe I've been able to accurately deduce most of the original global variable names (that weren't game specific).

As of now, I have completely commented/renamed the system scripts, as well as Main.sc. I have replaced all Control references (i.e. onControl and illegalBits) with names, instead of magic numbers.  I've renamed all classes to their original names (i.e. Act is Actor, and Rm is Room, etc.). I've named almost all the HQ-specific global variables, and procedures.

The source code compiles without error, and the game runs.  I haven't done a complete play-through, but I have wandered around and tried a few things. I know there is at least one game error related to either the decompile or the recompile (can't tell which), and possibly more [EDIT: the error is outside Baba Yaga's hut.  I cannot enter the hut.]. I've already corrected one error (which I can share in a later post).  I haven't gone bug hunting yet (other than one game crashing bug in the Waterfall room).  I've been focused on variable/procedure/class renaming, while keeping the game compilable and at least runable. 

This recent stretch has probably been a couple months of finding 10 minutes or an hour here, or an evening there.  I'm a bit burned out on it for the moment, which is a shame, because I've only just gotten to the game proper now.  Anyway, I wanted to share my progress.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on July 04, 2018, 03:02:46 PM
Consider me impressed.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on July 05, 2018, 09:29:25 AM
Scratch that error with Baba Yaga's hut... that exists in the original game.  There are no known errors in the compilation. But emphasis on known, because I haven't done a full playthrough.

The error I did fix, btw, was in script 82 (Waterfalls.sc).

Here's the code as decompiled (which crashed as soon as it ran, during the room's init):
Code: [Select]
(procedure (localproc_054e &tmp temp0)
(= temp0 0)
(while (< temp0 8)
([local6 temp0]
setLoop: 0
ignoreActors:
x: [local38 temp0]
y: [local46 (= [local14 temp0] (Clone aFallScript))]
init:
setPri: 1
cycleSpeed: 3
setScript: [local14 (= [local6 temp0] (Clone waterFalling))] 0 temp0
)
(++ temp0)
)
)

And here's the correction I made that worked:
Code: [Select]
(procedure (SetWaterfallHighDetail &tmp i)
;CI: NOTE: SCICompanion incorrectly decompiled this script, as follows:
;
;y: [yHighDetail (= [waterfallScripts i] (Clone aFallScript))]
;setScript: [waterfallScripts (= [waterfallProps i] (Clone waterFalling))] 0 i
;
;instead of calling the Cloning and assignment first. This caused this room to
;crash immedaitely upon the room's init.

(= i 0)
(while (< i 8)
(= [waterfallProps i] (Clone waterFalling))
(= [waterfallScripts i] (Clone aFallScript))

([waterfallProps i]
setLoop: 0
ignoreActors:
x: [xHighDetail i]
y: [yHighDetail i]
init:
setPri: 1
cycleSpeed: 3
setScript: [waterfallScripts i] NULL i
)
(++ i)
)
)

Please ignore the change in variable/procedure names.  I'm not sure exactly what the decompiler is trying to do there.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on July 05, 2018, 10:33:35 AM
Please ignore the change in variable/procedure names.  I'm not sure exactly what the decompiler is trying to do there.
Its best? I mean, those names aren't stored in the final script code, unlike selectors and class names and such.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on July 05, 2018, 10:41:54 AM
Oops, I didn’t mean to phrase that as a single thought. I know the variable names aren’t stored anywhere in the compiled code, and the decompiler does an amazing job substituting reasonable variable names whenever possible.

What I meant was, I’m not sure what the decompiler is trying to do in the first code snippet.  It looks like it’s trying to define a variable in one of the properties of that variable itself?
Title: Re: Recreating complete QFG1 EGA source code
Post by: OmerMor on July 06, 2018, 09:29:25 AM
Very nice Charles!
I'll need to clear up some time and go over the code.

Have you considered putting it over github? That way you would be able to get comments and pull requests. I know you don't own the code, but I don't think anyone would bother - especially if you don't include any resource other than scripts (e.g. PICs and VIEWs).
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on September 03, 2018, 04:55:34 PM
Has anyone started any kind of repository of working decompiled game scripts?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on September 03, 2018, 06:46:45 PM
Do you mean "working" as in if you do a Recompile All the result is playable?

I'd love to set something like that up, but I can never seem to get Git to let me push stuff, and I think Bitbucket has a limit on the amount of repos I can have so... best I could do is a read-only thing on my own server.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on September 03, 2018, 09:52:18 PM
Yes, that is what I meant. Bitbucket is limited to five users for free, but allows unlimited private repos. Of course a repository could be public, even if it only had 5 contributors. GitHub is free for public and open source projects. I could do something with one of the Wikis, too, but it may not be as convenient for someone to update bug fixes or add comments.
Title: Re: Recreating complete QFG1 EGA source code
Post by: EricOakford on October 06, 2018, 08:14:09 PM
Great work on this decompilation effort! I've been working to adapt this code for the QFG1EGA release (version 1.200). After downloading the code, I used the SCO files to help me in decompiling the newer version. Between the first and last releases, a lot has changed, not just the title.

As noted here (https://tcrf.net/Quest_for_Glory:_So_You_Want_to_Be_a_Hero_(1989)) at TCRF, the High Speed Hero option was removed. The Kobold's cave scripts were also massively overhauled. The Tavern added an additional note for earlier in the game. Finally, a lot of the game's text was revised.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 07, 2018, 12:26:35 AM
OK, I have added a page on the Wiki for decompiled scripts. So far I only have one, King's Quest IV version 1.006.004, int. 0.000.502. This is just to start working on a repository page design. One advantage to the Wiki over a static page is that there can be multiple contributors. Users with Wiki accounts can upload updated/modified versions and add notes or more info to an entry. Any vandalism can be easily rolled back. There is also a discussion sub-page.

http://sciwiki.sierrahelp.com//index.php?title=Source_Repository

Not as ideal as a git, but it can be private or at least some what obscured. I can look into restricting access to only members. Not sure how important this would be to do these days.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 07, 2018, 09:38:51 AM
It's a good source dump. I just felt it could use more gRoom where it has global2 now. Also went ahead and named almost everything in Class_255 but the script file itself. How do I get this to you?

Oh and there was something really weird about the copy protection script. I couldn't help myself, sorry. I think maybe it had a particularly messy skip patch? If you want to skip the CP, you really ought to just change KQ4::init to go to room 700 instead of 701 :D
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 07, 2018, 10:24:11 AM
You can attach it here. Also, if you want to contribute to repository I can add a Wiki account for you.

As to the CP script I forgot to remove the patch from the game's folder when I decompiled, so the decompiler used it instead of the original. It is messy because it was created with a hex editor many years ago. I am a bit torn about it. Since the archivist in me wants preserve the original, but CP annoys the f**k out of me. I guess the way to handle it is to decompile the original and just change the KQ4::init. That way the CP could easily be switched on or off.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 07, 2018, 10:54:14 AM
just change the KQ4::init. That way the CP could easily be switched on or off.
That's what I was thinking, yeah.

Edit: missed a bit in rewriting the print statement.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 07, 2018, 04:18:59 PM
That is throwing a bunch errors. I had it down to no errors. And now there is a TheMenuBar and a MenuBar? Looks like you changed proc255_ to Print and Class_255_0 to MenuBar. Is there possibly some confusion between proc255 and Class_255?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 07, 2018, 04:46:27 PM
Possibly, possibly. I did neglect to check if it compiled, doing this all from my favorite text editor instead of SCI Companion...


Okay, I fixed it!
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 07, 2018, 06:01:01 PM
Just typing "Hello" results in errors and then gives the expected result:

Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 07, 2018, 06:15:36 PM
Here is a decompile that does not include the CP skip patch. It is also using the name MenuBar instead of TheMenuBar. It does not include the changes you did, but there is no difference the print, so the problem must be elsewhere.
Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on October 07, 2018, 07:36:10 PM
Just typing "Hello" results in errors and then gives the expected result:
Well, the message pretty much says what is happening, doesn't it? There's a said spec somewhere that's been corrupted. What the cause is, I can't say just from looking at it, but since these are only individual Said calls failing, the rest will surely work.(Or fail, and eventually pass execution to the pragmaFail routine, I believe, where an error is printed "You've left me speechless" or whatever the game chooses)

EDIT: Aaaaand, the first error is this, in TheMenuBar.sc:

                (SetMenu 513 109 ',[/game]')

the number 500 (decimal; SCI Companion uses hex numbers, so it's 1f4 in the vocab view) resolves to 'game'.

The second error is this, in Main.sc (in one of the assembler parts):

                        lofsa    'chew/['

again, the number says so. Four things:

Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on October 07, 2018, 11:17:01 PM
So the problem really is that Companion carries a said spec around as a vector of uint16. This fails if a group number happens to fall within the range 240..255. Otherwise it works well. It would have to be a vector of tuples <uint16, bool> with the bool set to true if this is actually a word, false if it is an operator.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 08, 2018, 07:42:01 AM
Judging from SV's disassembly, that should be "quit [/ game]" for the first one. The second, I can see "chew" "chew / earthworm" "chew / bone" "chew / fruit" and "chew / chicken" in the saids block.
Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on October 08, 2018, 04:57:24 PM
No, quit game is already there:
Code: [Select]
               (SetMenu 513 109 ',[/game]')
                (SetMenu 514 109 'restore[/game]')
                (SetMenu 516 109 'restart[/game]')
                (SetMenu 517 109 'quit[/game]')
                (SetMenu 769 109 'pause[/game]')
                (SetMenu 770 109 'inventory')
The top one is bad; it has to be save game. Besides, quit has the group number 0x1d3 (467 decimal). It is not in the problematic range (save is 0xf0, which is in that range).
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on October 08, 2018, 06:19:44 PM
Good bug. I made a fix in my experimental branch of SCI Companion:
https://github.com/icefallgames/SCICompanion/commit/0f4582e5ae2d2c66b4876a73d7f1bb65126c4416

It doesn't look like github has a way to cherry pick a change from another branch, and I don't have the time to clone the main branch and do it myself (I just work on the experimental branch I use for cascadia quest). But if anyone (kawa?) feels inclined to make the same change on the main branch I'll approve it (of course it's possible they've diverged so much it's not straightforward, I dunno).

Attached are the words that could run into problems in a KQ4 decompile. In additino to those discovered so far, "rosella" might be a pretty obvious problem ;-)
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 08, 2018, 06:43:49 PM
I'll have to disable some of my own hacks, but I'll get on it.

Edit: compiling...

Edit: Now, Github does not work for me, at least not in the "put things back on" sense, but I've been tracking the repo Phil linked well enough to be up-to-date, so here's the latest build (http://helmet.kafuka.org/sci/SCICompanion.exe) with those vocab changes in it.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 09, 2018, 03:17:41 AM
Quick warning, I just realized as my laptop booted up for today that I'd forgotten to revert one particular hack: this build doesn't have SCI Studio syntax. I missed it because I hadn't marked the changes.

Copy in the stash replaced.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 09, 2018, 11:56:50 AM
I just tried Kawa's new build and did a fresh decompile from scratch. The resulting recompile no longer throws errors on input of "hello" or any of the works troflip found. Some of those words were inventory items.

The only changes that I have made are to skip the CP script and to set the cursor on the opening screen to normal from the wait icon. I also tried to fix the only warning the compiler was giving in the switch in the rm92 script that had duplicate case values. I changed them to be sequential, but don't know if that breaks anything.
Title: Re: Recreating complete QFG1 EGA source code
Post by: troflip on October 09, 2018, 12:37:13 PM
I also tried to fix the only warning the compiler was giving in the switch in the rm92 script that had duplicate case values. I changed them to be sequential, but don't know if that breaks anything.

I believe the correct fix would just be to remove or comment out the second duplicate case statement. It's unreachable code in the original.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 09, 2018, 01:18:38 PM
That room is Lolotte's throne room. I was able to get through it with no trouble after compiling with the duplicate case commented out.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 09, 2018, 01:21:37 PM
Reapplied some global names and the Print family, this time with testing.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 09, 2018, 03:11:55 PM
I don't know what version this is, so I can't give it an intricate name like Collector's KQ4, but here's SQ3 I guess? Bled to death on the second screen just to test. Got the first few globals and the Print family in.

Edit: wow, I added the #title selectors and such in Print's argument parser and suddenly the pushy clerk's prints use them too!
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 09, 2018, 06:45:56 PM
There was no VERSION file? The interpreter version can be extracted for the string in the EXE. If you don't feel like bothering you can upload it and I can check.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 09, 2018, 08:03:41 PM
There was. I just didn't bother. I'll put a version in the next upload.

Fun fact: I thought it a bug in the recompile but it seems the intro allows typing?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 10, 2018, 12:30:46 AM
I have 2 or 3 different versions of the game. It would probably be best to keep version info with the source.

Fun fact: I thought it a bug in the recompile but it seems the intro allows typing?
Never tried it.

Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 10, 2018, 04:07:15 AM
I got a version 0.000.685 interpreter running game 1.018.

Edit: I also have a German multilang 1.052 and an Amiga version with only the resources.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 10, 2018, 05:08:16 AM
Script differences between SQ3 1.018 main.sc and German 1.052 main.sc:

...and various vocabulary additions with matching saidspec changes, but still handsOn during the title sequence.

(incidentally, my SQ3 German runs on S.old.114)
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 10, 2018, 10:01:29 AM
I assume that S.old.114 has support for fonts with diacritics.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 10, 2018, 11:24:53 AM
It's funny you should say that. The 0.685 terp from SQ3 doesn't, yet not 24 hours ago I produced this screenshot?

(https://66.media.tumblr.com/2f3dfa23ddc87d0ed5e61efd269a5b05/tumblr_pgbt2vvPKS1r1hfpzo1_400.png)
That's KQ4 1988, SIERRA.EXE 0.274 with the green buttons, not 1989 SCIV.EXE 0.502 with the white buttons.

This is my 1252 font, but that makes no difference in the matter of i]support[/i].

Actually, I think I get what I did wrong just now when I tested SQ3's 0.685 for extended font support...

Edit: nope, still won't show 'em. Weird, huh?
Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on October 10, 2018, 05:06:25 PM
Well, one difference between SCI0 and SCI01 is the encoding of the vocab resource. The latter uses a standard NUL terminator, the former sets the high bit on the last character. Which means that you can't have 8-bit stuff in the vocab resource at least. It's possible they thought they weren't quite ethnocentric enough in their font decoding and changed it.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 10, 2018, 05:15:47 PM
And yet that's the chronologically first SCI0 game correctly asking for lööps :D
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 11, 2018, 02:44:10 PM
I have added your SQ3 source to the Wiki. Didn't troflip successfully decompile SQ4 CD for the SCI1.1 template?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on October 11, 2018, 03:01:49 PM
SQ5 actually. Older releases included leftover object files like Droole.sco, and had SQ5 Roger and UI views and all that.

I should know :)
Title: Re: Recreating complete QFG1 EGA source code
Post by: Collector on October 11, 2018, 07:34:47 PM
That's it. Now I remember about the simulator at the beginning of the game came up. I wonder if he has the source intact before it was modified for the template or if we would need to decompile it again.
Title: Re: Recreating complete QFG1 EGA source code
Post by: lskovlun on October 27, 2018, 08:05:40 PM
The S.old.xxx series is SCI01. The KQ1 remake used one; it was playable for quite a while under FreeSCI back when it had no support for the newer memory model. We can't know for sure, but it's possible that these games were developed initially under SCI0 and gradually upgraded, so that only a few rooms take advantage of the extra memory.

We saw the same thing with the QFG4, GK1 and PQ4 demos, which are SCI1.1 unlike the final games. With these it is less likely that the whole game was developed under SCI1.1 initially. While Sierra took some pains to make the high-level interfaces compatible, the graphics systems underneath are entirely different. It still could be true, though.
Title: Re: Recreating complete QFG1 EGA source code
Post by: EricOakford on November 01, 2018, 10:22:27 PM
After weeks of combing the code and identifying many local variables, I now have the decompiled source code for QFG1EGA, version 1.200! Obviously, some things were changed between 1.200 and 1.000, so I overhauled the game.sh file to reflect this, as well as gave more readable names for the event flags and shorter, more concise names for the inventory items.

I made sure to put as much of the comments from the HQ1 code into the QFG1EGA code. The names of some of the variables and procedures were changed, either to be consistent with Sierra's original names (such as Bclr and Btst) or to be more accurate (HasLockPickTools has been changed to CanPickLocks, for example)

The code compiles without any errors (save for warnings about duplicate case values, which are in the original scripts anyway). I have not yet played through the game to see if anything went awry.

11/3/2018 EDIT: Apparently, something DID go awry - the menu bar stopped working (because I removed the "The" from the menu bar class name by mistake) and the stats on the character sheet are over black boxes (the HQ1 code had that mistake by using the wrong define) Here's the new code with these mistakes fixed. Sorry for the inconvenience.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on November 01, 2018, 10:34:27 PM
either to be consistent with Sierra's original names (such as Bclr and Btst)
I'm curious. How exactly do you know that's what they're supposed to be called?
Title: Re: Recreating complete QFG1 EGA source code
Post by: EricOakford on November 01, 2018, 11:53:00 PM
The original names are based on the code snippets that OmerMor provided previously. Also, in the Main.sc of the HQ1 source zip, it is stated that Bclr and Btst are their original names.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on November 02, 2018, 08:56:10 AM
It's just, SCI16.zip doesn't have a main.sc, last I checked.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on November 02, 2018, 12:11:59 PM
He means in the main.sc of the decompiled HQ1 v1.000 source I did.  I'd commented that they were originally called Bclr and Btst.  My comments were based on OmerMor's code snippets, so it's just a single source confirmation.

That's awesome work Eric, I'm looking forward to comparing the differences between HQ1 v1.000 and QFG1 v1.200.

When you do get to test the game, give a look at Henry's Outlook. I know I had decompiler issues with that room, when I did HQ1 v1.000.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on November 02, 2018, 01:53:28 PM
So I ask again, what snippets?
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on November 02, 2018, 08:48:43 PM
Thanks for the update - very interesting!

Btst and Bclr are used in many SCI games, and it's not limited to event flags, so you if you don't like the original names, you might want to use bitTest and bitClear etc. Names like getEventFlag is too specific.
EDIT: So I noticed that these functions are actually tied to a specific global flags array. Here's the version from QfG2:
Code: [Select]
(procedure (Btst flagEnum)
   (& [gameFlags (/ flagEnum 16)] (>> $8000 (mod flagEnum 16)))
;; (DisposeScript FLAGS)
)

(procedure (Bset flagEnum  &tmp oldState)
   (= oldState (Btst flagEnum))
   (|= [gameFlags (/ flagEnum 16)] (>> $8000 (mod flagEnum 16)))
   oldState
;; (DisposeScript FLAGS)
)

(procedure (Bclr flagEnum  &tmp oldState)
   (= oldState (Btst flagEnum))
   (&= [gameFlags (/ flagEnum 16)] (~ (>> $8000 (mod flagEnum 16))))
   oldState
;; (DisposeScript FLAGS)
)

From OmerMor on the 1st page of this very thread.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Kawa on November 02, 2018, 10:46:22 PM
Ah. Thank you.
Title: Re: Recreating complete QFG1 EGA source code
Post by: Charles on November 05, 2018, 04:08:45 PM
Eric,

I was just looking through your code myself, and I noticed that you left script n982 and n896 out of the game.ini.  Even if you don't have a custom name for them, you should include them in the game.ini otherwise a Compile All won't include them.

Also, I believe n982 is SIGHT.SC. proc982_0 is IsOffScreen, proc982_1 is CantBeSeen, and proc982_2 is AngleDiff.
Title: Re: Recreating complete QFG1 EGA source code
Post by: EricOakford on November 05, 2018, 09:08:45 PM
I based the game.ini file on the one for 1.000. While I accounted for the changes in the game scripts, I didn't account for additional system scripts.

Two system scripts added by the time 1.200 was released were Avoider and Sight.

Here is an updated version of the code, with a few more local variables identified as well as the additional system scripts.

As for test results...

The game seems to play okay, but certain rooms have gotten bugged. In the fairy ring, the mushrooms are not placed properly on the screen, and the game may crash with an "Oops!" error when the fairies approach you at night.

Entering the Meep's Peep is now completely impossible, since the game crashes with an "Oops!" error when trying to enter it. Good thing Razzle Dazzle Root Beer is there to bypass those bugged parts.
Title: Re: Recreating complete QFG1 EGA source code
Post by: EricOakford on February 16, 2019, 11:22:53 AM
Here we go again! An updated decompile of QFG1EGA, using more original define and procedure names. Also, the SYSTEM, GAME, and KERNEL headers have been converted to Sierra Script, allowing for enum support. I've added in a DEBUG.BAT that automatically starts the game with the -d parameter making it easier to debug.

Now to just playtest this and work on the QFG1VGA decompile...