diff --git a/.gitignore b/.gitignore index 8655e68..e6de444 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ -game.com -lihouse.com -lihouse2.com lighthouse encrypt *.tap +*.com diff --git a/Makefile b/Makefile index 73f30a4..b75ea37 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ VERSION := $(or ${GITHUB_REF},${GITHUB_REF},"unreleased-git") all: lighthouse game-cpm game-spectrum + # Build the C version lighthouse: handlers.c inventory.c items.c main.c world.c util.c gcc -o lighthouse -Os -Wall -Wextra -Werror handlers.c inventory.c items.c main.c world.c util.c @@ -14,6 +15,12 @@ lighthouse: handlers.c inventory.c items.c main.c world.c util.c clean: rm -f lighthouse *.com *.tap encrypt || true + +# Build the encryption helper +encrypt: encrypt.c + gcc -o encrypt -Wall -Werror encrypt.c + + # Format our C-code format: astyle --style=allman -A1 --indent=spaces=4 --break-blocks --pad-oper --pad-header --unpad-paren --max-code-length=200 *.c *.h @@ -23,28 +30,51 @@ format: version: echo "DB \"$$(echo ${VERSION} | awk -F/ '{print $$NF}' )\"" > version.z80 + + # build the game for CP/M game-cpm: game.z80 bios.z80 version Makefile pasmo --equ ENTRYPOINT=100h --equ ENCRYPT_STRINGS=0 --equ SPECTRUM=0 game.z80 lihouse.com +# build the game for CP/M with encrypted strings/code +game-cpm-encrypted: game.z80 bios.z80 version Makefile encrypt + pasmo --equ ENTRYPOINT=100h --equ ENCRYPT_STRINGS=1 --equ SPECTRUM=0 game.z80 lihouse.com + ./encrypt lihouse.com lihousex.com + rm lihouse.com + + + # build the game for the ZX Spectrum game-spectrum: game.z80 bios.z80 version Makefile pasmo --tapbas --equ ENTRYPOINT=32768 --equ ENCRYPT_STRINGS=0 --equ SPECTRUM=1 game.z80 lihouse.tap -# Build the encryption helper -encrypt: encrypt.c - gcc -o encrypt -Wall -Werror encrypt.c +# build the game for the ZX Spectrum with encrypted strings +game-spectrum-encrypted: game.z80 bios.z80 version Makefile encrypt + pasmo --tapbas --equ ENTRYPOINT=32768 --equ ENCRYPT_STRINGS=1 --equ SPECTRUM=1 game.z80 lihouse.tap + ./encrypt -crc lihouse.tap lihousex.tap + rm lihouse.tap + # Build the game for release - with strings encrypted -release: game.z80 encrypt version - pasmo --equ ENTRYPOINT=100h --equ ENCRYPT_STRINGS=1 --equ SPECTRUM=0 game.z80 lihouse.com - ./encrypt - mv lihouse2.com lihouse.com +release: version + make game-cpm-encrypted + make game-cpm + make game-spectrum-encrypted + make game-spectrum -# Run for CP/M -run-cpm: game +# Run the CP/M version via runcpm +run-cpm: game-cpm ~/cpm/cpm lihouse -run-spectrum: - xspect -quick-load -load-immed -tap *.tap +# Run the CP/M version via runcpm, with the encrypted version of the code +run-cpm-encrypted: game-cpm-encrypted + ~/cpm/cpm lihouseX + +# Run the spectrum version +run-spectrum: game-spectrum + xspect -quick-load -load-immed -tap lihouse.tap + +# Run the spectrum version, with strings encrypted +run-spectrum-encrypted: game-spectrum-encrypted + xspect -quick-load -load-immed -tap lihousex.tap diff --git a/README.md b/README.md index 01a717a..1587dba 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Quick links within this README file: * [Bugs?](#bugs) + ## Play Online Thanks to the excellent [jsspeccy](https://github.com/gasman/jsspeccy3) ZX Spectrum emulator you can play this game with your browser here:

