/* * EXPOSED INTERFACE for the hashtable implementation * util_htnew(size) -- to make a new hashtable * util_htset(table, key, value, sizeof(value)) -- to set something in the table * util_htget(table, key) -- to get something from the table * util_htdel(table) -- to delete the table */ hash_table_t *util_htnew(size_t size) { hash_table_t *hashtable = NULL; stat_size_entry_t *find = NULL; if (size < 1) return NULL; if (!stat_size_hashtables) stat_size_hashtables = stat_size_new(); if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t)))) return NULL; if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) { mem_d(hashtable); return NULL; } if ((find = stat_size_get(stat_size_hashtables, size))) find->value++; else { stat_type_hashtables++; stat_size_put(stat_size_hashtables, size, 1); } hashtable->size = size; memset(hashtable->table, 0, sizeof(hash_node_t*) * size); stat_used_hashtables++; return hashtable; }
/* * The following functions below implement printing / dumping of statistical * information. */ static void stat_dump_mem_contents(stat_mem_block_t *block, uint16_t cols) { unsigned char *buffer = (unsigned char *)mem_a(cols); unsigned char *memory = (unsigned char *)(block + 1); size_t i; for (i = 0; i < block->size; i++) { if (!(i % 16)) { if (i != 0) con_out(" %s\n", buffer); con_out(" 0x%08X: ", i); } con_out(" %02X", memory[i]); buffer[i % cols] = ((memory[i] < 0x20) || (memory[i] > 0x7E)) ? '.' : memory[i]; buffer[(i % cols) + 1] = '\0'; } while ((i % cols) != 0) { con_out(" "); i++; } con_out(" %s\n", buffer); mem_d(buffer); }
/* * The reallocate function for resizing vectors. */ void _util_vec_grow(void **a, size_t i, size_t s) { vector_t *d = vec_meta(*a); size_t m = 0; stat_size_entry_t *e = NULL; void *p = NULL; if (*a) { m = 2 * d->allocated + i; p = mem_r(d, s * m + sizeof(vector_t)); } else { m = i + 1; p = mem_a(s * m + sizeof(vector_t)); ((vector_t*)p)->used = 0; stat_used_vectors++; } if (!stat_size_vectors) stat_size_vectors = stat_size_new(); if ((e = stat_size_get(stat_size_vectors, s))) { e->value ++; } else { stat_size_put(stat_size_vectors, s, 1); /* start off with 1 */ stat_type_vectors++; } *a = (vector_t*)p + 1; vec_meta(*a)->allocated = m; }
/* * The reallocate function for resizing vectors. */ void _util_vec_grow(void **a, size_t i, size_t s) { vector_t *d = (vector_t*)((char *)*a - IDENT_VEC_TOP); size_t m = 0; stat_size_entry_t *e = NULL; void *p = NULL; if (*a) { m = 2 * d->allocated + i; p = mem_r(d, s * m + IDENT_VEC_TOP); } else { m = i + 1; p = mem_a(s * m + IDENT_VEC_TOP); ((vector_t*)p)->used = 0; stat_used_vectors++; } if (!stat_size_vectors) stat_size_vectors = stat_size_new(); if ((e = stat_size_get(stat_size_vectors, s))) { e->value ++; } else { stat_size_put(stat_size_vectors, s, 1); /* start off with 1 */ stat_type_vectors++; } d = (vector_t*)p; d->allocated = m; memcpy(d + 1, IDENT_VEC, IDENT_SIZE); *a = (void *)((char *)d + IDENT_VEC_TOP); }
int platform_vasprintf(char **dat, const char *fmt, va_list args) { int ret; int len; char *tmp = NULL; char buf[128]; va_list cpy; va_copy(cpy, args); len = vsnprintf(buf, sizeof(buf), fmt, cpy); va_end (cpy); if (len < (int)sizeof(buf)) { *dat = util_strdup(buf); return len; } tmp = (char*)mem_a(len + 1); if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) { mem_d(tmp); *dat = NULL; return -1; } *dat = tmp; return len; }
code_t *code_init() { static lex_ctx_t empty_ctx = {0, 0, 0}; static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}}; static prog_section_statement_t empty_statement = {0,{0},{0},{0}}; static prog_section_def_t empty_def = {0, 0, 0}; code_t *code = (code_t*)mem_a(sizeof(code_t)); int i = 0; memset(code, 0, sizeof(code_t)); code->entfields = 0; code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024); /* * The way progs.dat is suppose to work is odd, there needs to be * some null (empty) statements, functions, and 28 globals */ for(; i < 28; i++) vec_push(code->globals, 0); vec_push(code->chars, '\0'); vec_push(code->functions, empty_function); code_push_statement(code, &empty_statement, empty_ctx); vec_push(code->defs, empty_def); vec_push(code->fields, empty_def); return code; }
static void stat_size_put(stat_size_table_t table, size_t key, size_t value) { size_t hash = (key % ST_SIZE); while (table[hash] && table[hash]->key != key) hash = (hash + 1) % ST_SIZE; table[hash] = (stat_size_entry_t*)mem_a(sizeof(stat_size_entry_t)); table[hash]->key = key; table[hash]->value = value; }
static pak_file_t *pak_open_read(const char *file) { pak_file_t *pak; size_t itr; if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t)))) return NULL; if (!(pak->handle = fs_file_open(file, "rb"))) { mem_d(pak); return NULL; } pak->directories = NULL; pak->insert = false; /* read doesn't allow insert */ memset (&pak->header, 0, sizeof(pak_header_t)); fs_file_read (&pak->header, sizeof(pak_header_t), 1, pak->handle); util_endianswap(&pak->header.magic, 1, sizeof(pak->header.magic)); util_endianswap(&pak->header.diroff, 1, sizeof(pak->header.diroff)); util_endianswap(&pak->header.dirlen, 1, sizeof(pak->header.dirlen)); /* * Every PAK file has "PACK" stored as FOURCC data in the * header. If this data cannot compare (as checked here), it's * probably not a PAK file. */ if (pak->header.magic != PAK_FOURCC) { fs_file_close(pak->handle); mem_d (pak); return NULL; } /* * Time to read in the directory handles and prepare the directories * vector. We're going to be reading some the file inwards soon. */ fs_file_seek(pak->handle, pak->header.diroff, FS_FILE_SEEK_SET); /* * Read in all directories from the PAK file. These are considered * to be the "file entries". */ for (itr = 0; itr < pak->header.dirlen / 64; itr++) { pak_directory_t dir; fs_file_read (&dir, sizeof(pak_directory_t), 1, pak->handle); /* Don't translate name - it's just an array of bytes. */ util_endianswap(&dir.pos, 1, sizeof(dir.pos)); util_endianswap(&dir.len, 1, sizeof(dir.len)); vec_push(pak->directories, dir); } return pak; }
void correct_add(correct_trie_t* table, size_t ***size, const char *ident) { size_t *data = NULL; const char *add = ident; if (!correct_update(&table, add)) { data = (size_t*)mem_a(sizeof(size_t)); *data = 1; vec_push((*size), data); correct_trie_set(table, add, data); } }
static GMQCC_INLINE void correct_trie_set(correct_trie_t *t, const char *key, void * const value) { const unsigned char *data = (const unsigned char*)key; while (*data) { if (!t->entries) { t->entries = (correct_trie_t*)mem_a(sizeof(correct_trie_t)*(sizeof(correct_alpha)-1)); memset(t->entries, 0, sizeof(correct_trie_t)*(sizeof(correct_alpha)-1)); } t = t->entries + correct_alpha_index[*data]; ++data; } t->value = value; }
/* * Some string utility functions, because strdup uses malloc, and we want * to track all memory (without replacing malloc). */ char *util_strdup(const char *s) { size_t len = 0; char *ptr = NULL; if (!s) return NULL; if ((len = strlen(s)) && (ptr = mem_a(len+1))) { memcpy(ptr, s, len); ptr[len] = '\0'; } return ptr; }
/* * When given a string like "a/b/c/d/e/file" * this function will handle the creation of * the directory structure, included nested * directories. */ static void pak_tree_build(const char *entry) { char *directory; char *elements[28]; char *pathsplit; char *token; size_t itr; size_t jtr; pathsplit = (char *)mem_a(56); directory = (char *)mem_a(56); memset(pathsplit, 0, 56); util_strncpy(directory, entry, 56); for (itr = 0; (token = pak_tree_sep(&directory, "/")) != NULL; itr++) { elements[itr] = token; } for (jtr = 0; jtr < itr - 1; jtr++) { util_strcat(pathsplit, elements[jtr]); util_strcat(pathsplit, "/"); if (fs_dir_make(pathsplit)) { mem_d(pathsplit); mem_d(directory); /* TODO: undo on fail */ return; } } mem_d(pathsplit); mem_d(directory); }
static hash_node_t *_util_htnewpair(const char *key, void *value) { hash_node_t *node; if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t)))) return NULL; if (!(node->key = util_strdupe(key))) { mem_d(node); return NULL; } node->value = value; node->next = NULL; return node; }
/* * Extraction abilities. These work as you expect them to. */ static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) { pak_directory_t *dir = NULL; unsigned char *dat = NULL; char *local = NULL; FILE *out = NULL; if (!pak_exists(pak, file, &dir)) { return false; } if (!(dat = (unsigned char *)mem_a(dir->len))) goto err; /* * Generate the directory structure / tree that will be required * to store the extracted file. */ pak_tree_build(file); /* TODO portable path seperators */ util_asprintf(&local, "%s/%s", outdir, file); /* * Now create the file, if this operation fails. Then abort * It shouldn't fail though. */ if (!(out = fs_file_open(local, "wb"))) goto err; /* free memory for directory string */ mem_d(local); /* read */ if (fs_file_seek (pak->handle, dir->pos, SEEK_SET) != 0) goto err; fs_file_read (dat, 1, dir->len, pak->handle); fs_file_write(dat, 1, dir->len, out); fs_file_close(out); mem_d(dat); return true; err: if (dat) mem_d(dat); if (out) fs_file_close(out); return false; }
static pak_file_t *pak_open_write(const char *file) { pak_file_t *pak; if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t)))) return NULL; /* * Generate the required directory structure / tree for * writing this PAK file too. */ pak_tree_build(file); if (!(pak->handle = fs_file_open(file, "wb"))) { /* * The directory tree that was created, needs to be * removed entierly if we failed to open a file. */ /* TODO backup directory clean */ mem_d(pak); return NULL; } memset(&(pak->header), 0, sizeof(pak_header_t)); /* * We're in "insert" mode, we need to do things like header * "patching" and writing the directories at the end of the * file. */ pak->insert = true; pak->header.magic = PAK_FOURCC; /* on BE systems we need to swap the byte order of the FOURCC */ util_endianswap(&pak->header.magic, 1, sizeof(uint32_t)); /* * We need to write out the header since files will be wrote out to * this even with directory entries, and that not wrote. The header * will need to be patched in later with a file_seek, and overwrite, * we could use offsets and other trickery. This is just easier. */ fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle); return pak; }
/* * Implements libc getline for systems that don't have it, which is * assmed all. This works the same as getline(). */ int util_getline(char **lineptr, size_t *n, FILE *stream) { int chr; int ret; char *pos; if (!lineptr || !n || !stream) return -1; if (!*lineptr) { if (!(*lineptr = (char*)mem_a((*n=64)))) return -1; } chr = *n; pos = *lineptr; for (;;) { int c = getc(stream); if (chr < 2) { *n += (*n > 16) ? *n : 64; chr = *n + *lineptr - pos; if (!(*lineptr = (char*)mem_r(*lineptr,*n))) return -1; pos = *n - chr + *lineptr; } if (ferror(stream)) return -1; if (c == EOF) { if (pos == *lineptr) return -1; else break; } *pos++ = c; chr--; if (c == '\n') break; } *pos = '\0'; return (ret = pos - *lineptr); }
fold_t *fold_init(parser_t *parser) { fold_t *fold = (fold_t*)mem_a(sizeof(fold_t)); fold->parser = parser; fold->imm_float = NULL; fold->imm_vector = NULL; fold->imm_string = NULL; fold->imm_string_untranslate = util_htnew(FOLD_STRING_UNTRANSLATE_HTSIZE); fold->imm_string_dotranslate = util_htnew(FOLD_STRING_DOTRANSLATE_HTSIZE); /* * prime the tables with common constant values at constant * locations. */ (void)fold_constgen_float (fold, 0.0f); (void)fold_constgen_float (fold, 1.0f); (void)fold_constgen_float (fold, -1.0f); (void)fold_constgen_float (fold, 2.0f); (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f)); (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f)); return fold; }
static int qc_strcat(qc_program_t *prog) { char *buffer; size_t len1, len2; qcany_t *str1, *str2; qcany_t out; const char *cstr1; const char *cstr2; CheckArgs(2); str1 = GetArg(0); str2 = GetArg(1); cstr1 = prog_getstring(prog, str1->string); cstr2 = prog_getstring(prog, str2->string); len1 = strlen(cstr1); len2 = strlen(cstr2); buffer = (char*)mem_a(len1 + len2 + 1); memcpy(buffer, cstr1, len1); memcpy(buffer+len1, cstr2, len2+1); out.string = prog_tempstring(prog, buffer); mem_d(buffer); Return(out); return 0; }
qc_program* prog_load(const char *filename) { qc_program *prog; prog_header header; FILE *file; file = util_fopen(filename, "rb"); if (!file) return NULL; if (fread(&header, sizeof(header), 1, file) != 1) { loaderror("failed to read header from '%s'", filename); fclose(file); return NULL; } if (header.version != 6) { loaderror("header says this is a version %i progs, we need version 6\n", header.version); fclose(file); return NULL; } prog = (qc_program*)mem_a(sizeof(qc_program)); if (!prog) { fclose(file); printf("failed to allocate program data\n"); return NULL; } memset(prog, 0, sizeof(*prog)); prog->entityfields = header.entfield; prog->crc16 = header.crc16; prog->filename = util_strdup(filename); if (!prog->filename) { loaderror("failed to store program name"); goto error; } #define read_data(hdrvar, progvar, reserved) \ if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ loaderror("seek failed"); \ goto error; \ } \ if (fread(vec_add(prog->progvar, header.hdrvar.length + reserved), \ sizeof(*prog->progvar), \ header.hdrvar.length, file) \ != header.hdrvar.length) \ { \ loaderror("read failed"); \ goto error; \ } #define read_data1(x) read_data(x, x, 0) #define read_data2(x, y) read_data(x, x, y) read_data (statements, code, 0); read_data1(defs); read_data1(fields); read_data1(functions); read_data1(strings); read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */ fclose(file); /* profile counters */ memset(vec_add(prog->profile, vec_size(prog->code)), 0, sizeof(prog->profile[0]) * vec_size(prog->code)); /* Add tempstring area */ prog->tempstring_start = vec_size(prog->strings); prog->tempstring_at = vec_size(prog->strings); memset(vec_add(prog->strings, 16*1024), 0, 16*1024); /* spawn the world entity */ vec_push(prog->entitypool, true); memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0])); prog->entities = 1; return prog; error: if (prog->filename) mem_d(prog->filename); vec_free(prog->code); vec_free(prog->defs); vec_free(prog->fields); vec_free(prog->functions); vec_free(prog->strings); vec_free(prog->globals); vec_free(prog->entitydata); vec_free(prog->entitypool); mem_d(prog); return NULL; }
/* * A tiny size_t key-value hashtbale for tracking vector and hashtable * sizes. We can use it for other things too, if we need to. This is * very TIGHT, and efficent in terms of space though. */ static stat_size_table_t stat_size_new(void) { return (stat_size_table_t)memset( mem_a(sizeof(stat_size_entry_t*) * ST_SIZE), 0, ST_SIZE * sizeof(stat_size_entry_t*) ); }
static GMQCC_INLINE void correct_pool_new(void) { correct_pool_addr = 0; correct_pool_this = (unsigned char *)mem_a(CORRECT_POOL_SIZE); vec_push(correct_pool_data, correct_pool_this); }
static task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) { /* a page should be enough */ char fullfile[4096]; size_t filepadd = 0; fs_file_t *tempfile = NULL; task_template_t *tmpl = NULL; util_snprintf(fullfile, sizeof(fullfile), "%s/%s", dir, file); tempfile = fs_file_open(fullfile, "r"); tmpl = (task_template_t*)mem_a(sizeof(task_template_t)); task_template_nullify(tmpl); /* * Create some padding for the printing to align the * printing of the rules file to the console. */ if ((filepadd = strlen(fullfile)) > pad[1]) pad[1] = filepadd; tmpl->rulesfile = util_strdup(fullfile); /* * Esnure the file even exists for the task, this is pretty useless * to even do. */ if (!tempfile) { con_err("template file: %s does not exist or invalid permissions\n", file ); goto failure; } if (!task_template_parse(file, tmpl, tempfile, pad)) { con_err("template parse error: error during parsing\n"); goto failure; } /* * Regardless procedure type, the following tags must exist: * D * T * C * I */ if (!tmpl->description) { con_err("template compile error: %s missing `D:` tag\n", file); goto failure; } if (!tmpl->proceduretype) { con_err("template compile error: %s missing `T:` tag\n", file); goto failure; } if (!tmpl->compileflags) { con_err("template compile error: %s missing `C:` tag\n", file); goto failure; } if (!tmpl->sourcefile) { con_err("template compile error: %s missing `I:` tag\n", file); goto failure; } /* * Now lets compile the template, compilation is really just * the process of validating the input. */ if (!strcmp(tmpl->proceduretype, "-compile")) { if (tmpl->executeflags) con_err("template compile warning: %s erroneous tag `E:` when only compiling\n", file); if (tmpl->comparematch) con_err("template compile warning: %s erroneous tag `M:` when only compiling\n", file); goto success; } else if (!strcmp(tmpl->proceduretype, "-execute")) { if (!tmpl->executeflags) { /* default to $null */ tmpl->executeflags = util_strdup("$null"); } if (!tmpl->comparematch) { con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file); goto failure; } } else if (!strcmp(tmpl->proceduretype, "-fail")) { if (tmpl->executeflags) con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file); if (tmpl->comparematch) con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file); } else if (!strcmp(tmpl->proceduretype, "-diagnostic")) { if (tmpl->executeflags) con_err("template compile warning: %s erroneous tag `E:` when only diagnostic\n", file); if (!tmpl->comparematch) { con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file); goto failure; } } else if (!strcmp(tmpl->proceduretype, "-pp")) { if (tmpl->executeflags) con_err("template compile warning: %s erroneous tag `E:` when only preprocessing\n", file); if (!tmpl->comparematch) { con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file); goto failure; } } else { con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype); goto failure; } success: fs_file_close(tempfile); return tmpl; failure: /* * The file might not exist and we jump here when that doesn't happen * so the check to see if it's not null here is required. */ if (tempfile) fs_file_close(tempfile); mem_d (tmpl); return NULL; }
/* * A fast space efficent trie for a dictionary of identifiers. This is * faster than a hashtable for one reason. A hashtable itself may have * fast constant lookup time, but the hash itself must be very fast. We * have one of the fastest hash functions for strings, but if you do a * lost of hashing (which we do, almost 3 million hashes per identifier) * a hashtable becomes slow. */ correct_trie_t* correct_trie_new() { correct_trie_t *t = (correct_trie_t*)mem_a(sizeof(correct_trie_t)); t->value = NULL; t->entries = NULL; return t; }
qc_program_t* prog_load(const char *filename, bool skipversion) { prog_header_t header; qc_program_t *prog; FILE *file = fopen(filename, "rb"); /* we need all those in order to support INSTR_STATE: */ bool has_self = false, has_time = false, has_think = false, has_nextthink = false, has_frame = false; if (!file) return nullptr; if (fread(&header, sizeof(header), 1, file) != 1) { loaderror("failed to read header from '%s'", filename); fclose(file); return nullptr; } util_swap_header(header); if (!skipversion && header.version != 6) { loaderror("header says this is a version %i progs, we need version 6\n", header.version); fclose(file); return nullptr; } prog = (qc_program_t*)mem_a(sizeof(qc_program_t)); if (!prog) { fclose(file); fprintf(stderr, "failed to allocate program data\n"); return nullptr; } memset(prog, 0, sizeof(*prog)); prog->entityfields = header.entfield; prog->crc16 = header.crc16; prog->filename = util_strdup(filename); if (!prog->filename) { loaderror("failed to store program name"); goto error; } #define read_data(hdrvar, progvar, reserved) \ if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ loaderror("seek failed"); \ goto error; \ } \ prog->progvar.resize(header.hdrvar.length + reserved); \ if (fread( \ &prog->progvar[0], \ sizeof(prog->progvar[0]), \ header.hdrvar.length, \ file \ )!= header.hdrvar.length \ ) { \ loaderror("read failed"); \ goto error; \ } #define read_data1(x) read_data(x, x, 0) #define read_data2(x, y) read_data(x, x, y) read_data (statements, code, 0); read_data1(defs); read_data1(fields); read_data1(functions); read_data1(strings); read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */ util_swap_statements(prog->code); util_swap_defs_fields(prog->defs); util_swap_defs_fields(prog->fields); util_swap_functions(prog->functions); util_swap_globals(prog->globals); fclose(file); /* profile counters */ memset(vec_add(prog->profile, prog->code.size()), 0, sizeof(prog->profile[0]) * prog->code.size()); /* Add tempstring area */ prog->tempstring_start = prog->strings.size(); prog->tempstring_at = prog->strings.size(); prog->strings.resize(prog->strings.size() + 16*1024, '\0'); /* spawn the world entity */ vec_push(prog->entitypool, true); memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0])); prog->entities = 1; /* cache some globals and fields from names */ for (auto &it : prog->defs) { const char *name = prog_getstring(prog, it.name); if (!strcmp(name, "self")) { prog->cached_globals.self = it.offset; has_self = true; } else if (!strcmp(name, "time")) { prog->cached_globals.time = it.offset; has_time = true; } } for (auto &it : prog->fields) { const char *name = prog_getstring(prog, it.name); if (!strcmp(name, "think")) { prog->cached_fields.think = it.offset; has_think = true; } else if (!strcmp(name, "nextthink")) { prog->cached_fields.nextthink = it.offset; has_nextthink = true; } else if (!strcmp(name, "frame")) { prog->cached_fields.frame = it.offset; has_frame = true; } } if (has_self && has_time && has_think && has_nextthink && has_frame) prog->supports_state = true; return prog; error: if (prog->filename) mem_d(prog->filename); vec_free(prog->entitydata); vec_free(prog->entitypool); mem_d(prog); fclose(file); return nullptr; }
void *code_t::operator new(std::size_t bytes) { return mem_a(bytes); }
/* * Insertion functions (the opposite of extraction). Yes for generating * PAKs. */ static bool pak_insert_one(pak_file_t *pak, const char *file) { pak_directory_t dir; unsigned char *dat; long len; FILE *fp; /* * We don't allow insertion on files that already exist within the * pak file. Weird shit can happen if we allow that ;). We also * don't allow insertion if the pak isn't opened in write mode. */ if (!pak || !file || !pak->insert || pak_exists(pak, file, NULL)) return false; if (!(fp = fs_file_open(file, "rb"))) return false; /* * Calculate the total file length, since it will be wrote to * the directory entry, and the actual contents of the file * to the PAK file itself. */ if (fs_file_seek(fp, 0, SEEK_END) != 0 || ((len = fs_file_tell(fp)) < 0)) goto err; if (fs_file_seek(fp, 0, SEEK_SET) != 0) goto err; dir.len = len; dir.pos = fs_file_tell(pak->handle); /* * We're limited to 56 bytes for a file name string, that INCLUDES * the directory and '/' seperators. */ if (strlen(file) >= 56) goto err; util_strncpy(dir.name, file, strlen(file)); /* * Allocate some memory for loading in the data that will be * redirected into the PAK file. */ if (!(dat = (unsigned char *)mem_a(dir.len))) goto err; fs_file_read (dat, dir.len, 1, fp); fs_file_close(fp); fs_file_write(dat, dir.len, 1, pak->handle); /* * Now add the directory to the directories vector, so pak_close * can actually write it. */ vec_push(pak->directories, dir); return true; err: fs_file_close(fp); return false; }