/* Utility to extract the embedded sounds from Commander Keen Episode 2 (keen2.exe) and Episode 3 (keen3.exe) Copyright (C) 2007 Hans de Goede Many thanks to Mitugu(Kou) Kurizono for the decompression algorithm. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include int bit_count; FILE *fin; int get_bit(void) { static unsigned short bits; int bit; bit = bits & 1; bit_count--; if (bit_count <= 0) { bits = getc(fin) | getc(fin) << 8; if (bit_count == -1) /* special case for first bit word */ { bit = bits & 1; bits >>= 1; } bit_count += 16; } else bits >>= 1; return bit; } int main(int argc, char *argv[]) { unsigned char buf[131072]; short offset; const char *output_filename; int pos, repeat, sounds_start, sounds_end, ret = 0; FILE *fout; pos = 0; bit_count = 0; if (argc != 2) { fprintf(stderr, "%s: Usage: %s keen?.exe\n", argv[0], argv[0]); return 1; } if (!strcmp(argv[1], "keen2.exe")) { output_filename = "sounds.ck2"; sounds_start = 0x12730; sounds_end = 0x14BDA; } else if (!strcmp(argv[1], "keen3.exe")) { output_filename = "sounds.ck3"; sounds_start = 0x13A70; sounds_end = 0x164D4; } else { fprintf(stderr, "%s: Error: Unknown keen executable name: %s\n", argv[0], argv[1]); return 1; } fin = fopen(argv[1], "r"); if (!fin) { fprintf(stderr, "%s: Error opening input file %s: ", argv[0], argv[1]); perror(NULL); } fout = fopen(output_filename, "w"); if (!fout) { fprintf(stderr, "%s: Error opening output file %s: ", argv[0], output_filename); perror(NULL); fclose(fin); return 1; } /* skip header */ fseek(fin, 32, SEEK_SET); while (1) { if (ferror(fin)) { fprintf(stderr, "%s: Error reading from input file %s: ", argv[0], argv[1]); perror(NULL); fclose(fin); fclose(fout); return 1; } if (get_bit()) { buf[pos++] = getc(fin); } else { if (get_bit()) { unsigned char tmp[2]; fread(tmp, 1, 2, fin); repeat = (tmp[1] & 0x07); offset = ((tmp[1] & ~0x07) << 5) | tmp[0] | 0xE000; if (repeat == 0) { repeat = getc (fin); if (repeat == 0) break; else if (repeat == 1) continue; else repeat++; } else repeat += 2; } else { repeat = ((get_bit() << 1) | get_bit()) + 2; offset = getc(fin) | 0xFF00; } while (repeat > 0) { buf[pos] = buf[pos + offset]; pos++; repeat--; } } } printf("%s: Decompression (unlzexe) of %s done\n", argv[0], argv[1]); if (strcmp(&buf[sounds_start], "SND")) { fprintf(stderr, "%s: Error: Beginning of sound data not found at expected offset\n", argv[0]); ret = 1; } else if (fwrite(&buf[sounds_start], 1, sounds_end - sounds_start, fout) != (sounds_end - sounds_start)) { fprintf(stderr, "%s: error writing to output file %s: ", argv[0], output_filename); perror(NULL); ret = 1; } printf("%s: %s has been successfully written\n", argv[0], output_filename); fclose(fin); fclose(fout); return ret; }