Commit Diff


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(&copy, "TEST.WAD") == 0);
+    assert_doom1_wad(&copy);
 
-    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(&copy, 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(&copy);
     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;
+}