From 80191b41352ad20493fb62e8f3683d69133d0d24 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Thu, 14 Feb 2013 18:03:09 -0500 Subject: Initial commit. --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fdb10a --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +sdlex +a.out + +*.s[a-w]? +Session.vim +*~ +*.orig diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3efdec5 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +.SUFFIXES = +.SUFFIXES = .c .h .o + +CC = gcc +CFLAGS = -Wall -Wextra -Werror -g + +SRCS = src/main.c src/init.c src/logging.c src/image.c \ + src/base64.c src/compression.c src/tmx.c +OBJS = $(SRCS:.c=.o) +LIBS = sdl SDL_image zlib expat + +all: sdlex + +sdlex: $(OBJS) + $(CC) $(LDFLAGS) -o sdlex $$(pkg-config --libs $(LIBS)) $(OBJS) + +.c.o: + $(CC) -c $(CFLAGS) -o $*.o $$(pkg-config --cflags $(LIBS)) $< + +clean: + rm -f $(OBJS) sdlex diff --git a/data/forest1.tmx b/data/forest1.tmx new file mode 100644 index 0000000..41c8241 --- /dev/null +++ b/data/forest1.tmx @@ -0,0 +1,63 @@ + + + + + + + + + eJztlsERAjEIRSlArUCtwLWVtQPtQDtwS18OHpwdIvwv7EFz+MMhkyEvgR9GEbmoRjDuVYeXjsT+q+pGxGVedD96zl/n3ai2hu7FvO9cLVXwRvNW8k6qwdC5mJfhyuBl3rHzdl6Et+VjlnaJvBE/+XRPrA+jeVu8qA97PvYM8qI+7NW5t876sFfn3jrbpxW8kT7tvN/zPsT3IG++YnhRZfEyeVnek9g9G5E1X0V5s2OUNzt23hre5f/t9XcFHzJvZL4nMm9k8k7i9/da9bsGb+Q/+zfeGUt8RgE= + + + + + eJztmNEJwzAMRLX/Dh2pi6QbtASajxqMiFzd6VQo5MAIQs7yI7FkvJnZ4xgbGL1Q//MYLyJ6of55/fdP3F0czxXyvNHcypxm57wdfF6ed6gzp9k5r8/dsYZvvF3cK95ORbzdUvGidUrFy9Y7lJet396P8rL12/tH/lswovWyqvKyqn5fVhdvTr/mzezPzFwob2Z/ZuZS96PuehWJrVdVXbzr9/6RF+29s6fCi/be2aM+b2T96vNG1l/lRaXiRXXxrsX+v9E8bP9V8VZjxKW+z6ne77wB2seF1Q== + + + + + eJzt1sEJAAAIAzH3H1qcQVAESSboo4+L4FpeDwAAaJhoF/3DJv8C+KcAmWED+Q== + + + + + eJzt1UEKACEMBMH8/wG+13sgiBBHGbthb0LtYcQI6mikz9XN3m+uKty987/t0N0lj27t5XW3+z7halyiE1X7rN7frl3jatzV/6hdIocmn4BVjQ== + + + + + eJzt1jENAAAIA0H8i0UDEwJIICx3Cjp0+Ahafg8AABjYaBf9wyX/AmBLAdpSA7k= + + + + + eJztl0EKgDAMBPv/p9Uf6Gf0YKCXmN0kLUgzILm4DQO2qb21drR19Lff+TzXwr7ST/qvonzXUL4+JM/WqK/k2cr6oj5WnvVFfay819dL1NdL+WLs4ovsT2Qt1hfZn8ha2fNo9nmlMfu80ijf7/f+6MvO3jET8WVn75jJvm+g+ez7BpqP+rJk+bKU7zfe71dbxzt/s3yjVfOy/o+iVfOy/o928b0BRnVFrg== + + + + + eJzt1cEKwCAMA1B38ej/f67XIStqm7oICZRdXJ7CcPUppTpmjKfjT3fseD+Rme0/K1/uCZ/pvHLlznqi9w+bG+1jcZsx1nveRM8rVy7CXVm/0nWLi9iXXLnM7u4/8EbXspD3D7O7G7mcrvc7YnWjk92Pcjt4pgtE + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/tilesets/collision.png b/data/tilesets/collision.png new file mode 100644 index 0000000..6d9a18b --- /dev/null +++ b/data/tilesets/collision.png Binary files differ diff --git a/data/tilesets/collision.tsx b/data/tilesets/collision.tsx new file mode 100644 index 0000000..b38d47a --- /dev/null +++ b/data/tilesets/collision.tsx @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/tilesets/collision.xcf b/data/tilesets/collision.xcf new file mode 100644 index 0000000..a873597 --- /dev/null +++ b/data/tilesets/collision.xcf Binary files differ diff --git a/data/tilesets/mountain_landscape_19-16.png b/data/tilesets/mountain_landscape_19-16.png new file mode 100644 index 0000000..54e619c --- /dev/null +++ b/data/tilesets/mountain_landscape_19-16.png Binary files differ diff --git a/src/base64.c b/src/base64.c new file mode 100644 index 0000000..6a2c873 --- /dev/null +++ b/src/base64.c @@ -0,0 +1,95 @@ +#include "base64.h" + +static void +base64_decode_block(const char *src, char *dest) +{ + int i; + char buf[4]; + + /* Convert base 64 characters into values. */ + /* Supports ASCII and EBCDIC systems. */ + for (i = 0; i < 4; ++i) { + if (src[i] >= 'A' && src[i] <= 'I') { + buf[i] = src[i] - 'A' + 0; + } else if (src[i] >= 'J' && src[i] <= 'R') { + buf[i] = src[i] - 'J' + 9; + } else if (src[i] >= 'S' && src[i] <= 'Z') { + buf[i] = src[i] - 'S' + 18; + } else if (src[i] >= 'a' && src[i] <= 'i') { + buf[i] = src[i] - 'a' + 26; + } else if (src[i] >= 'j' && src[i] <= 'r') { + buf[i] = src[i] - 'j' + 35; + } else if (src[i] >= 's' && src[i] <= 'z') { + buf[i] = src[i] - 's' + 44; + } else if (src[i] >= '0' && src[i] <= '9') { + buf[i] = src[i] - '0' + 52; + } else if (src[i] == '+') { + buf[i] = 62; + } else if (src[i] == '/') { + buf[i] = 63; + } else { + buf[i] = '\0'; + } + } + + /* Map four decoded value bytes into three output bytes. */ + dest[0] = (buf[0] << 2) | (buf[1] >> 4); + dest[1] = (buf[1] << 4) | (buf[2] >> 2); + dest[2] = (buf[2] << 6) | (buf[3] >> 0); +} + +void +base64_decode(const char *src, char *dest, size_t dest_len) +{ + size_t i; + + /* Decode the input one entire block at a time. */ + for (i = 0; i < (dest_len - 1) / 3; ++i) { + if (src[i * 4] == '\0' || + src[i * 4 + 1] == '\0' || + src[i * 4 + 2] == '\0' || + src[i * 4 + 3] == '\0') { + dest[i * 3] = '\0'; + break; + } + base64_decode_block(src + i * 4, dest + i * 3); + } + + /* Ensure NUL termination. */ + dest[dest_len - 1] = '\0'; +} + +#if 0 +int +main() +{ + char dest[1024]; + base64_decode( + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5v" + "dCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1" + "dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Np" + "b24gZnJvbSBvdGhlciBhbmltYWxzLCB3" + "aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1p" + "bmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFu" + "Y2Ugb2YgZGVsaWdodCBpbiB0aGUgY29u" + "dGludWVkIGFuZCBpbmRlZmF0aWdhYmxl" + "IGdlbmVyYXRpb24gb2Yga25vd2xlZGdl" + "LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhl" + "bWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVh" + "c3VyZS4=", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhc3VyZQ==", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhc3Vy", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhc3U=", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhcw==", dest, 1024); + printf("Decoded string: \"%s\"\n", dest); + base64_decode("YW55IGNhcm5hbCBwbGVhcw==", dest, 4); + printf("Decoded string: \"%s\"\n", dest); + return 0; +} +#endif diff --git a/src/base64.h b/src/base64.h new file mode 100644 index 0000000..b56b9db --- /dev/null +++ b/src/base64.h @@ -0,0 +1,8 @@ +#ifndef BASE64_H +#define BASE64_H + +#include + +void base64_decode(const char *src, char *dest, size_t dest_len); + +#endif diff --git a/src/compression.c b/src/compression.c new file mode 100644 index 0000000..cf88de6 --- /dev/null +++ b/src/compression.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include "logging.h" + +static void +zlib_err(int status) +{ + switch (status) { + case Z_OK: + case Z_STREAM_END: + break; + case Z_MEM_ERROR: + err(1, "zlib error: out of memory"); + break; + case Z_VERSION_ERROR: + err(1, "zlib error: incompatible library version"); + break; + case Z_STREAM_ERROR: + err(1, "zlib error: invalid stream"); + break; + case Z_NEED_DICT: + err(1, "zlib error: need dictionary"); + break; + case Z_DATA_ERROR: + err(1, "zlib error: corrupted data"); + break; + case Z_BUF_ERROR: + err(1, "zlib error: buffer error"); + break; + default: + err(1, "zlib error: unknown"); + break; + } +} + +void +decompress(const char *src, size_t src_len, char *dest, size_t dest_len) +{ + z_stream d_stream; + + d_stream.zalloc = Z_NULL; + d_stream.zfree = Z_NULL; + d_stream.opaque = Z_NULL; + d_stream.next_in = (Bytef *) src; + d_stream.avail_in = src_len; + d_stream.next_out = (Bytef *) dest; + d_stream.avail_out = dest_len; + + debug("Initializing inflation stream..."); + zlib_err(inflateInit2(&d_stream, 15 + 32)); + + debug("Inflating %d bytes into up to %d bytes...", src_len, dest_len); + zlib_err(inflate(&d_stream, Z_NO_FLUSH)); + + debug("Ending inflation stream..."); + zlib_err(inflateEnd(&d_stream)); +} + +#if 0 +#include +#include "base64.h" + +int +main() +{ + char decoded_data[4801], compressed_data[4801], decompressed_data[4801]; + + base64_decode( + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAAAMAAAADQAAAA0AAAANAAAADQAAAA0AAABvAAAA" + "HQAAAB0AAAAeAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAeAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAAAcAAAAHQAAAB0AAACAAAAALQAAAC0AAAAtAAAA" + "LQAAAC0AAAAuAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAeAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAAAMAAAADQAAAA0AAAANAAAADQAAAA0AAAANAAAA" + "DQAAAA0AAAANAAAADQAAAA4AAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB4AAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAAAMAAAADQAAAA0AAAANAAAA" + "DQAAAA0AAABvAAAAHQAAAB0AAAAeAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAA" + "HQAAAB4AAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAeAAAA" + "XwAAAGAAAAAcAAAAHQAAAB0AAACAAAAALQAAAC0AAAAtAAAA" + "LQAAAH8AAAAdAAAAHQAAAB4AAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAAAcAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAeAAAATwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAABwAAAAdAAAAHQAAAB4AAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAAAcAAAAHQAAAB0AAACAAAAA" + "LQAAAC0AAAAtAAAALQAAAC0AAAAuAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAABfAAAAYAAAABwAAAAdAAAA" + "HQAAAB4AAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAeAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAATwAAAFAAAABPAAAA" + "UAAAABwAAAAdAAAAHQAAAB4AAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAA" + "XwAAAGAAAABfAAAAYAAAABwAAAAdAAAAHQAAAB4AAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAAAcAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAeAAAATwAAAFAAAABPAAAAUAAAABwAAAAdAAAA" + "HQAAAB4AAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAAAcAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAAAcAAAAHQAAAB0AAAAeAAAAXwAAAGAAAABfAAAA" + "YAAAABwAAAAdAAAAHQAAAB4AAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAAAcAAAAHQAAAB0AAABwAAAADQAAAA0AAAANAAAA" + "DQAAAA0AAAANAAAADQAAAA0AAABvAAAAHQAAAB0AAAAeAAAA" + "TwAAAFAAAABPAAAAUAAAABwAAAAdAAAAHQAAAB4AAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAAAcAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAeAAAAXwAAAGAAAABfAAAAYAAAABwAAAAdAAAA" + "HQAAAB4AAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAAAcAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB0AAAAdAAAA" + "HQAAAB0AAAAdAAAAHQAAAB0AAAAeAAAATwAAAFAAAABPAAAA" + "UAAAABwAAAAdAAAAHQAAAB4AAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAAAsAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAA" + "LQAAAC0AAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAuAAAA" + "XwAAAGAAAABfAAAAYAAAABwAAAAdAAAAHQAAAB4AAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAABwAAAAdAAAA" + "HQAAAB4AAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAABwAAAAdAAAAHQAAAB4AAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAABwAAAAdAAAAHQAAAB4AAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAABwAAAAdAAAA" + "HQAAAB4AAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAADAAAAA0AAAANAAAA" + "DQAAAG8AAAAdAAAAHQAAAB4AAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "HAAAAB0AAAAdAAAAHQAAAB0AAAAdAAAAHQAAAB4AAABfAAAA" + "YAAAAF8AAABgAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAAHAAAAB0AAAAdAAAAHQAAAB0AAAAdAAAA" + "HQAAAB4AAABPAAAAUAAAAE8AAABQAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAHAAAAB0AAAAdAAAA" + "gAAAAC0AAAAtAAAALQAAAC4AAABfAAAAYAAAAF8AAABgAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "HAAAAB0AAAAdAAAAHgAAAE8AAABQAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAHAAAAB0AAAAdAAAAHgAAAF8AAABgAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAATwAAAFAAAABPAAAA" + "UAAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "TwAAAFAAAABPAAAAUAAAAE8AAABQAAAAHAAAAB0AAAAdAAAA" + "HgAAAE8AAABQAAAATwAAAFAAAABPAAAAUAAAAE8AAABQAAAA" + "XwAAAGAAAABfAAAAYAAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAAXwAAAGAAAABfAAAAYAAAAF8AAABgAAAA" + "HAAAAB0AAAAdAAAAHgAAAF8AAABgAAAAXwAAAGAAAABfAAAA" + "YAAAAF8AAABgAAAA", + decoded_data, 4801); + base64_decode( + "eJztlsERAjEIRSlArUCtwLWVtQPtQDtwS18OHpwdIvwv7EFz" + "+MMhkyEvgR9GEbmoRjDuVYeXjsT+q+pGxGVedD96zl/n3ai2" + "hu7FvO9cLVXwRvNW8k6qwdC5mJfhyuBl3rHzdl6Et+VjlnaJ" + "vBE/+XRPrA+jeVu8qA97PvYM8qI+7NW5t876sFfn3jrbpxW8" + "kT7tvN/zPsT3IG++YnhRZfEyeVnek9g9G5E1X0V5s2OUNzt2" + "3hre5f/t9XcFHzJvZL4nMm9k8k7i9/da9bsGb+Q/+zfeGUt8" + "RgE=", + compressed_data, 4801); + decompress(compressed_data, decompressed_data, 4801); + decoded_data[4800] = '\0'; + compressed_data[4800] = '\0'; + decompressed_data[4800] = '\0'; + if (strncmp(decoded_data, decompressed_data, 4800) == 0) { + puts("Matched!"); + } else { + puts("No match. :("); + } + + return 0; +} +#endif diff --git a/src/compression.h b/src/compression.h new file mode 100644 index 0000000..387d634 --- /dev/null +++ b/src/compression.h @@ -0,0 +1,6 @@ +#ifndef COMPRESSION_H +#define COMPRESSION_H + +void decompress(const char *src, size_t src_len, char *dest, size_t dest_len); + +#endif diff --git a/src/image.c b/src/image.c new file mode 100644 index 0000000..557e1de --- /dev/null +++ b/src/image.c @@ -0,0 +1,22 @@ +#include +#include +#include "logging.h" +#include "image.h" + +SDL_Surface * +load_png(const char *path) +{ + SDL_RWops *rwops; + SDL_Surface *img; + + debug("Loading PNG image \"%s\"...", path); + + rwops = SDL_RWFromFile(path, "rb"); + img = IMG_LoadPNG_RW(rwops); + if (!img) { + err(1, "Failed to load image \"%s\" (%s)", + path, IMG_GetError()); + } + + return img; +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..0de4083 --- /dev/null +++ b/src/image.h @@ -0,0 +1,6 @@ +#ifndef IMAGE_H +#define IMAGE_H + +SDL_Surface *load_png(const char *path); + +#endif diff --git a/src/init.c b/src/init.c new file mode 100644 index 0000000..f893d1d --- /dev/null +++ b/src/init.c @@ -0,0 +1,36 @@ +#include +#include +#include "init.h" +#include "logging.h" + +void +init(void) +{ + debug("Initializing SDL..."); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1) { + err(1, "Failed to initialize SDL (%s)", SDL_GetError()); + } + + debug("Initializing SDL_Image..."); + if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG) { + err(1, "Failed to initialize SDL_Image (%s)", IMG_GetError()); + } + + debug("Setting video mode..."); + screen = SDL_SetVideoMode(240, 160, 8, SDL_SWSURFACE | SDL_ANYFORMAT); + if (screen == NULL) { + err(1, "Failed to set video mode (%s)", SDL_GetError()); + } +} + +void +quit(int status) +{ + debug("Quitting SDL_Image..."); + IMG_Quit(); + + debug("Quitting SDL..."); + SDL_Quit(); + + exit(status); +} diff --git a/src/init.h b/src/init.h new file mode 100644 index 0000000..f04b1fe --- /dev/null +++ b/src/init.h @@ -0,0 +1,11 @@ +#ifndef INIT_H +#define INIT_H + +#include + +SDL_Surface *screen; + +void init(void); +void quit(int status); + +#endif diff --git a/src/layer.h b/src/layer.h new file mode 100644 index 0000000..126c3d9 --- /dev/null +++ b/src/layer.h @@ -0,0 +1,20 @@ +#ifndef LAYER_H +#define LAYER_H + +#include + +const int LAYER_GROUND = 0; +const int LAYER_OBJ_LOW = 1; +const int LAYER_CHAR_BOT = 2; +const int LAYER_OBJ_MID = 3; +const int LAYER_CHAR_TOP = 4; +const int LAYER_OBJ_HIGH = 5; +const int LAYER_WEATHER = 6; + +struct layer { + char *name; + Uint32 *tiles; + struct layer *next; +}; + +#endif diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..be03528 --- /dev/null +++ b/src/logging.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "init.h" +#include "logging.h" + +void +debug(const char *fmt, ...) +{ + va_list ap; + + printf("Debug: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putchar('\n'); +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + + printf("Warning: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putchar('\n'); +} + +void +err(int status, const char *fmt, ...) +{ + va_list ap; + + printf("Error: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putchar('\n'); + + quit(status); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000..e03dad6 --- /dev/null +++ b/src/logging.h @@ -0,0 +1,8 @@ +#ifndef LOGGING_H +#define LOGGING_H + +void debug(const char *fmt, ...); +void warn(const char *fmt, ...); +void err(int status, const char *fmt, ...); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..b30d0b2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,65 @@ +#include +#include "init.h" +#include "logging.h" +#include "image.h" +#include "tmx.h" + +int +main(void) +{ + SDL_Surface *img; + SDL_Rect imgrect, surfacerect; + + init(); + + tmx_load("data/forest1.tmx"); + + img = load_png("../forest-6-layer-test_ground.png"); + imgrect.w = 240; + imgrect.h = 160; + surfacerect.x = 0; + surfacerect.y = 0; + surfacerect.w = 240; + surfacerect.h = 160; + + imgrect.x = 208; + imgrect.y = 480; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + imgrect.x = 208; + imgrect.y = 464; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + imgrect.x = 224; + imgrect.y = 464; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + imgrect.x = 240; + imgrect.y = 464; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + imgrect.x = 256; + imgrect.y = 464; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + imgrect.x = 256; + imgrect.y = 448; + SDL_BlitSurface(img, &imgrect, screen, &surfacerect); + SDL_Flip(screen); + SDL_Delay(500); + + quit(0); + + /* Control doesn't actually reach here. */ + return 0; +} diff --git a/src/map.h b/src/map.h new file mode 100644 index 0000000..554b428 --- /dev/null +++ b/src/map.h @@ -0,0 +1,16 @@ +#ifndef MAP_H +#define MAP_H + +#include "tileset.h" +#include "layer.h" + +struct map { + int width; + int height; + int tilewidth; + int tileheight; + struct tileset *tilesets; + struct layer *layers; +}; + +#endif diff --git a/src/tileset.h b/src/tileset.h new file mode 100644 index 0000000..2fd384a --- /dev/null +++ b/src/tileset.h @@ -0,0 +1,12 @@ +#ifndef TILESET_H +#define TILESET_H + +struct tileset { + int firstgid; + int tilewidth; + int tileheight; + SDL_Surface *image; + struct tileset *next; +}; + +#endif diff --git a/src/tmx.c b/src/tmx.c new file mode 100644 index 0000000..0e401b3 --- /dev/null +++ b/src/tmx.c @@ -0,0 +1,370 @@ +#include +#include +#include +#include +#include +#include "logging.h" +#include "base64.h" +#include "compression.h" +#include "map.h" +#include "image.h" + +struct tmx { + char *dirname; + struct map *map; + enum { + TMX_PARSING_NONE = 0, + TMX_PARSING_MAP, + TMX_PARSING_TILESET, + TMX_PARSING_IMAGE, + TMX_PARSING_LAYER, + TMX_PARSING_DATA + } parsing; + struct tileset *cur_tileset; + struct layer *cur_layer; + enum { + TMX_DATA_ENC_NONE = 0, + TMX_DATA_ENC_BASE64, + TMX_DATA_ENC_CSV + } data_encoding; + enum { + TMX_DATA_COMP_NONE = 0, + TMX_DATA_COMP_GZIP, + TMX_DATA_COMP_ZLIB + } data_compression; + char *layer_data; +}; + +#define foreach_tmx_attr(attr) \ + for (; (attr)[0] != NULL; (attr) += 2) + +#define tmx_get_int_attr(attr, name, p) \ + do { \ + if (strcmp((attr)[0], (name)) == 0) { \ + if (sscanf((attr)[1], "%d", (p)) != 1) { \ + err(1, "Invalid \"%s\" attribute", (name)); \ + } \ + } \ + } while (0); + +#define tmx_get_string_attr(attr, name, p) \ + do { \ + if (strcmp((attr)[0], (name)) == 0) { \ + (p) = strdup((attr)[1]); \ + } \ + } while (0); + +static void +tmx_start_map(struct tmx *cur_tmx, const char **attr) +{ + if (cur_tmx->map != NULL) { + err(1, "Found multiple maps in TMX file"); + } + + cur_tmx->map = malloc(sizeof(*cur_tmx->map)); + if (cur_tmx->map == NULL) { + err(1, "Failed to allocate map"); + } + memset(cur_tmx->map, 0, sizeof(*cur_tmx->map)); + + foreach_tmx_attr (attr) { + tmx_get_int_attr(attr, "width", &cur_tmx->map->width); + tmx_get_int_attr(attr, "height", &cur_tmx->map->height); + tmx_get_int_attr(attr, "tilewidth", &cur_tmx->map->tilewidth); + tmx_get_int_attr(attr, "tileheight", &cur_tmx->map->tileheight); + } +} + +static void +tmx_start_tileset(struct tmx *cur_tmx, const char **attr) +{ + struct tileset *new_tileset; + + if (cur_tmx->map == NULL) { + err(1, "Malformed TMX file"); + } + + new_tileset = malloc(sizeof(*new_tileset)); + if (new_tileset == NULL) { + err(1, "Failed to allocate tileset"); + } + memset(new_tileset, 0, sizeof(*new_tileset)); + + if (cur_tmx->cur_tileset == NULL) { + cur_tmx->map->tilesets = new_tileset; + } else { + cur_tmx->cur_tileset->next = new_tileset; + } + cur_tmx->cur_tileset = new_tileset; + + foreach_tmx_attr (attr) { + tmx_get_int_attr(attr, "firstgid", + &cur_tmx->cur_tileset->firstgid); + tmx_get_int_attr(attr, "tilewidth", + &cur_tmx->cur_tileset->tilewidth); + tmx_get_int_attr(attr, "tileheight", + &cur_tmx->cur_tileset->tileheight); + } + + /* TODO: Parse tilesets referenced in "source" attributes. */ +} + +static void +tmx_start_image(struct tmx *cur_tmx, const char **attr) +{ + char *source; + char *path; + + if (cur_tmx->cur_tileset == NULL) { + err(1, "Malformed TMX file"); + } + + foreach_tmx_attr (attr) { + tmx_get_string_attr(attr, "source", source); + } + + /* TODO: Move to end tag handler and check for "loadimage" property. */ + path = malloc(strlen(cur_tmx->dirname) + strlen(source) + 2); + if (path == NULL) { + err(1, "Failed to allocate resource path string"); + } + sprintf(path, "%s/%s", cur_tmx->dirname, source); + cur_tmx->cur_tileset->image = load_png(path); + free(path); + + debug(" Found tileset with firstgid %d and source \"%s\"", + cur_tmx->cur_tileset->firstgid, source); + + free(source); +} + +static void +tmx_start_layer(struct tmx *cur_tmx, const char **attr) +{ + struct layer *new_layer; + + if (cur_tmx->map == NULL) { + err(1, "Malformed TMX file"); + } + + new_layer = malloc(sizeof(*new_layer)); + if (new_layer == NULL) { + err(1, "Failed to allocate layer"); + } + memset(new_layer, 0, sizeof(*new_layer)); + + if (cur_tmx->cur_layer == NULL) { + cur_tmx->map->layers = new_layer; + } else { + cur_tmx->cur_layer->next = new_layer; + } + cur_tmx->cur_layer = new_layer; + + foreach_tmx_attr (attr) { + tmx_get_string_attr(attr, "name", cur_tmx->cur_layer->name); + } + + debug(" Found layer with name \"%s\"", cur_tmx->cur_layer->name); +} + +static void +tmx_start_data(struct tmx *cur_tmx, const char **attr) +{ + if (cur_tmx->cur_layer == NULL) { + err(1, "Malformed TMX file"); + } + + foreach_tmx_attr (attr) { + if (strcmp(attr[0], "encoding") == 0) { + if (strcmp(attr[1], "base64") == 0) { + cur_tmx->data_encoding = TMX_DATA_ENC_BASE64; + } else if (strcmp(attr[1], "csv") == 0) { + cur_tmx->data_encoding = TMX_DATA_ENC_CSV; + } else { + err(1, "Unsupported data encoding in TMX file"); + } + } else if (strcmp(attr[0], "compression") == 0) { + if (strcmp(attr[1], "gzip") == 0) { + cur_tmx->data_compression = TMX_DATA_COMP_GZIP; + } else if (strcmp(attr[1], "zlib") == 0) { + cur_tmx->data_compression = TMX_DATA_COMP_ZLIB; + } else { + err(1, "Unsupported compression in TMX file"); + } + } + } +} + +static void XMLCALL +tmx_start(void *data, const char *el, const char **attr) +{ + struct tmx *cur_tmx; + + cur_tmx = (struct tmx *) data; + + if (strcmp(el, "map") == 0) { + tmx_start_map(cur_tmx, attr); + cur_tmx->parsing = TMX_PARSING_MAP; + } else if (strcmp(el, "tileset") == 0) { + tmx_start_tileset(cur_tmx, attr); + cur_tmx->parsing = TMX_PARSING_TILESET; + } else if (strcmp(el, "image") == 0) { + tmx_start_image(cur_tmx, attr); + cur_tmx->parsing = TMX_PARSING_IMAGE; + } else if (strcmp(el, "layer") == 0) { + tmx_start_layer(cur_tmx, attr); + cur_tmx->parsing = TMX_PARSING_LAYER; + } else if (strcmp(el, "data") == 0) { + tmx_start_data(cur_tmx, attr); + cur_tmx->parsing = TMX_PARSING_DATA; + } + + /* TODO: Handle objectgroup, object, properties, and property tags. */ +} + +static void +tmx_end_data(struct tmx *cur_tmx) +{ + size_t decoded_len; + size_t decomp_len; + char *decoded_buf; + char *decomp_buf; + + decoded_len = strlen(cur_tmx->layer_data); + + decomp_len = 4 * cur_tmx->map->width * cur_tmx->map->height; + debug(" Expected map data size: %d", decomp_len); + + decoded_buf = malloc(decoded_len + 1); + if (decoded_buf == NULL) { + err(1, "Failed to allocate map layer data buffer"); + } + + if (cur_tmx->data_encoding == TMX_DATA_ENC_BASE64) { + debug(" Decoding base 64 layer data..."); + base64_decode(cur_tmx->layer_data, + decoded_buf, decoded_len + 1); + } + + if (cur_tmx->data_compression == TMX_DATA_COMP_NONE) { + debug(" Layer data already decompressed"); + decomp_buf = decoded_buf; + } else if (cur_tmx->data_compression == TMX_DATA_COMP_ZLIB) { + debug(" Decompressing layer data with zlib..."); + decomp_buf = malloc(decomp_len); + if (decomp_buf == NULL) { + err(1, "Failed to allocate map layer data buffer"); + } + decompress(decoded_buf, decoded_len, decomp_buf, decomp_len); + free(decoded_buf); + } else if (cur_tmx->data_compression == TMX_DATA_COMP_GZIP) { + debug(" Decompressing layer data with gzip..."); + decomp_buf = malloc(decomp_len); + if (decomp_buf == NULL) { + err(1, "Failed to allocate map layer data buffer"); + } + decompress(decoded_buf, decoded_len, decomp_buf, decomp_len); + free(decoded_buf); + } + + free(cur_tmx->layer_data); + + cur_tmx->cur_layer->tiles = (Uint32 *) decomp_buf; +} + +static void XMLCALL +tmx_end(void *data, const char *el) +{ + struct tmx *cur_tmx; + + cur_tmx = (struct tmx *) data; + + if (strcmp(el, "data") == 0) { + tmx_end_data(cur_tmx); + } + + cur_tmx->parsing = TMX_PARSING_NONE; +} + +static void XMLCALL +tmx_data(void *data, const char *s, int len) +{ + struct tmx *cur_tmx; + char *s_z; + char *s_z_trimmed, *s_z_trimmed_end; + + cur_tmx = (struct tmx *) data; + + if (cur_tmx->parsing != TMX_PARSING_DATA) { + return; + } + + s_z = s_z_trimmed = strndup(s, len); + + while(isspace(*s_z_trimmed)) { + ++s_z_trimmed; + --len; + } + if (*s_z_trimmed == '\0') { + return; + } + s_z_trimmed_end = s_z_trimmed + len - 1; + while (s_z_trimmed_end > s_z_trimmed && isspace(*s_z_trimmed_end)) { + --s_z_trimmed_end; + } + *(s_z_trimmed_end + 1) = '\0'; + + cur_tmx->layer_data = strdup(s_z_trimmed); + + free(s_z); +} + +struct map * +tmx_load(const char *path) +{ + XML_Parser p; + struct tmx cur_tmx; + char *path_dup; + FILE *tmx_fp; + void *tmx_buf; + size_t len; + + debug("Parsing TMX file \"%s\"...", path); + + p = XML_ParserCreate(NULL); + if (p == NULL) { + err(1, "Failed to create TMX parser"); + } + + XML_SetElementHandler(p, tmx_start, tmx_end); + XML_SetCharacterDataHandler(p, tmx_data); + + memset(&cur_tmx, 0, sizeof(cur_tmx)); + path_dup = strdup(path); + cur_tmx.dirname = dirname(path_dup); + XML_SetUserData(p, &cur_tmx); + + tmx_fp = fopen(path, "rb"); + if (tmx_fp == NULL) { + err(1, "Failed to open TMX file"); + } + + tmx_buf = XML_GetBuffer(p, 8192); + if (tmx_buf == NULL) { + err(1, "Failed to create TMX parse buffer"); + } + + while (!feof(tmx_fp)) { + len = fread(tmx_buf, 1, 8192, tmx_fp); + if (XML_ParseBuffer(p, len, feof(tmx_fp)) == XML_STATUS_ERROR) { + err(1, "Failed to parse TMX file (%s)", + XML_ErrorString(XML_GetErrorCode(p))); + } + } + + fclose(tmx_fp); + + free(path_dup); + + return cur_tmx.map; +} diff --git a/src/tmx.h b/src/tmx.h new file mode 100644 index 0000000..e2ab766 --- /dev/null +++ b/src/tmx.h @@ -0,0 +1,8 @@ +#ifndef TMX_H +#define TMX_H + +#include "map.h" + +struct map *tmx_load(const char *path); + +#endif -- cgit v0.9.1