From: hnsngr@sirius.com (Ron Hunsinger) Newsgroups: alt.games.marathon Subject: Re: Pathways into Darkness Encryption? Message-ID: <hnsngr-ya023180001712000227320001@news.flash.net> References: <141220001926092035%petrich@netcom.com> <santiago-4558B0.16465215122000@newshost.cc.utexas.edu> <hnsngr-ya023180001612000036060001@news.flash.net> <161220001000247019%petrich@netcom.com> Organization: ErsteSoft Date: Sun, 17 Dec 2000 10:27:26 GMT NNTP-Posting-Date: Sun, 17 Dec 2000 04:27:26 CST In article <161220001000247019%petrich@netcom.com>, Loren Petrich <petrich@netcom.com> wrote: > In article <hnsngr-ya023180001612000036060001@news.flash.net>, Ron > Hunsinger <hnsngr@sirius.com> wrote: > > The code to encrypt/decrypt a scri resource is: > > > > void EncodeScript (Handle h) { > > int len = **(short**) h; > > Ptr p = *h + 2; > > for (int i = 0; i < len; ++i) { > > *p++ ^= i; } > > } > > > > Of course, there's still a little more to it than that, ... > > Thanx. I've gotten some intelligible text out of that, and I think I > have a clue as to those opcodes. My guess at the moment is that they > are 2-byte instead of 1-byte ones. I can give you some more information. First off, I want to correct an error in the above code. The length word at the beginning of the resource counts itself as part of the length. The above code assumes it doesn't, so it translates two bytes beyond the end of the resource. To correct it, the initial value for len should be: int len = **(short**) h - 2; ^^^ Oddly, the code I posted is exactly the code I used to decrypt the resources lo these many moons ago. I guess I was just lucky it didn't crash. You can partially DeRez the decrypted resource using this definition: type 'scri' { unsigned integer = StringEnd / 8; unsigned integer = $$Countof (StringArray); unsigned integer = StringStart / 8; unsigned integer = 1; /* I don't know what this is */ unsigned integer = 10; /* I don't know what this is */ literal longint; /* Type of corpse */ CodeStart: hex string [StringStart / 8 - CodeStart / 8]; StringStart: array StringArray { cstring; }; StringEnd: }; That hex string starting at CodeStart is the program. The instructions set is the following. (I've made up the names, and I'm a little unclear on the finer nuances of some of them. I think I knew the details at one time, but my notes are incomplete in places.) a (byte) is 8 bits a (word) is 16 bits an (OSType) is 32 bits containing 4 printable characters branch instructions contain a 16-bit signed delta which, if added to the address of the *beginning* of the current instruction, gives the address of the beginning of the instruction being branched to a filler is always ignored, and usually contains garbage strings are referenced by index, not offset, starting at zero Test Environment opcode = 0 (byte) filler (byte) what to check (OSType) delta (word) Tests the indicated condition in the environment, and branches if the condition is true. (For example, 'dark' is true if you do not have a turned-on flashlight. See scri#138.) Test Variable opcode = 1 (byte) filler (byte) mask (word) value (word) delta (word) There is a word-size variable associated with each corpse. The value is retained across uses of the yellow crystal. The condition being tested is ((variable & mask) == value). An unconditional branch is obtained by setting mask = value = 0. Speak opcode = 2 (byte) variations (byte) first string (word) Speaks a randomly selected string from among the <variations> strings starting with indicated <first string>. For example, the instruction 02030007 randomly selects one of string #7, string #8, or string #9 for output. Listen opcode = 3 (byte) prompted (byte) hidden (byte) filler (byte) first word (word) deltas (array of words) Waits for input from the user, then scans it looking for one of the known words. <prompted> is the number of words that are given to the user (in a popup menu, perhaps?). This is always zero in PiD. <hidden> is the number of words that the user has to stumble across by guesswork. The words themselves appear as strings, starting with string #<first word>. The array of deltas contains one element for each word; the instruction does a conditional branch using the delta from the best match found. If none of the words are found in the input, execution falls through to the next instruction. Set Variable opcode = 4 (byte) filler (byte) mask (word) value (word) Sets the indicated portion of the variable associated with the current corpse. I believe the calculation is: variable = (variable & ~mask) | (value & mask); but that's one of the things I didn't put in my notes. The most common use of this is to remember if you've talked to this corpse before, so it can say something different when you come back. ("Hello again" instead of "Who are you?") Callback opcode = 5 (byte) filler (byte) action (OSType) Performs the indicated action. (Must be one built into the engine.) The only action I know of is 'STOP'. The program for each scri resource ends with a callback to this action. (That is, the final instruction is always 05xx53544F50, where the xx is garbage.) That final instruction is never reachable. As an example, the program contained in scri#138 (for the corpse on "We Can See In the Dark, Can You?") begins as follows: 000E: 0000 6461 726B 003A // if 'dark' goto 0048 (000E+003A) 0016: 0201 0000 // speak 0 ("Get that light away from me! Get it away! No lights! They're coming!") 001A: 0300 045F 0001 // listen for 4 words starting at #1 0016 0016 0022 0022 // goto, respectively, // light -> 0030 (001A+0016) // flashlight -> 0030 (001A+0016) // they -> 003C (001A+0022) // who -> 003C (001A+0022) 0028: 019B 0000 0000 FFEE // goto 0016 (0028+FFEE) 0030: 0201 0005 // speak 5 ("They're attracted to your light. Fool! Get away from me!") 0034: 019B 0000 0000 FFE6 // goto 001A (0034+FFE6) 003C: 0201 0006 // speak 6 ("Those things, those things! They're all around, they hide in the corners until they see light ...") 0040: 019B 0000 0000 FFDA // goto 001A (0040+FFDA) 0048: 0201 0007 // speak 7 ("Are they following you? You don't have any lights, do you? Stay away ...") 004C: 0300 2365 0008 // listen for 35 words starting at #8 0070 00D0 ... // goto, respectively, ... -Ron Hunsinger