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