8d5b217266
r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines removed MAX_VISEDICTS limit. PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default. TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW. added android multitouch emulation for windows/rawinput (in_simulatemultitouch). split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature. now using utf-8 for windows consoles. qcc warnings/errors now give clickable console links for quick+easy editing. disabled menutint when the currently active item changes contrast or gamma (for OneManClan). Added support for drawfont/drawfontscale. tweaked the qcvm a little to reduce the number of pointers. .doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up. windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings. fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen. editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts. Added support for .framegroups files for psk(psa) and iqm formats. True support for ezquake's colour codes. Mutually exclusive with background colours. path command output slightly more readable. added support for digest_hex (MD4, SHA1, CRC16). skingroups now colourmap correctly. Fix terrain colour hints, and litdata from the wrong bsp. fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported). remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother. fix v *= v.x and similar opcodes. fteqcc: fixed support for áéíóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported. fteqcc: fixed '#if 1 == 3 && 4' parsing. fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable. fteqcc: copyright message now includes compile date instead. fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile. fteqccgui: the output window is now focused and scrolls down as compilation progresses. pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue. rewrote prespawn/modelist/soundlist code. server tracks progress now. ------------------------------------------------------------------------ git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
2225 lines
43 KiB
C
2225 lines
43 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// Z_zone.c
|
|
|
|
#include "quakedef.h"
|
|
#ifdef _WIN32
|
|
#include "winquake.h"
|
|
#endif
|
|
|
|
#undef malloc
|
|
#undef free
|
|
|
|
#define NOZONE
|
|
#define NOCACHE
|
|
|
|
void Cache_FreeLow (int new_low_hunk);
|
|
void Cache_FreeHigh (int new_high_hunk);
|
|
|
|
#ifdef _DEBUG
|
|
//#define MEMDEBUG 8192 //Debugging adds sentinels (the number is the size - I have the ram)
|
|
#endif
|
|
|
|
//must be multiple of 4.
|
|
#define TEMPDEBUG 4
|
|
#define ZONEDEBUG 4
|
|
#define HUNKDEBUG 4
|
|
#define CACHEDEBUG 4
|
|
|
|
//these need to be defined because it makes some bits of code simpler
|
|
#ifndef HUNKDEBUG
|
|
#define HUNKDEBUG 0
|
|
#endif
|
|
#ifndef ZONEDEBUG
|
|
#define ZONEDEBUG 0
|
|
#endif
|
|
#ifndef TEMPDEBUG
|
|
#define TEMPDEBUG 0
|
|
#endif
|
|
#ifndef CACHEDEBUG
|
|
#define CACHEDEBUG 0
|
|
#endif
|
|
|
|
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0
|
|
qbyte sentinalkey;
|
|
#endif
|
|
|
|
#define TAGLESS 1
|
|
|
|
int zmemtotal;
|
|
int zmemdelta;
|
|
|
|
typedef struct memheader_s {
|
|
int size;
|
|
int tag;
|
|
} memheader_t;
|
|
|
|
typedef struct zone_s {
|
|
struct zone_s *next;
|
|
struct zone_s *pvdn; // down if first, previous if not
|
|
memheader_t mh;
|
|
} zone_t;
|
|
zone_t *zone_head;
|
|
#ifdef MULTITHREAD
|
|
void *zonelock;
|
|
#endif
|
|
|
|
#if 0
|
|
static void Z_DumpTree(void)
|
|
{
|
|
zone_t *zone;
|
|
zone_t *nextlist;
|
|
zone_t *t;
|
|
zone_t *prev;
|
|
|
|
zone = zone_head;
|
|
while(zone)
|
|
{
|
|
nextlist = zone->pvdn;
|
|
|
|
fprintf(stderr, " +-+ %016x (tag: %08x)\n", zone, zone->mh.tag);
|
|
|
|
prev = zone;
|
|
t = zone->next;
|
|
while(t)
|
|
{
|
|
if (t->pvdn != prev)
|
|
fprintf(stderr, "Previous link failure\n");
|
|
|
|
prev = t;
|
|
t = t->next;
|
|
}
|
|
|
|
while(zone)
|
|
{
|
|
fprintf(stderr, " +-- %016x\n", zone);
|
|
|
|
zone = zone->next;
|
|
}
|
|
|
|
zone = nextlist;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void *VARGS Z_TagMalloc(int size, int tag)
|
|
{
|
|
zone_t *zone;
|
|
|
|
zone = (zone_t *)malloc(size + sizeof(zone_t));
|
|
if (!zone)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
Q_memset(zone, 0, size + sizeof(zone_t));
|
|
zone->mh.tag = tag;
|
|
zone->mh.size = size;
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
|
|
#if 0
|
|
fprintf(stderr, "Before alloc:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
if (zone_head == NULL)
|
|
zone_head = zone;
|
|
else
|
|
{
|
|
zone_t *s = zone_head;
|
|
|
|
while (s && s->mh.tag != tag)
|
|
s = s->pvdn;
|
|
|
|
if (s)
|
|
{ // tag match
|
|
zone->next = s->next;
|
|
if (s->next)
|
|
s->next->pvdn = zone;
|
|
zone->pvdn = s;
|
|
s->next = zone;
|
|
}
|
|
else
|
|
{
|
|
zone->pvdn = zone_head;
|
|
// if (s->next)
|
|
// s->next->pvdn = zone;
|
|
zone_head = zone;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
fprintf(stderr, "After alloc:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
return (void *)(zone + 1);
|
|
}
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
void *ZF_MallocNamed(int size, char *file, int line)
|
|
{
|
|
return _calloc_dbg(size, 1, _NORMAL_BLOCK, file, line);
|
|
}
|
|
void *Z_MallocNamed(int size, char *file, int line)
|
|
{
|
|
void *mem = ZF_MallocNamed(size, file, line);
|
|
if (!mem)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
#else
|
|
void *ZF_Malloc(int size)
|
|
{
|
|
return calloc(size, 1);
|
|
}
|
|
void *Z_Malloc(int size)
|
|
{
|
|
void *mem = ZF_Malloc(size);
|
|
if (!mem)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
#endif
|
|
|
|
void VARGS Z_TagFree(void *mem)
|
|
{
|
|
zone_t *zone = ((zone_t *)mem) - 1;
|
|
|
|
#if 0
|
|
fprintf(stderr, "Before free:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
if (zone->next)
|
|
zone->next->pvdn = zone->pvdn;
|
|
|
|
if (zone->pvdn && zone->pvdn->mh.tag == zone->mh.tag)
|
|
zone->pvdn->next = zone->next;
|
|
else
|
|
{ // zone is first entry in a tag list
|
|
zone_t *s = zone_head;
|
|
|
|
if (zone != s)
|
|
{ // traverse and update down list
|
|
while (s->pvdn != zone)
|
|
s = s->pvdn;
|
|
|
|
if (zone->next)
|
|
s->pvdn = zone->next;
|
|
else
|
|
s->pvdn = zone->pvdn;
|
|
}
|
|
}
|
|
|
|
if (zone == zone_head)
|
|
{ // freeing head node so update head pointer
|
|
if (zone->next) // move to next, pvdn should be maintained properly
|
|
zone_head = zone->next;
|
|
else // no more entries with this tag so move head down
|
|
zone_head = zone->pvdn;
|
|
}
|
|
|
|
#if 0
|
|
fprintf(stderr, "After free:\n");
|
|
Z_DumpTree();
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
free(zone);
|
|
}
|
|
|
|
void VARGS Z_Free(void *mem)
|
|
{
|
|
free(mem);
|
|
}
|
|
|
|
void VARGS Z_FreeTags(int tag)
|
|
{
|
|
zone_t *taglist;
|
|
zone_t *t;
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_LockMutex(zonelock);
|
|
#endif
|
|
if (zone_head)
|
|
{
|
|
if (zone_head->mh.tag == tag)
|
|
{ // just pull off the head
|
|
taglist = zone_head;
|
|
zone_head = zone_head->pvdn;
|
|
}
|
|
else
|
|
{ // search for tag list and isolate it
|
|
zone_t *z;
|
|
z = zone_head;
|
|
while (z->pvdn != NULL && z->pvdn->mh.tag != tag)
|
|
z = z->pvdn;
|
|
|
|
if (z->pvdn == NULL)
|
|
taglist = NULL;
|
|
else
|
|
{
|
|
taglist = z->pvdn;
|
|
z->pvdn = z->pvdn->pvdn;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
taglist = NULL;
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
Sys_UnlockMutex(zonelock);
|
|
#endif
|
|
|
|
// actually free list
|
|
while (taglist != NULL)
|
|
{
|
|
t = taglist->next;
|
|
free(taglist);
|
|
taglist = t;
|
|
}
|
|
}
|
|
|
|
/*
|
|
void *Z_Realloc(void *data, int newsize)
|
|
{
|
|
memheader_t *memref;
|
|
|
|
if (!data)
|
|
return Z_Malloc(newsize);
|
|
|
|
memref = ((memheader_t *)data) - 1;
|
|
|
|
if (memref[0].tag != TAGLESS)
|
|
{ // allocate a new block and copy since we need to maintain the lists
|
|
zone_t *zone = ((zone_t *)data) - 1;
|
|
int size = zone->mh.size;
|
|
if (size != newsize)
|
|
{
|
|
void *newdata = Z_Malloc(newsize);
|
|
|
|
if (size > newsize)
|
|
size = newsize;
|
|
memcpy(newdata, data, size);
|
|
|
|
Z_Free(data);
|
|
data = newdata;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int oldsize = memref[0].size;
|
|
memref = realloc(memref, newsize + sizeof(memheader_t));
|
|
memref->size = newsize;
|
|
if (newsize > oldsize)
|
|
memset((qbyte *)memref + sizeof(memheader_t) + oldsize, 0, newsize - oldsize);
|
|
data = ((memheader_t *)memref) + 1;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
*/
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
void *BZF_MallocNamed(int size, char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc.
|
|
{
|
|
void *mem;
|
|
mem = _malloc_dbg(size, _NORMAL_BLOCK, file, line);
|
|
if (mem)
|
|
{
|
|
zmemdelta += size;
|
|
zmemtotal += size;
|
|
}
|
|
return mem;
|
|
}
|
|
#else
|
|
void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc.
|
|
{
|
|
void *mem;
|
|
mem = malloc(size);
|
|
if (mem)
|
|
{
|
|
zmemdelta += size;
|
|
zmemtotal += size;
|
|
}
|
|
return mem;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
void *BZ_MallocNamed(int size, char *file, int line) //BZ_MallocNamed but allowed to fail - like straight malloc.
|
|
{
|
|
void *mem = BZF_MallocNamed(size, file, line);
|
|
if (!mem)
|
|
Sys_Error("BZ_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
#else
|
|
void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures.
|
|
{
|
|
void *mem = BZF_Malloc(size);
|
|
if (!mem)
|
|
Sys_Error("BZ_Malloc: Failed on allocation of %i bytes", size);
|
|
|
|
return mem;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
void *BZF_ReallocNamed(void *data, int newsize, char *file, int line)
|
|
{
|
|
return _realloc_dbg(data, newsize, _NORMAL_BLOCK, file, line);
|
|
}
|
|
|
|
void *BZ_ReallocNamed(void *data, int newsize, char *file, int line)
|
|
{
|
|
void *mem = BZF_ReallocNamed(data, newsize, file, line);
|
|
|
|
if (!mem)
|
|
Sys_Error("BZ_Realloc: Failed on reallocation of %i bytes", newsize);
|
|
|
|
return mem;
|
|
}
|
|
#else
|
|
void *BZF_Realloc(void *data, int newsize)
|
|
{
|
|
return realloc(data, newsize);
|
|
}
|
|
|
|
void *BZ_Realloc(void *data, int newsize)
|
|
{
|
|
void *mem = BZF_Realloc(data, newsize);
|
|
|
|
if (!mem)
|
|
Sys_Error("BZ_Realloc: Failed on reallocation of %i bytes", newsize);
|
|
|
|
return mem;
|
|
}
|
|
#endif
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
|
|
#if 0 //NOZONE //zone memory is for small dynamic things.
|
|
/*
|
|
void *Z_TagMalloc(int size, int tag)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void *Z_Malloc(int size)
|
|
{
|
|
qbyte *buf;
|
|
buf = Z_TagMalloc(size, 1);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
Q_memset(buf, 0, size);
|
|
return buf;
|
|
}
|
|
|
|
void Z_Free (void *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
void Z_FreeTags (void *buf)
|
|
{
|
|
free(buf);
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define ZONEID 0x1d4a11
|
|
|
|
#define ZONESENTINAL 0xdeadbeaf
|
|
|
|
typedef struct zone_s {
|
|
// int sentinal1;
|
|
|
|
struct zone_s *next;
|
|
struct zone_s *prev;
|
|
int size;
|
|
int tag;
|
|
|
|
// int sentinal2;
|
|
} zone_t;
|
|
zone_t *zone_head;
|
|
/*
|
|
void Z_CheckSentinals(void)
|
|
{
|
|
zone_t *zone;
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
if (zone->sentinal1 != ZONESENTINAL || zone->sentinal2 != ZONESENTINAL)
|
|
Sys_Error("Memory sentinal destroyed\n");
|
|
}
|
|
}*/
|
|
|
|
|
|
void VARGS Z_Free (void *c)
|
|
{
|
|
zone_t *nz;
|
|
nz = ((zone_t *)((char*)c-ZONEDEBUG))-1;
|
|
|
|
// Z_CheckSentinals();
|
|
|
|
#if ZONEDEBUG>0
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(nz+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += nz->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if (nz->sentinal1 != ZONESENTINAL || nz->sentinal2 != ZONESENTINAL)
|
|
// Sys_Error("zone was not z_malloced\n");
|
|
|
|
if (nz->next)
|
|
nz->next->prev = nz->prev;
|
|
if (nz->prev)
|
|
nz->prev->next = nz->next;
|
|
|
|
if (nz == zone_head)
|
|
zone_head = nz->next;
|
|
|
|
// Con_Printf("Free of %i bytes\n", nz->size);
|
|
|
|
free(nz);
|
|
}
|
|
|
|
void BZ_CheckSentinals(void *c)
|
|
{
|
|
#if ZONEDEBUG>0
|
|
zone_t *nz;
|
|
nz = ((zone_t *)((char*)c-ZONEDEBUG))-1;
|
|
|
|
// Z_CheckSentinals();
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(nz+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += nz->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", nz->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
//revive this function each time you get memory corruption and need to trace it.
|
|
void BZ_CheckAllSentinals(void)
|
|
{
|
|
zone_t *zone;
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(zone+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", zone->size);
|
|
}
|
|
buf+=ZONEDEBUG;
|
|
//app data
|
|
buf += zone->size;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("corrupt memory block (%i? bytes)\n", zone->size);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void VARGS Z_FreeTags(int tag)
|
|
{
|
|
zone_t *zone, *next;
|
|
for(zone = zone_head; zone; zone=next)
|
|
{
|
|
next = zone->next;
|
|
if (zone->tag == tag)
|
|
Z_Free((char*)(zone+1)+ZONEDEBUG);
|
|
}
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *Z_BaseTagMalloc (int size, int tag, qboolean clear, char *descrip, ...)
|
|
#else
|
|
void *Z_BaseTagMalloc (int size, int tag, qboolean clear)
|
|
#endif
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
va_list argptr;
|
|
char buffer[512];
|
|
#endif
|
|
void *buf;
|
|
zone_t *nt;
|
|
|
|
// Z_CheckSentinals();
|
|
//Con_Printf("Malloc of %i bytes\n", size);
|
|
//if (size>20)
|
|
//Con_Printf("Big malloc\n");
|
|
if (size <= 0)
|
|
Sys_Error ("Z_Malloc: size %i", size);
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
|
|
va_start (argptr, descrip);
|
|
vsprintf (buffer, descrip,argptr);
|
|
va_end (argptr);
|
|
|
|
nt = (zone_t*)malloc(size + sizeof(zone_t)+strlen(buffer)+1 + ZONEDEBUG*2);
|
|
#else
|
|
nt = (zone_t*)malloc(size + sizeof(zone_t)+ ZONEDEBUG*2);
|
|
#endif
|
|
if (!nt)
|
|
Sys_Error("Z_BaseTagMalloc: failed on allocation of %i bytes", size);
|
|
nt->next = zone_head;
|
|
nt->prev = NULL;
|
|
nt->size = size;
|
|
nt->tag = tag;
|
|
// nt->sentinal1 = ZONESENTINAL;
|
|
// nt->sentinal2 = ZONESENTINAL;
|
|
if (zone_head)
|
|
zone_head->prev = nt;
|
|
zone_head = nt;
|
|
buf = (void *)(nt+1);
|
|
|
|
#if ZONEDEBUG > 0
|
|
memset(buf, sentinalkey, ZONEDEBUG);
|
|
buf = (char*)buf+ZONEDEBUG;
|
|
memset((char*)buf+size, sentinalkey, ZONEDEBUG);
|
|
#endif
|
|
|
|
if (clear)
|
|
Q_memset(buf, 0, size);
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
strcpy((char *)(nt+1) + nt->size + ZONEDEBUG*2, buffer);
|
|
#endif
|
|
return buf;
|
|
}
|
|
void *VARGS Z_TagMalloc (int size, int tag)
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
return Z_BaseTagMalloc(size, tag, true, "");
|
|
#else
|
|
return Z_BaseTagMalloc(size, tag, true);
|
|
#endif
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *Z_MallocNamed (int size, char *file, int lineno)
|
|
{
|
|
qbyte *buf;
|
|
buf = Z_BaseTagMalloc(size, 1, true, "%s: %i", file, lineno);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
return buf;
|
|
}
|
|
#else
|
|
void *Z_Malloc(int size)
|
|
{
|
|
qbyte *buf;
|
|
buf = (qbyte*)Z_TagMalloc(size, 1);
|
|
if (!buf)
|
|
Sys_Error("Z_Malloc: Failed on allocation of %i bytes", size);
|
|
return buf;
|
|
}
|
|
|
|
|
|
|
|
void *BZ_Malloc(int size) //Doesn't clear. The expectation is a large file, rather than sensative data structures.
|
|
{
|
|
void *data = Z_BaseTagMalloc(size, 1, true);
|
|
if (!data)
|
|
Sys_Error("BZ_Malloc failed on %i bytes", size);
|
|
|
|
return data;
|
|
}
|
|
#endif
|
|
void *BZF_Malloc(int size) //BZ_Malloc but allowed to fail - like straight malloc.
|
|
{
|
|
#ifdef NAMEDMALLOCS
|
|
return Z_BaseTagMalloc(size, 1, false, "");
|
|
#else
|
|
return Z_BaseTagMalloc(size, 1, false);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
void *BZ_NamedRealloc(void *data, int newsize, char *file, int lineno)
|
|
#else
|
|
void *BZ_Realloc(void *data, int newsize)
|
|
#endif
|
|
{
|
|
zone_t *oldzone;
|
|
void *newdata;
|
|
#ifdef NAMEDMALLOCS
|
|
if (!data)
|
|
return Z_MallocNamed(newsize, file, lineno);
|
|
oldzone = ((zone_t *)((char *)data-ZONEDEBUG))-1;
|
|
if (oldzone->size == newsize)
|
|
return data;
|
|
newdata = Z_MallocNamed(newsize, file, lineno);
|
|
#else
|
|
if (!data)
|
|
return Z_Malloc(newsize);
|
|
oldzone = ((zone_t *)((char *)data-ZONEDEBUG))-1;
|
|
if (oldzone->size == newsize)
|
|
return data;
|
|
newdata = BZ_Malloc(newsize);
|
|
#endif
|
|
if (oldzone->size < newsize)
|
|
{
|
|
memcpy(newdata, data, oldzone->size);
|
|
memset((char *)newdata + oldzone->size, 0, newsize - oldzone->size);
|
|
}
|
|
else
|
|
memcpy(newdata, data, newsize);
|
|
BZ_Free(data);
|
|
|
|
return newdata;
|
|
}
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
Z_Free(data);
|
|
}
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
|
|
// Zone_Groups_f: prints out zones sorting into groups
|
|
// and tracking number of allocs and total group size as
|
|
// well as a group delta against the last Zone_Group_f call
|
|
#define ZONEGROUPS 64
|
|
void Zone_Groups_f(void)
|
|
{
|
|
zone_t *zone;
|
|
char *zonename[ZONEGROUPS];
|
|
int zonesize[ZONEGROUPS];
|
|
int zoneallocs[ZONEGROUPS];
|
|
static int zonelast[ZONEGROUPS];
|
|
int groups, i;
|
|
int allocated = 0;
|
|
|
|
// initialization
|
|
for (groups = 0; groups < ZONEGROUPS; groups++)
|
|
zonename[groups] = NULL;
|
|
|
|
groups = 0;
|
|
i = 0;
|
|
|
|
for (zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
char *czg = (char *)(zone+1) + zone->size+ZONEDEBUG*2;
|
|
// check against existing tracked groups
|
|
for (i = 0; i < groups; i++)
|
|
{
|
|
if (!strcmp(czg, zonename[i]))
|
|
{
|
|
// update stats for tracked group
|
|
zonesize[i] += zone->size;
|
|
zoneallocs[i]++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (groups == i) // no existing group found
|
|
{
|
|
// track new zone group
|
|
zonename[groups] = czg;
|
|
zonesize[groups] = zone->size;
|
|
zoneallocs[groups] = 1;
|
|
groups++;
|
|
|
|
// max groups bounds check
|
|
if (groups >= ZONEGROUPS)
|
|
{
|
|
groups = ZONEGROUPS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// print group statistics
|
|
for (i = 0; i < groups; i++)
|
|
{
|
|
allocated += zonesize[i];
|
|
Con_Printf("%s, size: %i, allocs: %i, delta: %i\n", zonename[i], zonesize[i], zoneallocs[i], zonesize[i] - zonelast[i]);
|
|
zonelast[i] = zonesize[i]; // update delta tracking for next call
|
|
}
|
|
|
|
Con_Printf("Total: %i bytes\n", allocated);
|
|
}
|
|
#endif
|
|
|
|
void Zone_Print_f(void)
|
|
{
|
|
int overhead=0;
|
|
int allocated = 0;
|
|
int blocks = 0;
|
|
int futurehide = false;
|
|
int minsize = 0;
|
|
zone_t *zone;
|
|
#if ZONEDEBUG > 0
|
|
#ifdef NAMEDMALLOCS
|
|
int i;
|
|
qbyte *sent;
|
|
#endif
|
|
qboolean testsent = false;
|
|
if (*Cmd_Argv(1) == 't')
|
|
{
|
|
Con_Printf("Testing Zone sentinels\n");
|
|
testsent = true;
|
|
}
|
|
else
|
|
#endif
|
|
if (*Cmd_Argv(1) == 'h')
|
|
futurehide = true;
|
|
else if (*Cmd_Argv(1))
|
|
minsize = atoi(Cmd_Argv(1));
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
blocks++;
|
|
allocated+= zone->size;
|
|
|
|
#ifdef NAMEDMALLOCS
|
|
if (*((char *)(zone+1)+zone->size+ZONEDEBUG*2)!='#')
|
|
{
|
|
#if ZONEDEBUG > 0
|
|
if (testsent)
|
|
{
|
|
sent = (qbyte *)(zone+1);
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (sent[i] != sentinalkey)
|
|
{
|
|
Con_Printf(CON_ERROR "%i %i-%s\n", zone->size, i, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
break;
|
|
}
|
|
}
|
|
sent += zone->size+ZONEDEBUG;
|
|
for (i = 0; i < ZONEDEBUG; i++)
|
|
{
|
|
if (sent[i] != sentinalkey)
|
|
{
|
|
Con_Printf(CON_ERROR "%i %i-%s\n", zone->size, i, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (zone->size >= minsize)
|
|
#endif
|
|
Con_Printf("%i-%s\n", zone->size, (char *)(zone+1) + zone->size+ZONEDEBUG*2);
|
|
if (futurehide)
|
|
*((char *)(zone+1)+zone->size+ZONEDEBUG*2) = '#';
|
|
|
|
// Sleep(10);
|
|
}
|
|
overhead += sizeof(zone_t)+ZONEDEBUG*2 + strlen((char *)(zone+1) + zone->size+ZONEDEBUG*2) +1;
|
|
#else
|
|
Con_Printf("%i-%i ", zone->size, zone->tag);
|
|
overhead += sizeof(zone_t)+ZONEDEBUG*2;
|
|
#endif
|
|
}
|
|
Con_Printf(CON_NOTICE "Zone:%i bytes in %i blocks\n", allocated, blocks);
|
|
Con_Printf(CON_NOTICE "Overhead %i bytes\n", overhead);
|
|
}
|
|
|
|
#elif 0//#else
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//dmw was 0x50000 19/12/02 - playing with dynamic sound system.
|
|
//was 0x80000 15/01/03 - playing with genuine pk3 files
|
|
#define DYNAMIC_SIZE 0x100000
|
|
|
|
#define ZONEID 0x1d4a11
|
|
#define MINFRAGMENT 64
|
|
|
|
typedef struct memblock_s
|
|
{
|
|
int size; // including the header and possibly tiny fragments
|
|
int tag; // a tag of 0 is a free block
|
|
int id; // should be ZONEID
|
|
struct memblock_s *next, *prev;
|
|
int pad; // pad to 64 bit boundary
|
|
} memblock_t;
|
|
|
|
typedef struct
|
|
{
|
|
int size; // total bytes malloced, including header
|
|
memblock_t blocklist; // start / end cap for linked list
|
|
memblock_t *rover;
|
|
} memzone_t;
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
ZONE MEMORY ALLOCATION
|
|
|
|
There is never any space between memblocks, and there will never be two
|
|
contiguous free memblocks.
|
|
|
|
The rover can be left pointing at a non-empty block
|
|
|
|
The zone calls are pretty much only used for small strings and structures,
|
|
all big things are allocated on the hunk.
|
|
==============================================================================
|
|
*/
|
|
|
|
memzone_t *mainzone;
|
|
|
|
void Z_ClearZone (memzone_t *zone, int size);
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_ClearZone
|
|
========================
|
|
*/
|
|
void Z_ClearZone (memzone_t *zone, int size)
|
|
{
|
|
memblock_t *block;
|
|
|
|
// set the entire zone to one free block
|
|
|
|
zone->blocklist.next = zone->blocklist.prev = block =
|
|
(memblock_t *)( (qbyte *)zone + sizeof(memzone_t) );
|
|
zone->blocklist.tag = 1; // in use block
|
|
zone->blocklist.id = 0;
|
|
zone->blocklist.size = 0;
|
|
zone->rover = block;
|
|
|
|
block->prev = block->next = &zone->blocklist;
|
|
block->tag = 0; // free block
|
|
block->id = ZONEID;
|
|
block->size = size - sizeof(memzone_t);
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Free
|
|
========================
|
|
*/
|
|
void Z_Free (void *ptr)
|
|
{
|
|
memblock_t *block, *other;
|
|
|
|
if (!ptr)
|
|
Sys_Error ("Z_Free: NULL pointer");
|
|
|
|
block = (memblock_t *) ( (qbyte *)ptr - sizeof(memblock_t));
|
|
if (block->id != ZONEID)
|
|
Sys_Error ("Z_Free: freed a pointer without ZONEID");
|
|
if (block->tag == 0)
|
|
Sys_Error ("Z_Free: freed a freed pointer");
|
|
|
|
block->tag = 0; // mark as free
|
|
|
|
other = block->prev;
|
|
if (!other->tag)
|
|
{ // merge with previous free block
|
|
other->size += block->size;
|
|
other->next = block->next;
|
|
other->next->prev = other;
|
|
if (block == mainzone->rover)
|
|
mainzone->rover = other;
|
|
block = other;
|
|
}
|
|
|
|
other = block->next;
|
|
if (!other->tag)
|
|
{ // merge the next free block onto the end
|
|
block->size += other->size;
|
|
block->next = other->next;
|
|
block->next->prev = block;
|
|
if (other == mainzone->rover)
|
|
mainzone->rover = block;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Malloc
|
|
========================
|
|
*/
|
|
#undef Z_Malloc
|
|
void *Z_Malloc (int size)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size);
|
|
Q_memset (buf, 0, size);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void *Z_MallocNamed (int size, char *name)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: %s failed on allocation of %i bytes", name, size);
|
|
// Sys_DebugLog("zmalloc.log", "%s allocates %i bytes\n", name, size);
|
|
Q_memset (buf, 0, size);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void *Z_MallocNamed2 (int size, char *name, int line)
|
|
{
|
|
void *buf;
|
|
|
|
Z_CheckHeap (); // DEBUG
|
|
buf = Z_TagMalloc (size, 1);
|
|
if (!buf)
|
|
Sys_Error ("Z_Malloc: %s %i failed on allocation of %i bytes", name, line, size);
|
|
// Sys_DebugLog("zmalloc.log", "%s %i allocates %i bytes\n", name, line, size);
|
|
Q_memset (buf, 0, size);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void *Z_TagMalloc (int size, int tag)
|
|
{
|
|
int extra;
|
|
memblock_t *start, *rover, *newz, *base;
|
|
|
|
if (!tag)
|
|
Sys_Error ("Z_TagMalloc: tried to use a 0 tag");
|
|
|
|
//
|
|
// scan through the block list looking for the first free block
|
|
// of sufficient size
|
|
//
|
|
size += sizeof(memblock_t); // account for size of block header
|
|
size += 4; // space for memory trash tester
|
|
size = (size + 7) & ~7; // align to 8-qbyte boundary
|
|
|
|
base = rover = mainzone->rover;
|
|
start = base->prev;
|
|
|
|
do
|
|
{
|
|
if (rover == start) // scaned all the way around the list
|
|
return NULL;
|
|
if (rover->tag)
|
|
base = rover = rover->next;
|
|
else
|
|
rover = rover->next;
|
|
} while (base->tag || base->size < size);
|
|
|
|
//
|
|
// found a block big enough
|
|
//
|
|
extra = base->size - size;
|
|
if (extra > MINFRAGMENT)
|
|
{ // there will be a free fragment after the allocated block
|
|
newz = (memblock_t *) ((qbyte *)base + size );
|
|
newz->size = extra;
|
|
newz->tag = 0; // free block
|
|
newz->prev = base;
|
|
newz->id = ZONEID;
|
|
newz->next = base->next;
|
|
newz->next->prev = newz;
|
|
base->next = newz;
|
|
base->size = size;
|
|
}
|
|
|
|
base->tag = tag; // no longer a free block
|
|
|
|
mainzone->rover = base->next; // next allocation will start looking here
|
|
|
|
base->id = ZONEID;
|
|
|
|
// marker for memory trash testing
|
|
*(int *)((qbyte *)base + base->size - 4) = ZONEID;
|
|
|
|
return (void *) ((qbyte *)base + sizeof(memblock_t));
|
|
}
|
|
|
|
|
|
/*
|
|
========================
|
|
Z_Print
|
|
========================
|
|
*/
|
|
void Z_Print (memzone_t *zone)
|
|
{
|
|
memblock_t *block;
|
|
|
|
Con_Printf ("zone size: %i location: %p\n",mainzone->size,mainzone);
|
|
|
|
for (block = zone->blocklist.next ; ; block = block->next)
|
|
{
|
|
Con_Printf ("block:%p size:%7i tag:%3i\n",
|
|
block, block->size, block->tag);
|
|
|
|
if (block->next == &zone->blocklist)
|
|
break; // all blocks have been hit
|
|
if ( (qbyte *)block + block->size != (qbyte *)block->next)
|
|
Con_Printf ("ERROR: block size does not touch the next block\n");
|
|
if ( block->next->prev != block)
|
|
Con_Printf ("ERROR: next block doesn't have proper back link\n");
|
|
if (!block->tag && !block->next->tag)
|
|
Con_Printf ("ERROR: two consecutive free blocks\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *BZ_Malloc(int size)
|
|
{
|
|
void *data;
|
|
data = malloc(size);
|
|
memset(data, 0, size);
|
|
return data;
|
|
}
|
|
|
|
void BZ_Free(void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
#define HUNK_SENTINAL 0x1df001ed
|
|
|
|
typedef struct
|
|
{
|
|
int sentinal;
|
|
int size; // including sizeof(hunk_t), -1 = not allocated
|
|
char name[8];
|
|
} hunk_t;
|
|
typedef struct hunkoverflow_s
|
|
{
|
|
struct hunkoverflow_s *prev;
|
|
struct hunkoverflow_s *next;
|
|
hunk_t hunk[0];
|
|
} hunkoverflow_t;
|
|
|
|
static hunkoverflow_t *hunkoverflow_first;
|
|
static hunkoverflow_t *hunkoverflow_top;
|
|
|
|
static qbyte *hunk_base;
|
|
static int hunk_size;
|
|
|
|
static int hunk_low_used;
|
|
static int hunk_high_used;
|
|
|
|
static qboolean hunk_tempactive;
|
|
static int hunk_tempmark;
|
|
|
|
void R_FreeTextures (void);
|
|
|
|
/*
|
|
==============
|
|
Hunk_Check
|
|
|
|
Run consistancy and sentinal trahing checks
|
|
==============
|
|
*/
|
|
void Hunk_Check (void)
|
|
{
|
|
hunk_t *h;
|
|
|
|
for (h = (hunk_t *)hunk_base ; (qbyte *)h != hunk_base + hunk_low_used ; )
|
|
{
|
|
if (h->sentinal != HUNK_SENTINAL)
|
|
Sys_Error ("Hunk_Check: trashed sentinal");
|
|
if (h->size < 16+HUNKDEBUG*2 || h->size + (qbyte *)h - hunk_base > hunk_size)
|
|
Sys_Error ("Hunk_Check: bad size");
|
|
#if HUNKDEBUG > 0
|
|
{
|
|
qbyte *present;
|
|
qbyte *postsent;
|
|
int i;
|
|
present = (qbyte *)(h+1);
|
|
postsent = (qbyte *)h + h->size-HUNKDEBUG;
|
|
for (i = 0; i < HUNKDEBUG; i++)
|
|
{
|
|
if (present[i] != sentinalkey)
|
|
Sys_Error ("Hunk_Check: trashed pre-sentinal on \"%s\"", h->name);
|
|
if (postsent[i] != sentinalkey)
|
|
Sys_Error ("Hunk_Check: trashed post-sentinal on \"%s\"", h->name);
|
|
}
|
|
}
|
|
#endif
|
|
h = (hunk_t *)((qbyte *)h+h->size);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Hunk_Print
|
|
|
|
If "all" is specified, every single allocation is printed.
|
|
Otherwise, allocations with the same name will be totaled up before printing.
|
|
==============
|
|
*/
|
|
void Hunk_Print (qboolean all)
|
|
{
|
|
hunk_t *h, *next, *endlow, *starthigh, *endhigh;
|
|
int count, sum;
|
|
int totalblocks;
|
|
char name[9];
|
|
|
|
name[8] = 0;
|
|
count = 0;
|
|
sum = 0;
|
|
totalblocks = 0;
|
|
|
|
h = (hunk_t *)hunk_base;
|
|
endlow = (hunk_t *)(hunk_base + hunk_low_used);
|
|
starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used);
|
|
endhigh = (hunk_t *)(hunk_base + hunk_size);
|
|
|
|
Con_Printf (" :%12i total hunk size\n", hunk_size);
|
|
Con_Printf ("-------------------------\n");
|
|
|
|
while (1)
|
|
{
|
|
//
|
|
// skip to the high hunk if done with low hunk
|
|
//
|
|
if ( h == endlow )
|
|
{
|
|
Con_Printf ("-------------------------\n");
|
|
Con_Printf (" : %12i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used);
|
|
Con_Printf (" : %12i USED\n", hunk_low_used + hunk_high_used);
|
|
Con_Printf ("-------------------------\n");
|
|
h = starthigh;
|
|
}
|
|
|
|
//
|
|
// if totally done, break
|
|
//
|
|
if ( h == endhigh )
|
|
break;
|
|
|
|
//
|
|
// run consistancy checks
|
|
//
|
|
if (h->sentinal != HUNK_SENTINAL)
|
|
Sys_Error ("Hunk_Check: trahsed sentinal");
|
|
if (h->size < 16 || h->size + (qbyte *)h - hunk_base > hunk_size)
|
|
Sys_Error ("Hunk_Check: bad size");
|
|
#if HUNKDEBUG > 0
|
|
{
|
|
qbyte *present;
|
|
qbyte *postsent;
|
|
int i;
|
|
present = (qbyte *)(h+1);
|
|
postsent = (qbyte *)h + h->size-HUNKDEBUG;
|
|
for (i = 0; i < HUNKDEBUG; i++)
|
|
{
|
|
if (present[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
if (postsent[i] != sentinalkey)
|
|
*(int*)0 = -3;
|
|
}
|
|
}
|
|
#endif
|
|
next = (hunk_t *)((qbyte *)h+h->size);
|
|
count++;
|
|
totalblocks++;
|
|
sum += h->size;
|
|
|
|
//
|
|
// print the single block
|
|
//
|
|
memcpy (name, h->name, 8);
|
|
if (all)
|
|
Con_Printf ("%8p :%12i %8s\n",h, h->size, name);
|
|
|
|
//
|
|
// print the total
|
|
//
|
|
if (next == endlow || next == endhigh ||
|
|
strncmp (h->name, next->name, 8) )
|
|
{
|
|
if (!all)
|
|
Con_Printf (" :%12i %8s (TOTAL)\n",sum, name);
|
|
count = 0;
|
|
sum = 0;
|
|
}
|
|
|
|
h = next;
|
|
}
|
|
|
|
Con_Printf ("-------------------------\n");
|
|
Con_Printf ("%8i total blocks\n", totalblocks);
|
|
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Hunk_AllocName
|
|
===================
|
|
*/
|
|
void *Hunk_AllocName (int size, char *name)
|
|
{
|
|
int roundup;
|
|
int roundupold;
|
|
hunk_t *h;
|
|
|
|
#ifdef PARANOID
|
|
Hunk_Check ();
|
|
#endif
|
|
|
|
if (size < 0)
|
|
Sys_Error ("Hunk_Alloc: bad size: %i", size);
|
|
|
|
size = sizeof(hunk_t) + HUNKDEBUG*2 + size;
|
|
size = (size + 15) & ~15;
|
|
|
|
if (hunk_size - hunk_low_used - hunk_high_used < size)
|
|
{
|
|
Sys_Error ("Not enough RAM allocated. Try starting using \"-mem %u\" on the " FULLENGINENAME " command line.", (hunk_size + 8*1024*1024) / 1024*1024);
|
|
}
|
|
|
|
h = (hunk_t *)(hunk_base + hunk_low_used);
|
|
|
|
roundupold = hunk_low_used;
|
|
roundupold += 1024*128;
|
|
roundupold &= ~(1024*128 - 1);
|
|
|
|
roundup = hunk_low_used+size;
|
|
roundup += 1024*128;
|
|
roundup &= ~(1024*128 - 1);
|
|
|
|
if (hunkoverflow_top || roundup > hunk_size)
|
|
{
|
|
hunkoverflow_t *newtop;
|
|
newtop = BZ_Malloc(sizeof(*newtop)+size);
|
|
newtop->next = NULL;
|
|
if (!hunkoverflow_top)
|
|
{
|
|
hunkoverflow_top = hunkoverflow_first = newtop;
|
|
newtop->prev = NULL;
|
|
}
|
|
else
|
|
{
|
|
hunkoverflow_top->next = newtop;
|
|
newtop->prev = hunkoverflow_top;
|
|
hunkoverflow_top = newtop;
|
|
}
|
|
h = newtop->hunk;
|
|
}
|
|
#ifdef _WIN32
|
|
else
|
|
{
|
|
if (!hunk_low_used || roundup != roundupold)
|
|
if (!VirtualAlloc (hunk_base, roundup, MEM_COMMIT, PAGE_READWRITE))
|
|
{
|
|
char *buf;
|
|
Hunk_Print(true);
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL);
|
|
Sys_Error ("VirtualCommit failed\nNot enough RAM allocated on allocation of \"%s\". Try starting using \"-heapsize %i\" on the " FULLENGINENAME " command line.", name, roundupold/512);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hunk_low_used += size;
|
|
|
|
Cache_FreeLow (hunk_low_used);
|
|
|
|
memset (h, 0, size-HUNKDEBUG);
|
|
|
|
#if HUNKDEBUG>0
|
|
memset ((h+1), sentinalkey, HUNKDEBUG);
|
|
memset ((qbyte *)h+size-HUNKDEBUG, sentinalkey, HUNKDEBUG);
|
|
#endif
|
|
|
|
h->size = size;
|
|
h->sentinal = HUNK_SENTINAL;
|
|
Q_strncpyz (h->name, COM_SkipPath(name), sizeof(h->name));
|
|
|
|
return (void *)((char *)(h+1)+HUNKDEBUG);
|
|
}
|
|
|
|
/*
|
|
===================
|
|
Hunk_Alloc
|
|
===================
|
|
*/
|
|
void *Hunk_Alloc (int size)
|
|
{
|
|
return Hunk_AllocName (size, "unknown");
|
|
}
|
|
|
|
int Hunk_LowMark (void)
|
|
{
|
|
return hunk_low_used;
|
|
}
|
|
|
|
int Hunk_LowMemAvailable(void)
|
|
{
|
|
return hunk_size - hunk_low_used - hunk_high_used;
|
|
}
|
|
|
|
void Hunk_FreeToLowMark (int mark)
|
|
{
|
|
if (mark < 0 || mark > hunk_low_used)
|
|
Sys_Error ("Hunk_FreeToLowMark: bad mark %i", mark);
|
|
|
|
while(hunkoverflow_top)
|
|
{
|
|
if (mark > hunk_size)
|
|
{
|
|
hunkoverflow_t *top = hunkoverflow_top;
|
|
mark -= top->hunk[0].size;
|
|
hunk_low_used -= top->hunk[0].size;
|
|
hunkoverflow_top = top->prev;
|
|
hunkoverflow_top->next = NULL;
|
|
BZ_Free(top);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
memset (hunk_base + mark, 0, hunk_low_used - mark);
|
|
hunk_low_used = mark;
|
|
|
|
#ifdef _WIN32
|
|
if (!VirtualAlloc (hunk_base, hunk_low_used+sizeof(hunk_t), MEM_COMMIT, PAGE_READWRITE))
|
|
{
|
|
char *buf;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &buf, 0, NULL);
|
|
Sys_Error ("VirtualAlloc commit failed.\n%s", buf);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Hunk_TempAlloc
|
|
|
|
Return space from the top of the hunk
|
|
clears old temp.
|
|
=================
|
|
*/
|
|
typedef struct hnktemps_s {
|
|
struct hnktemps_s *next;
|
|
#if TEMPDEBUG>0
|
|
int len;
|
|
#endif
|
|
} hnktemps_t;
|
|
hnktemps_t *hnktemps;
|
|
|
|
void Hunk_TempFree(void)
|
|
{
|
|
hnktemps_t *nt;
|
|
|
|
while (hnktemps)
|
|
{
|
|
#if TEMPDEBUG>0
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(hnktemps+1);
|
|
for (i = 0; i < TEMPDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
*(int*)0 = -3; //force a crash... this'll get our attention.
|
|
}
|
|
buf+=TEMPDEBUG;
|
|
//app data
|
|
buf += hnktemps->len;
|
|
for (i = 0; i < TEMPDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
*(int*)0 = -3; //force a crash... this'll get our attention.
|
|
}
|
|
#endif
|
|
|
|
nt = hnktemps->next;
|
|
|
|
free(hnktemps);
|
|
hnktemps = nt;
|
|
}
|
|
}
|
|
|
|
|
|
//allocates without clearing previous temp.
|
|
//safer than my hack that fuh moaned about...
|
|
void *Hunk_TempAllocMore (int size)
|
|
{
|
|
void *buf;
|
|
#if TEMPDEBUG>0
|
|
hnktemps_t *nt;
|
|
nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t) + TEMPDEBUG*2);
|
|
if (!nt)
|
|
return NULL;
|
|
nt->next = hnktemps;
|
|
nt->len = size;
|
|
hnktemps = nt;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, sentinalkey, TEMPDEBUG);
|
|
buf = (char *)buf + TEMPDEBUG;
|
|
memset(buf, 0, size);
|
|
memset((char *)buf + size, sentinalkey, TEMPDEBUG);
|
|
return buf;
|
|
#else
|
|
hnktemps_t *nt;
|
|
nt = (hnktemps_t*)malloc(size + sizeof(hnktemps_t));
|
|
if (!nt)
|
|
return NULL;
|
|
nt->next = hnktemps;
|
|
hnktemps = nt;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, 0, size);
|
|
return buf;
|
|
#endif
|
|
}
|
|
|
|
|
|
void *Hunk_TempAlloc (int size)
|
|
{
|
|
Hunk_TempFree();
|
|
|
|
return Hunk_TempAllocMore(size);
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
CACHE MEMORY
|
|
|
|
===============================================================================
|
|
*/
|
|
#ifdef NOCACHE
|
|
|
|
|
|
typedef struct cache_system_s {
|
|
cache_user_t *user;
|
|
struct cache_system_s *next;
|
|
struct cache_system_s *prev;
|
|
int size;
|
|
char name[16];
|
|
} cache_system_t;
|
|
cache_system_t *cache_head;
|
|
|
|
void Cache_Free (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
if (c->data == NULL)
|
|
{
|
|
cache_head = NULL; //this is evil and should never happen
|
|
Sys_Error("Cache was already free\n");
|
|
return;
|
|
}
|
|
cs = ((cache_system_t *)c->data)-1;
|
|
cs = (cache_system_t*)((char*)cs - CACHEDEBUG);
|
|
|
|
cs->user->data = NULL;
|
|
|
|
#if CACHEDEBUG>0
|
|
{
|
|
int i;
|
|
qbyte *buf;
|
|
buf = (qbyte *)(cs+1);
|
|
for (i = 0; i < CACHEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("Cache memory corrupted (%i? bytes)", cs->size);
|
|
}
|
|
buf+=CACHEDEBUG;
|
|
//app data
|
|
buf += cs->size;
|
|
for (i = 0; i < CACHEDEBUG; i++)
|
|
{
|
|
if (buf[i] != sentinalkey)
|
|
Sys_Error("Cache memory corrupted (%i? bytes)", cs->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
if (cs->next)
|
|
cs->next->prev = cs->prev;
|
|
if (cs->prev)
|
|
cs->prev->next = cs->next;
|
|
|
|
if (cs == cache_head)
|
|
cache_head = cs->next;
|
|
|
|
BZ_Free(cs);
|
|
}
|
|
|
|
void *Cache_Check(cache_user_t *c)
|
|
{
|
|
if (!c->data)
|
|
return NULL;
|
|
|
|
return c->data;
|
|
}
|
|
|
|
void Cache_Flush(void)
|
|
{
|
|
//this generically named function is hyjacked to flush models and sounds, as well as ragdolls etc
|
|
#ifdef RAGDOLL
|
|
rag_flushdolls(true);
|
|
#endif
|
|
#ifndef SERVERONLY
|
|
S_Purge(false);
|
|
#endif
|
|
while(cache_head)
|
|
{
|
|
Cache_Free(cache_head->user);
|
|
}
|
|
}
|
|
|
|
void *Cache_Alloc (cache_user_t *c, int size, char *name)
|
|
{
|
|
void *buf;
|
|
cache_system_t *nt;
|
|
qboolean resize = false;
|
|
|
|
if (size <= 0)
|
|
Sys_Error ("Cache_Alloc: size %i", size);
|
|
|
|
if (c->data)
|
|
{
|
|
Sys_Error ("Cache_Alloc: %s already allocated", name);
|
|
/*
|
|
//resize instead
|
|
nt = c->data;
|
|
nt--;
|
|
nt = (cache_system_t*)BZ_Realloc(nt, size + sizeof(cache_system_t) + CACHEDEBUG*2);
|
|
|
|
resize = true;*/
|
|
}
|
|
else
|
|
{
|
|
// size = (size + 15) & ~15;
|
|
nt = (cache_system_t*)BZ_Malloc(size + sizeof(cache_system_t) + CACHEDEBUG*2);
|
|
}
|
|
|
|
if (!nt)
|
|
Sys_Error("Cache_Alloc: failed on allocation of %i bytes", size);
|
|
|
|
if (resize)
|
|
{
|
|
if (nt->next)
|
|
nt->next->prev = nt;
|
|
if (nt->prev)
|
|
nt->prev->next = nt;
|
|
else
|
|
cache_head = nt;
|
|
}
|
|
else
|
|
{
|
|
nt->next = cache_head;
|
|
nt->prev = NULL;
|
|
if (cache_head)
|
|
cache_head->prev = nt;
|
|
cache_head = nt;
|
|
}
|
|
nt->user = c;
|
|
nt->size = size;
|
|
Q_strncpyz(nt->name, name, sizeof(nt->name));
|
|
nt->user->fake = false;
|
|
buf = (void *)(nt+1);
|
|
memset(buf, sentinalkey, CACHEDEBUG);
|
|
buf = (char*)buf+CACHEDEBUG;
|
|
if (!resize)
|
|
memset(buf, 0, size);
|
|
memset((char *)buf+size, sentinalkey, CACHEDEBUG);
|
|
c->data = buf;
|
|
return c->data;
|
|
}
|
|
|
|
void Cache_FreeLow(int newlow)
|
|
{
|
|
}
|
|
|
|
void Cache_FreeHigh(int newhigh)
|
|
{
|
|
}
|
|
|
|
void Cache_Report (void)
|
|
{
|
|
}
|
|
|
|
void Hunk_Print_f (void)
|
|
{
|
|
cache_system_t *cs;
|
|
int zoneblocks;
|
|
int cacheused;
|
|
int zoneused;
|
|
Hunk_Print(true);
|
|
|
|
cacheused = 0;
|
|
zoneused = 0;
|
|
zoneblocks = 0;
|
|
for (cs = cache_head; cs; cs = cs->next)
|
|
{
|
|
cacheused += cs->size;
|
|
}
|
|
Con_Printf("Cache: %iKB\n", cacheused/1024);
|
|
|
|
Con_Printf("Z Delta: %iKB\n", zmemdelta/1024); zmemdelta = 0;
|
|
Con_Printf("Z Total: %iKB\n", zmemtotal/1024);
|
|
//note: Zone memory isn't tracked reliably. we don't track the mem that is freed, so it'll just climb and climb
|
|
//we don't track reallocs either.
|
|
|
|
#if 0
|
|
{
|
|
zone_t *zone;
|
|
|
|
for(zone = zone_head; zone; zone=zone->next)
|
|
{
|
|
zoneused += zone->size + sizeof(zone_t);
|
|
zoneblocks++;
|
|
}
|
|
Con_Printf("Zone: %i containing %iKB\n", zoneblocks, zoneused/1024);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_MSVCRT_DEBUG
|
|
{
|
|
static struct _CrtMemState savedstate;
|
|
static qboolean statesaved;
|
|
|
|
_CrtMemDumpAllObjectsSince(statesaved?&savedstate:NULL);
|
|
_CrtMemCheckpoint(&savedstate);
|
|
statesaved = true;
|
|
}
|
|
#endif
|
|
}
|
|
void Cache_Init(void)
|
|
{
|
|
Cmd_AddCommand ("flush", Cache_Flush);
|
|
Cmd_AddCommand ("hunkprint", Hunk_Print_f);
|
|
#if 0
|
|
Cmd_AddCommand ("zoneprint", Zone_Print_f);
|
|
#endif
|
|
#ifdef NAMEDMALLOCS
|
|
Cmd_AddCommand ("zonegroups", Zone_Groups_f);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
typedef struct cache_system_s
|
|
{
|
|
int size; // including this header
|
|
cache_user_t *user;
|
|
char name[16];
|
|
struct cache_system_s *prev, *next;
|
|
struct cache_system_s *lru_prev, *lru_next; // for LRU flushing
|
|
} cache_system_t;
|
|
|
|
cache_system_t *Cache_TryAlloc (int size, qboolean nobottom);
|
|
|
|
cache_system_t cache_head;
|
|
|
|
/*
|
|
===========
|
|
Cache_Move
|
|
===========
|
|
*/
|
|
void Cache_Move ( cache_system_t *c)
|
|
{
|
|
cache_system_t *newc;
|
|
|
|
// we are clearing up space at the bottom, so only allocate it late
|
|
newc = Cache_TryAlloc (c->size, true);
|
|
if (newc)
|
|
{
|
|
// Con_Printf ("cache_move ok\n");
|
|
|
|
Q_memcpy ( newc+1, c+1, c->size - sizeof(cache_system_t) );
|
|
newc->user = c->user;
|
|
Q_memcpy (newc->name, c->name, sizeof(newc->name));
|
|
Cache_Free (c->user);
|
|
newc->user->data = (void *)(newc+1);
|
|
}
|
|
else
|
|
{
|
|
// Con_Printf ("cache_move failed\n");
|
|
|
|
Cache_Free (c->user); // tough luck...
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_FreeLow
|
|
|
|
Throw things out until the hunk can be expanded to the given point
|
|
============
|
|
*/
|
|
void Cache_FreeLow (int new_low_hunk)
|
|
{
|
|
cache_system_t *c;
|
|
|
|
while (1)
|
|
{
|
|
c = cache_head.next;
|
|
if (c == &cache_head)
|
|
return; // nothing in cache at all
|
|
if ((qbyte *)c >= hunk_base + new_low_hunk)
|
|
return; // there is space to grow the hunk
|
|
Cache_Move ( c ); // reclaim the space
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_FreeHigh
|
|
|
|
Throw things out until the hunk can be expanded to the given point
|
|
============
|
|
*/
|
|
void Cache_FreeHigh (int new_high_hunk)
|
|
{
|
|
cache_system_t *c, *prev;
|
|
|
|
prev = NULL;
|
|
while (1)
|
|
{
|
|
c = cache_head.prev;
|
|
if (c == &cache_head)
|
|
return; // nothing in cache at all
|
|
if ( (qbyte *)c + c->size <= hunk_base + hunk_size - new_high_hunk)
|
|
return; // there is space to grow the hunk
|
|
if (c == prev)
|
|
Cache_Free (c->user); // didn't move out of the way
|
|
else
|
|
{
|
|
Cache_Move (c); // try to move it
|
|
prev = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cache_UnlinkLRU (cache_system_t *cs)
|
|
{
|
|
if (!cs->lru_next || !cs->lru_prev)
|
|
Sys_Error ("Cache_UnlinkLRU: NULL link");
|
|
|
|
cs->lru_next->lru_prev = cs->lru_prev;
|
|
cs->lru_prev->lru_next = cs->lru_next;
|
|
|
|
cs->lru_prev = cs->lru_next = NULL;
|
|
}
|
|
|
|
void Cache_MakeLRU (cache_system_t *cs)
|
|
{
|
|
if (cs->lru_next || cs->lru_prev)
|
|
Sys_Error ("Cache_MakeLRU: active link");
|
|
|
|
cache_head.lru_next->lru_prev = cs;
|
|
cs->lru_next = cache_head.lru_next;
|
|
cs->lru_prev = &cache_head;
|
|
cache_head.lru_next = cs;
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_TryAlloc
|
|
|
|
Looks for a free block of memory between the high and low hunk marks
|
|
Size should already include the header and padding
|
|
============
|
|
*/
|
|
cache_system_t *Cache_TryAlloc (int size, qboolean nobottom)
|
|
{
|
|
cache_system_t *cs, *newc;
|
|
|
|
// is the cache completely empty?
|
|
|
|
if (!nobottom && cache_head.prev == &cache_head)
|
|
{
|
|
if (hunk_size - hunk_high_used - hunk_low_used < size)
|
|
Sys_Error ("Cache_TryAlloc: %i is greater then free hunk", size);
|
|
|
|
newc = (cache_system_t *) (hunk_base + hunk_low_used);
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
cache_head.prev = cache_head.next = newc;
|
|
newc->prev = newc->next = &cache_head;
|
|
|
|
Cache_MakeLRU (newc);
|
|
return newc;
|
|
}
|
|
|
|
// search from the bottom up for space
|
|
|
|
newc = (cache_system_t *) (hunk_base + hunk_low_used);
|
|
cs = cache_head.next;
|
|
|
|
do
|
|
{
|
|
if (!nobottom || cs != cache_head.next)
|
|
{
|
|
if ( (qbyte *)cs - (qbyte *)newc >= size)
|
|
{ // found space
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
newc->next = cs;
|
|
newc->prev = cs->prev;
|
|
cs->prev->next = newc;
|
|
cs->prev = newc;
|
|
|
|
Cache_MakeLRU (newc);
|
|
|
|
return newc;
|
|
}
|
|
}
|
|
|
|
// continue looking
|
|
newc = (cache_system_t *)((qbyte *)cs + cs->size);
|
|
cs = cs->next;
|
|
|
|
} while (cs != &cache_head);
|
|
|
|
// try to allocate one at the very end
|
|
if ( hunk_base + hunk_size - hunk_high_used - (qbyte *)newc >= size)
|
|
{
|
|
memset (newc, 0, sizeof(*newc));
|
|
newc->size = size;
|
|
|
|
newc->next = &cache_head;
|
|
newc->prev = cache_head.prev;
|
|
cache_head.prev->next = newc;
|
|
cache_head.prev = newc;
|
|
|
|
Cache_MakeLRU (newc);
|
|
|
|
return newc;
|
|
}
|
|
|
|
return NULL; // couldn't allocate
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Flush
|
|
|
|
Throw everything out, so new data will be demand cached
|
|
============
|
|
*/
|
|
void Cache_Flush (void)
|
|
{
|
|
while (cache_head.next != &cache_head)
|
|
Cache_Free ( cache_head.next->user ); // reclaim the space
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
Cache_Print
|
|
|
|
============
|
|
*/
|
|
void Cache_Print (void)
|
|
{
|
|
cache_system_t *cd;
|
|
|
|
for (cd = cache_head.next ; cd != &cache_head ; cd = cd->next)
|
|
{
|
|
Con_Printf ("%8i : %s\n", cd->size, cd->name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Report
|
|
|
|
============
|
|
*/
|
|
void Cache_Report (void)
|
|
{
|
|
Con_DPrintf ("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024*1024) );
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Compact
|
|
|
|
============
|
|
*/
|
|
void Cache_Compact (void)
|
|
{
|
|
}
|
|
|
|
/*
|
|
============
|
|
Cache_Init
|
|
|
|
============
|
|
*/
|
|
void Hunk_Print_f (void) {Hunk_Print(true);}
|
|
void Cache_Init (void)
|
|
{
|
|
cache_head.next = cache_head.prev = &cache_head;
|
|
cache_head.lru_next = cache_head.lru_prev = &cache_head;
|
|
|
|
Cmd_AddCommand ("flush", Cache_Flush);
|
|
|
|
Cmd_AddCommand ("hp", Hunk_Print_f);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
Cache_Free
|
|
|
|
Frees the memory and removes it from the LRU list
|
|
==============
|
|
*/
|
|
void Cache_Free (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (!c->data)
|
|
Sys_Error ("Cache_Free: not allocated");
|
|
|
|
cs = ((cache_system_t *)c->data) - 1;
|
|
|
|
cs->prev->next = cs->next;
|
|
cs->next->prev = cs->prev;
|
|
cs->next = cs->prev = NULL;
|
|
|
|
c->data = NULL;
|
|
|
|
Cache_UnlinkLRU (cs);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
==============
|
|
Cache_Check
|
|
==============
|
|
*/
|
|
void *Cache_Check (cache_user_t *c)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (!c->data)
|
|
return NULL;
|
|
|
|
if (c->fake) //malloc or somesuch.
|
|
return c->data;
|
|
|
|
cs = ((cache_system_t *)c->data) - 1;
|
|
|
|
// move to head of LRU
|
|
Cache_UnlinkLRU (cs);
|
|
Cache_MakeLRU (cs);
|
|
|
|
return c->data;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
Cache_Alloc
|
|
==============
|
|
*/
|
|
void *Cache_Alloc (cache_user_t *c, int size, char *name)
|
|
{
|
|
cache_system_t *cs;
|
|
|
|
if (c->data)
|
|
Sys_Error ("Cache_Alloc: already allocated");
|
|
|
|
if (size <= 0)
|
|
Sys_Error ("Cache_Alloc: size %i", size);
|
|
|
|
size = (size + sizeof(cache_system_t) + 15) & ~15;
|
|
|
|
// find memory for it
|
|
while (1)
|
|
{
|
|
cs = Cache_TryAlloc (size, false);
|
|
if (cs)
|
|
{
|
|
strncpy (cs->name, name, sizeof(cs->name)-1);
|
|
c->data = (void *)(cs+1);
|
|
cs->user = c;
|
|
break;
|
|
}
|
|
|
|
// free the least recently used cahedat
|
|
if (cache_head.lru_prev == &cache_head)
|
|
Sys_Error ("Cache_Alloc: out of memory");
|
|
// not enough memory at all
|
|
Cache_Free ( cache_head.lru_prev->user );
|
|
}
|
|
|
|
return Cache_Check (c);
|
|
}
|
|
#endif
|
|
//============================================================================
|
|
|
|
/*
|
|
========================
|
|
Memory_Init
|
|
========================
|
|
*/
|
|
void Memory_Init (void *buf, int size)
|
|
{
|
|
#if 0 //ndef NOZONE
|
|
int p;
|
|
int zonesize = DYNAMIC_SIZE;
|
|
#endif
|
|
|
|
hunk_base = (qbyte*)buf;
|
|
hunk_size = size;
|
|
hunk_low_used = 0;
|
|
hunk_high_used = 0;
|
|
|
|
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0
|
|
srand(time(0));
|
|
sentinalkey = rand() & 0xff;
|
|
#endif
|
|
|
|
Cache_Init ();
|
|
|
|
#ifdef MULTITHREAD
|
|
if (!zonelock)
|
|
zonelock = Sys_CreateMutex(); // this can fail!
|
|
#endif
|
|
|
|
#if 0 //ndef NOZONE
|
|
p = COM_CheckParm ("-zone");
|
|
if (p)
|
|
{
|
|
if (p < com_argc-1)
|
|
zonesize = Q_atoi (com_argv[p+1]) * 1024;
|
|
else
|
|
Sys_Error ("Memory_Init: you must specify a size in KB after -zone");
|
|
}
|
|
mainzone = Hunk_AllocName ( zonesize, "zone" );
|
|
Z_ClearZone (mainzone, zonesize);
|
|
#endif
|
|
}
|
|
|
|
void Memory_DeInit(void)
|
|
{
|
|
Hunk_TempFree();
|
|
Cache_Flush();
|
|
|
|
#ifdef MULTITHREAD
|
|
if (zonelock)
|
|
{
|
|
Sys_DestroyMutex(zonelock);
|
|
zonelock = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|