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