diff options
author | quou <quou@disroot.org> | 2024-09-23 21:11:29 +1000 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-09-23 21:11:29 +1000 |
commit | 259237fae792e8f19d9b7920b2354f75bc1789e2 (patch) | |
tree | af079430f30f7df1b76efc4f96f520b0b6eee0af |
initial commit, basic platform and rendering code.
-rw-r--r-- | .gitignore | 9 | ||||
-rw-r--r-- | 1bitjam.c | 50 | ||||
-rw-r--r-- | Makefile | 64 | ||||
-rw-r--r-- | asset.c | 10 | ||||
-rw-r--r-- | asset.h | 11 | ||||
-rw-r--r-- | config.h | 15 | ||||
-rw-r--r-- | convimg.c | 98 | ||||
-rw-r--r-- | error.h | 11 | ||||
-rw-r--r-- | intermediate/guy.bmp | bin | 0 -> 394 bytes | |||
-rw-r--r-- | intermediate/hello.bmp | bin | 0 -> 4234 bytes | |||
-rw-r--r-- | maths.c | 36 | ||||
-rw-r--r-- | maths.h | 22 | ||||
-rw-r--r-- | memory.c | 225 | ||||
-rw-r--r-- | memory.h | 56 | ||||
-rw-r--r-- | packassets.c | 126 | ||||
-rw-r--r-- | plat.c | 429 | ||||
-rw-r--r-- | plat.h | 76 | ||||
-rw-r--r-- | rect.c | 85 | ||||
-rw-r--r-- | rect.h | 13 | ||||
-rw-r--r-- | render.c | 97 | ||||
-rw-r--r-- | render.h | 29 |
21 files changed, 1462 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00f7063 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +tags + +*.d +*.o +pack.h +/data/ +/convimg +/packassets +/1bitjam diff --git a/1bitjam.c b/1bitjam.c new file mode 100644 index 0000000..d573fe1 --- /dev/null +++ b/1bitjam.c @@ -0,0 +1,50 @@ +#include "asset.h" +#include "config.h" +#include "memory.h" +#include "plat.h" +#include "rect.h" +#include "render.h" + +int entrypoint(int argc, const char** argv, Arena* m) { + Heap h; + App* a; + FPS f; + Renderer r; + Rect rect = { 0, 0, 32, 32 }; + Rect rect2 = { 0, 0, 8, 8 }; + int x = 0, y = 0; + (void)argc; + (void)argv; + init_heap( + &h, + arena_alloc(m, app_memory_size), + app_memory_size + ); + a = new_app(&h, "1 bit jam"); + init_fps(&f, default_mpf); + while (a->o) { + fps_begin(&f); + while (f.now >= f.next && a->o) { + app_begin(a); + ren_begin(&r, a->fb, viewport_w, viewport_h); + ren_clear(&r); + if (a->btn_states[btn_left] & btn_state_pressed) + x--; + if (a->btn_states[btn_right] & btn_state_pressed) + x++; + if (a->btn_states[btn_up] & btn_state_pressed) + y--; + if (a->btn_states[btn_down] & btn_state_pressed) + y++; + ren_text(&r, 30, 60, "Hello"); + ren_map(&r, 10, 5, &rect, get_bitmap(asset_id_hello_img)); + ren_map(&r, x, y, &rect2, get_bitmap(asset_id_guy_img)); + ren_end(&r); + app_end(a); + fps_update(&f); + } + fps_end(&f); + } + deinit_app(a); + return error_none; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bf15054 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +compiler = gcc +tool_compiler = gcc +linker = gcc +cflags = -I./ -g -DDEBUG -Dplat_x11 -Dplat_x86 \ +-Dplat_posix -Dallocation_default_alignment=8 \ +-Wall -Wextra -pedantic -std=c90 +lflags = -lX11 -lXi +target = 1bitjam +int_dir = intermediate +data_dir = data +convimg = convimg +packassets = packassets +pack = pack.h + +sources = \ + 1bitjam.c \ + asset.c \ + maths.c \ + memory.c \ + plat.c \ + rect.c \ + render.c \ + +image_sources = \ + $(int_dir)/guy.bmp \ + $(int_dir)/hello.bmp + +objects = $(sources:%.c=%.o) +images = $(image_sources:$(int_dir)/%.bmp=$(data_dir)/%.img) + +all: $(target) $(pack) + +$(objects): %.o : %.c | $(pack) + $(compiler) -MMD -MF $(basename $@).d $(cflags) -o $@ -c $< + +$(images): $(data_dir)/%.img : $(int_dir)/%.bmp | $(convimg) $(data_dir) + ./$(convimg) $< $@ + +$(pack): $(packassets) $(images) + ./$(packassets) \ + $(pack) \ + $(data_dir) \ + $(notdir $(images)) + +$(convimg): convimg.c + $(tool_compiler) convimg.c $(cflags) -o $@ + +$(packassets): packassets.c + $(tool_compiler) packassets.c $(cflags) -o $@ + +$(target): $(objects) + $(linker) $(objects) -o $@ $(lflags) + +$(data_dir): + mkdir -p $(data_dir) + +clean: + rm *.d + rm *.o + rm $(pack) + rm -r $(data_dir) + rm $(target) + +-include $(sources:%.c=%.d) @@ -0,0 +1,10 @@ +#include "pack.h" +#include "asset.h" + +const unsigned char* get_asset(Asset_ID id) { + return &asset_data[asset_offset_table[id]]; +} + +const struct Bitmap* get_bitmap(Asset_ID id) { + return (const struct Bitmap*)get_asset(id); +} @@ -0,0 +1,11 @@ +#ifndef asset_h +#define asset_h + +#define enum_only +#include "pack.h" +#undef enum_only + +const unsigned char* get_asset(Asset_ID id); +const struct Bitmap* get_bitmap(Asset_ID id); + +#endif diff --git a/config.h b/config.h new file mode 100644 index 0000000..ed03b67 --- /dev/null +++ b/config.h @@ -0,0 +1,15 @@ +#ifndef config_h +#define config_h + +#define memory_size (1024 * 8) +#define app_memory_size (1024 * 4) + +#define max_pc_window_w 3000 +#define max_pc_window_h 3000 + +#define viewport_w 128 +#define viewport_h 128 +#define default_scale 3 +#define default_mpf 50 + +#endif diff --git a/convimg.c b/convimg.c new file mode 100644 index 0000000..631a8b1 --- /dev/null +++ b/convimg.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +int main(int argc, char** argv) { + FILE* outfile, * infile; + char bmp_magic[2]; + int ret = 0; + unsigned bmp_offset; + int bmp_w, bmp_h, x, y, s, bs; + unsigned short bmp_bits; + unsigned* bitmap; + Colour pixel, * buffer; + unsigned char t; + + if (argc < 3) { + fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]); + return 1; + } + + infile = fopen(argv[1], "rb"); + if (!infile) { + fprintf(stderr, "Failed to open %s.\n", argv[1]); + return 2; + } + + fread(bmp_magic, 2, 1, infile); + if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { + ret = 3; + fprintf(stderr, "Not a valid bitmap file.\n"); + goto end; + } + + fseek(infile, 10, SEEK_SET); + fread(&bmp_offset, 4, 1, infile); + + fseek(infile, 18, SEEK_SET); + fread(&bmp_w, 4, 1, infile); + fread(&bmp_h, 4, 1, infile); + fseek(infile, 28, SEEK_SET); + fread(&bmp_bits, 2, 1, infile); + + if (bmp_bits != 32) { + ret = 4; + fprintf(stderr, "Bitmap must have 32 bit pixels. %d\n", bmp_bits); + goto end; + } + + fseek(infile, bmp_offset, SEEK_SET); + + s = bmp_w * bmp_h; + buffer = malloc(s * 4); + /* Read & flip */ + for (y = 0; y < bmp_h; y++) { + for (x = 0; x < bmp_w; x++) { + fread(&pixel, 1, 4, infile); + t = pixel.r; + pixel.r = pixel.b; + pixel.b = t; + if (pixel.a == 0) { + pixel.r = 0; + pixel.g = 0; + pixel.b = 0; + } + buffer[x + (bmp_h - y - 1) * bmp_w] = pixel; + } + } + + outfile = fopen(argv[2], "wb"); + if (!outfile) { + fprintf(stderr, "Failed to open %s.\n", argv[2]); + fclose(infile); + return 2; + } + bs = (bmp_w * bmp_h + 32) / 32 * 4; + bitmap = calloc(bs, 1); + for (y = 0; y < bmp_h; y++) { + for (x = 0; x < bmp_w; x++) { + int idx = x + y * bmp_w; + if (buffer[idx].r) { + bitmap[idx / 32] |= 1 << (idx % 32); + } + } + } + fwrite(&bmp_w, 1, 2, outfile); + fwrite(&bmp_h, 1, 2, outfile); + fwrite(bitmap, 1, bs, outfile); + free(buffer); + +end: + fclose(infile); + fclose(outfile); + return ret; +} @@ -0,0 +1,11 @@ +#ifndef error_h +#define error_h + +typedef enum { + error_none, + error_assertion_failed, + error_platform_error, + error_out_of_memory +} Error; + +#endif diff --git a/intermediate/guy.bmp b/intermediate/guy.bmp Binary files differnew file mode 100644 index 0000000..9a881e0 --- /dev/null +++ b/intermediate/guy.bmp diff --git a/intermediate/hello.bmp b/intermediate/hello.bmp Binary files differnew file mode 100644 index 0000000..433cd18 --- /dev/null +++ b/intermediate/hello.bmp @@ -0,0 +1,36 @@ +#include "maths.h" + +int sin_table[sin_table_count] = { + 0x000, 0x00c, 0x019, 0x025, 0x032, 0x03e, 0x04b, 0x057, + 0x063, 0x070, 0x07c, 0x088, 0x094, 0x0a0, 0x0ac, 0x0b8, + 0x0c3, 0x0cf, 0x0da, 0x0e6, 0x0f1, 0x0fc, 0x107, 0x111, + 0x11c, 0x126, 0x130, 0x13a, 0x144, 0x14e, 0x157, 0x161, + 0x16a, 0x172, 0x17b, 0x183, 0x18b, 0x193, 0x19b, 0x1a2, + 0x1a9, 0x1b0, 0x1b7, 0x1bd, 0x1c3, 0x1c9, 0x1ce, 0x1d4, + 0x1d9, 0x1dd, 0x1e2, 0x1e6, 0x1e9, 0x1ed, 0x1f0, 0x1f3, + 0x1f6, 0x1f8, 0x1fa, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x1ff, + 0x200, 0x1ff, 0x1ff, 0x1fe, 0x1fd, 0x1fc, 0x1fa, 0x1f8, + 0x1f6, 0x1f3, 0x1f0, 0x1ed, 0x1e9, 0x1e6, 0x1e2, 0x1dd, + 0x1d9, 0x1d4, 0x1ce, 0x1c9, 0x1c3, 0x1bd, 0x1b7, 0x1b0, + 0x1a9, 0x1a2, 0x19b, 0x193, 0x18b, 0x183, 0x17b, 0x172, + 0x16a, 0x161, 0x157, 0x14e, 0x144, 0x13a, 0x130, 0x126, + 0x11c, 0x111, 0x107, 0x0fc, 0x0f1, 0x0e6, 0x0da, 0x0cf, + 0x0c3, 0x0b8, 0x0ac, 0x0a0, 0x094, 0x088, 0x07c, 0x070, + 0x063, 0x057, 0x04b, 0x03e, 0x032, 0x025, 0x019, 0x00c, +}; +int cos_table[sin_table_count]; + +void init_maths(void) { + int i, hs = sin_table_count >> 1; + for ( + i = hs; + i < sin_table_count; + i++ + ) { + sin_table[i] = -sin_table[i - hs]; + } + hs >>= 1; + for (i = 0; i < sin_table_count; i++) { + cos_table[i] = sin_table[(i + hs) & sin_table_mask]; + } +} @@ -0,0 +1,22 @@ +#ifndef maths_h +#define maths_h + +#define mini(a_, b_) ((a_) < (b_) ? (a_) : (b_)) +#define maxi(a_, b_) ((a_) > (b_) ? (a_) : (b_)) +#define clamp(v_, min_, max_) (maxi(min_, mini(max_, v_))) +#define absolute(v_) ((v_) < 0? -(v_): (v_)) + +#define fbits 0x9 +#define f1 0x200 + +#define sin_table_count 0x100 +#define sin_table_mask 0x0ff +#define sqrt_table_count 0x800 +#define sqrt_table_mask 0x07f + +extern int sin_table[sin_table_count]; +extern int cos_table[sin_table_count]; + +void init_maths(void); + +#endif diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..b5c6a70 --- /dev/null +++ b/memory.c @@ -0,0 +1,225 @@ +#include "memory.h" +#include "plat.h" +#include <stdint.h> +#include <stddef.h> + +int aligned(void* p, int a) { + return (uintptr_t)p % a == 0; +} + +int align_size(int s, int a) { + return (s + (a - 1)) & -a; +} + +static uintptr_t align_address( + uintptr_t ad, + size_t al +) { + size_t m; + m = al - 1; + return (ad + m) & ~m; +} + +void init_arena( + Arena* a, + void* mem, + int size +) { + a->buf = mem; + a->size = size; + a->ptr = 0; +} + +void clear_arena(Arena* a) { + a->ptr = 0; +} + +void* imp_arena_alloc( + Arena* a, + int size +) { + char* r; + assert(a->ptr + size < a->size); + r = &a->buf[a->ptr]; + a->ptr += size; + return r; +} + +void* arena_alloc( + Arena* a, + int size +) { + return arena_alloc_aligned( + a, + size, + allocation_default_alignment + ); +} + +void* arena_alloc_aligned( + Arena* a, + int size, + int align +) { + void* p; + p = imp_arena_alloc( + a, + size + align + 1 + ); + return (void*)align_address((uintptr_t)p, align); +} + +void init_heap( + Heap* h, + void* mem, + int size +) { + int* fb; + assert(aligned(mem, 4)); + assert(size > 8); + h->buf = mem; + h->size = size; + h->blocks = 1; + fb = (int*)h->buf; + fb[0] = size; +} + +void* imp2_heap_alloc( + Heap* h, + int size +) { + int o, i; + int hs = sizeof(int); + int as = align_size(size + hs, hs); + int f = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + if (as == bs) { + phdr[0] |= 1; + return phdr + 1; + } else { + int ns = bs - as; + if (ns > hs) { + int* nhdr = &phdr[ns / 4]; + phdr[0] = ns; + nhdr[0] = as | f; + h->blocks++; + return &nhdr[1]; + } + } + } else + o += bs; + } + return 0; +} + +void* imp_heap_alloc(Heap* h, int s) { + void* p = imp2_heap_alloc(h, s); + if (!p) { + heap_defrag(h); + p = imp2_heap_alloc(h, s); + } + return p; +} + +void imp_heap_free(Heap* h, void* p) { + assert((char*)p > h->buf); + assert((char*)p < h->buf + h->size); + (void)h; + ((int*)p)[-1] &= (unsigned)-1 >> 1; +} + +void* heap_alloc_aligned( + Heap* h, + int size, + int align +) { + unsigned char* p, * a; + ptrdiff_t shift; + size += (int)align; + p = imp_heap_alloc(h, size); + if (!p) { return 0; } + a = (unsigned char*)align_address((uintptr_t)p, align); + a += align * (unsigned)(p == a); + shift = a - p; + a[-1] = shift & 0xff; + return a; +} + +void heap_free_aligned(Heap* h, void* p) { + unsigned char* a; + ptrdiff_t shift; + a = p; + shift = a[-1]; + shift += 256 * shift == 0; + a -= shift; + imp_heap_free(h, a); +} + +void heap_defrag(Heap* h) { + int i, o, mtc; + int f = ~((unsigned)-1 >> 1); + for (i = o = mtc = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs, m, mc; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + for ( + m = bs, mc = 0, i++; + i < h->blocks; + i++, mc++ + ) { + int mhdr = *(int*)&h->buf[o + m]; + if (~mhdr & f) + m += mhdr & ~f; + else + break; + } + i--; + bs = m; + phdr[0] = bs; + mtc += mc; + } + o += bs; + } + h->blocks -= mtc; +} + +void* heap_alloc( + Heap* h, + int size +) { + return heap_alloc_aligned( + h, + size, + allocation_default_alignment + ); +} + +void heap_free( + Heap* h, + void* p +) { + heap_free_aligned(h, p); +} + +/* +void print_blocks(Heap* h) { + int i, o; + int fb = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int b = *(int*)&h->buf[o]; + int bs = b & ~fb; + int f = ~b & fb; + printf("%s %d\n", f? "free": " ", bs); + o += bs; + } + assert(o == h->size); +} +*/ + diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..38e632a --- /dev/null +++ b/memory.h @@ -0,0 +1,56 @@ +#ifndef memory_h +#define memory_h + +int aligned(void* p, int a); +int align_size(int s, int a); + +typedef struct Arena { + char* buf; + int size, ptr; +} Arena; + +void init_arena( + Arena* a, + void* mem, + int size +); +void clear_arena(Arena* a); +void* arena_alloc( + Arena* a, + int size +); +void* arena_alloc_aligned( + Arena* a, + int size, + int align +); + +typedef struct Heap { + char* buf; + int size, blocks; +} Heap; + +void init_heap( + Heap* h, + void* mem, + int size +); +void* heap_alloc( + Heap* h, + int size +); +void* heap_alloc_aligned( + Heap* h, + int size, + int align +); +void heap_free_aligned( + Heap* h, + void* p +); +void heap_free( + Heap* h, + void* p +); +void heap_defrag(Heap* h); +#endif diff --git a/packassets.c b/packassets.c new file mode 100644 index 0000000..85e0ddb --- /dev/null +++ b/packassets.c @@ -0,0 +1,126 @@ +#include "error.h" + +#include <stdio.h> +#include <string.h> + +#define buffer_size 8 + +const char* enum_header = +"#ifndef pack_h\n" +"#define pack_h\n" +"typedef enum {\n"; + +const char* enum_footer = +"\tasset_id_count\n" +"} Asset_ID;\n"; + +const char* footer = +"#endif\n"; + +typedef struct { + int size; + int offset; + char name[56]; +} Entry; + +int main(int argc, char** argv) { + int i, j, file_count, read; + char buffer[buffer_size]; + char name[56]; + const char* workingdir, * dsep = ""; + Entry entry; + FILE* outfile, * infile; + const char* filename = argv[1]; + + outfile = fopen(filename, "wb"); + if (!outfile) { + fprintf( + stderr, + "Failed to open '%s' for writing.\n", + filename + ); + return 1; + } + + file_count = argc - 3; + workingdir = argv[2]; + if (workingdir[strlen(workingdir) - 1] != '/') + dsep = "/"; + + fprintf(outfile, "%s", enum_header); + for (i = 0; i < file_count; i++) { + char* c; + strcpy(name, "asset_id_"); + strcat(name, argv[i + 3]); + for (c = name; *c; c++) { + if (c[0] == '.') c[0] = '_'; + } + fprintf(outfile, "\t%s,\n", name); + } + fprintf(outfile, "%s", enum_footer); + fprintf(outfile, "#ifndef enum_only\n"); + fprintf(outfile, "const int asset_offset_table[] = {\n"); + entry.offset = 0; + for (i = 0; i < file_count; i++) { + strcpy(entry.name, argv[i + 3]); + strcpy(name, workingdir); + strcat(name, dsep); + strcat(name, entry.name); + + infile = fopen(name, "rb"); + if (!infile) { + fprintf( + stderr, + "Failed to open '%s' for reading.\n", + entry.name + ); + return 1; + } + + fseek(infile, 0, SEEK_END); + entry.size = ftell(infile); + fclose(infile); + fprintf(outfile, "\t0x%08x,\n", entry.offset); + entry.offset += entry.size; + } + fprintf(outfile, "};\nconst unsigned char asset_data[] = {\n"); + for (i = 0; i < file_count; i++) { + strcpy(entry.name, argv[i + 3]); + strcpy(name, workingdir); + strcat(name, dsep); + strcat(name, entry.name); + + infile = fopen(name, "rb"); + if (!infile) { + fprintf( + stderr, + "Failed to open '%s' for reading.\n", + entry.name + ); + return 1; + } + + fseek(infile, 0, SEEK_END); + entry.size = ftell(infile); + rewind(infile); + + for (j = 0; j < entry.size; j += buffer_size) { + int k; + read = fread(buffer, 1, buffer_size, infile); + fprintf(outfile, "\t"); + for (k = 0; k < read; k++) { + fprintf(outfile, "0x%02x, ", (unsigned char)buffer[k]); + } + fseek(outfile, ftell(outfile) - 1, SEEK_SET); + fprintf(outfile, "\n"); + } + + fclose(infile); + } + + fprintf(outfile, "};\n#endif\n#endif\n"); + + fclose(outfile); + + return 0; +} @@ -0,0 +1,429 @@ +#include "config.h" +#include "maths.h" +#include "memory.h" +#include "plat.h" + +#ifdef plat_posix +#define _POSIX_SOURCE +#define _GNU_SOURCE + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/time.h> +#include <time.h> +#include <stdarg.h> +#include <stdio.h> + +extern int isatty(int); +extern int fileno(FILE*); + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +) { + if (!val) { + print_err( + "%d:%s: Assertion failed: %s.\n", + line, + file, + expr + ); + pbreak(error_assertion_failed); + return 0; + } + return 1; +} + +void print(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void print_err(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;31m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void print_war(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;35m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void pbreak(Error code) { +#if defined(DEBUG) && defined(plat_x86) + __asm__("int3;"); + (void)code; +#else + exit(code); +#endif +} + +static clockid_t global_clock; +static unsigned long global_freq; + +void init_timer(void) { + struct timespec ts; + global_clock = CLOCK_REALTIME; + global_freq = 1000000000; +#if defined(_POSIX_MONOTONIC_CLOCK) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + global_clock = CLOCK_MONOTONIC; + } +#else + (void)ts; +#endif +} + +unsigned long get_timer(void) { + struct timespec ts; + clock_gettime(global_clock, &ts); + return + (unsigned long)ts.tv_sec * global_freq + + (unsigned long)ts.tv_nsec; +} + +void sleep_ns(unsigned long ns) { + struct timespec t = { 0 }; + t.tv_nsec = ns; + nanosleep(&t, &t); +} + +void init_fps(FPS* f, int mpf) { + f->now = get_timer(); + f->next = f->now; + f->mpf = mpf; + f->pt = 0; + f->ct = -1; +} + +void fps_begin(FPS* f) { + f->now = get_timer(); +} + +void fps_end(FPS* f) { + unsigned long ts = f->next - f->now; + if (ts > 0) { + sleep_ns(ts); + } +} + +void fps_update(FPS* f) { + f->next += f->mpf * 1000000; + f->pt = f->ct; + f->ct = get_timer(); + f->fps = 1000000000 / (f->ct - f->pt); +} + +extern int entrypoint(int, const char**, Arena*); + +int main(int argc, const char** argv) { + Arena a; + void* mem = malloc(memory_size); + if (!mem) { + print_err("Out of memory.\n"); + return error_out_of_memory; + } + init_timer(); + init_arena( + &a, + mem, + memory_size + ); + return entrypoint(argc, argv, &a); +} + +#endif + + +#ifdef plat_x11 + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +typedef struct { + Display* d; + Window wi; + GC gc; + int w, h; + int rmx, rmy; + int omx, omy; + int ms; + unsigned long begin, end; + XImage* bb; + Colour* bbp; + Atom wm_delete; +} App_Internal; + +void init_rendering( + App* a, + App_Internal* i, + XWindowAttributes* wa +) { + Colour* p; + int w = max_pc_window_w; + int h = max_pc_window_h; + a->fb = heap_alloc( + a->heap, + (viewport_w * viewport_h + 32) / 32 * sizeof *a->fb + ); + p = malloc(sizeof *p * w * h); + if (!p || !a->fb) { + print_err("Out of memory.\n"); + pbreak(error_out_of_memory); + } + i->bb = XCreateImage( + i->d, + wa->visual, + wa->depth, + ZPixmap, + 0, + (char*)p, + w, + h, + 32, + w * sizeof *p + ); + if (!i->bb) { + print_err("Failed to create X11 backbuffer.\n"); + pbreak(error_platform_error); + } + i->bbp = p; +} + +void init_window(App* a, App_Internal* i, const char* n) { + Window root, w; + Display* d; + XWindowAttributes wa; + unsigned rm, bm; + i->bb = 0; + i->bbp = 0; + d = i->d; + root = DefaultRootWindow(d); + w = XCreateSimpleWindow( + d, + root, + 0, + 0, + viewport_w * default_scale, + viewport_h * default_scale, + 0, + WhitePixel(d, 0), + BlackPixel(d, 0) + ); + i->wi = w; + i->wm_delete = XInternAtom( + d, + "WM_DELETE_WINDOW", + 0 + ); + XSetWMProtocols(d, w, &i->wm_delete, 1); + XStoreName(d, w, n); + XSelectInput( + d, + w, + ExposureMask | + KeyPressMask | + KeyReleaseMask | + PointerMotionMask | + ButtonPressMask | + ButtonReleaseMask + ); + XMapRaised(d, w); + i->gc = XCreateGC(d, w, 0, 0); + if (!i->gc) { + print_err("Failed to create X graphics context.\n"); + pbreak(error_platform_error); + } + XGetWindowAttributes(d, w, &wa); + if (wa.depth != 24 && wa.depth != 32) { + print_err("Only true colour displays are supported.\n"); + pbreak(error_platform_error); + } + rm = wa.visual->red_mask & 0x1000000; + bm = wa.visual->blue_mask & 0xffffff; + if ((rm == 0xff && bm == 0xff0000)) { + print_war("Detected BGR. Colours will look fucked.\n"); + } + init_rendering(a, i, &wa); + i->w = wa.width; + i->h = wa.height; +} + +Btn btn_from_xkey(unsigned key) { + switch (key) { + case XK_Up: return btn_up; + case XK_Down: return btn_down; + case XK_Left: return btn_left; + case XK_Right: return btn_right; + case 0x78: return btn_shoot; + case 0x7a: return btn_jump; + case XK_Return: return btn_start; + default: return btn_unknown; + } +} + +App* new_app(Heap* mem, const char* n) { + App* a; + App_Internal* i; + a = heap_alloc(mem, sizeof *a + sizeof *i); + i = (App_Internal*)(&a[1]); + a->heap = mem; + a->s = 2; + a->o = 0; + a->err = error_none; + i->d = XOpenDisplay(0); + i->omx = i->omy = 0; + i->ms = 1; + if (!i->d) { + print_err("Failed to open X11 display.\n"); + pbreak(error_platform_error); + } + init_window(a, i, n); + a->o = 1; + return a; +} + +void deinit_app(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + XDestroyImage(i->bb); + XFreeGC(i->d, i->gc); + XDestroyWindow(i->d, i->wi); + XCloseDisplay(i->d); + heap_free(a->heap, a); +} + +void app_begin(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + Display* d = i->d; + Window w = i->wi; + int j; + i->begin = get_timer(); + for (j = 0; j < btn_count; j++) + a->btn_states[j] &= ~( + btn_state_just_pressed | btn_state_just_released + ); + while (XPending(d)) { + XEvent e; + XWindowAttributes wa; + KeySym sym; + Btn btn; + XNextEvent(d, &e); + switch (e.type) { + case ClientMessage: + if ( + (Atom)e.xclient.data.l[0] == + i->wm_delete + ) { + a->o = 0; + } + break; + case Expose: + XGetWindowAttributes(d, w, &wa); + i->w = mini(wa.width, max_pc_window_w); + i->h = mini(wa.height, max_pc_window_h); + break; + case KeyPress: + sym = XLookupKeysym(&e.xkey, 0); + btn = btn_from_xkey(sym); + a->btn_states[btn] |= + btn_state_pressed | + btn_state_just_pressed; + break; + case KeyRelease: + sym = XLookupKeysym(&e.xkey, 0); + btn = btn_from_xkey(sym); + a->btn_states[btn] &= ~btn_state_pressed; + a->btn_states[btn] |= btn_state_just_released; + break; + default: + break; + } + } +} + +void app_rencpy( + App* a, + App_Internal* i, + int sx, + int sy, + int s +) { + const Colour white = { 0xff, 0xff, 0xff, 0xff }; + const Colour black = { 0x00, 0x00, 0x00, 0xff }; + int x, y, sj = 0; + int w = viewport_w * s; + int h = viewport_h * s; + int ex = sx + w; + int ey = sy + h; + for (y = sy; y < ey; y++, sj++) { + int sy = sj / s; + int si = 0; + for (x = sx; x < ex; x++, si++) { + int sx = si / s; + int idx = sx + sy * viewport_w; + int bit = 1 << (idx & 0x1f); + bit &= a->fb[idx >> 5]; + i->bbp[x + y * max_pc_window_w] = bit? white: black; + } + } + XPutImage( + i->d, + i->wi, + i->gc, + i->bb, + 0, 0, + sx, sy, + w, h + ); +} + +void app_end(App* a) { + int s = 1; + int m1, m2; + App_Internal* i = (App_Internal*)(&a[1]); + if (i->w < i->h) { + m1 = i->w; + m2 = viewport_w; + } else { + m1 = i->h; + m2 = viewport_h; + } + while (m2 * s < m1) s++; /* lol */ + app_rencpy(a, i, 0, 0, s); + i->end = get_timer(); + a->fps = 1000000000 / (i->end - i->begin); +} + +#endif @@ -0,0 +1,76 @@ +#ifndef plat_h +#define plat_h + +#include "error.h" + +struct Heap; + +#ifdef assert +#undef assert +#endif + +#define assert(expr) \ + imp_assert( \ + expr, \ + #expr, \ + __FILE__, \ + __LINE__ \ + ) + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +); + +void print(const char* fmt, ...); +void print_err(const char* fmt, ...); +void print_war(const char* fmt, ...); +void pbreak(Error code); + +typedef struct { + int mpf; + int fps; + unsigned long pt, ct; + unsigned long now, next; +} FPS; + +void init_fps(FPS* f, int mpf); +void fps_begin(FPS* f); +void fps_end(FPS* f); +void fps_update(FPS* f); + +typedef enum { + btn_unknown, + btn_left, + btn_right, + btn_up, + btn_down, + btn_shoot, + btn_jump, + btn_start, + btn_count +} Btn; + +typedef enum { + btn_state_pressed = 1 << 0, + btn_state_just_pressed = 1 << 1, + btn_state_just_released = 1 << 2 +} Key_State; + +typedef struct App { + int o, s; + int fps; + Error err; + unsigned* fb; + struct Heap* heap; + unsigned char btn_states[btn_count]; +} App; + +App* new_app(struct Heap* mem, const char* n); +void deinit_app(App* a); +void app_begin(App* a); +void app_end(App* a); + +#endif @@ -0,0 +1,85 @@ +#include "rect.h" + +Rect* rect_clip(Rect* r, const Rect* c) { + int n; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + } + if ((n = r->x + r->w - (c->x + c->w)) > 0) + r->w -= n; + if ((n = r->y + r->h - (c->y + c->h)) > 0) + r->h -= n; + return r; +} + +Rect* rect_clipr(Rect* r, const int* c) { + int n; + if ((n = c[0] - r->x) > 0) { + r->w -= n; + r->x += n; + } + if ((n = c[1] - r->y) > 0) { + r->h -= n; + r->y += n; + } + if ((n = r->x + r->w - c[2]) > 0) + r->w -= n; + if ((n = r->y + r->h - c[3]) > 0) + r->h -= n; + return r; +} + +Rect* rect_clips(Rect* r, Rect* s, const Rect* c) { + int n; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + s->w -= n; + s->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + s->h -= n; + s->y += n; + } + if ((n = r->x + r->w - (c->x + c->w)) > 0) { + r->w -= n; + s->w -= n; + } + if ((n = r->y + r->h - (c->y + c->h)) > 0) { + r->h -= n; + s->h -= n; + } + return r; +} + +Rect* rect_clipsr(Rect* r, Rect* s, const int* c) { + int n; + if ((n = c[0] - r->x) > 0) { + r->w -= n; + r->x += n; + s->w -= n; + s->x += n; + } + if ((n = c[1] - r->y) > 0) { + r->h -= n; + r->y += n; + s->h -= n; + s->y += n; + } + if ((n = r->x + r->w - c[2]) > 0) { + r->w -= n; + s->w -= n; + } + if ((n = r->y + r->h - c[3]) > 0) { + r->h -= n; + s->h -= n; + } + return r; +} @@ -0,0 +1,13 @@ +#ifndef rect_h +#define rect_h + +typedef struct Rect { + int x, y, w, h; +} Rect; + +Rect* rect_clip(Rect* r, const Rect* c); +Rect* rect_clipr(Rect* r, const int* c); +Rect* rect_clips(Rect* r, Rect* s, const Rect* c); +Rect* rect_clipsr(Rect* r, Rect* s, const int* c); + +#endif diff --git a/render.c b/render.c new file mode 100644 index 0000000..34416c0 --- /dev/null +++ b/render.c @@ -0,0 +1,97 @@ +#include "rect.h" +#include "render.h" + +static int smol_font_w = 384; +static unsigned smol_font_data[] = { + 0x2296a520, 0x40000524, 0x77757767, 0x60002077, + 0x6773637e, 0x69b15475, 0x95576767, 0x02616755, + 0x66640102, 0x00021421, 0x00020000, 0xf0326000, + 0x2543e520, 0x20002242, 0x41152645, 0x92740277, + 0x1135175d, 0x9bf13427, 0x95523595, 0x05422272, + 0x72d66364, 0x73f25003, 0x95576677, 0xfa6233a9, + 0x06267000, 0x20727542, 0x27674145, 0x44022045, + 0x53151571, 0x9d915525, 0xf55243d7, 0x00422122, + 0x47351550, 0x55d23425, 0xb5522277, 0xf5222266, + 0x0b935020, 0x12022024, 0x277437e7, 0x42742277, + 0x61736756, 0x69975775, 0xb27235e1, 0x70646725, + 0x32666370, 0x75945625, 0xf2763241, 0xf0326639 +}; + +void ren_begin(Renderer* r, unsigned* t, int w, int h) { + r->t = t; + r->w = w; + r->h = h; + r->clip[0] = 0; + r->clip[1] = 0; + r->clip[2] = w; + r->clip[3] = h; +} + +void ren_end(Renderer* r) { + (void)r; +} + +void ren_clear(Renderer* r) { + int e = r->w * r->h >> 5, i; + for (i = 0; i < e; i++) + r->t[i] = 0; +} + +void ren_char(Renderer* r, int x, int y, char ch) { + Rect re, su; + int i, j, sx, sy, ex, ey; + re.x = x; + re.y = y; + re.w = 4; + re.h = 4; + su.x = (ch - ' ') * 4; + su.y = 0; + su.w = 4; + su.h = 4; + rect_clipsr(&re, &su, r->clip); + ex = re.x + re.w; + ey = re.y + re.h; + for (j = re.y, sy = su.y; j < ey; j++, sy++) { + for (i = re.x, sx = su.x; i < ex; i++, sx++) { + int di = (i + j * r->w); + int si = (sx + sy * smol_font_w); + int bit = (1 << (si & 0x1f)); + bit &= smol_font_data[si >> 5]; + r->t[di >> 5] ^= (1 << (di & 0x1f)) * (bit != 0); + } + } +} + +void ren_text(Renderer* r, int x, int y, const char* t) { + for (; *t; t++, x += 4) + ren_char(r, x, y, *t); +} + +void ren_map( + Renderer* r, + int x, + int y, + const Rect* re, + const Bitmap* bm +) { + Rect rect, su = *re; + const unsigned* data = (const unsigned*)&bm[1]; + int i, j, sx, sy, ex, ey; + rect.x = x; + rect.y = y; + rect.w = re->w; + rect.h = re->h; + rect_clipsr(&rect, &su, r->clip); + ex = rect.x + rect.w; + ey = rect.y + rect.h; + for (j = rect.y, sy = su.y; j < ey; j++, sy++) { + for (i = rect.x, sx = su.x; i < ex; i++, sx++) { + int di = (i + j * r->w); + int si = (sx + sy * bm->w); + int bit = (1 << (si & 0x1f)); + bit &= data[si >> 5]; + r->t[di >> 5] ^= (1 << (di & 0x1f)) * (bit != 0); + } + } +} + diff --git a/render.h b/render.h new file mode 100644 index 0000000..01391c5 --- /dev/null +++ b/render.h @@ -0,0 +1,29 @@ +#ifndef render_h +#define render_h + +struct Rect; + +typedef struct Bitmap { + short w, h; +} Bitmap; + +typedef struct { + unsigned* t; + int w, h; + int clip[4]; +} Renderer; + +void ren_begin(Renderer* r, unsigned* t, int w, int h); +void ren_end(Renderer* r); +void ren_clear(Renderer* r); +void ren_char(Renderer* r, int x, int y, char ch); +void ren_text(Renderer* r, int x, int y, const char* t); +void ren_map( + Renderer* r, + int x, + int y, + const struct Rect* re, + const Bitmap* bm +); + +#endif |