// Reads .gat files by name-mapping .wlk files
#include "grfio.hpp"
#include <sys/stat.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#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;
}