#include "memory.h" #include "pack.h" #include "plat.h" #include "str.h" #include #include #include #define pack_max_files 4 typedef struct Pack_File { FILE* handle; Pack_Entry* entry; int offset; } Pack_File; Pack_Entry* get_pack_table(Pack* p) { return (Pack_Entry*)( (char*)&p[1] + pack_max_files * sizeof(Pack_File) ); } int get_pack_size(int entry_count) { return sizeof(Pack) + pack_max_files * sizeof(Pack_File) + entry_count * sizeof(Pack_Entry); } Pack_Entry* pack_find_entry(Pack* p, const char* name) { Pack_Entry* tab = get_pack_table(p); uint32_t hash = hash_string(name); int idx = (int)(hash % p->file_count), i; int e = (int)p->file_count; for (i = 0; i < e; i++) { Pack_Entry* e = &tab[idx]; if (!e->name[0] || string_equal(name, e->name)) return e; idx = (int)((idx + 1) % p->file_count); } return 0; } static void insert_entry(Pack* p, const Pack_Entry* e) { Pack_Entry* d = pack_find_entry(p, e->name); assert(d != 0); if (d->name[0]) print_war("Duplicate entry in %s: %s\n", p->filename, e->name); *d = *e; } static void read_table(Pack* p, FILE* f) { uint32_t i, e = p->file_count; Pack_Entry* tab = get_pack_table(p); for (i = 0; i < e; i++) tab[i].name[0] = 0; for (i = 0; i < e; i++) { Pack_Entry e; fread(&e, sizeof e, 1, f); insert_entry(p, &e); } } static void init_files(Pack* p) { int i; Pack_File* files = (Pack_File*)&p[1]; for (i = 0; i < pack_max_files; i++) { files[i].handle = 0; } } Pack* pack_open(const char* filename, Arena* a) { Pack* p; char magic[4]; uint32_t entry_count; FILE* f = fopen(filename, "rb"); if (!f) return 0; fread(magic, 1, 4, f); if ( magic[0] != 'P' || magic[1] != 'A' || magic[2] != 'C' || magic[3] != 'K' ) { fclose(f); return 0; } fread(&entry_count, 4, 1, f); p = arena_alloc(a, get_pack_size(entry_count)); p->filename = dup_string(a, filename); p->file_count = entry_count; init_files(p); read_table(p, f); fclose(f); return p; } void pack_close(Pack* p) { (void)p; } Pack_Entry* pack_get_entry(Pack* p, const char* name) { Pack_Entry* e = pack_find_entry(p, name); if (!e || e->name[0] == 0) return 0; return e; } static Pack_File* init_handle( Pack* p, Pack_File* h, const char* name ) { FILE* f; Pack_Entry* e = pack_find_entry(p, name); if (!e || !e->name[0]) return 0; f = fopen(p->filename, "rb"); if (!f) return 0; h->offset = 0; h->handle = f; h->entry = e; return h; } Pack_File* pack_open_file(Pack* p, const char* name) { int i; Pack_File* files = (Pack_File*)&p[1]; for (i = 0; i < pack_max_files; i++) { Pack_File* file = &files[i]; if (!file->handle) return init_handle(p, file, name); } return 0; } void pack_close_file(Pack_File* f) { fclose(f->handle); f->handle = 0; } int pack_read(Pack_File* f, void* buf, int size) { int diff = (f->offset + size) - f->entry->size; int read; if (diff > 0) size -= diff; fseek(f->handle, f->entry->offset + f->offset, SEEK_SET); read = fread(buf, 1, size, f->handle); f->offset += read; return read; } void pack_seek(Pack_File* f, int offset, Seek_Rel rel) { switch (rel) { case seek_rel_cur: f->offset += offset; break; case seek_rel_start: f->offset = offset; break; case seek_rel_end: f->offset = f->entry->size - offset; break; } } int pack_tell(Pack_File* f) { return f->offset; }