Norbert wrote:You could contact tacosalad and ask if he's willing to share that executable.
No need. I wish I had checked out his forum profile and found his site earlier.
His mod already contains the modified EXE.
Norbert wrote:Then we could do a binary comparison to get some pointers on how to tackle this.
Code: Select all
Comparing files Prince.exe and PrinceTS.exe
0001ABC7: 09 41
0001ABC9: 01 41
0001ABCB: 06 42
0001ABCD: 04 42
0001ABCF: 05 43
0001ABD1: 03 43
0001ABD3: 06 44
0001ABD5: 03 44
0001ABD7: 04 45
0001ABD9: 04 46
0001ABDB: 03 46
0001ABDD: 02 47
0001ABDF: 0C 48
0001ABE1: 05 48
0001ABE3: 0D 49
0001ABE5: 01 49
0001ABE7: 09 4A
0001ABE9: 02 4A
0001ABEB: 02 4B
0001ABED: 04 4C
0001ABEF: 09 4C
0001ABF1: 04 4D
0001ABF3: 0B 4D
0001ABF5: 08 4E
0001ABF7: 05 4F
0001ABF9: 04 4F
0001ABFB: 01 50
0001ABFD: 06 50
0001ABFF: 02 52
0001AC01: 04 52
0001AC03: 06 53
0001AC05: 08 53
0001AC07: 04 54
0001AC09: 02 54
0001AC0B: 07 55
0001AC0D: 0B 55
0001AC0F: 05 56
0001AC11: 04 59
0001AC13: 01 57
0001AC15: 02 59
0001B4A2: 57 20
0001B4A3: 4F 20
0001B4A4: 52 20
0001B4A5: 44 20
0001B4A6: 20 44
0001B4A7: 25 52
0001B4A8: 64 49
0001B4A9: 20 4E
0001B4AA: 4C 4B
0001B4AB: 49 20
0001B4AC: 4E 50
0001B4AD: 45 4F
0001B4AE: 20 54
0001B4AF: 25 49
0001B4B0: 64 4F
0001B4B1: 20 4E
0001B4B2: 50 20
0001B4B3: 41 25
0001B4B4: 47 63
0001B4B5: 45 20
0001B4B7: 25 20
0001B4B8: 64 20
0001B4D0: 74 6C
0001B4D1: 68 65
0001B4D2: 65 74
0001B4D3: 20 74
0001B4D4: 66 65
0001B4D5: 69 72
0001B4D6: 72 20
0001B4D7: 73 25
0001B4D8: 74 63
0001B4D9: 20 2E
0001B4DA: 6C 20
0001B4DB: 65 20
0001B4DC: 74 44
0001B4DD: 74 72
0001B4DE: 65 69
0001B4DF: 72 6E
0001B4E0: 20 6B
0001B4E1: 6F 69
0001B4E2: 66 6E
0001B4E3: 20 67
0001B4E4: 57 20
0001B4E5: 6F 61
0001B4E6: 72 6E
0001B4E7: 64 79
0001B4E9: 25 6F
0001B4EA: 64 74
0001B4EB: 20 68
0001B4EC: 6F 65
0001B4ED: 6E 72
0001B4EF: 4C 70
0001B4F0: 69 6F
0001B4F1: 6E 74
0001B4F2: 65 69
0001B4F3: 20 6F
0001B4F4: 25 6E
0001B4F5: 64 20
0001B4F6: 0D 77
0001B4F7: 6F 69
0001B4F8: 66 6C
0001B4F9: 20 6C
0001B4FA: 50 20
0001B4FB: 61 6E
0001B4FC: 67 6F
0001B4FD: 65 74
0001B4FF: 25 62
0001B500: 64 65
0001B502: 6F 68
0001B503: 66 65
0001B504: 20 61
0001B505: 74 6C
0001B506: 68 74
0001B507: 65 68
0001B508: 20 79
0001B509: 6D 2E
0001B50A: 61 20
0001B50B: 6E 20
0001B50C: 75 20
0001B50D: 61 20
0001B50E: 6C 20
0001B50F: 2E 20
Comparing the two in a hex editor, the first set of edits seems to involve modifying the potion letters to range from A to Z alphabetically. I can't really figure out the second set of edits though, that deal with the dialog box and statusbar message. How did whoever hex edited the EXE replace the %d vars for Page/Line/Word with a %c for the char of the potion to be drunk?
Edit: Ok, not that difficult to understand after all, and no opcodes need to be edited either to pull this off. The EXE stores all the 40 ints for Page, Line and Word one after the other (40 x 2 bytes/int = 80 bytes each), followed by the 40 chars for the Codes (40 x 1 byte/char = 40 bytes). In this edited EXE the ints for Word are simply replaced by the same chars as used for Codes, in the same order. This works because an int (-32768 to +32767) can of course fit any char (-128 to +127).
Next we have two strings: 1) "WORD %d LINE %d PAGE %d" used for the statusbar, and 2) "Drink potion matching the first letter of Word %d on Line %d of Page %d of the manual." used for the dialog box. Note that the first int (%d) value in both strings is for the Word. The edited EXE simply changes the strings to "DRINK POTION %c" and "Drink potion matching letter %c. Drinking any other potion will not be healthy." respectively. Since we already replaced all Word values with Code values earlier, the strings print the value of Code now which is precisely what we want.
In terms of C code, something like this:
Code: Select all
printf("WORD %d LINE %d PAGE %d", wordarray[i], linearray[i], pagearray[i]);
printf("Drink potion matching the first letter of Word %d on Line %d of Page %d of the manual.", wordarray[i], linearray[i], pagearray[i]);
is replaced with something like this:
Code: Select all
printf("DRINK POTION %c", wordarray[i], linearray[i], pagearray[i]);
printf("Drink potion matching letter %c. Drinking any other potion will not be healthy.", wordarray[i], linearray[i], pagearray[i]);
Since wordarray now contains the same values as codearray
*, et voilà!
Of course the Line and Page values are unused now but who cares. An interesting thing to note is that from PoP1 1.1 onwards, the statusbar string is "PAGE %d LINE %d WORD %d" (the dialog box string and the array order in the EXE are still the same however). This mismatch between the first params passed to the two strings means that
both the Page and Word int values would need to be replaced with the Code chars. Either that, or more complicated hex edits would be required to swap the first and third params passed to the statusbar string.
Pulling off something similar for PoP2 (where you're told exactly which symbol to select) is bound to be a lot more difficult I guess, but that's something only an assembly language expert like David can tackle.
*Technically the low byte of each int in wordarray stores the byte of each corresponding char from codearray (since Intel x86 is little endian), but again, I doubt anyone cares.