King’s Quest 4 – The Perils of Rosella starts with a copy protection challenge right off the bat.
If you were playing the original 1988 version you could just enter the magic word “bobalu” and be done with it, but the 1989 version removed this.
It’s a pretty simple challenge-reply system, but the interesting bit is how your answers are considered. If someone were to somehow find the challenges they would also find the answers in the same order, but there’s a catch: the answers are hashed.
Very simply so, but they are. And of course the script code containing the challenges and answer hashes is compressed in the
RESOURCE.001 file, and the whole thing is script code and nobody outside of Sierra could be expected to be able to read that stuff back in 1988. Sure, maybe some people could but still good luck figuring out how this worked. Even the backdoor phrase was compressed.
But now it’s 2018, nearly 2019, and I for one have made happy use of the tools now available to us, mostly to slake my own thirst for knowledge. So here’s how it works.
The copy protection script has several local variables: a random number from 1 to 79, the challenge text, the correct answer’s hash, a buffer for the user’s input, the hash for said input, and some work variables.
On startup, the random number is chosen. Then, in a big ol’
switch statement, the correct hash is decided on:
(switch (= randomPick (Random 1 79))
(1 (= requestSum 431))
(2 (= requestSum 521))
(3 (= requestSum 535))
(79 (= requestSum 686))
In another big ol’
switch (instead of doing it at once?), the matching challenge is set up:
(1 (= requestText "On page 2, what is the fourth word of the first sentence?"))
(2 (= requestText "On page 2, what is the fourth word of the second paragraph?"))
(3 (= requestText "On page 3, what is the fourth word in the first paragraph?"))
(79 (= requestText "In the section TIPS FOR NEW ADVENTURE PLAYERS, what is the eighth word in the first paragraph of tip #2 (STAY OUT OF DANGER)?"))
Incidentally, I said our guess would be stored in a buffer variable, that is an array in memory large enough to contain it, but I did not say any such thing about the challenge text. That’s because it’s stored as a pointer to the text, in the place it was loaded to as part of the script. From then on these challenges don’t mutate in any way. Our input can be literally anything.
Anyway, after displaying the challenge, we have our input in a buffer. This is where the magic happens:
(= i 0)
(while (< i (StrLen @userInput))
(= ch (& (= ch (StrAt @userInput i)) $005f))
(StrAt @userInput i ch)
(= inputSum (+ inputSum ch))
Iterating through the user’s input, we read the next line inside-out. Using the
StrAt function we fetch the next character and store its value in our work variable. Then we use some binary magic on that same value to turn it into UPPERCASE, and assign that to our work var. Now, as I write this I feel like this can be simplified a little bit…
(= ch (& (StrAt @userInput i) $005f))
…Yeah, that seems nice. I don’t think it’d hurt functionality to do this. Anyway, the next line shows how SCI function and kernel calls can be variadic as all get out — given three arguments,
StrAt will set the character on the given spot. In the third line, we add the character’s value to our running sum.
And that’s it! We can now compare our input to the expected answer, and either continue on to the title screen or display an error and quit.
But that’s not all there is to it. First, for some reason, the input is uppercased and then stored again, character by character. This is so the 1988 release can compare it against the magic backdoor word, which is also in uppercase. This seems like an awful waste when you could compare it against a number instead. Not to mention, the 1989 release doesn’t even have the backdoor and still does all this. (For the record, that would be 437.)
Second, this is such a simple method that there are guaranteed to be words with the same hashes. For example, “voice” and “licks” are both 374.
But yeah, that’s just about all there is to know about the copy protection in King’s Quest 4 – The Perils of Rosella.