1 /*
2 Copyright (C) 1994-1995 Apogee Software, Ltd.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // Z_zone.c
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #ifdef DOS
27 #include <dos.h>
28 #include <conio.h>
29 #endif
30 
31 #include "rt_def.h"
32 #include "_z_zone.h"
33 #include "z_zone.h"
34 #include "rt_util.h"
35 #include "develop.h"
36 #include "rt_net.h"
37 
38 #if (DEVELOPMENT == 1)
39 #include "rt_main.h"
40 #endif
41 //MED
42 #include "memcheck.h"
43 
44 int lowmemory=0;
45 
46 /*
47 ==============================================================================
48 
49                                                 ZONE MEMORY ALLOCATION
50 
51 There is never any space between memblocks, and there will never be two
52 contiguous free memblocks.
53 
54 The rover can be left pointing at a non-empty block
55 
56 It is of no value to free a cachable block, because it will get overwritten
57 automatically if needed
58 
59 ==============================================================================
60 */
61 
62 //Globals
63 
64 int zonememorystarted=0;
65 
66 // Statics
67 
68 static memzone_t       *mainzone;
69 static memzone_t       *levelzone;
70 static int levelzonesize=LEVELZONESIZE;
71 static struct meminfo
72    {
73    unsigned LargestBlockAvail;
74    unsigned MaxUnlockedPage;
75    unsigned LargestLockablePage;
76    unsigned LinAddrSpace;
77    unsigned NumFreePagesAvail;
78    unsigned NumPhysicalPagesFree;
79    unsigned TotalPhysicalPages;
80    unsigned FreeLinAddrSpace;
81    unsigned SizeOfPageFile;
82    unsigned Reserved[3];
83    } MemInfo;
84 
85 /*
86 ========================
87 =
88 = Z_ClearZone
89 =
90 ========================
91 */
92 
Z_ClearZone(memzone_t * zone)93 void Z_ClearZone (memzone_t *zone)
94 {
95         memblock_t      *block;
96 
97 // set the entire zone to one free block
98 
99         zone->blocklist.next = zone->blocklist.prev = block =
100                 (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
101         zone->blocklist.user = (void *)zone;
102         zone->blocklist.tag = PU_STATIC;
103         zone->rover = block;
104 
105         block->prev = block->next = &zone->blocklist;
106         block->user = NULL;     // free block
107         block->size = zone->size - sizeof(memzone_t);
108 }
109 
110 
111 /*
112 ========================
113 =
114 = Z_AllocateZone
115 =
116 ========================
117 */
118 
Z_AllocateZone(int size)119 memzone_t *Z_AllocateZone (int size)
120 {
121         memzone_t       *header;
122 
123         header = malloc (size+sizeof(memzone_t));
124         if (!header)
125                 Error ("Z_AllocateZone: Couldn't malloc %i bytes avail=%ld\n",
126                 size+sizeof(memzone_t), Z_AvailHeap());
127         header->size = size;
128         Z_ClearZone (header);
129         return header;
130 }
131 
132 /*
133 ========================
134 =
135 = Z_Init
136 =
137 ========================
138 */
Z_Init(int size,int min)139 void Z_Init (int size, int min)
140 {
141    int maxsize;
142    int sz;
143 
144    if (zonememorystarted==1)
145       return;
146    zonememorystarted=1;
147 
148    sz = GamePacketSize();
149 
150    sz*=MAXCMDS;
151 
152    if (ConsoleIsServer() == true)
153       levelzonesize+=((numplayers*2)+1)*sz;
154    else
155       levelzonesize+=(numplayers+1)*sz;
156 
157    maxsize=((int)(Z_AvailHeap())-size-levelzonesize);
158    if (maxsize<min)
159       {
160       UL_DisplayMemoryError (min-maxsize);
161       }
162    if (maxsize>MAXMEMORYSIZE)
163       maxsize=(MAXMEMORYSIZE-levelzonesize);
164 
165    mainzone = Z_AllocateZone (maxsize);
166    levelzone = Z_AllocateZone (levelzonesize);
167 
168    if (!quiet)
169       printf("Z_INIT: %ld bytes\n",(long int)(maxsize+levelzonesize));
170 
171    if (maxsize<(min+(min>>1)))
172       {
173       lowmemory = 1;
174 
175       printf("==============================================================================\n");
176       printf("WARNING: You are running ROTT with very little memory.  ROTT runs best with\n");
177       printf("8 Megabytes of memory and no TSR's loaded in memory.  If you can free up more\n");
178       printf("memory for ROTT then you should press CTRL-BREAK at this time. If you are\n");
179       printf("unable to do this you will experience momentary pauses in game-play whenever\n");
180       printf("you enter new areas of the game as well as an overall decreased performance.\n");
181       printf("                        Press any key to continue\n");
182       printf("==============================================================================\n");
183       getch();
184       }
185 }
186 
187 /*
188 ========================
189 =
190 = Z_ShutDown
191 =
192 ========================
193 */
194 
Z_ShutDown(void)195 void Z_ShutDown( void )
196 {
197    if (zonememorystarted==0)
198       return;
199    zonememorystarted=0;
200    free(mainzone);
201    free(levelzone);
202 }
203 
204 /*
205 ========================
206 =
207 = Z_GetSize
208 =
209 ========================
210 */
211 
Z_GetSize(void * ptr)212 int Z_GetSize (void *ptr)
213 {
214         memblock_t      *block;
215 
216         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
217         return (block->size - sizeof(memblock_t));
218 }
219 
220 
221 /*
222 ========================
223 =
224 = Z_Free
225 =
226 ========================
227 */
228 
Z_Free(void * ptr)229 void Z_Free (void *ptr)
230 {
231         memblock_t      *block, *other;
232 
233         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
234         if (!block->user)
235                 Error ("Z_Free: freed a freed pointer");
236 
237         if (block->user > (void **)0x100)       // smaller values are not pointers
238                 *block->user = 0;               // clear the user's mark
239         block->user = NULL;     // mark as free
240 
241         other = block->prev;
242         if (!other->user)
243         {       // merge with previous free block
244                 other->size += block->size;
245                 other->next = block->next;
246                 other->next->prev = other;
247                 if (block == mainzone->rover)
248                         mainzone->rover = other;
249                 else if (block == levelzone->rover)
250                         levelzone->rover = other;
251                 block = other;
252         }
253 
254         other = block->next;
255         if (!other->user)
256         {       // merge the next free block onto the end
257                 block->size += other->size;
258                 block->next = other->next;
259                 block->next->prev = block;
260                 if (other == mainzone->rover)
261                         mainzone->rover = block;
262                 else if (other == levelzone->rover)
263                         levelzone->rover = block;
264         }
265 }
266 
267 
268 /*
269 ========================
270 =
271 = Z_Malloc
272 =
273 = You can pass a NULL user if the tag is < PU_PURGELEVEL
274 ========================
275 */
276 
277 #if (DEVELOPMENT == 1)
278 int totallevelsize=0;
279 #endif
280 
Z_Malloc(int size,int tag,void * user)281 void *Z_Malloc (int size, int tag, void *user)
282 {
283         int             extra;
284         memblock_t      *start, *rover, *new, *base;
285 
286 //
287 // scan through the block list looking for the first free block
288 // of sufficient size, throwing out any purgable blocks along the way
289 //
290 //        size += sizeof(memblock_t);     // account for size of block header
291         size = (size + sizeof(memblock_t) + 3)&~3;     // account for size of block header
292 
293 
294 //
295 // if there is a free block behind the rover, back up over them
296 //
297         base = mainzone->rover;
298         if (!base->prev->user)
299                 base = base->prev;
300 
301         rover = base;
302         start = base->prev;
303 
304         do
305         {
306                 if (rover == start)     // scaned all the way around the list
307                         {
308                         SoftError("OHSHIT\n");
309                         Z_DumpHeap(0,200);
310                         Error ("Z_Malloc: failed on allocation of %i bytes",size);
311                         }
312                 if (rover->user)
313                 {
314                         if (rover->tag < PU_PURGELEVEL)
315                         // hit a block that can't be purged, so move base past it
316                                 base = rover = rover->next;
317                         else
318                         {
319                         // free the rover block (adding the size to base)
320                                 base = base->prev;      // the rover can be the base block
321                                 Z_Free ((byte *)rover+sizeof(memblock_t));
322                                 base = base->next;
323                                 rover = base->next;
324                         }
325                 }
326                 else
327                         rover = rover->next;
328         } while (base->user || base->size < size);
329 
330 //
331 // found a block big enough
332 //
333         extra = base->size - size;
334         if (extra >  MINFRAGMENT)
335         {       // there will be a free fragment after the allocated block
336                 new = (memblock_t *) ((byte *)base + size );
337                 new->size = extra;
338                 new->user = NULL;               // free block
339                 new->tag = 0;
340                 new->prev = base;
341                 new->next = base->next;
342                 new->next->prev = new;
343                 base->next = new;
344                 base->size = size;
345         }
346 
347         if (user)
348         {
349                 base->user = user;                      // mark as an in use block
350                 *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
351         }
352         else
353         {
354                 if (tag >= PU_PURGELEVEL)
355                         Error ("Z_Malloc: an owner is required for purgable blocks");
356                 base->user = (void *)2;         // mark as in use, but unowned
357         }
358         base->tag = tag;
359 
360         mainzone->rover = base->next;   // next allocation will start looking here
361 
362 
363 #if (MEMORYCORRUPTIONTEST==1)
364          base->posttag=MEMORYPOSTTAG;
365          base->pretag=MEMORYPRETAG;
366 #endif
367 
368         return (void *) ((byte *)base + sizeof(memblock_t));
369 }
370 
371 /*
372 ========================
373 =
374 = Z_LevelMalloc
375 =
376 = Only use this for level structures.
377 = You can pass a NULL user if the tag is < PU_PURGELEVEL
378 ========================
379 */
380 
Z_LevelMalloc(int size,int tag,void * user)381 void *Z_LevelMalloc (int size, int tag, void *user)
382 {
383         int             extra;
384         memblock_t      *start, *rover, *new, *base;
385 
386 //
387 // scan through the block list looking for the first free block
388 // of sufficient size, throwing out any purgable blocks along the way
389 //
390 //        size += sizeof(memblock_t);     // account for size of block header
391         size = (size + sizeof(memblock_t) + 3)&~3;     // account for size of block header
392 
393 
394 //
395 // if there is a free block behind the rover, back up over them
396 //
397         base = levelzone->rover;
398         if (!base->prev->user)
399                 base = base->prev;
400 
401         rover = base;
402         start = base->prev;
403 
404         do
405         {
406                 if (rover == start)     // scaned all the way around the list
407                         {
408                         SoftError("OHSHIT\n");
409                         Z_DumpHeap(0,200);
410                         Error ("Z_LevelMalloc: failed on allocation of %i bytes",size);
411                         }
412                 if (rover->user)
413                 {
414                         if (rover->tag < PU_PURGELEVEL)
415                         // hit a block that can't be purged, so move base past it
416                                 base = rover = rover->next;
417                         else
418                         {
419                         // free the rover block (adding the size to base)
420                                 base = base->prev;      // the rover can be the base block
421                                 Z_Free ((byte *)rover+sizeof(memblock_t));
422                                 base = base->next;
423                                 rover = base->next;
424                         }
425                 }
426                 else
427                         rover = rover->next;
428         } while (base->user || base->size < size);
429 
430 //
431 // found a block big enough
432 //
433         extra = base->size - size;
434         if (extra >  MINFRAGMENT)
435         {       // there will be a free fragment after the allocated block
436                 new = (memblock_t *) ((byte *)base + size );
437                 new->size = extra;
438                 new->user = NULL;               // free block
439                 new->tag = 0;
440                 new->prev = base;
441                 new->next = base->next;
442                 new->next->prev = new;
443                 base->next = new;
444                 base->size = size;
445         }
446 
447         if (user)
448         {
449                 base->user = user;                      // mark as an in use block
450                 *(void **)user = (void *) ((byte *)base + sizeof(memblock_t));
451         }
452         else
453         {
454                 if (tag >= PU_PURGELEVEL)
455                         Error ("Z_Malloc: an owner is required for purgable blocks");
456                 base->user = (void *)2;         // mark as in use, but unowned
457         }
458         base->tag = tag;
459 
460         levelzone->rover = base->next;   // next allocation will start looking here
461 
462 #if (MEMORYCORRUPTIONTEST==1)
463          base->posttag=MEMORYPOSTTAG;
464          base->pretag=MEMORYPRETAG;
465 #endif
466 
467         return (void *) ((byte *)base + sizeof(memblock_t));
468 }
469 
470 
471 
472 /*
473 ========================
474 =
475 = Z_FreeTags
476 =
477 ========================
478 */
479 
Z_FreeTags(int lowtag,int hightag)480 void Z_FreeTags (int lowtag, int hightag)
481 {
482         memblock_t      *block, *next;
483 
484         for (block = mainzone->blocklist.next ; block != &mainzone->blocklist
485         ; block = next)
486         {
487                 next = block->next;             // get link before freeing
488                 if (!block->user)
489                         continue;                       // free block
490                 if (block->tag >= lowtag && block->tag <= hightag)
491                         Z_Free ( (byte *)block+sizeof(memblock_t));
492         }
493         for (block = levelzone->blocklist.next ; block != &levelzone->blocklist
494         ; block = next)
495         {
496                 next = block->next;             // get link before freeing
497                 if (!block->user)
498                         continue;                       // free block
499                 if (block->tag >= lowtag && block->tag <= hightag)
500                         Z_Free ( (byte *)block+sizeof(memblock_t));
501         }
502 }
503 
504 /*
505 ========================
506 =
507 = Z_DumpHeap
508 =
509 ========================
510 */
511 
Z_DumpHeap(int lowtag,int hightag)512 void Z_DumpHeap (int lowtag, int hightag)
513 {
514         memblock_t      *block;
515         int             totalsize;
516 
517         SoftError("MAIN ZONE\n");
518         SoftError("zone size: %i  location: %p\n",mainzone->size,mainzone);
519         SoftError("tag range: %i to %i\n",lowtag, hightag);
520 
521         totalsize=0;
522 
523         for (block = mainzone->blocklist.next ; ; block = block->next)
524         {
525                 if (block->tag >= lowtag && block->tag <= hightag)
526                         {
527                         SoftError("block:%p    size:%7i    user:%p    tag:%3i\n",
528                         block, block->size, block->user, block->tag);
529                         totalsize+=block->size;
530                         }
531 
532                 if (block->next == &mainzone->blocklist) {
533                         break;                  // all blocks have been hit
534 		}
535                 if ( (byte *)block + block->size != (byte *)block->next) {
536                         SoftError("ERROR: block size does not touch the next block\n");
537 		}
538                 if ( block->next->prev != block) {
539                         SoftError("ERROR: next block doesn't have proper back link\n");
540 		}
541                 if (!block->user && !block->next->user) {
542                         SoftError("ERROR: two consecutive free blocks\n");
543 		}
544         }
545         SoftError("Total Size of blocks = %ld\n",totalsize);
546 
547         SoftError("LEVEL ZONE\n");
548         SoftError("zone size: %i  location: %p\n",levelzone->size,levelzone);
549         SoftError("tag range: %i to %i\n",lowtag, hightag);
550 
551         totalsize=0;
552 
553         for (block = levelzone->blocklist.next ; ; block = block->next)
554         {
555                 if (block->tag >= lowtag && block->tag <= hightag)
556                         {
557                         SoftError("block:%p    size:%7i    user:%p    tag:%3i\n",
558                         block, block->size, block->user, block->tag);
559                         totalsize+=block->size;
560                         }
561 
562                 if (block->next == &levelzone->blocklist)
563                         break;                  // all blocks have been hit
564                 if ( (byte *)block + block->size != (byte *)block->next) {
565                         SoftError("ERROR: block size does not touch the next block\n");
566 		}
567                 if ( block->next->prev != block) {
568                         SoftError("ERROR: next block doesn't have proper back link\n");
569 		}
570                 if (!block->user && !block->next->user) {
571                         SoftError("ERROR: two consecutive free blocks\n");
572 		}
573         }
574         SoftError("Total Size of blocks = %ld\n",totalsize);
575 
576 }
577 
578 /*
579 ========================
580 =
581 = Z_UsedHeap
582 =
583 ========================
584 */
585 
Z_UsedHeap(void)586 int Z_UsedHeap ( void )
587 {
588         memblock_t      *block;
589         int heapsize;
590 
591 
592         heapsize=0;
593         for (block = mainzone->blocklist.next ; ; block = block->next)
594         {
595                 if ((block->tag>0) && (block->user>(void **)0))
596                    heapsize+=(block->size);
597                 if (block->next == &mainzone->blocklist)
598                    break;                  // all blocks have been hit
599         }
600         return heapsize;
601 }
602 
603 /*
604 ========================
605 =
606 = Z_UsedLevelHeap
607 =
608 ========================
609 */
610 
Z_UsedLevelHeap(void)611 int Z_UsedLevelHeap ( void )
612 {
613         memblock_t      *block;
614         int heapsize;
615 
616 
617         heapsize=0;
618         for (block = levelzone->blocklist.next ; ; block = block->next)
619         {
620                 if ((block->tag>0) && (block->user>(void **)0))
621                    heapsize+=(block->size);
622                 if (block->next == &levelzone->blocklist)
623                    break;                  // all blocks have been hit
624         }
625         return heapsize;
626 }
627 
628 
629 /*
630 ========================
631 =
632 = Z_UsedStaticHeap
633 =
634 ========================
635 */
636 
Z_UsedStaticHeap(void)637 int Z_UsedStaticHeap ( void )
638 {
639         memblock_t      *block;
640         int heapsize;
641 
642 
643         heapsize=0;
644         for (block = mainzone->blocklist.next ; ; block = block->next)
645         {
646                 if ((block->tag>0) && (block->tag<PU_PURGELEVEL) && (block->user>(void **)0))
647                    heapsize+=(block->size);
648                 if (block->next == &mainzone->blocklist)
649                    break;                  // all blocks have been hit
650         }
651         return heapsize;
652 }
653 
654 
655 /*
656 ========================
657 =
658 = Z_HeapSize
659 =
660 ========================
661 */
662 
Z_HeapSize(void)663 int Z_HeapSize ( void )
664 {
665    return mainzone->size;
666 }
667 
668 
669 /*
670 ========================
671 =
672 = Z_CheckHeap
673 =
674 ========================
675 */
676 
Z_CheckHeap(void)677 void Z_CheckHeap (void)
678 {
679         memblock_t      *block;
680 
681         // Check mainzone
682 
683         for (block = mainzone->blocklist.next ; ; block = block->next)
684         {
685                 if (block->next == &mainzone->blocklist)
686                         break;                  // all blocks have been hit
687                 if ( (byte *)block + block->size != (byte *)block->next)
688                         Error ("Z_CheckHeap: block size does not touch the next block\n");
689                 if ( block->next->prev != block)
690                         Error ("Z_CheckHeap: next block doesn't have proper back link\n");
691                 if (!block->user && !block->next->user)
692                         Error ("Z_CheckHeap: two consecutive free blocks\n");
693 #if (MEMORYCORRUPTIONTEST==1)
694                 if ((block->tag>0) && (block->user>0))
695                    {
696                    if (block->posttag!=MEMORYPOSTTAG)
697                       Error("Z_CheckHeap: Corrupted posttag\n");
698                    if (block->pretag!=MEMORYPRETAG)
699                       Error("Z_CheckHeap: Corrupted pretag\n");
700                    }
701 #endif
702         }
703 
704         // Check levelzone
705 
706         for (block = levelzone->blocklist.next ; ; block = block->next)
707         {
708                 if (block->next == &levelzone->blocklist)
709                         break;                  // all blocks have been hit
710                 if ( (byte *)block + block->size != (byte *)block->next)
711                         Error ("Z_CheckHeap: block size does not touch the next block\n");
712                 if ( block->next->prev != block)
713                         Error ("Z_CheckHeap: next block doesn't have proper back link\n");
714                 if (!block->user && !block->next->user)
715                         Error ("Z_CheckHeap: two consecutive free blocks\n");
716 #if (MEMORYCORRUPTIONTEST==1)
717                 if ((block->tag>0) && (block->user>0))
718                    {
719                    if (block->posttag!=MEMORYPOSTTAG)
720                       Error("Z_CheckHeap: Corrupted posttag\n");
721                    if (block->pretag!=MEMORYPRETAG)
722                       Error("Z_CheckHeap: Corrupted pretag\n");
723                    }
724 #endif
725         }
726 }
727 
728 
729 /*
730 ========================
731 =
732 = Z_ChangeTag
733 =
734 ========================
735 */
736 
Z_ChangeTag(void * ptr,int tag)737 void Z_ChangeTag (void *ptr, int tag)
738 {
739         memblock_t      *block;
740 
741         block = (memblock_t *) ( (byte *)ptr - sizeof(memblock_t));
742         block->tag = tag;
743 }
744 
745 /*
746 ========================
747 =
748 = Z_AvailHeap
749 =
750 ========================
751 */
752 
Z_AvailHeap(void)753 int Z_AvailHeap ( void )
754 {
755 #ifdef DOS
756    union REGS zregs;
757    struct SREGS zsregs;
758 
759 
760    zregs.x.eax = 0x00000500;
761    memset( &zsregs, 0, sizeof(zsregs) );
762    zsregs.es = FP_SEG( &MemInfo );
763    zregs.x.edi = FP_OFF( &MemInfo );
764 
765    int386x( DPMI_INT, &zregs, &zregs, &zsregs );
766 
767    return ((int)MemInfo.LargestBlockAvail);
768 #else
769 	return MAXMEMORYSIZE;
770 #endif
771 }
772 
773 /*
774 ========================
775 =
776 = Z_Realloc
777 =
778 ========================
779 */
780 
Z_Realloc(void ** ptr,int newsize)781 void Z_Realloc (void ** ptr, int newsize)
782 {
783    memblock_t      *block;
784    void * newptr;
785    int oldsize;
786 
787    block = (memblock_t *) ( (byte *)(*ptr) - sizeof(memblock_t));
788    oldsize = block->size;
789    newptr = SafeMalloc(newsize);
790    if (oldsize > newsize)
791       {
792       oldsize = newsize;
793       }
794    memcpy( newptr, *ptr, oldsize );
795    SafeFree( *ptr );
796    *ptr = newptr;
797 }
798 
799