// Reads .gat files by name-mapping .wlk files #include "grfio.hpp" #include #include #include #include #include "mmo.hpp" #include "socket.hpp" #include "utils.hpp" //---------------------------- // file entry table struct //---------------------------- typedef struct { size_t declen; int16_t next; // next index into the filelist[] array, or -1 char fn[128 - 4 - 2]; // file name } FILELIST; #define FILELIST_LIMIT 32768 // limit to number of filelists - if you increase this, change all shorts to int #define FILELIST_ADDS 1024 // amount to increment when reallocing static FILELIST *filelist = NULL; /// Number of entries used static uint16_t filelist_entrys = 0; /// Number of FILELIST entries actually allocated static uint16_t filelist_maxentry = 0; /// First index of the given hash, into the filelist[] array #define l -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 static int16_t filelist_hash[256] = {l,l,l,l,l,l,l,l,l,l,l,l,l,l,l,l}; #undef l /// Hash a filename static uint8_t filehash(const char *fname) { // Larger than the return type - upper bits are used in the process uint32_t hash = 0; while (*fname) { hash = (hash << 1) + (hash >> 7) * 9 + (unsigned char)*fname; fname++; } return hash; } /// Find the filelist entry for the given filename, or NULL if it is not static FILELIST *filelist_find(const char *fname) { int16_t index = filelist_hash[filehash(fname)]; while (index >= 0) { if (strcmp(filelist[index].fn, fname) == 0) return &filelist[index]; index = filelist[index].next; } return NULL; } /// Copy a temporary entry into the hash map static FILELIST *filelist_add(FILELIST * entry) { if (filelist_entrys >= FILELIST_LIMIT) { fprintf(stderr, "filelist limit : filelist_add\n"); exit(1); } if (filelist_entrys >= filelist_maxentry) { RECREATE(filelist, FILELIST, filelist_maxentry + FILELIST_ADDS); memset(filelist + filelist_maxentry, '\0', FILELIST_ADDS * sizeof(FILELIST)); filelist_maxentry += FILELIST_ADDS; } uint16_t new_index = filelist_entrys++; uint8_t hash = filehash(entry->fn); entry->next = filelist_hash[hash]; filelist_hash[hash] = new_index; filelist[new_index] = *entry; return &filelist[new_index]; } static FILELIST *filelist_modify(FILELIST * entry) { FILELIST *fentry = filelist_find(entry->fn); if (fentry) { entry->next = fentry->next; *fentry = *entry; return fentry; } return filelist_add(entry); } /// Change fname data/*.gat to lfname data/*.wlk // TODO even if the file exists, don't keep reopening it every time one loads static void grfio_resnametable(const char *fname, char *lfname) { char restable[] = "data/resnametable.txt"; FILE *fp = fopen_(restable, "rb"); if (fp == NULL) { fprintf(stderr, "No resnametable, can't look for %s\n", fname); strcpy(lfname, fname); char* ext = lfname + strlen(lfname) - 4; if (!strcmp(ext, ".gat")) strcpy(ext, ".wlk"); return; } char line[512]; while (fgets(line, sizeof(line), fp)) { char w1[256], w2[256]; if ( // line is of the form foo.gat#foo.wlk# (sscanf(line, "%[^#]#%[^#]#", w1, w2) == 2) // strip data/ from foo.gat before comparing && (!strcmp(w1, fname + 5))) { strcpy(lfname, "data/"); strcpy(lfname + 5, w2); fclose_(fp); return; } } fprintf(stderr, "Unable to find resource: %s\n", fname); fclose_(fp); strcpy(lfname, fname); char* ext = lfname + strlen(lfname) - 4; if (!strcmp(ext, ".gat")) strcpy(ext, ".wlk"); return; } /// Size of resource size_t grfio_size(const char *fname) { FILELIST *entry = filelist_find(fname); if (entry) return entry->declen; char lfname[256]; FILELIST lentry; struct stat st; grfio_resnametable(fname, lfname); for (char *p = lfname; *p; p++) if (*p == '\\') *p = '/'; if (stat(lfname, &st) == 0) { strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); lentry.declen = st.st_size; entry = filelist_modify(&lentry); } else { printf("%s not found\n", fname); return 0; } return entry->declen; } void *grfio_reads(const char *fname, size_t *size) { char lfname[256]; grfio_resnametable(fname, lfname); for (char *p = &lfname[0]; *p != 0; p++) if (*p == '\\') *p = '/'; // * At the time of Unix FILE *in = fopen_(lfname, "rb"); if (!in) { fprintf(stderr, "%s not found\n", fname); return NULL; } FILELIST lentry; FILELIST *entry = filelist_find(fname); if (entry) { lentry.declen = entry->declen; } else { fseek(in, 0, SEEK_END); lentry.declen = ftell(in); fseek(in, 0, SEEK_SET); strncpy(lentry.fn, fname, sizeof(lentry.fn) - 1); entry = filelist_modify(&lentry); } uint8_t *buf2; CREATE(buf2, uint8_t, lentry.declen + 1024); if (fread(buf2, 1, lentry.declen, in) != lentry.declen) exit(1); fclose_(in); in = NULL; if (size) *size = entry->declen; return buf2; }