@@ -88,27 +89,25 @@ defining `SPECTRUM`, and `ENTRYPOINT` as appropriate. * Along the way I realized that having fixed inventory slots made the coding more of a challenge, so I made the location of each object a property of the object itself. * The Z80 version has more easter-eggs (Try typing "`xyzzy`" a few times). * There are __two__ victory conditions. -* The CP/M version of the game can be built with the text-strings, and game code, protected by simple XOR encryption: +* The game can be built with the text-strings, and game code, protected by simple XOR encryption: * This stops users from looking through the binary for hints. - * Run `make release` to build the _protected_ CP/M version. + * Run `make release` to build both "normal" and "protected" versions of the release. + * The encrypted versions of the games have an X suffix in their filenames. + ## Compiling & Running It Ensure you have the `pasmo` assembler installed, and then use the supplied Makefile to compile the game. -Running `make` will generate the default targets: - -* `make lighthouse` -> Build the game for linux. -* `make lihouse.com` -> Build the game for CP/M, without the XOR encryption. -* `make lihouse.tap` -> Build the game for the 48k ZX Spectrum. - -If you wish to build only individual things then : +Running `make` will generate the default targets, if you wish to build only individual things then : * `make game-cpm` to build a normal CP/M version. + * `make game-cpm-encrypted` to build an encrypted CP/M version. * `make game-spectrum` to build the ZX Spectrum version. + * `make game-spectrum-encrypted` to build the encrypted ZX Spectrum version. * `make lighthouse` will build the C-game for Linux -* `make release` will build the _protected_ CP/M version. +* `make release` will build both versions of the CP/M and ZX Spectrum release. @@ -117,7 +116,10 @@ If you wish to build only individual things then : If you look on our [release page](https://github.com/skx/lighthouse-of-doom/releases/) you can find the latest stable build. * For CP/M download `lihouse.com` to your system, and then run `LIHOUSE` to launch it. + * `lihousex.com` is the encrypted version. * For the ZX Spectrum download `lihouse.tap` to your system, and then launch in your favourite emulator. + * `lihousex.tap` is the encrypted version. + ## Bugs? diff --git a/encrypt.c b/encrypt.c index ea7c0d5..2e977bc 100644 --- a/encrypt.c +++ b/encrypt.c @@ -1,5 +1,8 @@ /* - * Load `lihouse.com` and scramble the strings, saving to `lihouse2.com`. + * Generate an encrypted version of the specified input file. + * + * Optionally update the CRC check of the file - this is necessary + * for the .TAP version we generate for the ZX Spectrum. */ #include @@ -7,41 +10,121 @@ #include #include +typedef unsigned char byte; + + +// calculate the checksum for the given region +byte crc(byte *start, int length) +{ + byte tcrc = 0; + + for (int i = 0; i < length; i++) + tcrc = tcrc ^ start[i]; + + return tcrc; +} + int main(int argc, char *argv[]) { - FILE *f = fopen("lihouse.com", "r"); + // check argument count + if (argc != 3 && argc != 4) + { + printf("Usage: encrypt [-crc] input output\n"); + return 1; + } + + // flags / arguments + int do_crc = 0; + char *input = NULL; + char *output = NULL; + + // look for flags + if (argc == 4) + { + // Should we run a CRC update? + for (int i = 1; i < argc ; i++) + { + if (strcmp(argv[i], "-crc") == 0) + { + do_crc = 1; + } + } + } + + // look for input/output names. + for (int i = 1; i < argc ; i++) + { + // Ignore flags + if (strcmp(argv[i], "-crc") == 0) + { + continue; + } + + // input file goes first + if (input == NULL) + { + input = argv[i]; + continue; + } + + // output file goes second. + if (output == NULL) + { + output = argv[i]; + continue; + } + } + + printf("Input file: %s\n", input); + printf("Output file: %s\n", output); + + if (do_crc) + { + printf("CRC will be updated\n"); + } + else + { + printf("No CRC update\n"); + } + + // Open the file for reading + FILE *f = fopen(input, "r"); if (f == NULL) { - printf("Failed to open file\n"); - return 0; + printf("Failed to open input file: %s\n", input); + return 1; } // get file size fseek(f, 0L, SEEK_END); size_t sz = ftell(f); + printf("File size is %ld bytes\n", sz); + // get back to start fseek(f, 0L, SEEK_SET); // allocate memory - char *buf = malloc(sz); + unsigned char *buf = malloc(sz); if (buf == NULL) { printf("failed to allocate memory\n"); - return 0; + return 1; } // read the file - int n = fread(buf, sizeof(char), sz, f); + int n = fread(buf, sizeof(unsigned char), sz, f); if (ferror(f) != 0) { printf("error reading\n"); return 1; } - if ( n != sz ) { + + if (n != sz) + { printf("short read\n"); return 1; } @@ -68,12 +151,40 @@ int main(int argc, char *argv[]) k++; } + if (do_crc) + { + // Now we have to fixup the CRC + unsigned char *pos = buf; + + for (int i = 1; i < sz; i++) + { + size_t blocksize = pos[0] | (pos[1] << 8); + + if ((pos + blocksize + 1) < (buf + sz)) + { + if (blocksize > 1) + { + char ccrc = crc(&pos[2], blocksize - 1); + + if (pos[blocksize + 1] != ccrc) + { + printf("Updated CRC\n"); + pos[blocksize + 1] = ccrc; + } + } + } + + + pos += blocksize + 2; + } + } + // Write - FILE *nw = fopen("lihouse2.com", "w"); + FILE *nw = fopen(output, "w"); if (nw == NULL) { - printf("Failed to open file for writing\n"); + printf("Failed to open file for writing: %s\n", output); } fwrite(buf, sz, 1, nw);