1 #if 1
2 
3 
4 #include "3dc.h"
5 
6 #include <sys/stat.h>
7 
8 #include "inline.h"
9 
10 #ifdef RIFF_SYSTEM
11 #include "chnktexi.h"
12 #endif
13 
14 #define UseLocalAssert 0
15 #include "ourasert.h"
16 
17 #else
18 
19 #include <stdio.h>
20 #include <sys/stat.h>
21 
22 #include "system.h"
23 #include "equates.h"
24 #include "platform.h"
25 #include "shape.h"
26 #include "prototyp.h"
27 #include "inline.h"
28 
29 #ifdef RIFF_SYSTEM
30 #include "chnktexi.h"
31 #endif
32 
33 
34 #endif
35 
36 
37 #include "awtexld.h"
38 
39 #if 0 /* SBF - commented out */
40 #include "alt_tab.h"
41 #endif
42 
43 /*
44 	#define for experimental purposes
45 	ONLY!!!
46 */
47 
48 #define DefinedTextureType TextureTypePPM
49 
50 
51 /*
52 
53  externs for commonly used global variables and arrays
54 
55 */
56 
57 	extern SHAPEHEADER **mainshapelist;
58 	extern SCREENDESCRIPTORBLOCK ScreenDescriptorBlock;
59 	extern unsigned char *ScreenBuffer;
60 	extern char projectsubdirectory[];
61     extern int ScanDrawMode;
62 	extern int VideoModeType;
63 
64 /*
65 
66  Global Variables for PC Functions
67 
68 */
69 
70 	TEXTURE *ImageBuffer;							/* Memory Resident Image Data */
71 
72 	#ifdef MaxImageGroups
73 	#if MaxImageGroups < 2 /* optimize if this multiple groups are not required */
74 	#undef MaxImageGroups
75 	#endif /* MaxImageGroups < 2 */
76 	#endif /* MaxImageGroups */
77 
78 	#ifdef MaxImageGroups
79 
80 	#include "txioctrl.h"
81 
82 	/*
83 	basically, I want there to be more than one image header array
84 	so that I can load some images once only, then load shapes, call
85 	InitializeTextures, DeallocateAllImages, etc. and only the images associated
86 	with the shapes load are deallocated.
87 	I might want to load shapes in two blocks, calling InitializeTextures
88 	for each load.
89 
90 	There will need to be as many slots for ImageHeaderArrays as MaxImageGroups
91 
92 	I want this to be completely invisible to anyone except programmers on
93 	PC projects that require this feature
94 
95 	Jake.
96 	*/
97 
98 	/* these three globals must behave the same */
99 	int NumImages = 0;								/* # current images */
100 	IMAGEHEADER *ImageHeaderPtrs[MaxImageGroups*MaxImages];	/* Ptrs to Image Header Blocks */
101 	IMAGEHEADER ImageHeaderArray[MaxImageGroups*MaxImages];	/* Array of Image Headers */
102 
103 	int NumImagesArray[MaxImageGroups]; /* must be static to ensure initialization to zero */
104 	static int CurrentImageGroup = 0;
105 	static IMAGEHEADER *NextFreeImageHeaderPtr[MaxImageGroups];
106 
107 	#else /* ! MaxImageGroups */
108 
109 	int NumImages = 0;								/* # current images */
110 	IMAGEHEADER *ImageHeaderPtrs[MaxImages];	/* Ptrs to Image Header Blocks */
111 	IMAGEHEADER ImageHeaderArray[MaxImages];	/* Array of Image Headers */
112 
113 	static IMAGEHEADER *NextFreeImageHeaderPtr;
114 
115 	#endif /* ! MaxImageGroups */
116 
117 
118 /*
119 
120  Initialise General Texture Data Structures and Variables
121 
122 */
123 
124 
125 #if LoadingMapsShapesAndTexturesEtc
126 
127 
InitialiseImageHeaders(void)128 void InitialiseImageHeaders(void)
129 
130 {
131 	#ifdef MaxImageGroups
132 
133 	NumImages = CurrentImageGroup * MaxImages;
134 	NextFreeImageHeaderPtr[CurrentImageGroup] = &ImageHeaderArray[CurrentImageGroup*MaxImages];
135 
136 	#else
137 
138 	NumImages = 0;
139 	NextFreeImageHeaderPtr = ImageHeaderArray;
140 
141 	#endif
142 }
143 
144 #else
145 
146 
147 #define InitTexPrnt No
148 
InitialiseTextures(void)149 int InitialiseTextures(void)
150 
151 {
152 
153 	SHAPEHEADER **shlistptr;
154 	SHAPEHEADER *shptr;
155 	char **txfiles;
156 	int TxIndex;
157 	int LTxIndex;
158 
159 	/*
160 
161 	Free up any currently loaded images
162 
163 	The caller is responsible for any other structures which might refer
164 	to the currently loaded images
165 
166 	*/
167 
168 	#ifdef MaxImageGroups
169 
170     DeallocateCurrentImages();
171 
172 	NumImages = CurrentImageGroup * MaxImages;
173 	NextFreeImageHeaderPtr[CurrentImageGroup] = &ImageHeaderArray[CurrentImageGroup*MaxImages];
174 
175 	#else
176 
177     DeallocateAllImages();
178 
179 	/* Initialise Image Header Variables */
180 
181 	NumImages = 0;
182 	NextFreeImageHeaderPtr = ImageHeaderArray;
183 
184 	#endif
185 
186 	/* Added 23/3/98 by DHM so that this can be called without loading any
187 	shapes (to get textprint working in the menus):
188 	*/
189 	if ( NULL == mainshapelist )
190 	{
191 		return Yes;
192 			// early exit
193 	}
194 
195 	/* Build the Texture List */
196 
197 	shlistptr = &mainshapelist[0];
198 
199 	while(*shlistptr) {
200 
201 		shptr = *shlistptr++;
202 
203 		/* If the shape has textures */
204 
205 		if(shptr->sh_localtextures) {
206 
207 			#if InitTexPrnt
208 			textprint("This shape has textures\n");
209 			#endif
210 
211 			txfiles = shptr->sh_localtextures;
212 
213 			LTxIndex = 0;
214 
215 			while(*txfiles) {
216 
217 				/* The RIFF Image loaders have changed to support not loading the same image twice - JH 17-2-96 */
218 
219 				char *src;
220 				char *dst;
221 				char fname[ImageNameSize];
222 				char *txfilesptr;
223 				#ifndef RIFF_SYSTEM
224 				int i, j, NewImage;
225 				char *iname;
226 				IMAGEHEADER *ihptr;
227 				IMAGEHEADER *new_ihptr;
228 				void* im;
229 				#endif
230 
231 				txfilesptr = *txfiles++;
232 
233 				/*
234 
235 				"txfilesptr" is in the form "textures\<fname>". We need to
236 				prefix that text with the name of the current textures path.
237 
238 				Soon this path may be varied but for now it is just the name of
239 				the current project subdirectory.
240 
241 				*/
242 
243 				src = projectsubdirectory;
244 				dst = fname;
245 
246 				while(*src)
247 					*dst++ = *src++;
248 
249 				src = txfilesptr;
250 
251 				while(*src)
252 					*dst++ = *src++;
253 
254 				*dst = 0;
255 
256 
257 				#if InitTexPrnt
258 				textprint(" A Texture\n");
259 				#endif
260 
261 
262 				#ifdef RIFF_SYSTEM
263 
264 				/* This function calls GetExistingImageHeader to figure out if the image is already loaded */
265 				TxIndex = CL_LoadImageOnce(fname,(ScanDrawDirectDraw == ScanDrawMode ? LIO_CHIMAGE : LIO_D3DTEXTURE)|LIO_TRANSPARENT|LIO_RELATIVEPATH|LIO_RESTORABLE);
266 				GLOBALASSERT(GEI_NOTLOADED != TxIndex);
267 
268 				#else
269 
270 				/* If there are already images, try and find this one */
271 
272 				NewImage = Yes;
273 
274 				#ifdef MaxImageGroups
275 
276 				TxIndex = CurrentImageGroup * MaxImages;			/* Assume image 0 */
277 
278 				if(NumImagesArray[CurrentImageGroup]) {
279 
280 					for(i=NumImagesArray[CurrentImageGroup]; i!=0 && NewImage!=No; i--) {
281 
282 				#else
283 
284 				TxIndex = 0;			/* Assume image 0 */
285 
286 				if(NumImages) {
287 
288 					for(i=NumImages; i!=0 && NewImage!=No; i--) {
289 
290 				#endif
291 
292 						ihptr = ImageHeaderPtrs[TxIndex];
293 
294 						iname = &ihptr->ImageName[0];
295 
296 						j = CompareFilenameCH(txfilesptr, iname);
297 
298 						if(j) NewImage = No;
299 
300 						else TxIndex++;
301 
302 					}
303 
304 				}
305 
306 
307 				/* If this is a new image, add it */
308 
309 				if(NewImage) {
310 
311 					#if InitTexPrnt
312 					textprint("New Image\n");
313 					WaitForReturn();
314 					#endif
315 
316 					/* Get an Image Header */
317 
318 					new_ihptr = GetImageHeader();
319 
320 					if(new_ihptr) {
321 
322 			            if (ScanDrawMode == ScanDrawDirectDraw)
323 						  im = (void*) LoadImageCH(&fname[0], new_ihptr);
324 						else
325 						  im = LoadImageIntoD3DImmediateSurface
326 						     (&fname[0], new_ihptr, DefinedTextureType);
327 
328 						if(im) {
329 
330 							#if InitTexPrnt
331 							textprint("Load OK, NumImages = %d\n", NumImages);
332 							WaitForReturn();
333 							#endif
334 
335 						}
336 
337 					}
338 
339 				}
340 
341 
342 				/* test */
343 
344 				#if InitTexPrnt
345 				else textprint("Image Already Exists\n");
346 				#endif
347 
348 				#endif
349 
350 				/*
351 
352 					The local index for this image in this shape is
353 					"LTxIndex".
354 
355 					The global index for the image is "TxIndex".
356 
357 					We must go through the shape's items and change all the
358 					local references to global.
359 
360 				*/
361 
362 				#if InitTexPrnt
363 				textprint("\nLocal to Global for Shape\n");
364 				#endif
365 
366 				MakeShapeTexturesGlobal(shptr, TxIndex, LTxIndex);
367 
368 				LTxIndex++;			/* Next Local Texture */
369 
370 			}
371 
372 			/* Is this shape a sprite that requires resizing? */
373 
374 			if((shptr->shapeflags & ShapeFlag_Sprite) &&
375 				(shptr->shapeflags & ShapeFlag_SpriteResizing)) {
376 
377 				SpriteResizing(shptr);
378 
379 			}
380 
381 		}
382 
383 	}
384 
385 	#if InitTexPrnt
386 	textprint("\nFinished PP for textures\n");
387 
388 	WaitForReturn();
389 
390 	#endif
391 
392 	return Yes;
393 }
394 
395 
396 #endif
397 
398 
399 /*
400 
401  This function accepts a shape header, a global texture index and a local
402  index. It searches through all the items that use textures and converts all
403  local references to a texture to global references.
404 
405  The index to a texture that refers to the IMAGEHEADER pointer array is in
406  the low word of the colour int.
407 
408  Bit #15 is set if this index refers to a local texture.
409 
410  Here is an example of a textured item:
411 
412 
413 int CUBE_item3[]={
414 
415 	I_2dTexturedPolygon,3*vsize,0,
416 
417 	(3<<TxDefn) + TxLocal + 0,
418 
419 	7*vsize,5*vsize,1*vsize,3*vsize,
420 	Term
421 
422 };
423 
424 
425  To test for a local texture use:
426 
427 	if(ColourInt & TxLocal)
428 
429 
430 
431  NOTE
432 
433  The procedure is NOT reversible!
434 
435  If one wishes to reconstruct the table, all the shapes and textures must be
436  reloaded and the initialisation function recalled.
437 
438  Actually a function could be written that relates global filenames back to
439  local filenames, so in a sense that is not true. This function will only be
440  written if it is needed.
441 
442 */
443 
444 void MakeShapeTexturesGlobal(SHAPEHEADER *shptr, int TxIndex, int LTxIndex)
445 
446 {
447 
448 	int **ShapeItemArrayPtr;
449 	POLYHEADER *ShapeItemPtr;
450 
451 	#if SupportBSP
452 	SHAPEDATA_BSP_BLOCK *ShapeBSPPtr;
453 	int num_bsp_blocks;
454 	int j, k;
455 	#endif
456 
457 	int i, txi;
458 
459 
460 	/* Are the items in a pointer array? */
461 
462 	if(shptr->items) {
463 
464 		#if InitTexPrnt
465 		textprint("Item Array\n");
466 		#endif
467 
468 		ShapeItemArrayPtr = shptr->items;
469 
470 		for(i = shptr->numitems; i!=0; i--) {
471 
472 			ShapeItemPtr = (POLYHEADER *) *ShapeItemArrayPtr++;
473 
474 			#if SupportZBuffering
475 
476 			if(ShapeItemPtr->PolyItemType == I_2dTexturedPolygon
477 				|| ShapeItemPtr->PolyItemType == I_ZB_2dTexturedPolygon
478 				|| ShapeItemPtr->PolyItemType == I_Gouraud2dTexturedPolygon
479 				|| ShapeItemPtr->PolyItemType == I_ZB_Gouraud2dTexturedPolygon
480 				|| ShapeItemPtr->PolyItemType == I_Gouraud3dTexturedPolygon
481 				|| ShapeItemPtr->PolyItemType == I_ZB_Gouraud3dTexturedPolygon
482 				|| ShapeItemPtr->PolyItemType == I_ScaledSprite
483 				|| ShapeItemPtr->PolyItemType == I_3dTexturedPolygon
484 				|| ShapeItemPtr->PolyItemType == I_ZB_3dTexturedPolygon) {
485 			#else
486 			if(ShapeItemPtr->PolyItemType == I_2dTexturedPolygon
487 				|| ShapeItemPtr->PolyItemType == I_Gouraud2dTexturedPolygon
488 				|| ShapeItemPtr->PolyItemType == I_Gouraud3dTexturedPolygon
489 				|| ShapeItemPtr->PolyItemType == I_ScaledSprite
490 				|| ShapeItemPtr->PolyItemType == I_3dTexturedPolygon){
491 			#endif /* SupportZBuffering */
492 
493 				if(ShapeItemPtr->PolyFlags & iflag_txanim) {
494 
495 					MakeTxAnimFrameTexturesGlobal(shptr, ShapeItemPtr,
496 															LTxIndex, TxIndex);
497 
498 				}
499 
500 				if(ShapeItemPtr->PolyColour & TxLocal) {
501 
502 					txi = ShapeItemPtr->PolyColour;
503 					txi &= ~TxLocal;							/* Clear Flag */
504 					txi &= ClrTxDefn;							/* Clear UV array index */
505 
506 					/* Is this the local index? */
507 
508 					if(txi == LTxIndex) {
509 
510 						/* Clear low word, OR in global index */
511 
512 						ShapeItemPtr->PolyColour &= ClrTxIndex;
513 						ShapeItemPtr->PolyColour |= TxIndex;
514 
515 					}
516 
517 				}
518 
519 			}
520 
521 		}
522 
523 	}
524 
525 	#if SupportBSP
526 
527 	/* Or are they in a BSP block array? */
528 
529 	else if(shptr->sh_bsp_blocks) {
530 
531 		#if InitTexPrnt
532 		textprint("BSP Block Array\n");
533 		#endif
534 
535 		#if 0
536 		/* Find the BSP Instruction */
537 		ShInstrPtr = shptr->sh_instruction;
538 		num_bsp_blocks = 0;
539 		while(ShInstrPtr->sh_instr != I_ShapeEnd) {
540 			if(ShInstrPtr->sh_instr == I_ShapeBSPTree) {
541 				num_bsp_blocks = ShInstrPtr->sh_numitems;
542 			}
543 			ShInstrPtr++;
544 		}
545 		#endif
546 
547 		num_bsp_blocks = FindNumBSPNodes(shptr);
548 
549 
550 		#if InitTexPrnt
551 		textprint("Number of BSP blocks = %d\n", num_bsp_blocks);
552 		#endif
553 
554 		if(num_bsp_blocks) {
555 
556 			ShapeBSPPtr = shptr->sh_bsp_blocks;
557 
558 			for(k=num_bsp_blocks; k!=0; k--) {
559 
560 				ShapeItemArrayPtr = ShapeBSPPtr->bsp_block_data;
561 
562 				for(j=ShapeBSPPtr->bsp_numitems; j!=0; j--) {
563 
564 					ShapeItemPtr = (POLYHEADER *) *ShapeItemArrayPtr++;
565 
566 					#if InitTexPrnt
567 					textprint("shape item\n");
568 					#endif
569 
570 					if(ShapeItemPtr->PolyItemType == I_2dTexturedPolygon
571 						|| ShapeItemPtr->PolyItemType == I_ZB_2dTexturedPolygon
572 						|| ShapeItemPtr->PolyItemType == I_Gouraud2dTexturedPolygon
573 						|| ShapeItemPtr->PolyItemType == I_ZB_Gouraud2dTexturedPolygon
574 						|| ShapeItemPtr->PolyItemType == I_Gouraud3dTexturedPolygon
575 						|| ShapeItemPtr->PolyItemType == I_ZB_Gouraud3dTexturedPolygon
576 						|| ShapeItemPtr->PolyItemType == I_ScaledSprite
577 						|| ShapeItemPtr->PolyItemType == I_3dTexturedPolygon
578 						|| ShapeItemPtr->PolyItemType == I_ZB_3dTexturedPolygon) {
579 
580 						if(ShapeItemPtr->PolyFlags & iflag_txanim) {
581 
582 							MakeTxAnimFrameTexturesGlobal(shptr, ShapeItemPtr,
583 																	LTxIndex, TxIndex);
584 
585 						}
586 
587 						#if InitTexPrnt
588 						textprint(" - textured\n");
589 						#endif
590 
591 						if(ShapeItemPtr->PolyColour & TxLocal) {
592 
593 							#if InitTexPrnt
594 							textprint("  - local index\n");
595 							#endif
596 
597 							txi = ShapeItemPtr->PolyColour;
598 							txi &= ~(TxLocal);					/* Clear Flag */
599 							txi &= ClrTxDefn;						/* Clear Defn */
600 
601 							#if InitTexPrnt
602 							textprint("  - is %d\n", txi);
603 							textprint("  - LTxIndex is %d\n", LTxIndex);
604 							#endif
605 
606 							/* Is this the local index? */
607 
608 							if(txi == LTxIndex) {
609 
610 								/* Clear low word, OR in global index */
611 
612 								ShapeItemPtr->PolyColour &= ClrTxIndex;
613 								ShapeItemPtr->PolyColour |= TxIndex;
614 
615 								#if InitTexPrnt
616 								textprint("Local %d, Global %d\n", LTxIndex, TxIndex);
617 								#endif
618 
619 							}
620 
621 						}
622 
623 					}
624 
625 				}
626 
627 				ShapeBSPPtr++;
628 
629 			}
630 
631 		}
632 
633 	}
634 
635 	#endif	/* SupportBSP */
636 
637 	/* Otherwise the shape has no item data */
638 
639 
640 }
641 
642 
643 /*
644 
645  The animated texture frames each have a local image index in their frame
646  header structure. Convert these to global texture list indices in the same
647  way that the item function does.
648 
649 */
650 
651 void MakeTxAnimFrameTexturesGlobal(SHAPEHEADER *sptr,
652 														POLYHEADER *pheader,
653 														int LTxIndex, int TxIndex)
654 
655 {
656 
657 	TXANIMHEADER **txah_ptr;
658 	TXANIMHEADER *txah;
659 	TXANIMFRAME *txaf;
660 	int **shape_textures;
661 	int *txf_imageptr;
662 	int texture_defn_index;
663 	int i, txi, image;
664 
665 
666 	#if 0
667 	textprint("LTxIndex = %d, TxIndex = %d\n", LTxIndex, TxIndex);
668 	WaitForReturn();
669 	#endif
670 
671 
672 	/* Get the animation sequence header */
673 
674 	shape_textures = sptr->sh_textures;
675 	texture_defn_index = (pheader->PolyColour >> TxDefn);
676 	txah_ptr = (TXANIMHEADER **) shape_textures[texture_defn_index];
677 
678 
679 	/* The first array element is the sequence shadow, which we skip here */
680 
681 	txah_ptr++;
682 
683 
684 	/* Process the animation sequences */
685 
686 	while(*txah_ptr) {
687 
688 		/* Get the animation header */
689 
690 		txah = *txah_ptr++;
691 
692 		/* Process the animation frames */
693 
694 		if(txah && txah->txa_numframes) {
695 
696 			txaf = txah->txa_framedata;
697 
698 			for(i = txah->txa_numframes; i!=0; i--) {
699 
700 				/* Multi-View Sprite? */
701 
702 				if(sptr->shapeflags & ShapeFlag_MultiViewSprite) {
703 
704 					txf_imageptr = (int *) txaf->txf_image;
705 
706 					for(image = txah->txa_num_mvs_images; image!=0; image--) {
707 
708 						if(*txf_imageptr & TxLocal) {
709 
710 							txi = *txf_imageptr;
711 							txi &= ~TxLocal;					/* Clear Flag */
712 
713 							if(txi == LTxIndex) {
714 
715 								*txf_imageptr = TxIndex;
716 
717 							}
718 
719 						}
720 
721 						txf_imageptr++;
722 
723 					}
724 
725 				}
726 
727 				else {
728 
729 					if(txaf->txf_image & TxLocal) {
730 
731 						txi = txaf->txf_image;
732 						txi &= ~TxLocal;					/* Clear Flag */
733 
734 						if(txi == LTxIndex) {
735 
736 							txaf->txf_image = TxIndex;
737 
738 						}
739 
740 					}
741 
742 				}
743 
744 				txaf++;
745 
746 			}
747 
748 		}
749 
750 	}
751 
752 }
753 
754 
755 
756 /*
757 
758  Sprite Resizing
759 
760  or
761 
762  An Optimisation For Sprite Images
763 
764  This shape is a sprite which has requested the UV rescaling optimisation.
765  The UV array is resized to fit the sprite image bounding rectangle, and
766  after the UV array, in the space provided (don't forget that!), is a new
767  shape local space XY array of points which overwrite the standard values
768  when the shape is displayed.
769 
770 */
771 
772 #define sr_print No
773 
774 void SpriteResizing(SHAPEHEADER *sptr)
775 
776 {
777 
778 	TXANIMHEADER **txah_ptr;
779 	TXANIMHEADER *txah;
780 	TXANIMFRAME *txaf;
781 	int **shape_textures;
782 	IMAGEHEADER *ihdr;
783 	IMAGEEXTENTS e;
784 	IMAGEEXTENTS e_curr;
785 	IMAGEPOLYEXTENTS e_poly;
786 	int *uvptr;
787 	int texture_defn_index;
788 	int **item_array_ptr;
789 	int *item_ptr;
790 	POLYHEADER *pheader;
791 	int i, f;
792 	int polypts[4 * vsize];
793 	int *iptr;
794 	int *iptr2;
795 	int *mypolystart;
796 	int *ShapePoints = *(sptr->points);
797 	VECTOR2D cen_poly;
798 	VECTOR2D size_poly;
799 	VECTOR2D cen_uv_curr;
800 	VECTOR2D size_uv_curr;
801 	VECTOR2D cen_uv;
802 	VECTOR2D size_uv;
803 	VECTOR2D tv;
804 	int *txf_imageptr;
805 	int **txf_uvarrayptr;
806 	int *txf_uvarray;
807 	int image;
808 	int num_images;
809 
810 
811 	#if sr_print
812 	textprint("\nSprite Resize Shape\n\n");
813 	#endif
814 
815 
816 	/* Get the animation sequence header */
817 
818 	shape_textures = sptr->sh_textures;
819 
820 	item_array_ptr = sptr->items;		/* Assume item array */
821 	item_ptr = item_array_ptr[0];		/* Assume only one polygon */
822 	pheader = (POLYHEADER *) item_ptr;
823 
824 
825 	/* Get the polygon points, and at the same time the extents, assuming an XY plane polygon */
826 
827 	e_poly.x_low = bigint;
828 	e_poly.y_low = bigint;
829 
830 	e_poly.x_high = smallint;
831 	e_poly.y_high = smallint;
832 
833 	iptr = polypts;
834 	mypolystart = &pheader->Poly1stPt;
835 
836 	for(i = 4; i!=0; i--) {
837 
838 		iptr[ix] = ((VECTORCH*)ShapePoints)[*mypolystart].vx;
839 		iptr[iy] = ((VECTORCH*)ShapePoints)[*mypolystart].vy;
840 		iptr[iz] = ((VECTORCH*)ShapePoints)[*mypolystart].vz;
841 
842 		if(iptr[ix] < e_poly.x_low) e_poly.x_low = iptr[ix];
843 		if(iptr[iy] < e_poly.y_low) e_poly.y_low = iptr[iy];
844 		if(iptr[ix] > e_poly.x_high) e_poly.x_high = iptr[ix];
845 		if(iptr[iy] > e_poly.y_high) e_poly.y_high = iptr[iy];
846 
847 		iptr += vsize;
848 		mypolystart++;
849 
850 	}
851 
852 
853 	texture_defn_index = (pheader->PolyColour >> TxDefn);
854 	txah_ptr = (TXANIMHEADER **) shape_textures[texture_defn_index];
855 
856 
857 	/* The first array element is the sequence shadow, which we skip here */
858 
859 	txah_ptr++;
860 
861 
862 	/* Process the animation sequences */
863 
864 	while(*txah_ptr) {
865 
866 		/* Get the animation header */
867 
868 		txah = *txah_ptr++;
869 
870 		/* Process the animation frames */
871 
872 		if(txah && txah->txa_numframes) {
873 
874 			txaf = txah->txa_framedata;
875 
876 			for(f = txah->txa_numframes; f!=0; f--) {
877 
878 
879 				/* Multi-View Sprite? */
880 
881 				if(sptr->shapeflags & ShapeFlag_MultiViewSprite) {
882 
883 					txf_imageptr = (int *) txaf->txf_image;
884 					num_images = txah->txa_num_mvs_images;
885 
886 					txf_uvarrayptr = (int **) txaf->txf_uvdata;
887 
888 				}
889 
890 				/* A standard "Single View" Sprite has just one image */
891 
892 				else {
893 
894 					txf_imageptr = &txaf->txf_image;
895 					num_images = 1;
896 
897 					txf_uvarrayptr = &txaf->txf_uvdata;
898 
899 				}
900 
901 
902 				for(image = 0; image < num_images; image++) {
903 
904 
905 					#if sr_print
906 					textprint("image %d of %d   \n", (image + 1), num_images);
907 					#endif
908 
909 
910 					/* Get the image */
911 
912 					ihdr = ImageHeaderPtrs[txf_imageptr[image]];
913 
914 					/* Get the uv array ptr */
915 
916 					txf_uvarray = txf_uvarrayptr[image];
917 
918 					/* Find the extents of the image, assuming transparency */
919 
920 					#if 0
921 					FindImageExtents(ihdr, txaf->txf_numuvs, txaf->txf_uvdata, &e, &e_curr);
922 					#else
923 					FindImageExtents(ihdr, txaf->txf_numuvs, txf_uvarray, &e, &e_curr);
924 					#endif
925 
926 					/* Convert the image extents to fixed point */
927 
928 					#if sr_print
929 					textprint("extents = %d, %d\n", e.u_low, e.v_low);
930 					textprint("          %d, %d\n", e.u_high, e.v_high);
931 					WaitForReturn();
932 					#endif
933 
934 					e.u_low  <<= 16;
935 					e.v_low  <<= 16;
936 					e.u_high <<= 16;
937 					e.v_high <<= 16;
938 
939 					/*
940 
941 					We now have all the information needed to create a NEW UV array and a NEW
942 					polygon points XY array.
943 
944 					*/
945 
946 					/* Centre of the polygon */
947 
948 					cen_poly.vx = (e_poly.x_low + e_poly.x_high) / 2;
949 					cen_poly.vy = (e_poly.y_low + e_poly.y_high) / 2;
950 
951 					/* Size of the polygon */
952 
953 					size_poly.vx = e_poly.x_high - e_poly.x_low;
954 					size_poly.vy = e_poly.y_high - e_poly.y_low;
955 
956 
957 					/* Centre of the current cookie */
958 
959 					cen_uv_curr.vx = (e_curr.u_low + e_curr.u_high) / 2;
960 					cen_uv_curr.vy = (e_curr.v_low + e_curr.v_high) / 2;
961 
962 					/* Size of the current cookie */
963 
964 					size_uv_curr.vx = e_curr.u_high - e_curr.u_low;
965 					size_uv_curr.vy = e_curr.v_high - e_curr.v_low;
966 
967 
968 					/* Centre of the new cookie */
969 
970 					cen_uv.vx = (e.u_low + e.u_high) / 2;
971 					cen_uv.vy = (e.v_low + e.v_high) / 2;
972 
973 					/* Size of the new cookie */
974 
975 					size_uv.vx = e.u_high - e.u_low;
976 					size_uv.vy = e.v_high - e.v_low;
977 
978 
979 					/* Write out the new UV data */
980 
981 					#if 0
982 					uvptr = txaf->txf_uvdata;
983 					#else
984 					uvptr = txf_uvarray;
985 					#endif
986 
987 
988 					/*
989 
990 					Convert the duplicate UV array to this new scale
991 					ASSUME that the format is "TL, BL, BR, TR"
992 
993 					*/
994 
995 					uvptr[0] = e.u_low;
996 					uvptr[1] = e.v_low;
997 
998 					uvptr[2] = e.u_low;
999 					uvptr[3] = e.v_high;
1000 
1001 					uvptr[4] = e.u_high;
1002 					uvptr[5] = e.v_high;
1003 
1004 					uvptr[6] = e.u_high;
1005 					uvptr[7] = e.v_low;
1006 
1007 
1008 					/*
1009 
1010 					Create the new polygon XY array
1011 
1012 					*/
1013 
1014 					uvptr += (txaf->txf_numuvs * 2);	/* Advance the pointer past the UV array */
1015 
1016 
1017 					/* Copy the polygon points (XY only) to the UV array space */
1018 
1019 					iptr  = polypts;
1020 					iptr2 = uvptr;
1021 
1022 					for(i = 4; i!=0; i--) {
1023 
1024 						iptr2[0] = iptr[ix];
1025 						iptr2[1] = iptr[iy];
1026 
1027 						iptr  += vsize;
1028 						iptr2 += 2;
1029 
1030 					}
1031 
1032 
1033 					/* Scale the polygon points */
1034 
1035 					iptr = uvptr;
1036 
1037 					for(i = 4; i!=0; i--) {
1038 
1039 						iptr[0] = WideMulNarrowDiv(iptr[0], size_uv.vx, size_uv_curr.vx);
1040 						iptr[1] = WideMulNarrowDiv(iptr[1], size_uv.vy, size_uv_curr.vy);
1041 
1042 						iptr += 2;
1043 
1044 					}
1045 
1046 
1047 					/* The translation vector in UV space */
1048 
1049 					tv.vx = cen_uv.vx - cen_uv_curr.vx;
1050 					tv.vy = cen_uv.vy - cen_uv_curr.vy;
1051 
1052 
1053 					/* And now in world space */
1054 
1055 					tv.vx = WideMulNarrowDiv(tv.vx, size_poly.vx, size_uv_curr.vx);
1056 					tv.vy = WideMulNarrowDiv(tv.vy, size_poly.vy, size_uv_curr.vy);
1057 
1058 
1059 					/* Translate the polygon points */
1060 
1061 					iptr = uvptr;
1062 
1063 					for(i = 4; i!=0; i--) {
1064 
1065 						iptr[0] += tv.vx;
1066 						iptr[1] += tv.vy;
1067 
1068 						iptr += 2;
1069 
1070 					}
1071 
1072 
1073 					#if sr_print
1074 					textprint(" (image done)\n");
1075 					#endif
1076 
1077 
1078 				}
1079 
1080 
1081 				#if sr_print
1082 				textprint("\n");
1083 				#endif
1084 
1085 
1086 				/* Next Texture Animation Frame */
1087 
1088 				txaf++;
1089 
1090 			}
1091 
1092 		}
1093 
1094 	}
1095 
1096 
1097 	#if sr_print
1098 	textprint("\nResize done\n\n");
1099 	#endif
1100 
1101 }
1102 
1103 
1104 /*
1105 
1106  This function is for animated sprites, although it has been given an interface that would
1107  allow anyone to call it for their own purposes. It assumes that colour 0 is transparent and
1108  then calculates the actual maximum extents of the image.
1109 
1110  NOTE:
1111 
1112  The image scanned is that given by the CURRENT UV ARRAY and may therefore be smaller than
1113  the actual image. This allows for animation sequences with frames on the same image.
1114 
1115 */
1116 
1117 void FindImageExtents(IMAGEHEADER *ihdr, int numuvs, int *uvdata, IMAGEEXTENTS *e, IMAGEEXTENTS *e_curr)
1118 
1119 {
1120 
1121 	int i;
1122 	int *uvptr;
1123 	int u, v;
1124 	int startu, endu;
1125 	int startv, endv;
1126 
1127 
1128 	/* Find the current UV extents */
1129 
1130 	e_curr->u_low = bigint;
1131 	e_curr->v_low = bigint;
1132 
1133 	e_curr->u_high = smallint;
1134 	e_curr->v_high = smallint;
1135 
1136 	uvptr = uvdata;
1137 
1138 	for(i = numuvs; i!=0; i--) {
1139 
1140 		if(uvptr[0] < e_curr->u_low) e_curr->u_low = uvptr[0];
1141 		if(uvptr[1] < e_curr->v_low) e_curr->v_low = uvptr[1];
1142 
1143 		if(uvptr[0] > e_curr->u_high) e_curr->u_high = uvptr[0];
1144 		if(uvptr[1] > e_curr->v_high) e_curr->v_high = uvptr[1];
1145 
1146 		uvptr += 2;
1147 
1148 	}
1149 
1150 
1151 	/* Look for the actual UV extents, assuming that colour 0 is transparent */
1152 
1153 	switch(VideoModeType) {
1154 
1155 		case VideoModeType_8:
1156 
1157 			{
1158 
1159 				TEXTURE *tptr = ihdr->ImagePtr;
1160 				TEXTURE texel;
1161 
1162 
1163 				/* Search for u_low and v_low */
1164 
1165 				e->u_low = bigint;
1166 				e->v_low = bigint;
1167 
1168 				startv = e_curr->v_low  >> 16;
1169 				endv   = e_curr->v_high >> 16;
1170 				startu = e_curr->u_low  >> 16;
1171 				endu   = e_curr->u_high >> 16;
1172 
1173 				for(v = startv; v <= endv; v++) {
1174 
1175 					for(u = startu; u <= endu; u++) {
1176 
1177 						texel = tptr[(v * ihdr->ImageWidth) + u];
1178 
1179 						if(texel) {
1180 
1181 							if(u < e->u_low) e->u_low = u;
1182 							if(v < e->v_low) e->v_low = v;
1183 
1184 						}
1185 
1186 					}
1187 
1188 				}
1189 
1190 				if(e->u_low == bigint) e->u_low = e_curr->u_low;
1191 				if(e->v_low == bigint) e->v_low = e_curr->v_low;
1192 
1193 
1194 				/* Search for u_high and v_high */
1195 
1196 				e->u_high = smallint;
1197 				e->v_high = smallint;
1198 
1199 				for(v = endv; v >= startv; v--) {
1200 
1201 					for(u = endu; u >= startu;  u--) {
1202 
1203 						texel = tptr[(v * ihdr->ImageWidth) + u];
1204 
1205 						if(texel) {
1206 
1207 							if(u > e->u_high) e->u_high = u;
1208 							if(v > e->v_high) e->v_high = v;
1209 
1210 						}
1211 
1212 					}
1213 
1214 				}
1215 
1216 				if(e->u_high == smallint) e->u_high = e_curr->u_high;
1217 				if(e->v_high == smallint) e->v_high = e_curr->v_high;
1218 
1219 			}
1220 
1221 			break;
1222 
1223 		case VideoModeType_15:
1224 			break;
1225 
1226 		case VideoModeType_24:
1227 			break;
1228 
1229 		case VideoModeType_8T:
1230 			break;
1231 
1232 	}
1233 
1234 }
1235 
1236 
1237 
1238 /*
1239 
1240  Return a pointer to the next image header
1241 
1242 */
1243 
1244 
1245 IMAGEHEADER* GetImageHeader(void)
1246 {
1247 
1248 	IMAGEHEADER *iheader;
1249 
1250 	#ifdef MaxImageGroups
1251 
1252 	/* NumImages always points to the correct point in the array */
1253 	do
1254 	{
1255 		iheader = NextFreeImageHeaderPtr[CurrentImageGroup]++;
1256 	}
1257 	while (IsImageInUse(CurrentImageGroup,NumImagesArray[CurrentImageGroup]++) ? ++NumImages : 0);
1258 
1259 	GLOBALASSERT(NumImagesArray[CurrentImageGroup] < MaxImages);
1260 
1261 	#else
1262 
1263 	iheader = NextFreeImageHeaderPtr++;
1264 	GLOBALASSERT(NumImages < MaxImages);
1265 
1266 	#endif
1267 
1268 	/* ensure flags are zero */
1269 	memset(iheader,0,sizeof(IMAGEHEADER));
1270 
1271 	ImageHeaderPtrs[NumImages] = iheader;
1272 	NumImages++;
1273 
1274 	return iheader;
1275 
1276 }
1277 
1278 
1279 /*
1280 
1281  Return the address of a texture image in memory, else null
1282 
1283 */
1284 
1285 #if 0
1286 void* GetTexture(int texindex)
1287 
1288 {
1289 
1290     /* Ahem... */
1291 
1292 	return No;
1293 
1294 }
1295 #endif
1296 
1297 /*
1298 
1299  Allocate memory for a Texture Image
1300 
1301 */
1302 
1303 #if 0
1304 TEXTURE* GetTextureMemory(int txsize)
1305 
1306 {
1307 
1308     /* Err... */
1309 	return No;
1310 
1311 }
1312 #endif
1313 
1314 /*
1315 
1316  Deallocate memory for a Texture Image
1317 
1318 */
1319 
1320 #if 0
1321 void ReturnTextureMemory(TEXTURE *txptr)
1322 
1323 {
1324 
1325 }
1326 #endif
1327 
1328 static void DeallocateImageHeader(IMAGEHEADER * ihptr)
1329 {
1330 	if (ihptr->hBackup)
1331 	{
1332 		if (ihptr->DDSurface)
1333 		{
1334 			GLOBALASSERT(!ihptr->D3DTexture);
1335 			ATRemoveSurface(ihptr->DDSurface);
1336 		}
1337 		else if (ihptr->D3DTexture)
1338 		{
1339 			GLOBALASSERT(!ihptr->DDSurface);
1340 			ATRemoveTexture(ihptr->D3DTexture);
1341 		}
1342 
1343 		AwDestroyBackupTexture(ihptr->hBackup);
1344 		ihptr->hBackup = 0;
1345 	}
1346 
1347 	if (ihptr->ImagePtr)
1348 	{
1349 		DeallocateMem(ihptr->ImagePtr);
1350 		ihptr->ImagePtr = 0;
1351 	}
1352 
1353 	if (ihptr->DDSurface)
1354 	{
1355 		ReleaseDDSurface(ihptr->DDSurface);
1356 		ihptr->DDSurface = (void*) 0;
1357 	}
1358 
1359 	if (ihptr->D3DTexture)
1360 	{
1361 		ReleaseD3DTexture(ihptr->D3DTexture);
1362 		ihptr->D3DTexture = (void*) 0;
1363 		ihptr->D3DHandle = /* (void*) */ 0;
1364 	}
1365 }
1366 
1367 static void MinimizeImageHeader(IMAGEHEADER * ihptr)
1368 {
1369 	if (ihptr->DDSurface)
1370 	{
1371 		ReleaseDDSurface(ihptr->DDSurface);
1372 		ihptr->DDSurface = (void*) 0;
1373 	}
1374 
1375 	if (ihptr->D3DTexture)
1376 	{
1377 		ReleaseD3DTexture(ihptr->D3DTexture);
1378 		ihptr->D3DTexture = (void*) 0;
1379 		ihptr->D3DHandle = /* (void*) */ 0;
1380 	}
1381 }
1382 
1383 static void RestoreImageHeader(IMAGEHEADER * ihptr)
1384 {
1385 	if (ScanDrawDirectDraw != ScanDrawMode)
1386 		ReloadImageIntoD3DImmediateSurface(ihptr);
1387 }
1388 
1389 #ifdef MaxImageGroups
1390 
1391 void SetCurrentImageGroup(unsigned int group)
1392 {
1393 	GLOBALASSERT(group < MaxImageGroups);
1394 	CurrentImageGroup = group;
1395 	NumImages = group*MaxImages + NumImagesArray[group];
1396 }
1397 
1398 int DeallocateCurrentImages(void)
1399 {
1400 	int i;
1401 	IMAGEHEADER *ihptr;
1402 
1403 	if (NumImagesArray[CurrentImageGroup])
1404 	{
1405 		ihptr = &ImageHeaderArray[CurrentImageGroup*MaxImages];
1406 		for (i = 0; i < NumImagesArray[CurrentImageGroup]; ++i)
1407 		{
1408 			if (CanDeleteImage(CurrentImageGroup,i))
1409 				DeallocateImageHeader(ihptr);
1410 			++ihptr;
1411 		}
1412 		NumImagesArray[CurrentImageGroup] = 0;
1413 		NumImages = CurrentImageGroup * MaxImages;
1414 		NextFreeImageHeaderPtr[CurrentImageGroup] = &ImageHeaderArray[CurrentImageGroup*MaxImages];
1415 		ImageGroupFreed(CurrentImageGroup);
1416 	}
1417 
1418 	return Yes; /* ok for the moment */
1419 }
1420 
1421 void NowDeleteImage(int img_group, int img_num_offset)
1422 {
1423 	DeallocateImageHeader(&ImageHeaderArray[img_group*MaxImages+img_num_offset]);
1424 }
1425 
1426 int DeallocateAllImages(void)
1427 {
1428 	int i, j;
1429 	IMAGEHEADER *ihptr;
1430 
1431 	for (j=0; j<MaxImageGroups; ++j)
1432 	{
1433 		if (NumImagesArray[j])
1434 		{
1435 			ihptr = &ImageHeaderArray[j*MaxImages];
1436 			for (i = 0; i<NumImagesArray[j]; ++i)
1437 			{
1438 				if (CanDeleteImage(j,i))
1439 					DeallocateImageHeader(ihptr);
1440 				++ihptr;
1441 			}
1442 			NumImagesArray[j] = 0;
1443 		}
1444 		ImageGroupFreed(j);
1445 	}
1446 	NumImages = CurrentImageGroup * MaxImages;
1447 	NextFreeImageHeaderPtr[CurrentImageGroup] = &ImageHeaderArray[CurrentImageGroup*MaxImages];
1448 
1449 	return Yes; /* ok for the moment */
1450 }
1451 
1452 static void MinimizeImageCallback(int i, void * gP)
1453 {
1454 	int g = *(int *)gP;
1455 	MinimizeImageHeader(ImageHeaderPtrs[g*MaxImages+i]);
1456 }
1457 
1458 int MinimizeAllImages(void)
1459 {
1460 	int i, j;
1461 	IMAGEHEADER *ihptr;
1462 
1463 	for (j=0; j<MaxImageGroups; ++j)
1464 	{
1465 		if (NumImagesArray[j])
1466 		{
1467 			ihptr = &ImageHeaderArray[j*MaxImages];
1468 			for (i = 0; i<NumImagesArray[j]; ++i)
1469 			{
1470 				MinimizeImageHeader(ihptr);
1471 				++ihptr;
1472 			}
1473 		}
1474 		EnumLeftoverImages(j,NumImagesArray[j],MinimizeImageCallback,&j);
1475 	}
1476 
1477 	return Yes; /* ok for the moment */
1478 }
1479 
1480 static void RestoreImageCallback(int i, void * gP)
1481 {
1482 	int g = *(int *)gP;
1483 	RestoreImageHeader(ImageHeaderPtrs[g*MaxImages+i]);
1484 }
1485 
1486 int RestoreAllImages(void)
1487 {
1488 	int i, j;
1489 	IMAGEHEADER *ihptr;
1490 
1491 	for (j=0; j<MaxImageGroups; ++j)
1492 	{
1493 		if (NumImagesArray[j])
1494 		{
1495 			ihptr = &ImageHeaderArray[j*MaxImages];
1496 			for (i = 0; i<NumImagesArray[j]; ++i)
1497 			{
1498 				RestoreImageHeader(ihptr);
1499 				++ihptr;
1500 			}
1501 		}
1502 		EnumLeftoverImages(j,NumImagesArray[j],RestoreImageCallback,&j);
1503 	}
1504 
1505 	return Yes; /* ok for the moment */
1506 }
1507 
1508 #if debug
1509 
1510 struct ImageGroupDebugInfo
1511 {
1512 	int num_texels;
1513 	int num_images;
1514 	int num_shared;
1515 	int num_leftover;
1516 };
1517 
1518 static struct ImageGroupDebugInfo db_gp_info[MaxImageGroups];
1519 
1520 static void DbShareImgCallback(int imgnum, void * user)
1521 {
1522 	int g = *(int *)user;
1523 
1524 	++db_gp_info[g].num_shared;
1525 }
1526 
1527 static void DbLeftoverImgCallback(int i, void * user)
1528 {
1529 	int g = *(int *)user;
1530 
1531 	++db_gp_info[g].num_leftover;
1532 
1533 	db_gp_info[g].num_texels += ImageHeaderPtrs[g*MaxImages+i]->ImageWidth * ImageHeaderPtrs[g*MaxImages+i]->ImageHeight;
1534 }
1535 
1536 void ImageGroupsDebugPrintInit(void)
1537 {
1538 	int g;
1539 	for (g=0; g<MaxImageGroups; g++)
1540 	{
1541 		int i;
1542 
1543 		db_gp_info[g].num_texels = 0;
1544 		db_gp_info[g].num_images = NumImagesArray[g];
1545 		db_gp_info[g].num_shared = 0;
1546 		db_gp_info[g].num_leftover = 0;
1547 
1548 		EnumSharedImages(g,NumImagesArray[g],DbShareImgCallback,&g);
1549 		EnumLeftoverImages(g,NumImagesArray[g],DbLeftoverImgCallback,&g);
1550 
1551 		for (i=0; i<NumImagesArray[g]; ++i)
1552 		{
1553 			db_gp_info[g].num_texels += ImageHeaderPtrs[g*MaxImages+i]->ImageWidth * ImageHeaderPtrs[g*MaxImages+i]->ImageHeight;
1554 		}
1555 	}
1556 }
1557 
1558 void ImageGroupsDebugPrint(void)
1559 {
1560 	int g;
1561 	textprint("IMAGE GROUP DEBUG INFO\nGP  N_IMG  N_SHR  N_LFT  N_TEXELS\n");
1562 	for (g=0; g<MaxImageGroups; ++g)
1563 	{
1564 		textprint("%2d  %5d  %5d  %5d  %8d\n",g,db_gp_info[g].num_images,db_gp_info[g].num_shared,db_gp_info[g].num_leftover,db_gp_info[g].num_texels);
1565 	}
1566 }
1567 
1568 #endif
1569 
1570 #else
1571 
1572 int DeallocateAllImages(void)
1573 {
1574 	int i;
1575 	IMAGEHEADER *ihptr;
1576 
1577 	if (NumImages)
1578 	{
1579 		ihptr = ImageHeaderArray;
1580 		for (i = NumImages; i!=0; i--)
1581 		{
1582 			DeallocateImageHeader(ihptr++);
1583 		}
1584 		NumImages = 0;
1585 		NextFreeImageHeaderPtr = ImageHeaderArray;
1586 	}
1587 
1588 	return Yes; /* ok for the moment */
1589 }
1590 
1591 int MinimizeAllImages(void)
1592 {
1593 	int i;
1594 	IMAGEHEADER *ihptr;
1595 
1596 	if (NumImages)
1597 	{
1598 		ihptr = ImageHeaderArray;
1599 		for (i = NumImages; i!=0; i--)
1600 		{
1601 			MinimizeImageHeader(ihptr++);
1602 		}
1603 	}
1604 
1605 	return Yes; /* ok for the moment */
1606 }
1607 
1608 int RestoreAllImages(void)
1609 {
1610 	int i;
1611 	IMAGEHEADER *ihptr;
1612 
1613 	if (NumImages)
1614 	{
1615 		ihptr = ImageHeaderArray;
1616 		for (i = NumImages; i!=0; i--)
1617 		{
1618 			RestoreImageHeader(ihptr++);
1619 		}
1620 	}
1621 
1622 	return Yes; /* ok for the moment */
1623 }
1624 
1625 #endif
1626 
1627 
1628 #ifdef RIFF_SYSTEM
1629 
1630 /*
1631 The RIFF_SYSTEM uses this function to return an image number
1632 for an image which might be already loaded. The argument
1633 passed points to the full pathname of the image that the
1634 system wants to load, so an explicit stricmp on the
1635 image names of already loaded images will suffice. To
1636 avoid loading images more than once, it ensures that the
1637 path generated for two identical images will always be
1638 the same. It also fills in the ImageName field with the
1639 fill path of images it loads.
1640 
1641 Currently I am assuming that users of ImageGroups will
1642 no longer need images in group n+1 when images in group
1643 n are deleted, so I can check in all groups from 0...Current
1644 to see if the image is already loaded.
1645 
1646 Jake.
1647 */
1648 
1649 int GetExistingImageNum(char const * fname)
1650 {
1651 	int i;
1652 	IMAGEHEADER * iharrayptr;
1653 
1654 	#ifdef MaxImageGroups
1655 
1656 	int g;
1657 
1658 	for (g=0; g<MaxImageGroups; ++g)
1659 	{
1660 		for (i=0, iharrayptr = &ImageHeaderArray[g*MaxImages]; i<NumImagesArray[g]; ++i, ++iharrayptr)
1661 		{
1662 			if (!stricmp(iharrayptr->ImageName,fname))
1663 			{
1664 				if (g!=CurrentImageGroup)
1665 					MarkImageInUseByGroup(g,i,CurrentImageGroup);
1666 				return i+g*MaxImages;
1667 			}
1668 		}
1669 	}
1670 
1671 	#else
1672 
1673 	for (i=0, iharrayptr = ImageHeaderArray; i<NumImages; ++i, ++iharrayptr)
1674 	{
1675 		if (!stricmp(iharrayptr->ImageName,fname)) return i;
1676 	}
1677 
1678 	#endif
1679 
1680 	return GEI_NOTLOADED;
1681 }
1682 
1683 #endif
1684 
1685