commit 1c83e9394bf7c14ba4ae8712b7098eaca1fe5565 from: Evan Burkey date: Sun Mar 15 05:51:50 2026 UTC Rewrite as generic WAD library with per-lump storage and mutation API commit - f76a6cd42ebbf205c570bde5a9864c0dd2b2d947 commit + 1c83e9394bf7c14ba4ae8712b7098eaca1fe5565 blob - c632bbe2c1d9d1b97e31551197ebbd333c4b0c5d (mode 644) blob + /dev/null --- .gitea/workflows/jobs.yaml +++ /dev/null @@ -1,65 +0,0 @@ ---- -name: Test and Deploy -on: - push: - branches: - - master - -jobs: - test: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake build-essential - - - name: Build and test - run: | - mkdir build - cd build - cmake .. - make - ./test - - docs: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Install SSH client - run: | - sudo apt-get update - sudo apt-get install -y openssh-client - - - name: Setup SSH - run: | - mkdir -p ~/.ssh - ssh-keyscan -H burkey.co >> ~/.ssh/known_hosts - echo "${{ secrets.BURKEY_CO_KEY}}" >> ~/.ssh/id_rsa - chmod -R 700 ~/.ssh - eval "$(ssh-agent -s)" - ssh-add ~/.ssh/id_rsa - - - name: Install mkdocs - run: | - pip install mkdocs - - - name: Build documentation - run: | - mkdocs build - - - name: Deploy documentation - run: | - ssh debian@burkey.co "rm -rf /var/www/docs/spitwad" - ssh debian@burkey.co "mkdir -p /var/www/docs/spitwad" - ssh debian@burkey.co "chmod 755 /var/www/docs/spitwad" - scp -r ./site/* debian@burkey.co:/var/www/docs/spitwad/ - blob - ee600298adafd60c227f38fdde624f67759eb4b7 (mode 644) blob + /dev/null --- docs/index.md +++ /dev/null @@ -1,14 +0,0 @@ -# spitwad - -Originally conceived as a joke between some friends after we laughed about how it would be better to use WADs to send data -between applications then JSON, because JSON is stupid and wasteful. `spitwad` is a library for interacting with WAD -files, popularized by ID games like DOOM and Quake. You can use it in your DOOM clone, or to package your data and -send it over a network as a WAD. Why? Why not! - -## Requirements - -Should work on any POSIX platform out of the box - -## Usage - -Docs are a work in progress! \ No newline at end of file blob - 9127b893dd48e64a6564fcd143fc9fe0dfea7116 (mode 644) blob + /dev/null --- mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: spitwad -site_url: https://fputs.com/docs/spitwad -theme: - name: readthedocs -nav: - - 'index.md' \ No newline at end of file blob - 8e8812898f17bcdf50c44202a7b180e4dfd225d0 blob + 73461c2c15a535c506582c2d2005c1359d48dad6 --- spitwad.c +++ spitwad.c @@ -3,128 +3,283 @@ #include "spitwad.h" -static int fail(const char* msg) { +static int fail(const char *msg) { fprintf(stderr, "%s\n", msg); return 1; } -static uint32_t getlong(const unsigned char* data, size_t offset) { - return data[offset] | data[offset + 1] << 8 | data[offset + 2] << 16 | data[offset + 3] << 24; +static uint32_t read_u32(const unsigned char *data, size_t offset) { + return (uint32_t)data[offset] + | (uint32_t)data[offset + 1] << 8 + | (uint32_t)data[offset + 2] << 16 + | (uint32_t)data[offset + 3] << 24; } -static uint16_t getshort(const unsigned char* data, size_t offset) { - return data[offset] | data[offset + 1] << 8; +static void write_u32(unsigned char *buf, size_t offset, uint32_t val) { + buf[offset] = val & 0xFF; + buf[offset + 1] = (val >> 8) & 0xFF; + buf[offset + 2] = (val >> 16) & 0xFF; + buf[offset + 3] = (val >> 24) & 0xFF; } -static char* getstring(const unsigned char* data, size_t offset) { - char* s = malloc(sizeof(char) * 9); - strncpy(s, data + offset, 8); - return s; +static int grow_lumps(struct WAD *wad) { + uint32_t new_cap = wad->capacity ? wad->capacity * 2 : 8; + struct Lump *new_lumps = realloc(wad->lumps, sizeof(struct Lump) * new_cap); + if (new_lumps == NULL) + return fail("Failed to grow lump array"); + wad->lumps = new_lumps; + wad->capacity = new_cap; + return 0; } -int new_WAD_from_data(struct WAD* wad, const unsigned char* data, size_t data_sz) { - if (wad == NULL) { - return fail("Supplied wad to new_WAD_from_data was NULL"); - } +int wad_init(struct WAD *wad, enum WAD_Type type) { + if (wad == NULL) + return fail("NULL wad passed to wad_init"); + wad->type = type; + wad->num_lumps = 0; + wad->capacity = 0; + wad->lumps = NULL; + return 0; +} - wad->data = malloc(sizeof(unsigned char) * data_sz); - if (wad->data == NULL) { - destroy_WAD(wad); - return fail("Allocation failure for wad->data"); - } - memcpy(wad->data, data, data_sz); - wad->data_sz = data_sz; +void wad_destroy(struct WAD *wad) { + if (wad == NULL) + return; + for (uint32_t i = 0; i < wad->num_lumps; i++) + free(wad->lumps[i].data); + free(wad->lumps); + wad->lumps = NULL; + wad->num_lumps = 0; + wad->capacity = 0; +} - char name[5]; - memcpy(name, data, 4); - name[4] = '\0'; - if (strcmp(name, "IWAD") == 0) { +int wad_load_data(struct WAD *wad, const unsigned char *data, size_t size) { + if (wad == NULL) + return fail("NULL wad passed to wad_load_data"); + if (data == NULL || size < 12) + return fail("Invalid data passed to wad_load_data"); + + char sig[5]; + memcpy(sig, data, 4); + sig[4] = '\0'; + if (strcmp(sig, "IWAD") == 0) wad->type = IWAD; - } else if (strcmp(name, "PWAD") == 0) { + else if (strcmp(sig, "PWAD") == 0) wad->type = PWAD; + else + return fail("WAD signature was not IWAD or PWAD"); + + uint32_t num_lumps = read_u32(data, 4); + uint32_t dir_offset = read_u32(data, 8); + + if (dir_offset + (size_t)num_lumps * 16 > size) + return fail("Directory extends beyond data"); + + wad->num_lumps = 0; + wad->capacity = 0; + wad->lumps = NULL; + + for (uint32_t i = 0; i < num_lumps; i++) { + size_t entry = dir_offset + (size_t)i * 16; + uint32_t lump_offset = read_u32(data, entry); + uint32_t lump_size = read_u32(data, entry + 4); + + if (lump_offset + (size_t)lump_size > size) { + wad_destroy(wad); + return fail("Lump data extends beyond input"); + } + + char name[9]; + memcpy(name, data + entry + 8, 8); + name[8] = '\0'; + + if (wad_add_lump(wad, name, data + lump_offset, lump_size) != 0) { + wad_destroy(wad); + return fail("Failed to add lump during load"); + } + } + + return 0; +} + +int wad_load_file(struct WAD *wad, const char *path) { + if (wad == NULL) + return fail("NULL wad passed to wad_load_file"); + + FILE *fp = fopen(path, "rb"); + if (fp == NULL) { + fprintf(stderr, "Failed to open %s\n", path); + return 1; + } + + fseek(fp, 0, SEEK_END); + size_t fsz = ftell(fp); + rewind(fp); + + unsigned char *buf = malloc(fsz); + if (buf == NULL) { + fclose(fp); + return fail("Failed to allocate read buffer"); + } + + if (fread(buf, 1, fsz, fp) != fsz) { + free(buf); + fclose(fp); + return fail("Failed to read file"); + } + fclose(fp); + + int ret = wad_load_data(wad, buf, fsz); + free(buf); + return ret; +} + +static size_t wad_serialized_size(const struct WAD *wad) { + size_t size = 12; // header + for (uint32_t i = 0; i < wad->num_lumps; i++) + size += wad->lumps[i].size; + size += (size_t)wad->num_lumps * 16; // directory + return size; +} + +int wad_write_data(const struct WAD *wad, unsigned char **out, size_t *out_size) { + if (wad == NULL || out == NULL || out_size == NULL) + return fail("NULL argument to wad_write_data"); + + size_t total = wad_serialized_size(wad); + unsigned char *buf = calloc(1, total); + if (buf == NULL) + return fail("Failed to allocate write buffer"); + + // Header + const char *sig = (wad->type == IWAD) ? "IWAD" : "PWAD"; + memcpy(buf, sig, 4); + write_u32(buf, 4, wad->num_lumps); + + size_t data_offset = 12; + for (uint32_t i = 0; i < wad->num_lumps; i++) + data_offset += wad->lumps[i].size; + write_u32(buf, 8, (uint32_t)data_offset); + + // Lump data + size_t offset = 12; + size_t dir_pos = data_offset; + for (uint32_t i = 0; i < wad->num_lumps; i++) { + struct Lump *l = &wad->lumps[i]; + if (l->size > 0) + memcpy(buf + offset, l->data, l->size); + + // Directory entry + write_u32(buf, dir_pos, (uint32_t)offset); + write_u32(buf, dir_pos + 4, l->size); + memset(buf + dir_pos + 8, 0, 8); + memcpy(buf + dir_pos + 8, l->name, strlen(l->name)); + dir_pos += 16; + + offset += l->size; + } + + *out = buf; + *out_size = total; + return 0; +} + +int wad_write_file(const struct WAD *wad, const char *path) { + if (wad == NULL || path == NULL) + return fail("NULL argument to wad_write_file"); + + unsigned char *buf = NULL; + size_t size = 0; + if (wad_write_data(wad, &buf, &size) != 0) + return 1; + + FILE *fp = fopen(path, "wb"); + if (fp == NULL) { + free(buf); + fprintf(stderr, "Failed to open %s for writing\n", path); + return 1; + } + + size_t written = fwrite(buf, 1, size, fp); + fclose(fp); + free(buf); + + if (written != size) + return fail("Failed to write all data"); + return 0; +} + +int wad_add_lump(struct WAD *wad, const char *name, const unsigned char *data, uint32_t size) { + if (wad == NULL || name == NULL) + return fail("NULL argument to wad_add_lump"); + + if (wad->num_lumps >= wad->capacity) { + if (grow_lumps(wad) != 0) + return 1; + } + + struct Lump *l = &wad->lumps[wad->num_lumps]; + memset(l->name, 0, 9); + strncpy(l->name, name, 8); + + if (size > 0 && data != NULL) { + l->data = malloc(size); + if (l->data == NULL) + return fail("Failed to allocate lump data"); + memcpy(l->data, data, size); } else { - destroy_WAD(wad); - return fail("WAD Type was not IWAD or PWAD"); + l->data = NULL; } + l->size = size; - wad->dir_sz = getlong(data, 4); - wad->dir_offset = getlong(data, 8); + wad->num_lumps++; + return 0; +} - wad->directory = malloc(sizeof(struct DirEntry) * wad->dir_sz); - if (wad->directory == NULL) { - destroy_WAD(wad); - return fail("Allocation failure for wad->directory"); +int wad_remove_lump(struct WAD *wad, const char *name) { + if (wad == NULL || name == NULL) + return fail("NULL argument to wad_remove_lump"); + + for (uint32_t i = 0; i < wad->num_lumps; i++) { + if (strncmp(wad->lumps[i].name, name, 8) == 0) + return wad_remove_lump_at(wad, i); } + return fail("Lump not found"); +} - for (size_t i = wad->dir_offset, j = 0; i < data_sz; i += 16, ++j) { - wad->directory[j].offset = getlong(data, i); - wad->directory[j].length = getlong(data, i + 4); +int wad_remove_lump_at(struct WAD *wad, uint32_t index) { + if (wad == NULL) + return fail("NULL wad passed to wad_remove_lump_at"); + if (index >= wad->num_lumps) + return fail("Index out of bounds"); - char *s = getstring(data, i + 8); - strncpy(wad->directory[j].name, s, 8); - wad->directory[j].name[8] = '\0'; - free(s); - } + free(wad->lumps[index].data); + for (uint32_t i = index; i < wad->num_lumps - 1; i++) + wad->lumps[i] = wad->lumps[i + 1]; + + wad->num_lumps--; return 0; } -int new_WAD_from_file(struct WAD* wad, const char* path) { - if (wad == NULL) { - fprintf(stderr, "Supplied wad to new_WAD_from_file was NULL\n"); +const struct Lump *wad_find_lump(const struct WAD *wad, const char *name) { + if (wad == NULL || name == NULL) + return NULL; + for (uint32_t i = 0; i < wad->num_lumps; i++) { + if (strncmp(wad->lumps[i].name, name, 8) == 0) + return &wad->lumps[i]; + } + return NULL; +} + +const struct Lump *wad_get_lump(const struct WAD *wad, uint32_t index) { + if (wad == NULL || index >= wad->num_lumps) + return NULL; + return &wad->lumps[index]; +} + +uint32_t wad_num_lumps(const struct WAD *wad) { + if (wad == NULL) return 0; - } - - FILE *fp = NULL; - fp = fopen(path, "r"); - if (fp == NULL) { - destroy_WAD(wad); - fprintf(stderr, "Failed to open %s. Returning NULL\n", path); - return 1; - } - - fseek(fp, 0, SEEK_END); - size_t fsz = ftell(fp); - rewind(fp); - - unsigned char *buf = NULL; - buf = (unsigned char*)malloc(sizeof(unsigned char) * fsz); - if (buf == NULL) { - destroy_WAD(wad); - fclose(fp); - return fail("Failed to allocate buf in new_WAD_from_file"); - } - - fread(buf, 1, fsz, fp); - fclose(fp); - - new_WAD_from_data(wad, buf, fsz); - free(buf); - return 0; + return wad->num_lumps; } - -int new_WAD(struct WAD* wad) { - wad->data = NULL; - wad->data_sz = 0; - wad->directory = NULL; - wad->dir_sz = 0; - wad->dir_offset = 0; -} - -void destroy_WAD(struct WAD* wad) { - free(wad->data); - free(wad->directory); - free(wad); -} - -int write_to_file(struct WAD* wad, const char* path) { - FILE *fp = NULL; - fp = fopen(path, "wb"); - if (fp == NULL) { - fprintf(stderr, "Failed to open %s\n", path); - return 1; - } - fwrite(wad->data, sizeof(unsigned char), wad->data_sz, fp); - fclose(fp); - return 0; -} blob - 3783f1be9e6f56351d578b08410921ddabb5abb9 blob + e45d9cf73e95356bf471b71cc306002301726ef2 --- spitwad.h +++ spitwad.h @@ -9,26 +9,34 @@ enum WAD_Type { PWAD }; -struct DirEntry { - uint32_t offset; - uint32_t length; +struct Lump { char name[9]; + unsigned char *data; + uint32_t size; }; struct WAD { enum WAD_Type type; - uint32_t dir_sz; - uint32_t dir_offset; - unsigned char *data; - size_t data_sz; - struct DirEntry *directory; + uint32_t num_lumps; + uint32_t capacity; + struct Lump *lumps; }; -int new_WAD_from_data(struct WAD* wad, const unsigned char* data, size_t data_sz); -int new_WAD_from_file(struct WAD* wad, const char* path); -int new_WAD(struct WAD* wad); -void destroy_WAD(struct WAD* wad); -int write_to_file(struct WAD* wad, const char* path); +int wad_init(struct WAD *wad, enum WAD_Type type); +void wad_destroy(struct WAD *wad); +int wad_load_file(struct WAD *wad, const char *path); +int wad_load_data(struct WAD *wad, const unsigned char *data, size_t size); +int wad_write_file(const struct WAD *wad, const char *path); +int wad_write_data(const struct WAD *wad, unsigned char **out, size_t *out_size); + +int wad_add_lump(struct WAD *wad, const char *name, const unsigned char *data, uint32_t size); +int wad_remove_lump(struct WAD *wad, const char *name); +int wad_remove_lump_at(struct WAD *wad, uint32_t index); + +const struct Lump *wad_find_lump(const struct WAD *wad, const char *name); +const struct Lump *wad_get_lump(const struct WAD *wad, uint32_t index); +uint32_t wad_num_lumps(const struct WAD *wad); + #endif //SPITWAD_H blob - fd787e23c487a32031b5cede1f970877bd3c81e6 blob + 3d90a01631e5ac9e00e5677382b7bd65581fd2ef --- test.c +++ test.c @@ -5,41 +5,191 @@ #include "spitwad.h" -// Values from manually inspecting DOOM1.WAD with a third-party tool void assert_doom1_wad(struct WAD *wad) { assert(wad->type == IWAD); - assert(wad->dir_sz == 1264); - assert(wad->dir_offset == 4175796); - assert(wad->directory[0].offset == 12); - assert(wad->directory[0].length == 10752); - assert(strcmp(wad->directory[0].name, "PLAYPAL") == 0); + assert(wad->num_lumps == 1264); + const struct Lump *first = wad_get_lump(wad, 0); + assert(first != NULL); + assert(first->size == 10752); + assert(strcmp(first->name, "PLAYPAL") == 0); } -void basic_test() { - struct WAD *wad = malloc(sizeof(struct WAD)); - assert(new_WAD_from_file(wad, "DOOM1.WAD") == 0); - assert_doom1_wad(wad); - destroy_WAD(wad); - wad = NULL; +void test_load_file() { + struct WAD wad; + assert(wad_load_file(&wad, "DOOM1.WAD") == 0); + assert_doom1_wad(&wad); + wad_destroy(&wad); } -void read_write_test() { - struct WAD *orig = malloc(sizeof(struct WAD)); - assert(new_WAD_from_file(orig, "DOOM1.WAD") == 0); +void test_round_trip() { + struct WAD orig; + assert(wad_load_file(&orig, "DOOM1.WAD") == 0); + assert(wad_write_file(&orig, "TEST.WAD") == 0); - assert(write_to_file(orig, "TEST.WAD") == 0); - struct WAD *new = malloc(sizeof(struct WAD)); - assert(new_WAD_from_file(new, "TEST.WAD") == 0); - assert_doom1_wad(new); + struct WAD copy; + assert(wad_load_file(©, "TEST.WAD") == 0); + assert_doom1_wad(©); - destroy_WAD(orig); - orig = NULL; - destroy_WAD(new); - new = NULL; + assert(orig.num_lumps == copy.num_lumps); + for (uint32_t i = 0; i < orig.num_lumps; i++) { + const struct Lump *a = wad_get_lump(&orig, i); + const struct Lump *b = wad_get_lump(©, i); + assert(strcmp(a->name, b->name) == 0); + assert(a->size == b->size); + if (a->size > 0) + assert(memcmp(a->data, b->data, a->size) == 0); + } + + wad_destroy(&orig); + wad_destroy(©); remove("TEST.WAD"); } +void test_create_and_manipulate() { + struct WAD wad; + assert(wad_init(&wad, PWAD) == 0); + assert(wad.type == PWAD); + assert(wad_num_lumps(&wad) == 0); + + const unsigned char data1[] = "hello world"; + const unsigned char data2[] = {0xDE, 0xAD, 0xBE, 0xEF}; + const unsigned char data3[] = "test data here"; + + assert(wad_add_lump(&wad, "GREETING", data1, sizeof(data1)) == 0); + assert(wad_add_lump(&wad, "DEADBEEF", data2, sizeof(data2)) == 0); + assert(wad_add_lump(&wad, "TESTDATA", data3, sizeof(data3)) == 0); + assert(wad_num_lumps(&wad) == 3); + + // Find by name + const struct Lump *found = wad_find_lump(&wad, "DEADBEEF"); + assert(found != NULL); + assert(found->size == 4); + assert(memcmp(found->data, data2, 4) == 0); + + // Get by index + const struct Lump *first = wad_get_lump(&wad, 0); + assert(first != NULL); + assert(strcmp(first->name, "GREETING") == 0); + + // Remove by name + assert(wad_remove_lump(&wad, "DEADBEEF") == 0); + assert(wad_num_lumps(&wad) == 2); + assert(wad_find_lump(&wad, "DEADBEEF") == NULL); + assert(strcmp(wad_get_lump(&wad, 1)->name, "TESTDATA") == 0); + + // Remove by index + assert(wad_remove_lump_at(&wad, 0) == 0); + assert(wad_num_lumps(&wad) == 1); + assert(strcmp(wad_get_lump(&wad, 0)->name, "TESTDATA") == 0); + + wad_destroy(&wad); +} + +void test_write_and_reload() { + struct WAD wad; + assert(wad_init(&wad, PWAD) == 0); + + const unsigned char data1[] = "first lump data"; + const unsigned char data2[] = {1, 2, 3, 4, 5, 6, 7, 8}; + assert(wad_add_lump(&wad, "FIRST", data1, sizeof(data1)) == 0); + assert(wad_add_lump(&wad, "SECOND", data2, sizeof(data2)) == 0); + + // Write to buffer + unsigned char *buf = NULL; + size_t buf_sz = 0; + assert(wad_write_data(&wad, &buf, &buf_sz) == 0); + assert(buf != NULL); + assert(buf_sz > 0); + + // Reload from buffer + struct WAD wad2; + assert(wad_load_data(&wad2, buf, buf_sz) == 0); + assert(wad2.type == PWAD); + assert(wad_num_lumps(&wad2) == 2); + + const struct Lump *l1 = wad_get_lump(&wad2, 0); + assert(strcmp(l1->name, "FIRST") == 0); + assert(l1->size == sizeof(data1)); + assert(memcmp(l1->data, data1, sizeof(data1)) == 0); + + const struct Lump *l2 = wad_get_lump(&wad2, 1); + assert(strcmp(l2->name, "SECOND") == 0); + assert(l2->size == sizeof(data2)); + assert(memcmp(l2->data, data2, sizeof(data2)) == 0); + + free(buf); + wad_destroy(&wad); + wad_destroy(&wad2); + + // Also test file round-trip + assert(wad_init(&wad, PWAD) == 0); + assert(wad_add_lump(&wad, "FIRST", data1, sizeof(data1)) == 0); + assert(wad_add_lump(&wad, "SECOND", data2, sizeof(data2)) == 0); + assert(wad_write_file(&wad, "TEST2.WAD") == 0); + + struct WAD wad3; + assert(wad_load_file(&wad3, "TEST2.WAD") == 0); + assert(wad_num_lumps(&wad3) == 2); + assert(memcmp(wad_get_lump(&wad3, 0)->data, data1, sizeof(data1)) == 0); + + wad_destroy(&wad); + wad_destroy(&wad3); + remove("TEST2.WAD"); +} + +void test_name_truncation() { + struct WAD wad; + assert(wad_init(&wad, PWAD) == 0); + assert(wad_add_lump(&wad, "LONGERNAME", (const unsigned char *)"x", 1) == 0); + const struct Lump *l = wad_get_lump(&wad, 0); + assert(strlen(l->name) == 8); + assert(strcmp(l->name, "LONGERNA") == 0); + wad_destroy(&wad); +} + +void test_empty_lump() { + struct WAD wad; + assert(wad_init(&wad, PWAD) == 0); + assert(wad_add_lump(&wad, "EMPTY", NULL, 0) == 0); + const struct Lump *l = wad_get_lump(&wad, 0); + assert(l->size == 0); + assert(l->data == NULL); + + unsigned char *buf = NULL; + size_t sz = 0; + assert(wad_write_data(&wad, &buf, &sz) == 0); + + struct WAD wad2; + assert(wad_load_data(&wad2, buf, sz) == 0); + assert(wad_get_lump(&wad2, 0)->size == 0); + + free(buf); + wad_destroy(&wad); + wad_destroy(&wad2); +} + +void test_error_cases() { + assert(wad_init(NULL, PWAD) != 0); + assert(wad_load_file(NULL, "foo") != 0); + assert(wad_load_data(NULL, NULL, 0) != 0); + + struct WAD wad; + assert(wad_load_data(&wad, (const unsigned char *)"short", 5) != 0); + assert(wad_load_data(&wad, (const unsigned char *)"NOPE01234567", 12) != 0); + + assert(wad_find_lump(NULL, "X") == NULL); + assert(wad_get_lump(NULL, 0) == NULL); + assert(wad_num_lumps(NULL) == 0); +} + int main() { - basic_test(); - read_write_test(); -} \ No newline at end of file + test_load_file(); + test_round_trip(); + test_create_and_manipulate(); + test_write_and_reload(); + test_name_truncation(); + test_empty_lump(); + test_error_cases(); + printf("All tests passed.\n"); + return 0; +}