1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_psp.c
8 //
9 // Description: Reads a Paint Shop Pro file.
10 //
11 //-----------------------------------------------------------------------------
12 
13 
14 #include "il_internal.h"
15 #include "il_psp.h"
16 #ifndef IL_NO_PSP
17 
18 
19 ILubyte PSPSignature[32] = {
20 	0x50, 0x61, 0x69, 0x6E, 0x74, 0x20, 0x53, 0x68, 0x6F, 0x70, 0x20, 0x50, 0x72, 0x6F, 0x20, 0x49,
21 	0x6D, 0x61, 0x67, 0x65, 0x20, 0x46, 0x69, 0x6C, 0x65, 0x0A, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00
22 };
23 
24 ILubyte GenAttHead[4] = {
25 	0x7E, 0x42, 0x4B, 0x00
26 };
27 
28 
29 // Make these global, since they contain most of the image information.
30 GENATT_CHUNK	AttChunk;
31 PSPHEAD			Header;
32 ILuint			NumChannels;
33 ILubyte			**Channels = NULL;
34 ILubyte			*Alpha = NULL;
35 ILpal			Pal;
36 
37 
38 
39 //! Checks if the file specified in FileName is a valid Psp file.
ilIsValidPsp(ILconst_string FileName)40 ILboolean ilIsValidPsp(ILconst_string FileName)
41 {
42 	ILHANDLE	PspFile;
43 	ILboolean	bPsp = IL_FALSE;
44 
45 	if (!iCheckExtension(FileName, IL_TEXT("psp"))) {
46 		ilSetError(IL_INVALID_EXTENSION);
47 		return bPsp;
48 	}
49 
50 	PspFile = iopenr(FileName);
51 	if (PspFile == NULL) {
52 		ilSetError(IL_COULD_NOT_OPEN_FILE);
53 		return bPsp;
54 	}
55 
56 	bPsp = ilIsValidPspF(PspFile);
57 	icloser(PspFile);
58 
59 	return bPsp;
60 }
61 
62 
63 //! Checks if the ILHANDLE contains a valid Psp file at the current position.
ilIsValidPspF(ILHANDLE File)64 ILboolean ilIsValidPspF(ILHANDLE File)
65 {
66 	ILuint		FirstPos;
67 	ILboolean	bRet;
68 
69 	iSetInputFile(File);
70 	FirstPos = itell();
71 	bRet = iIsValidPsp();
72 	iseek(FirstPos, IL_SEEK_SET);
73 
74 	return bRet;
75 }
76 
77 
78 //! Checks if Lump is a valid Psp lump.
ilIsValidPspL(const void * Lump,ILuint Size)79 ILboolean ilIsValidPspL(const void *Lump, ILuint Size)
80 {
81 	iSetInputLump(Lump, Size);
82 	return iIsValidPsp();
83 }
84 
85 
86 // Internal function used to get the Psp header from the current file.
iGetPspHead()87 ILboolean iGetPspHead()
88 {
89 	if (iread(Header.FileSig, 1, 32) != 32)
90 		return IL_FALSE;
91 	Header.MajorVersion = GetLittleUShort();
92 	Header.MinorVersion = GetLittleUShort();
93 
94 	return IL_TRUE;
95 }
96 
97 
98 // Internal function to get the header and check it.
iIsValidPsp()99 ILboolean iIsValidPsp()
100 {
101 	if (!iGetPspHead())
102 		return IL_FALSE;
103 	iseek(-(ILint)sizeof(PSPHEAD), IL_SEEK_CUR);
104 
105 	return iCheckPsp();
106 }
107 
108 
109 // Internal function used to check if the HEADER is a valid Psp header.
iCheckPsp()110 ILboolean iCheckPsp()
111 {
112 	if (stricmp(Header.FileSig, "Paint Shop Pro Image File\n\x1a"))
113 		return IL_FALSE;
114 	if (Header.MajorVersion < 3 || Header.MajorVersion > 5)
115 		return IL_FALSE;
116 	if (Header.MinorVersion != 0)
117 		return IL_FALSE;
118 
119 
120 	return IL_TRUE;
121 }
122 
123 
124 //! Reads a PSP file
ilLoadPsp(ILconst_string FileName)125 ILboolean ilLoadPsp(ILconst_string FileName)
126 {
127 	ILHANDLE	PSPFile;
128 	ILboolean	bPsp = IL_FALSE;
129 
130 	PSPFile = iopenr(FileName);
131 	if (PSPFile == NULL) {
132 		ilSetError(IL_COULD_NOT_OPEN_FILE);
133 		return bPsp;
134 	}
135 
136 	bPsp = ilLoadPspF(PSPFile);
137 	icloser(PSPFile);
138 
139 	return bPsp;
140 }
141 
142 
143 //! Reads an already-opened PSP file
ilLoadPspF(ILHANDLE File)144 ILboolean ilLoadPspF(ILHANDLE File)
145 {
146 	ILuint		FirstPos;
147 	ILboolean	bRet;
148 
149 	iSetInputFile(File);
150 	FirstPos = itell();
151 	bRet = iLoadPspInternal();
152 	iseek(FirstPos, IL_SEEK_SET);
153 
154 	return bRet;
155 }
156 
157 
158 //! Reads from a memory "lump" that contains a PSP
ilLoadPspL(const void * Lump,ILuint Size)159 ILboolean ilLoadPspL(const void *Lump, ILuint Size)
160 {
161 	iSetInputLump(Lump, Size);
162 	return iLoadPspInternal();
163 }
164 
165 
166 // Internal function used to load the PSP.
iLoadPspInternal()167 ILboolean iLoadPspInternal()
168 {
169 	if (iCurImage == NULL) {
170 		ilSetError(IL_ILLEGAL_OPERATION);
171 		return IL_FALSE;
172 	}
173 
174 	Channels = NULL;
175 	Alpha = NULL;
176 	Pal.Palette = NULL;
177 
178 	if (!iGetPspHead())
179 		return IL_FALSE;
180 	if (!iCheckPsp()) {
181 		ilSetError(IL_INVALID_FILE_HEADER);
182 		return IL_FALSE;
183 	}
184 
185 	if (!ReadGenAttributes())
186 		return IL_FALSE;
187 	if (!ParseChunks())
188 		return IL_FALSE;
189 	if (!AssembleImage())
190 		return IL_FALSE;
191 
192 	Cleanup();
193 	return ilFixImage();
194 }
195 
196 
ReadGenAttributes()197 ILboolean ReadGenAttributes()
198 {
199 	BLOCKHEAD		AttHead;
200 	ILint			Padding;
201 	ILuint			ChunkLen;
202 
203 	if (iread(&AttHead, sizeof(AttHead), 1) != 1)
204 		return IL_FALSE;
205 	UShort(&AttHead.BlockID);
206 	UInt(&AttHead.BlockLen);
207 
208 	if (AttHead.HeadID[0] != 0x7E || AttHead.HeadID[1] != 0x42 ||
209 		AttHead.HeadID[2] != 0x4B || AttHead.HeadID[3] != 0x00) {
210 		ilSetError(IL_INVALID_FILE_HEADER);
211 		return IL_FALSE;
212 	}
213 	if (AttHead.BlockID != PSP_IMAGE_BLOCK) {
214 		ilSetError(IL_INVALID_FILE_HEADER);
215 		return IL_FALSE;
216 	}
217 
218 	ChunkLen = GetLittleUInt();
219 	if (Header.MajorVersion != 3)
220 		ChunkLen -= 4;
221 	if (iread(&AttChunk, IL_MIN(sizeof(AttChunk), ChunkLen), 1) != 1)
222 		return IL_FALSE;
223 
224 	// Can have new entries in newer versions of the spec (4.0).
225 	Padding = (ChunkLen) - sizeof(AttChunk);
226 	if (Padding > 0)
227 		iseek(Padding, IL_SEEK_CUR);
228 
229 	// @TODO:  Anything but 24 not supported yet...
230 	if (AttChunk.BitDepth != 24 && AttChunk.BitDepth != 8) {
231 		ilSetError(IL_INVALID_FILE_HEADER);
232 		return IL_FALSE;
233 	}
234 
235 	// @TODO;  Add support for compression...
236 	if (AttChunk.Compression != PSP_COMP_NONE && AttChunk.Compression != PSP_COMP_RLE) {
237 		ilSetError(IL_INVALID_FILE_HEADER);
238 		return IL_FALSE;
239 	}
240 
241 	// @TODO: Check more things in the general attributes chunk here.
242 
243 	return IL_TRUE;
244 }
245 
246 
ParseChunks()247 ILboolean ParseChunks()
248 {
249 	BLOCKHEAD	Block;
250 	ILuint		Pos;
251 
252 	do {
253 		if (iread(&Block, 1, sizeof(Block)) != sizeof(Block)) {
254 			ilGetError();  // Get rid of the erroneous IL_FILE_READ_ERROR.
255 			return IL_TRUE;
256 		}
257 		if (Header.MajorVersion == 3)
258 			Block.BlockLen = GetLittleUInt();
259 		else
260 			UInt(&Block.BlockLen);
261 
262 		if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
263 			Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
264 				return IL_TRUE;
265 		}
266 		UShort(&Block.BlockID);
267 		UInt(&Block.BlockLen);
268 
269 		Pos = itell();
270 
271 		switch (Block.BlockID)
272 		{
273 			case PSP_LAYER_START_BLOCK:
274 				if (!ReadLayerBlock(Block.BlockLen))
275 					return IL_FALSE;
276 				break;
277 
278 			case PSP_ALPHA_BANK_BLOCK:
279 				if (!ReadAlphaBlock(Block.BlockLen))
280 					return IL_FALSE;
281 				break;
282 
283 			case PSP_COLOR_BLOCK:
284 				if (!ReadPalette(Block.BlockLen))
285 					return IL_FALSE;
286 				break;
287 
288 			// Gets done in the next iseek, so this is now commented out.
289 			//default:
290 				//iseek(Block.BlockLen, IL_SEEK_CUR);
291 		}
292 
293 		// Skip to next block just in case we didn't read the entire block.
294 		iseek(Pos + Block.BlockLen, IL_SEEK_SET);
295 
296 		// @TODO: Do stuff here.
297 
298 	} while (1);
299 
300 	return IL_TRUE;
301 }
302 
303 
ReadLayerBlock(ILuint BlockLen)304 ILboolean ReadLayerBlock(ILuint BlockLen)
305 {
306 	BLOCKHEAD			Block;
307 	LAYERINFO_CHUNK		LayerInfo;
308 	LAYERBITMAP_CHUNK	Bitmap;
309 	ILuint				ChunkSize, Padding, i, j;
310 	ILushort			NumChars;
311 
312 	BlockLen;
313 
314 	// Layer sub-block header
315 	if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
316 		return IL_FALSE;
317 	if (Header.MajorVersion == 3)
318 		Block.BlockLen = GetLittleUInt();
319 	else
320 		UInt(&Block.BlockLen);
321 
322 	if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
323 		Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
324 			return IL_FALSE;
325 	}
326 	if (Block.BlockID != PSP_LAYER_BLOCK)
327 		return IL_FALSE;
328 
329 
330 	if (Header.MajorVersion == 3) {
331 		iseek(256, IL_SEEK_CUR);  // We don't care about the name of the layer.
332 		iread(&LayerInfo, sizeof(LayerInfo), 1);
333 		if (iread(&Bitmap, sizeof(Bitmap), 1) != 1)
334 			return IL_FALSE;
335 	}
336 	else {  // Header.MajorVersion >= 4
337 		ChunkSize = GetLittleUInt();
338 		NumChars = GetLittleUShort();
339 		iseek(NumChars, IL_SEEK_CUR);  // We don't care about the layer's name.
340 
341 		ChunkSize -= (2 + 4 + NumChars);
342 
343 		if (iread(&LayerInfo, IL_MIN(sizeof(LayerInfo), ChunkSize), 1) != 1)
344 			return IL_FALSE;
345 
346 		// Can have new entries in newer versions of the spec (5.0).
347 		Padding = (ChunkSize) - sizeof(LayerInfo);
348 		if (Padding > 0)
349 			iseek(Padding, IL_SEEK_CUR);
350 
351 		ChunkSize = GetLittleUInt();
352 		if (iread(&Bitmap, sizeof(Bitmap), 1) != 1)
353 			return IL_FALSE;
354 		Padding = (ChunkSize - 4) - sizeof(Bitmap);
355 		if (Padding > 0)
356 			iseek(Padding, IL_SEEK_CUR);
357 	}
358 
359 
360 	Channels = (ILubyte**)ialloc(sizeof(ILubyte*) * Bitmap.NumChannels);
361 	if (Channels == NULL) {
362 		return IL_FALSE;
363 	}
364 
365 	NumChannels = Bitmap.NumChannels;
366 
367 	for (i = 0; i < NumChannels; i++) {
368 		Channels[i] = GetChannel();
369 		if (Channels[i] == NULL) {
370 			for (j = 0; j < i; j++)
371 				ifree(Channels[j]);
372 			return IL_FALSE;
373 		}
374 	}
375 
376 	return IL_TRUE;
377 }
378 
379 
ReadAlphaBlock(ILuint BlockLen)380 ILboolean ReadAlphaBlock(ILuint BlockLen)
381 {
382 	BLOCKHEAD		Block;
383 	ALPHAINFO_CHUNK	AlphaInfo;
384 	ALPHA_CHUNK		AlphaChunk;
385 	ILushort		NumAlpha, StringSize;
386 	ILuint			ChunkSize, Padding;
387 
388 	if (Header.MajorVersion == 3) {
389 		NumAlpha = GetLittleUShort();
390 	}
391 	else {
392 		ChunkSize = GetLittleUInt();
393 		NumAlpha = GetLittleUShort();
394 		Padding = (ChunkSize - 4 - 2);
395 		if (Padding > 0)
396 			iseek(Padding, IL_SEEK_CUR);
397 	}
398 
399 	// Alpha channel header
400 	if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
401 		return IL_FALSE;
402 	if (Header.MajorVersion == 3)
403 		Block.BlockLen = GetLittleUInt();
404 	else
405 		UInt(&Block.BlockLen);
406 
407 	if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
408 		Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
409 			return IL_FALSE;
410 	}
411 	if (Block.BlockID != PSP_ALPHA_CHANNEL_BLOCK)
412 		return IL_FALSE;
413 
414 
415 	if (Header.MajorVersion >= 4) {
416 		ChunkSize = GetLittleUInt();
417 		StringSize = GetLittleUShort();
418 		iseek(StringSize, IL_SEEK_CUR);
419 		if (iread(&AlphaInfo, sizeof(AlphaInfo), 1) != 1)
420 			return IL_FALSE;
421 		Padding = (ChunkSize - 4 - 2 - StringSize - sizeof(AlphaInfo));
422 		if (Padding > 0)
423 			iseek(Padding, IL_SEEK_CUR);
424 
425 		ChunkSize = GetLittleUInt();
426 		if (iread(&AlphaChunk, sizeof(AlphaChunk), 1) != 1)
427 			return IL_FALSE;
428 		Padding = (ChunkSize - 4 - sizeof(AlphaChunk));
429 		if (Padding > 0)
430 			iseek(Padding, IL_SEEK_CUR);
431 	}
432 	else {
433 		iseek(256, IL_SEEK_CUR);
434 		iread(&AlphaInfo, sizeof(AlphaInfo), 1);
435 		if (iread(&AlphaChunk, sizeof(AlphaChunk), 1) != 1)
436 			return IL_FALSE;
437 	}
438 
439 
440 	/*Alpha = (ILubyte*)ialloc(AlphaInfo.AlphaRect.x2 * AlphaInfo.AlphaRect.y2);
441 	if (Alpha == NULL) {
442 		return IL_FALSE;
443 	}*/
444 
445 
446 	Alpha = GetChannel();
447 	if (Alpha == NULL)
448 		return IL_FALSE;
449 
450 	return IL_TRUE;
451 }
452 
453 
GetChannel()454 ILubyte *GetChannel()
455 {
456 	BLOCKHEAD		Block;
457 	CHANNEL_CHUNK	Channel;
458 	ILubyte			*CompData, *Data;
459 	ILuint			ChunkSize, Padding;
460 
461 	if (iread(&Block, 1, sizeof(Block)) != sizeof(Block))
462 		return NULL;
463 	if (Header.MajorVersion == 3)
464 		Block.BlockLen = GetLittleUInt();
465 	else
466 		UInt(&Block.BlockLen);
467 
468 	if (Block.HeadID[0] != 0x7E || Block.HeadID[1] != 0x42 ||
469 		Block.HeadID[2] != 0x4B || Block.HeadID[3] != 0x00) {
470 			ilSetError(IL_ILLEGAL_FILE_VALUE);
471 			return NULL;
472 	}
473 	if (Block.BlockID != PSP_CHANNEL_BLOCK) {
474 		ilSetError(IL_ILLEGAL_FILE_VALUE);
475 		return NULL;
476 	}
477 
478 
479 	if (Header.MajorVersion >= 4) {
480 		ChunkSize = GetLittleUInt();
481 		if (iread(&Channel, sizeof(Channel), 1) != 1)
482 			return NULL;
483 
484 		Padding = (ChunkSize - 4) - sizeof(Channel);
485 		if (Padding > 0)
486 			iseek(Padding, IL_SEEK_CUR);
487 	}
488 	else {
489 		if (iread(&Channel, sizeof(Channel), 1) != 1)
490 			return NULL;
491 	}
492 
493 
494 	CompData = (ILubyte*)ialloc(Channel.CompLen);
495 	Data = (ILubyte*)ialloc(AttChunk.Width * AttChunk.Height);
496 	if (CompData == NULL || Data == NULL) {
497 		ifree(Data);
498 		ifree(CompData);
499 		return NULL;
500 	}
501 
502 	if (iread(CompData, 1, Channel.CompLen) != Channel.CompLen) {
503 		ifree(CompData);
504 		ifree(Data);
505 		return NULL;
506 	}
507 
508 	switch (AttChunk.Compression)
509 	{
510 		case PSP_COMP_NONE:
511 			ifree(Data);
512 			return CompData;
513 			break;
514 
515 		case PSP_COMP_RLE:
516 			if (!UncompRLE(CompData, Data, Channel.CompLen)) {
517 				ifree(CompData);
518 				ifree(Data);
519 				return IL_FALSE;
520 			}
521 			break;
522 
523 		default:
524 			ifree(CompData);
525 			ifree(Data);
526 			ilSetError(IL_INVALID_FILE_HEADER);
527 			return NULL;
528 	}
529 
530 	ifree(CompData);
531 
532 	return Data;
533 }
534 
535 
UncompRLE(ILubyte * CompData,ILubyte * Data,ILuint CompLen)536 ILboolean UncompRLE(ILubyte *CompData, ILubyte *Data, ILuint CompLen)
537 {
538 	ILubyte	Run, Colour;
539 	ILint	i, /*x, y,*/ Count/*, Total = 0*/;
540 
541 	/*for (y = 0; y < AttChunk.Height; y++) {
542 		for (x = 0, Count = 0; x < AttChunk.Width; ) {
543 			Run = *CompData++;
544 			if (Run > 128) {
545 				Run -= 128;
546 				Colour = *CompData++;
547 				memset(Data, Colour, Run);
548 				Data += Run;
549 				Count += 2;
550 			}
551 			else {
552 				memcpy(Data, CompData, Run);
553 				CompData += Run;
554 				Data += Run;
555 				Count += Run;
556 			}
557 			x += Run;
558 		}
559 
560 		Total += Count;
561 
562 		if (Count % 4) {  // Has to be on a 4-byte boundary.
563 			CompData += (4 - (Count % 4)) % 4;
564 			Total += (4 - (Count % 4)) % 4;
565 		}
566 
567 		if (Total >= CompLen)
568 			return IL_FALSE;
569 	}*/
570 
571 	for (i = 0, Count = 0; i < (ILint)CompLen; ) {
572 		Run = *CompData++;
573 		i++;
574 		if (Run > 128) {
575 			Run -= 128;
576 			Colour = *CompData++;
577 			i++;
578 			memset(Data, Colour, Run);
579 		}
580 		else {
581 			memcpy(Data, CompData, Run);
582 			CompData += Run;
583 			i += Run;
584 		}
585 		Data += Run;
586 		Count += Run;
587 	}
588 
589 	return IL_TRUE;
590 }
591 
592 
ReadPalette(ILuint BlockLen)593 ILboolean ReadPalette(ILuint BlockLen)
594 {
595 	ILuint ChunkSize, PalCount, Padding;
596 
597 	if (Header.MajorVersion >= 4) {
598 		ChunkSize = GetLittleUInt();
599 		PalCount = GetLittleUInt();
600 		Padding = (ChunkSize - 4 - 4);
601 		if (Padding > 0)
602 			iseek(Padding, IL_SEEK_CUR);
603 	}
604 	else {
605 		PalCount = GetLittleUInt();
606 	}
607 
608 	Pal.PalSize = PalCount * 4;
609 	Pal.PalType = IL_PAL_BGRA32;
610 	Pal.Palette = (ILubyte*)ialloc(Pal.PalSize);
611 	if (Pal.Palette == NULL)
612 		return IL_FALSE;
613 
614 	if (iread(Pal.Palette, Pal.PalSize, 1) != 1) {
615 		ifree(Pal.Palette);
616 		return IL_FALSE;
617 	}
618 
619 	return IL_TRUE;
620 }
621 
622 
AssembleImage()623 ILboolean AssembleImage()
624 {
625 	ILuint Size, i, j;
626 
627 	Size = AttChunk.Width * AttChunk.Height;
628 
629 	if (NumChannels == 1) {
630 		ilTexImage(AttChunk.Width, AttChunk.Height, 1, 1, IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL);
631 		for (i = 0; i < Size; i++) {
632 			iCurImage->Data[i] = Channels[0][i];
633 		}
634 
635 		if (Pal.Palette) {
636 			iCurImage->Format = IL_COLOUR_INDEX;
637 			iCurImage->Pal.PalSize = Pal.PalSize;
638 			iCurImage->Pal.PalType = Pal.PalType;
639 			iCurImage->Pal.Palette = Pal.Palette;
640 		}
641 	}
642 	else {
643 		if (Alpha) {
644 			ilTexImage(AttChunk.Width, AttChunk.Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
645 			for (i = 0, j = 0; i < Size; i++, j += 4) {
646 				iCurImage->Data[j  ] = Channels[0][i];
647 				iCurImage->Data[j+1] = Channels[1][i];
648 				iCurImage->Data[j+2] = Channels[2][i];
649 				iCurImage->Data[j+3] = Alpha[i];
650 			}
651 		}
652 
653 		else if (NumChannels == 4) {
654 
655 			ilTexImage(AttChunk.Width, AttChunk.Height, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
656 
657 			for (i = 0, j = 0; i < Size; i++, j += 4) {
658 
659 				iCurImage->Data[j  ] = Channels[0][i];
660 
661 				iCurImage->Data[j+1] = Channels[1][i];
662 
663 				iCurImage->Data[j+2] = Channels[2][i];
664 
665 				iCurImage->Data[j+3] = Channels[3][i];
666 
667 			}
668 
669 		}
670 		else if (NumChannels == 3) {
671 			ilTexImage(AttChunk.Width, AttChunk.Height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL);
672 			for (i = 0, j = 0; i < Size; i++, j += 3) {
673 				iCurImage->Data[j  ] = Channels[0][i];
674 				iCurImage->Data[j+1] = Channels[1][i];
675 				iCurImage->Data[j+2] = Channels[2][i];
676 			}
677 		}
678 		else
679 			return IL_FALSE;
680 	}
681 
682 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
683 
684 	return IL_TRUE;
685 }
686 
687 
Cleanup()688 ILboolean Cleanup()
689 {
690 	ILuint	i;
691 
692 	if (Channels) {
693 		for (i = 0; i < NumChannels; i++) {
694 			ifree(Channels[i]);
695 		}
696 		ifree(Channels);
697 	}
698 
699 	if (Alpha) {
700 		ifree(Alpha);
701 	}
702 
703 	Channels = NULL;
704 	Alpha = NULL;
705 	Pal.Palette = NULL;
706 
707 	return IL_TRUE;
708 }
709 
710 
711 
712 #endif//IL_NO_PSP
713