/* * A program to remove copy protection on various Infocom games * * The license on this source code is simple: Do whatever you want with * it as long as you do not run it on any games that you do not legally * own. Anyone violating this license is cursed to being lathered up in * honey and tossed outside the lair of a hungry bear. * * I started writing this because Michael Gerway, the deaf-blind guy, couldn't * play AMFV because he couldn't read the manual. Also, I always had a * tendency to lose parts of the game package. In fact, when I moved last * year, I seem to have lost the entire Lost Treasures of Infocom, vol II, * book. It still hasn't turned up. * * (c) 1996 Allen Garvin (earendil@faeryland.tamu-commerce.edu) */ #include #include #include #include #include #include #include #include char *fcontents, *progfile, *gamefile; int gamesize; #define ZBYTE(ad) ((unsigned char) fcontents[ad]) #define ZWORD(ad) ((unsigned char) fcontents[ad] * 256 + \ (unsigned char) fcontents[ad+1]) union zword { unsigned char a[2]; short num; }; union zword make_zword(int n) { union zword tmp; tmp.num = 0; if( n < 0 ) { n = 65536 + n; } tmp.a[1] = (char) (n % 256); n >>= 8; tmp.a[0] |= (char) n; return tmp; } #define FATAL(str) { printf str; exit(1); } #define MAX_NAME 60 #define DONT_KNOW 2 #define DONT_HAVE 1 #define HAVE 0 int sorcerer_fix(); int ballyhoo_fix(); int starcross_fix(); int amfv_fix(); int stationfall_fix(); int arthur_fix(); int spellbreaker_fix(); int lurking_fix(); int moonmist_explanation(); int infidel_explanation(); int zork0_explanation(); int trinity_explanation(); int bureau_fix(); int cutthroat_fix(); struct games { int release; char serial[7]; char name[MAX_NAME]; int copy_protected; int not_owned; int (*remove_prot)(); } game_list[] = { 77, "850814", "AMFV", 1, HAVE, amfv_fix, 79, "851122", "AMFV", 1, DONT_HAVE, NULL, 54, "890606", "Arthur", 1, DONT_HAVE, NULL, 63, "890622", "Arthur", 1, DONT_HAVE, NULL, 74, "890714", "Arthur", 1, HAVE, arthur_fix, 97, "851218", "Ballyhoo", 1, HAVE, ballyhoo_fix, 47, "870915", "Beyond Zork", 0, DONT_HAVE, NULL, 49, "870917", "Beyond Zork", 0, DONT_HAVE, NULL, 51, "870923", "Beyond Zork", 0, HAVE, NULL, 57, "871221", "Beyond Zork", 0, HAVE, NULL, 9, "871008", "Border Zone", DONT_KNOW, HAVE, NULL, 86, "870212", "Bureaucracy", 1, HAVE, bureau_fix, 116, "870602", "Bureaucracy", 1, HAVE, bureau_fix, 23, "840809", "Cutthroats", 1, HAVE, cutthroat_fix, 18, "820311", "Deadline", 0, DONT_HAVE, NULL, 19, "820427", "Deadline", 0, DONT_HAVE, NULL, 21, "820512", "Deadline", 0, DONT_HAVE, NULL, 22, "820809", "Deadline", 0, HAVE, NULL, 26, "821108", "Deadline", 0, HAVE, NULL, 27, "831005", "Deadline", 0, HAVE, NULL, 10, "830810", "Enchanter", 0, HAVE, NULL, 15, "831107", "Enchanter", 0, DONT_HAVE, NULL, 16, "831118", "Enchanter", 0, HAVE, NULL, 24, "851118", "Enchanter", 0, DONT_HAVE, NULL, 29, "860820", "Enchanter", 0, HAVE, NULL, 26, "840731", "Four in One Sampler", 0, HAVE, NULL, 53, "850407", "Four in One Sampler", 0, HAVE, NULL, 55, "850823", "Four in One Sampler", 0, HAVE, NULL, 97, "870601", "Four in One Sampler", 0, HAVE, NULL, 42, "850323", "Hitchhiker's Guide", 0, HAVE, NULL, 47, "840914", "Hitchhiker's Guide", 0, HAVE, NULL, 56, "841221", "Hitchhiker's Guide", 0, HAVE, NULL, 58, "851002", "Hitchhiker's Guide", 0, HAVE, NULL, 59, "851108", "Hitchhiker's Guide", 0, DONT_HAVE, NULL, 31, "871119", "Hitchhiker's Guide", 0, HAVE, NULL, 37, "861215", "Hollywood Hijinx", DONT_KNOW, HAVE, NULL, 22, "830916", "Infidel", 1, HAVE, infidel_explanation, 26, "890316", "Journey", DONT_KNOW, DONT_HAVE, NULL, 30, "890322", "Journey", DONT_KNOW, DONT_HAVE, NULL, 77, "890616", "Journey", DONT_KNOW, DONT_HAVE, NULL, 83, "890706", "Journey", DONT_KNOW, DONT_HAVE, NULL, 118, "860325", "Leather Goddesses", DONT_KNOW, DONT_HAVE, NULL, 50, "860711", "Leather Goddesses", DONT_KNOW, DONT_HAVE, NULL, 59, "860730", "Leather Goddesses", DONT_KNOW, HAVE, NULL, 59, "861114", "Leather Goddesses", DONT_KNOW, DONT_HAVE, NULL, 4, "880405", "Leather Goddesses", DONT_KNOW, HAVE, NULL, 203, "870506", "Lurking Horror", 1, HAVE, lurking_fix, 219, "870912", "Lurking Horror", 1, HAVE, lurking_fix, 221, "870918", "Lurking Horror", 1, HAVE, lurking_fix, 4, "860918", "Moonmist", 1, HAVE, moonmist_explanation, 9, "861022", "Moonmist", 1, HAVE, moonmist_explanation, 19, "870722", "Nord and Bert", DONT_KNOW, HAVE, NULL, 20, "830708", "Planetfall", 0, HAVE, NULL, 26, "831004", "Planetfall", 0, HAVE, NULL, 29, "840118", "Planetfall", 0, DONT_HAVE, NULL, 37, "851003", "Planetfall", 0, HAVE, NULL, 10, "880531", "Planetfall", 0, HAVE, NULL, 26, "870730", "Plundered Hearts", 0, HAVE, NULL, 86, "840320", "Seastalker", DONT_KNOW, HAVE, NULL, 15, "840501", "Seastalker", DONT_KNOW, HAVE, NULL, 15, "840522", "Seastalker", DONT_KNOW, DONT_HAVE, NULL, 16, "850515", "Seastalker", DONT_KNOW, DONT_HAVE, NULL, 16, "850603", "Seastalker", DONT_KNOW, HAVE, NULL, 21, "871214", "Sherlock", DONT_KNOW, HAVE, NULL, 26, "880127", "Sherlock", DONT_KNOW, HAVE, NULL, 292, "890314", "Shogun", 0, DONT_HAVE, NULL, 295, "890321", "Shogun", 0, DONT_HAVE, NULL, 311, "890510", "Shogun", 0, DONT_HAVE, NULL, 322, "890706", "Shogun", 0, HAVE, NULL, 67, "000000", "Sorcerer", 1, DONT_HAVE, sorcerer_fix, 4, "840131", "Sorcerer", 1, HAVE, sorcerer_fix, 6, "840508", "Sorcerer", 1, DONT_HAVE, sorcerer_fix, 13, "851021", "Sorcerer", 1, HAVE, sorcerer_fix, 15, "851108", "Sorcerer", 1, HAVE, sorcerer_fix, 18, "860904", "Sorcerer", 1, DONT_HAVE, sorcerer_fix, 63, "850916", "Spellbreaker", 1, HAVE, spellbreaker_fix, 87, "860904", "Spellbreaker", 1, HAVE, spellbreaker_fix, 15, "820901", "Starcross", 1, HAVE, starcross_fix, 17, "821021", "Starcross", 1, HAVE, starcross_fix, 14, "841005", "Suspect", 0, HAVE, NULL, 5, "830222", "Suspended", 0, HAVE, NULL, 7, "830419", "Suspended", 0, HAVE, NULL, 8, "830521", "Suspended", 0, HAVE, NULL, 8, "840521", "Suspended", 0, HAVE, NULL, 107, "870430", "Stationfall", 1, HAVE, stationfall_fix, 11, "860509", "Trinity", 1, DONT_HAVE, trinity_explanation, 12, "860926", "Trinity", 1, DONT_HAVE, trinity_explanation, 68, "850501", "Wishbringer", 0, HAVE, NULL, 69, "850920", "Wishbringer", 0, HAVE, NULL, 23, "880706", "Wishbringer", 0, HAVE, NULL, 13, "830524", "Witness", 0, HAVE, NULL, 18, "830910", "Witness", 0, HAVE, NULL, 20, "831119", "Witness", 0, DONT_HAVE, NULL, 21, "831208", "Witness", 0, DONT_HAVE, NULL, 22, "840924", "Witness", 0, HAVE, NULL, 34, "871124", "Mini-Zork I", 0, HAVE, NULL, 23, "820428", "Zork I", 0, DONT_HAVE, NULL, 25, "820515", "Zork I", 0, HAVE, NULL, 26, "820803", "Zork I", 0, DONT_HAVE, NULL, 28, "821013", "Zork I", 0, DONT_HAVE, NULL, 30, "830330", "Zork I", 0, HAVE, NULL, 75, "850929", "Zork I", 0, HAVE, NULL, 76, "840509", "Zork I", 0, DONT_HAVE, NULL, 88, "840726", "Zork I", 0, HAVE, NULL, 52, "871125", "Zork I Special Edition", 0, HAVE, NULL, 3, "880113", "Zork I German Version", 0, HAVE, NULL, 15, "820308", "Zork II", 0, HAVE, NULL, 17, "820427", "Zork II", 0, DONT_HAVE, NULL, 18, "820512", "Zork II", 0, DONT_HAVE, NULL, 18, "820517", "Zork II", 0, DONT_HAVE, NULL, 19, "820721", "Zork II", 0, DONT_HAVE, NULL, 22, "830331", "Zork II", 0, HAVE, NULL, 23, "830411", "Zork II", 0, HAVE, NULL, 48, "840904", "Zork II", 0, HAVE, NULL, 10, "820818", "Zork III", 0, HAVE, NULL, 15, "830331", "Zork III", 0, HAVE, NULL, 16, "830410", "Zork III", 0, DONT_HAVE, NULL, 15, "840518", "Zork III", 0, DONT_HAVE, NULL, 17, "840727", "Zork III", 0, HAVE, NULL, 296, "881019", "Zork Zero", 1, HAVE, zork0_explanation, 366, "890323", "Zork Zero", 1, HAVE, zork0_explanation, 383, "890602", "Zork Zero", 1, DONT_HAVE, zork0_explanation, 393, "890714", "Zork Zero", 1, HAVE, zork0_explanation, -1, "foo", "bar", 0, 0, NULL, }; char *alphabet[3] = { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", " \n0123456789.,!?_#'\"/\\-:()" }; int alphabet_number(char c) { int i, j; for( i=0; i<3; i++ ) for( j=0; j<26; j++ ) { if( alphabet[i][j] == c ) return i; } printf("Text encode: Unable to find `%c' in alphabet\n", c); exit(1); } int string_index(char c) { int i, j; for( i=0; i<3; i++ ) for( j=0; j<26; j++ ) if( alphabet[i][j] == c ) return j; printf("Text encode: Unable to find `%c' in alphabet\n", c); exit(1); } void binary(union zword z) { int i; for( i=0; i<8; i++ ) printf("%d", ((int) z.a[0]) & (1 << (7-i)) ? 1 : 0); for( i=0; i<8; i++ ) printf("%d", ((int) z.a[1]) & (1 << (7-i)) ? 1 : 0); printf(" %04x\n", z.a[0] * 256 + z.a[1]); } char *encode_text(char *s) { int *zchars; char *zstr; int i, j; int alpha, index; zchars = (int *) malloc(sizeof(int) * (strlen(s) * 2+1)); /* max length */ for( j=i=0; ist_size; } void fix_game(int ndx) { struct games g; g = game_list[ndx]; printf("%s\t(Zver %d / Release %d / Serial %s)\n\n", g.name, zmachine_version, release_number, serial); if( g.copy_protected == 0 ) { printf("This game has no copy-protection built-in.\n"); exit(0); } if( g.copy_protected == DONT_KNOW ) { printf("I've never gotten around to playing Seastalker.\n"); printf("The rest I don't remember. I might check later.\n"); exit(0); } if( g.not_owned ) { printf("This requires copy protection, but I don't own this release.\n"); exit(0); } if( !g.remove_prot() ) { printf("Unsuccessful for some reason.\n"); exit(1); } } int examine_game() { struct games g; int i; for( i=0; (g = game_list[i]).release != -1; i++ ) if( g.release == release_number && strncmp(g.serial, serial, 6) == 0 ) return i; return -1; } void read_info() { int i; zmachine_version = ZBYTE(0); if( zmachine_version == 0 || zmachine_version > 8 ) FATAL(("%s does not appear to be an Infocom game\n", gamefile)); strncpy(serial, fcontents + 0x12, 6); for( i=0; i<6; i++ ) if( !isdigit(serial[i]) ) FATAL(("%s does not appear to be an Infocom game (serial # is invalid)\n", gamefile)); serial[6] = 0; release_number = ZWORD(0x2); checksum = ZWORD(0x1c); file_length = ZWORD(0x1a); global_vars = ZWORD(0xc); switch( zmachine_version ) { case 1: case 2: case 3: file_length *= 2; break; case 4: case 5: file_length *= 4; break; case 6: case 7: case 8: file_length *= 8; break; } if( file_length > gamesize ) FATAL(("Header says length is %d bytes, only read %d from %s\n", file_length, gamesize, gamefile)); } /* Adjust the time... otherwise it's difficult to keep track of which files are original */ void adjust_time() { struct tm *t; time_t *tmp; char *buf; tmp = (time_t *) malloc(sizeof(time_t)); time(tmp); t = (struct tm *) malloc(sizeof(struct tm)); t = localtime(tmp); buf = (char *) malloc(7); strftime(buf, 7, "%y%m%d", t); strncpy(fcontents + 0x12, buf, 6); } void build_checksum() { union zword zw; unsigned short newchecksum = 0; int i; for( i=64; i