/* Copyright (C) 1994-1995 Apogee Software, Ltd. 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 #include #include #ifdef DOS #include #include #endif #include "rt_def.h" #include "_z_zone.h" #include "z_zone.h" #include "rt_util.h" #include "develop.h" #include "rt_net.h" #if (DEVELOPMENT == 1) #include "rt_main.h" #endif //MED #include "memcheck.h" int lowmemory=0; /* ============================================================================== 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 It is of no value to free a cachable block, because it will get overwritten automatically if needed ============================================================================== */ //Globals int zonememorystarted=0; // Statics static memzone_t *mainzone; static memzone_t *levelzone; static int levelzonesize=LEVELZONESIZE; static struct meminfo { unsigned LargestBlockAvail; unsigned MaxUnlockedPage; unsigned LargestLockablePage; unsigned LinAddrSpace; unsigned NumFreePagesAvail; unsigned NumPhysicalPagesFree; unsigned TotalPhysicalPages; unsigned FreeLinAddrSpace; unsigned SizeOfPageFile; unsigned Reserved[3]; } MemInfo; /* ======================== = = Z_ClearZone = ======================== */ void Z_ClearZone (memzone_t *zone) { memblock_t *block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)( (byte *)zone + sizeof(memzone_t) ); zone->blocklist.user = (void *)zone; zone->blocklist.tag = PU_STATIC; zone->rover = block; block->prev = block->next = &zone->blocklist; block->user = NULL; // free block block->size = zone->size - sizeof(memzone_t); } /* ======================== = = Z_AllocateZone = ======================== */ memzone_t *Z_AllocateZone (int size) { memzone_t *header; header = malloc (size+sizeof(memzone_t)); if (!header) Error ("Z_AllocateZone: Couldn't malloc %zd bytes avail=%d\n", size+sizeof(memzone_t), Z_AvailHeap()); header->size = size; Z_ClearZone (header); return header; } /* ======================== = = Z_Init = ======================== */ void Z_Init (int size, int min) { int maxsize; int sz; if (zonememorystarted==1) return; zonememorystarted=1; sz = GamePacketSize(); sz*=MAXCMDS; if (ConsoleIsServer() == true) levelzonesize+=((numplayers*2)+1)*sz; else levelzonesize+=(numplayers+1)*sz; maxsize=((int)(Z_AvailHeap())-size-levelzonesize); if (maxsizeMAXMEMORYSIZE) maxsize=(MAXMEMORYSIZE-levelzonesize); mainzone = Z_AllocateZone (maxsize); levelzone = Z_AllocateZone (levelzonesize); if (!quiet) printf("Z_INIT: %ld bytes\n",(long int)(maxsize+levelzonesize)); if (maxsize<(min+(min>>1))) { lowmemory = 1; printf("==============================================================================\n"); printf("WARNING: You are running ROTT with very little memory. ROTT runs best with\n"); printf("8 Megabytes of memory and no TSR's loaded in memory. If you can free up more\n"); printf("memory for ROTT then you should press CTRL-BREAK at this time. If you are\n"); printf("unable to do this you will experience momentary pauses in game-play whenever\n"); printf("you enter new areas of the game as well as an overall decreased performance.\n"); printf(" Press any key to continue\n"); printf("==============================================================================\n"); getch(); } } /* ======================== = = Z_ShutDown = ======================== */ void Z_ShutDown( void ) { if (zonememorystarted==0) return; zonememorystarted=0; free(mainzone); free(levelzone); } /* ======================== = = Z_GetSize = ======================== */ int Z_GetSize (void *ptr) { memblock_t *block; block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); return (block->size - sizeof(memblock_t)); } /* ======================== = = Z_Free = ======================== */ void Z_Free (void *ptr) { memblock_t *block, *other; block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); if (!block->user) Error ("Z_Free: freed a freed pointer"); if (block->user > (void **)0x100) // smaller values are not pointers *block->user = 0; // clear the user's mark block->user = NULL; // mark as free other = block->prev; if (!other->user) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == mainzone->rover) mainzone->rover = other; else if (block == levelzone->rover) levelzone->rover = other; block = other; } other = block->next; if (!other->user) { // 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; else if (other == levelzone->rover) levelzone->rover = block; } } /* ======================== = = Z_Malloc = = You can pass a NULL user if the tag is < PU_PURGELEVEL ======================== */ #if (DEVELOPMENT == 1) int totallevelsize=0; #endif void *Z_Malloc (int size, int tag, void *user) { int extra; memblock_t *start, *rover, *new, *base; // // scan through the block list looking for the first free block // of sufficient size, throwing out any purgable blocks along the way // // size += sizeof(memblock_t); // account for size of block header size = (size + sizeof(memblock_t) + 3)&~3; // account for size of block header // // if there is a free block behind the rover, back up over them // base = mainzone->rover; if (!base->prev->user) base = base->prev; rover = base; start = base->prev; do { if (rover == start) // scaned all the way around the list { SoftError("OHSHIT\n"); Z_DumpHeap(0,200); Error ("Z_Malloc: failed on allocation of %i bytes",size); } if (rover->user) { if (rover->tag < PU_PURGELEVEL) // hit a block that can't be purged, so move base past it base = rover = rover->next; else { // free the rover block (adding the size to base) base = base->prev; // the rover can be the base block Z_Free ((byte *)rover+sizeof(memblock_t)); base = base->next; rover = base->next; } } else rover = rover->next; } while (base->user || 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 new = (memblock_t *) ((byte *)base + size ); new->size = extra; new->user = NULL; // free block new->tag = 0; new->prev = base; new->next = base->next; new->next->prev = new; base->next = new; base->size = size; } if (user) { base->user = user; // mark as an in use block *(void **)user = (void *) ((byte *)base + sizeof(memblock_t)); } else { if (tag >= PU_PURGELEVEL) Error ("Z_Malloc: an owner is required for purgable blocks"); base->user = (void *)2; // mark as in use, but unowned } base->tag = tag; mainzone->rover = base->next; // next allocation will start looking here #if (MEMORYCORRUPTIONTEST==1) base->posttag=MEMORYPOSTTAG; base->pretag=MEMORYPRETAG; #endif return (void *) ((byte *)base + sizeof(memblock_t)); } /* ======================== = = Z_LevelMalloc = = Only use this for level structures. = You can pass a NULL user if the tag is < PU_PURGELEVEL ======================== */ void *Z_LevelMalloc (int size, int tag, void *user) { int extra; memblock_t *start, *rover, *new, *base; // // scan through the block list looking for the first free block // of sufficient size, throwing out any purgable blocks along the way // // size += sizeof(memblock_t); // account for size of block header size = (size + sizeof(memblock_t) + 3)&~3; // account for size of block header // // if there is a free block behind the rover, back up over them // base = levelzone->rover; if (!base->prev->user) base = base->prev; rover = base; start = base->prev; do { if (rover == start) // scaned all the way around the list { SoftError("OHSHIT\n"); Z_DumpHeap(0,200); Error ("Z_LevelMalloc: failed on allocation of %i bytes",size); } if (rover->user) { if (rover->tag < PU_PURGELEVEL) // hit a block that can't be purged, so move base past it base = rover = rover->next; else { // free the rover block (adding the size to base) base = base->prev; // the rover can be the base block Z_Free ((byte *)rover+sizeof(memblock_t)); base = base->next; rover = base->next; } } else rover = rover->next; } while (base->user || 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 new = (memblock_t *) ((byte *)base + size ); new->size = extra; new->user = NULL; // free block new->tag = 0; new->prev = base; new->next = base->next; new->next->prev = new; base->next = new; base->size = size; } if (user) { base->user = user; // mark as an in use block *(void **)user = (void *) ((byte *)base + sizeof(memblock_t)); } else { if (tag >= PU_PURGELEVEL) Error ("Z_Malloc: an owner is required for purgable blocks"); base->user = (void *)2; // mark as in use, but unowned } base->tag = tag; levelzone->rover = base->next; // next allocation will start looking here #if (MEMORYCORRUPTIONTEST==1) base->posttag=MEMORYPOSTTAG; base->pretag=MEMORYPRETAG; #endif return (void *) ((byte *)base + sizeof(memblock_t)); } /* ======================== = = Z_FreeTags = ======================== */ void Z_FreeTags (int lowtag, int hightag) { memblock_t *block, *next; for (block = mainzone->blocklist.next ; block != &mainzone->blocklist ; block = next) { next = block->next; // get link before freeing if (!block->user) continue; // free block if (block->tag >= lowtag && block->tag <= hightag) Z_Free ( (byte *)block+sizeof(memblock_t)); } for (block = levelzone->blocklist.next ; block != &levelzone->blocklist ; block = next) { next = block->next; // get link before freeing if (!block->user) continue; // free block if (block->tag >= lowtag && block->tag <= hightag) Z_Free ( (byte *)block+sizeof(memblock_t)); } } /* ======================== = = Z_DumpHeap = ======================== */ void Z_DumpHeap (int lowtag, int hightag) { memblock_t *block; int totalsize; SoftError("MAIN ZONE\n"); SoftError("zone size: %i location: %p\n",mainzone->size,mainzone); SoftError("tag range: %i to %i\n",lowtag, hightag); totalsize=0; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->tag >= lowtag && block->tag <= hightag) { SoftError("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); totalsize+=block->size; } if (block->next == &mainzone->blocklist) { break; // all blocks have been hit } if ( (byte *)block + block->size != (byte *)block->next) { SoftError("ERROR: block size does not touch the next block\n"); } if ( block->next->prev != block) { SoftError("ERROR: next block doesn't have proper back link\n"); } if (!block->user && !block->next->user) { SoftError("ERROR: two consecutive free blocks\n"); } } SoftError("Total Size of blocks = %d\n",totalsize); SoftError("LEVEL ZONE\n"); SoftError("zone size: %i location: %p\n",levelzone->size,levelzone); SoftError("tag range: %i to %i\n",lowtag, hightag); totalsize=0; for (block = levelzone->blocklist.next ; ; block = block->next) { if (block->tag >= lowtag && block->tag <= hightag) { SoftError("block:%p size:%7i user:%p tag:%3i\n", block, block->size, block->user, block->tag); totalsize+=block->size; } if (block->next == &levelzone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) { SoftError("ERROR: block size does not touch the next block\n"); } if ( block->next->prev != block) { SoftError("ERROR: next block doesn't have proper back link\n"); } if (!block->user && !block->next->user) { SoftError("ERROR: two consecutive free blocks\n"); } } SoftError("Total Size of blocks = %d\n",totalsize); } /* ======================== = = Z_UsedHeap = ======================== */ int Z_UsedHeap ( void ) { memblock_t *block; int heapsize; heapsize=0; for (block = mainzone->blocklist.next ; ; block = block->next) { if ((block->tag>0) && (block->user>(void **)0)) heapsize+=(block->size); if (block->next == &mainzone->blocklist) break; // all blocks have been hit } return heapsize; } /* ======================== = = Z_UsedLevelHeap = ======================== */ int Z_UsedLevelHeap ( void ) { memblock_t *block; int heapsize; heapsize=0; for (block = levelzone->blocklist.next ; ; block = block->next) { if ((block->tag>0) && (block->user>(void **)0)) heapsize+=(block->size); if (block->next == &levelzone->blocklist) break; // all blocks have been hit } return heapsize; } /* ======================== = = Z_UsedStaticHeap = ======================== */ int Z_UsedStaticHeap ( void ) { memblock_t *block; int heapsize; heapsize=0; for (block = mainzone->blocklist.next ; ; block = block->next) { if ((block->tag>0) && (block->taguser>(void **)0)) heapsize+=(block->size); if (block->next == &mainzone->blocklist) break; // all blocks have been hit } return heapsize; } /* ======================== = = Z_HeapSize = ======================== */ int Z_HeapSize ( void ) { return mainzone->size; } /* ======================== = = Z_CheckHeap = ======================== */ void Z_CheckHeap (void) { memblock_t *block; // Check mainzone for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (!block->user && !block->next->user) Error ("Z_CheckHeap: two consecutive free blocks\n"); #if (MEMORYCORRUPTIONTEST==1) if ((block->tag>0) && (block->user>0)) { if (block->posttag!=MEMORYPOSTTAG) Error("Z_CheckHeap: Corrupted posttag\n"); if (block->pretag!=MEMORYPRETAG) Error("Z_CheckHeap: Corrupted pretag\n"); } #endif } // Check levelzone for (block = levelzone->blocklist.next ; ; block = block->next) { if (block->next == &levelzone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (!block->user && !block->next->user) Error ("Z_CheckHeap: two consecutive free blocks\n"); #if (MEMORYCORRUPTIONTEST==1) if ((block->tag>0) && (block->user>0)) { if (block->posttag!=MEMORYPOSTTAG) Error("Z_CheckHeap: Corrupted posttag\n"); if (block->pretag!=MEMORYPRETAG) Error("Z_CheckHeap: Corrupted pretag\n"); } #endif } } /* ======================== = = Z_ChangeTag = ======================== */ void Z_ChangeTag (void *ptr, int tag) { memblock_t *block; block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t)); block->tag = tag; } /* ======================== = = Z_AvailHeap = ======================== */ int Z_AvailHeap ( void ) { #ifdef DOS union REGS zregs; struct SREGS zsregs; zregs.x.eax = 0x00000500; memset( &zsregs, 0, sizeof(zsregs) ); zsregs.es = FP_SEG( &MemInfo ); zregs.x.edi = FP_OFF( &MemInfo ); int386x( DPMI_INT, &zregs, &zregs, &zsregs ); return ((int)MemInfo.LargestBlockAvail); #else return MAXMEMORYSIZE; #endif } /* ======================== = = Z_Realloc = ======================== */ void Z_Realloc (void ** ptr, int newsize) { memblock_t *block; void * newptr; int oldsize; block = (memblock_t *) ( (byte *)(*ptr) - sizeof(memblock_t)); oldsize = block->size; newptr = SafeMalloc(newsize); if (oldsize > newsize) { oldsize = newsize; } memcpy( newptr, *ptr, oldsize ); SafeFree( *ptr ); *ptr = newptr; }