Coverage Report

Created: 2025-06-16 07:00

/src/imagemagick/coders/tim2.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                          TTTTT  IIIII  M   M   222                          %
7
%                            T      I    MM MM  2   2                         %
8
%                            T      I    M M M     2                          %
9
%                            T      I    M   M    2                           %
10
%                            T    IIIII  M   M  22222                         %
11
%                                                                             %
12
%                                                                             %
13
%                          Read PSX TIM2 Image Format                         %
14
%                                                                             %
15
%                               Software Design                               %
16
%                             Ramiro Balado Ordax                             %
17
%                                   May 2019                                  %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/script/license.php                               %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38
39
40
/*
41
  Include declarations.
42
*/
43
#include "MagickCore/studio.h"
44
#include "MagickCore/blob.h"
45
#include "MagickCore/blob-private.h"
46
#include "MagickCore/cache.h"
47
#include "MagickCore/colormap.h"
48
#include "MagickCore/channel.h"
49
#include "MagickCore/exception.h"
50
#include "MagickCore/exception-private.h"
51
#include "MagickCore/image.h"
52
#include "MagickCore/image-private.h"
53
#include "MagickCore/list.h"
54
#include "MagickCore/magick.h"
55
#include "MagickCore/memory_.h"
56
#include "MagickCore/monitor.h"
57
#include "MagickCore/monitor-private.h"
58
#include "MagickCore/pixel-accessor.h"
59
#include "MagickCore/quantum-private.h"
60
#include "MagickCore/static.h"
61
#include "MagickCore/string_.h"
62
#include "MagickCore/module.h"
63

64
/*
65
 Typedef declarations
66
*/
67
typedef struct _TIM2FileHeader
68
{
69
  unsigned int
70
    magic_num;
71
72
  unsigned char
73
    format_vers,
74
    format_type;
75
76
  unsigned short
77
    image_count;
78
} TIM2FileHeader;
79
80
typedef struct _TIM2ImageHeader
81
{
82
  unsigned int
83
    total_size,
84
    clut_size,
85
    image_size;
86
87
  unsigned short
88
    header_size,
89
    clut_color_count;
90
91
  unsigned char
92
    img_format,
93
    mipmap_count,
94
    clut_type,
95
    bpp_type;
96
97
  unsigned short
98
    width,
99
    height;
100
101
  MagickSizeType
102
    GsTex0,
103
    GsTex1;
104
105
  unsigned int
106
    GsRegs,
107
    GsTexClut;
108
} TIM2ImageHeader;
109
110
typedef enum
111
{
112
  CSM1=0,
113
  CSM2=1,
114
} CSM;
115
116
typedef enum
117
{
118
  RGBA32=0,
119
  RGB24=1,
120
  RGBA16=2,
121
} TIM2ColorEncoding;
122

123
/*
124
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
%                                                                             %
126
%                                                                             %
127
%                                                                             %
128
%  R e a d T I M 2 I m a g e                                                  %
129
%                                                                             %
130
%                                                                             %
131
%                                                                             %
132
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133
%
134
%  ReadTIM2Image() reads a PS2 TIM image file and returns it.  It
135
%  allocates the memory necessary for the new Image structure and returns a
136
%  pointer to the new image.
137
%
138
%  The format of the ReadTIM2Image method is:
139
%
140
%      Image *ReadTIM2Image(const ImageInfo *image_info,
141
%        ExceptionInfo *exception)
142
%
143
%  A description of each parameter follows:
144
%
145
%    o image_info: the image info.
146
%
147
%    o exception: return any errors or warnings in this structure.
148
%
149
*/
150
static inline void ReadTIM2ImageHeader(Image *image,TIM2ImageHeader *header)
151
7
{
152
7
  header->total_size=ReadBlobLSBLong(image);
153
7
  header->clut_size=ReadBlobLSBLong(image);
154
7
  header->image_size=ReadBlobLSBLong(image);
155
7
  header->header_size=ReadBlobLSBShort(image);
156
157
7
  header->clut_color_count=ReadBlobLSBShort(image);
158
7
  header->img_format=(unsigned char) ReadBlobByte(image);
159
7
  header->mipmap_count=(unsigned char) ReadBlobByte(image);
160
7
  header->clut_type=(unsigned char) ReadBlobByte(image);
161
7
  header->bpp_type=(unsigned char) ReadBlobByte(image);
162
163
7
  header->width=ReadBlobLSBShort(image);
164
7
  header->height=ReadBlobLSBShort(image);
165
166
7
  header->GsTex0=ReadBlobMSBLongLong(image);
167
7
  header->GsTex1=ReadBlobMSBLongLong(image);
168
7
  header->GsRegs=ReadBlobMSBLong(image);
169
7
  header->GsTexClut=ReadBlobMSBLong(image);
170
7
}
171
172
static inline Quantum GetChannelValue(unsigned int word,unsigned char channel,
173
  TIM2ColorEncoding ce)
174
0
{
175
0
  switch(ce)
176
0
  {
177
0
    case RGBA16:
178
      /* Documentation specifies padding with zeros for converting from 5 to 8 bits. */
179
0
      return ScaleCharToQuantum((word>>channel*5 & ~(~0x0U<<5))<<3);
180
0
    case RGB24:
181
0
    case RGBA32:
182
0
      return ScaleCharToQuantum(word>>channel*8 & ~(~0x0U<<8));
183
0
    default:
184
0
      return QuantumRange;
185
0
  }
186
0
}
187
188
static inline Quantum GetAlpha(unsigned int word,TIM2ColorEncoding ce)
189
0
{
190
0
  switch(ce)
191
0
  {
192
0
    case RGBA16:
193
0
      return ScaleCharToQuantum((word>>3*5&0x1F)==0?0:0xFF);
194
0
    case RGBA32:
195
      /* 0x80 -> 1.0 alpha. Multiply by 2 and clamp to 0xFF */
196
0
      return ScaleCharToQuantum(MagickMin((word>>3*8&0xFF)<<1,0xFF));
197
0
    default:
198
0
      return 0xFF;
199
0
  }
200
0
}
201
202
static inline void deshufflePalette(Image *image,PixelInfo* oldColormap)
203
0
{
204
0
  const size_t
205
0
    pages=image->colors/32,  /* Pages per CLUT */
206
0
    blocks=4,  /* Blocks per page */
207
0
    colors=8;  /* Colors per block */
208
209
0
  int
210
0
    page;
211
212
0
  size_t
213
0
    i=0;
214
215
0
  (void) memcpy(oldColormap,image->colormap,(size_t)image->colors*
216
0
    sizeof(*oldColormap));
217
218
  /*
219
   * Swap the 2nd and 3rd block in each page
220
   */
221
0
  for (page=0; page < (ssize_t) pages; page++)
222
0
  {
223
0
    memcpy(&(image->colormap[i+1*colors]),&(oldColormap[i+2*colors]),colors*
224
0
      sizeof(PixelInfo));
225
0
    memcpy(&(image->colormap[i+2*colors]),&(oldColormap[i+1*colors]),colors*
226
0
      sizeof(PixelInfo));
227
228
0
    i+=blocks*colors;
229
0
  }
230
0
}
231
232
static MagickBooleanType ReadTIM2ImageData(const ImageInfo *image_info,
233
  Image *image,TIM2ImageHeader *header,char clut_depth,char bits_per_pixel,
234
  ExceptionInfo *exception)
235
0
{
236
0
  MagickBooleanType
237
0
    status;
238
239
0
  ssize_t
240
0
    x;
241
242
0
  Quantum
243
0
    *q;
244
245
0
  size_t
246
0
    bits_per_line,
247
0
    bytes_per_line;
248
249
0
  ssize_t
250
0
    count,
251
0
    y;
252
253
0
  unsigned char
254
0
    *p,
255
0
    *row_data;
256
257
0
  unsigned int
258
0
    word;
259
260
0
  status=SetImageExtent(image,image->columns,image->rows,exception);
261
0
  if (status == MagickFalse)
262
0
    return(MagickFalse);
263
  /*
264
   * User data
265
   */
266
0
  status=DiscardBlobBytes(image,header->header_size-48);
267
0
  if (status == MagickFalse)
268
0
    return(MagickFalse);
269
  /*
270
   * Image data
271
   */
272
0
  bits_per_line=image->columns*(size_t) bits_per_pixel;
273
0
  bytes_per_line=bits_per_line/8 + ((bits_per_line%8==0) ? 0 : 1);
274
0
  row_data=(unsigned char*) AcquireQuantumMemory(1,bytes_per_line);
275
0
  if (row_data == (unsigned char *) NULL)
276
0
    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
277
0
      image_info->filename);
278
0
  if (clut_depth != 0)
279
0
    {
280
0
      image->colors=header->clut_color_count;
281
0
      if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
282
0
        {
283
0
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
284
0
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
285
0
            image_info->filename);
286
0
        }
287
0
      switch (bits_per_pixel)
288
0
      {
289
0
        case 4:
290
0
        {
291
0
          for (y=0; y<(ssize_t) image->rows; y++)
292
0
          {
293
0
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
294
0
            if (q == (Quantum *) NULL)
295
0
              break;
296
0
            count=ReadBlob(image,bytes_per_line,row_data);
297
0
            if (count != (ssize_t) bytes_per_line)
298
0
              {
299
0
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
300
0
                ThrowBinaryException(CorruptImageError,
301
0
                  "InsufficientImageDataInFile",image_info->filename);
302
0
              }
303
0
            p=row_data;
304
0
            for (x=0; x < ((ssize_t) image->columns-1); x+=2)
305
0
            {
306
0
              SetPixelIndex(image,(Quantum)((*p >> 0) & 0x0f),q);
307
0
              q+=(ptrdiff_t) GetPixelChannels(image);
308
0
              SetPixelIndex(image,(Quantum)((*p >> 4) & 0x0f),q);
309
0
              p++;
310
0
              q+=(ptrdiff_t) GetPixelChannels(image);
311
0
            }
312
0
            if ((image->columns % 2) != 0)
313
0
              {
314
0
                SetPixelIndex(image,(Quantum)((*p >> 4) & 0x0f),q);
315
0
                p++;
316
0
                q+=(ptrdiff_t) GetPixelChannels(image);
317
0
              }
318
0
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
319
0
              break;
320
0
            if (image->previous == (Image *) NULL)
321
0
              {
322
0
                status=SetImageProgress(image,LoadImageTag,
323
0
                  (MagickOffsetType) y,image->rows);
324
0
                if (status == MagickFalse)
325
0
                  break;
326
0
              }
327
0
          }
328
0
          break;
329
0
        }
330
0
        case 8:
331
0
        {
332
0
          for (y=0;y<(ssize_t) image->rows; y++)
333
0
          {
334
0
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
335
0
            if (q == (Quantum *) NULL)
336
0
              break;
337
0
            count=ReadBlob(image,bytes_per_line,row_data);
338
0
            if (count != (ssize_t) bytes_per_line)
339
0
              {
340
0
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
341
0
                ThrowBinaryException(CorruptImageError,
342
0
                  "InsufficientImageDataInFile",image_info->filename);
343
0
              }
344
0
            p=row_data;
345
0
            for (x=0; x < (ssize_t) image->columns; x++)
346
0
            {
347
0
              SetPixelIndex(image,*p,q);
348
0
              p++;
349
0
              q+=(ptrdiff_t) GetPixelChannels(image);
350
0
            }
351
0
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
352
0
              break;
353
0
            if (image->previous == (Image *) NULL)
354
0
              {
355
0
                status=SetImageProgress(image,LoadImageTag,
356
0
                  (MagickOffsetType) y,image->rows);
357
0
                if (status == MagickFalse)
358
0
                  break;
359
0
              }
360
0
          }
361
0
          break;
362
0
        }
363
0
        default:
364
0
        {
365
0
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
366
0
          ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
367
0
            image_info->filename);
368
0
        }
369
0
      }
370
0
      SyncImage(image,exception);
371
0
    }
372
0
  else  /* has_clut==false */
373
0
    {
374
0
      switch (bits_per_pixel)
375
0
      {
376
0
        case 16:
377
0
        {
378
0
          for (y=0; y<(ssize_t) image->rows; y++)
379
0
          {
380
0
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
381
0
            if (q == (Quantum *) NULL)
382
0
              break;
383
0
            count=ReadBlob(image,bytes_per_line,row_data);
384
0
            if (count != (ssize_t) bytes_per_line)
385
0
              {
386
0
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
387
0
                ThrowBinaryException(CorruptImageError,
388
0
                  "InsufficientImageDataInFile",image_info->filename);
389
0
              }
390
0
            p=row_data;
391
0
            for (x=0; x < (ssize_t) image->columns; x++)
392
0
            {
393
0
              word = ((unsigned int)* p   )<<0*8 |
394
0
                      ((unsigned int)*(p+1))<<1*8;
395
396
0
              SetPixelRed(image,GetChannelValue(word,0,RGBA16),q);
397
0
              SetPixelGreen(image,GetChannelValue(word,1,RGBA16),q);
398
0
              SetPixelBlue(image,GetChannelValue(word,2,RGBA16),q);
399
0
              SetPixelAlpha(image,GetAlpha(word,RGBA16),q);
400
0
              q+=(ptrdiff_t) GetPixelChannels(image);
401
0
              p+=(ptrdiff_t) sizeof(unsigned short);
402
0
            }
403
0
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
404
0
              break;
405
0
            if (image->previous == (Image *) NULL)
406
0
              {
407
0
                status=SetImageProgress(image,LoadImageTag,
408
0
                  (MagickOffsetType) y,image->rows);
409
0
                if (status == MagickFalse)
410
0
                  break;
411
0
              }
412
0
          }
413
0
          break;
414
0
        }
415
0
        case 24:
416
0
        {
417
0
          for (y = 0; y<(ssize_t) image->rows; y++)
418
0
          {
419
0
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
420
0
            if (q == (Quantum *) NULL)
421
0
              break;
422
0
            count=ReadBlob(image,bytes_per_line,row_data);
423
0
            if (count != (ssize_t) bytes_per_line)
424
0
              {
425
0
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
426
0
                ThrowBinaryException(CorruptImageError,
427
0
                  "InsufficientImageDataInFile",image_info->filename);
428
0
              }
429
0
            p=row_data;
430
0
            for (x=0; x < (ssize_t) image->columns; x++)
431
0
            {
432
0
              word = (unsigned int)(* p   )<<0*8 |
433
0
                      (unsigned int)(*(p+1))<<1*8 |
434
0
                      (unsigned int)(*(p+2))<<2*8;
435
436
0
              SetPixelRed(image,GetChannelValue(word,0,RGB24),q);
437
0
              SetPixelGreen(image,GetChannelValue(word,1,RGB24),q);
438
0
              SetPixelBlue(image,GetChannelValue(word,2,RGB24),q);
439
0
              q+=(ptrdiff_t) GetPixelChannels(image);
440
0
              p+=(ptrdiff_t) 3;
441
0
            }
442
0
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
443
0
              break;
444
0
            if (image->previous == (Image *) NULL)
445
0
              {
446
0
                status=SetImageProgress(image,LoadImageTag,
447
0
                  (MagickOffsetType) y,image->rows);
448
0
                if (status == MagickFalse)
449
0
                  break;
450
0
              }
451
0
          }
452
0
          break;
453
0
        }
454
0
        case 32:
455
0
        {  
456
0
          for (y = 0; y<(ssize_t) image->rows; y++)
457
0
          {
458
0
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
459
0
            if (q == (Quantum *) NULL)
460
0
              break;
461
0
            count=ReadBlob(image,bytes_per_line,row_data);
462
0
            if (count != (ssize_t) bytes_per_line)
463
0
              {
464
0
                row_data=(unsigned char *) RelinquishMagickMemory(row_data);
465
0
                ThrowBinaryException(CorruptImageError,
466
0
                  "InsufficientImageDataInFile",image_info->filename);
467
0
              }
468
0
            p=row_data;
469
0
            for (x=0; x < (ssize_t) image->columns; x++)
470
0
            {
471
0
              word = ((unsigned int)* p   )<<0*8 |
472
0
                      ((unsigned int)*(p+1))<<1*8 |
473
0
                      ((unsigned int)*(p+2))<<2*8 |
474
0
                      ((unsigned int)*(p+3))<<3*8;
475
476
0
              SetPixelRed(image,GetChannelValue(word,0,RGBA32),q);
477
0
              SetPixelGreen(image,GetChannelValue(word,1,RGBA32),q);
478
0
              SetPixelBlue(image,GetChannelValue(word,2,RGBA32),q);
479
0
              SetPixelAlpha(image,GetAlpha(word,RGBA32),q);
480
0
              q+=(ptrdiff_t) GetPixelChannels(image);
481
0
              p+=(ptrdiff_t) 4;
482
0
            }
483
0
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
484
0
              break;
485
0
            if (image->previous == (Image *) NULL)
486
0
              {
487
0
                status=SetImageProgress(image,LoadImageTag,
488
0
                  (MagickOffsetType) y,image->rows);
489
0
                if (status == MagickFalse)
490
0
                  break;
491
0
              }
492
0
          }
493
0
          break;
494
0
        }
495
0
        default:
496
0
        {
497
0
          row_data=(unsigned char *) RelinquishMagickMemory(row_data);
498
0
          ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
499
0
            image_info->filename);
500
0
        }
501
0
      }
502
0
    }
503
0
  row_data=(unsigned char *) RelinquishMagickMemory(row_data);
504
0
  if ((status != MagickFalse) && (clut_depth != 0))
505
0
  {
506
0
    CSM
507
0
      csm;
508
509
0
    size_t
510
0
      clut_size;
511
512
0
    ssize_t
513
0
      i;
514
515
0
    unsigned char
516
0
      *clut_data;
517
518
    /*
519
      * ### Read CLUT Data ###
520
      */
521
0
    clut_size=MagickMax(header->clut_size,(size_t) (clut_depth/8)*
522
0
      image->colors);
523
0
    clut_data=(unsigned char *) AcquireQuantumMemory(clut_size,
524
0
      sizeof(*clut_data));
525
0
    if (clut_data == (unsigned char *) NULL)
526
0
      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
527
0
        image_info->filename);
528
0
    (void) memset(clut_data,0,clut_size);
529
0
    count=ReadBlob(image,clut_size,clut_data);
530
0
    if (count != (ssize_t) clut_size)
531
0
      {
532
0
        clut_data=(unsigned char *) RelinquishMagickMemory(clut_data);
533
0
        ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
534
0
          image_info->filename);
535
0
      }
536
    /*
537
      * ### Process CLUT Data ###
538
      */
539
0
    p=clut_data;
540
0
    switch(clut_depth)
541
0
    {
542
0
      case 16:
543
0
      {
544
0
        for (i=0; i < (ssize_t) image->colors; i++)
545
0
        {
546
0
          word = (unsigned int) (((unsigned short)* p   )<<0*8 |
547
0
                  ((unsigned short)*(p+1))<<1*8);
548
549
0
          image->colormap[i].red=GetChannelValue(word,0,RGBA16);
550
0
          image->colormap[i].green=GetChannelValue(word,1,RGBA16);
551
0
          image->colormap[i].blue=GetChannelValue(word,2,RGBA16);
552
0
          image->colormap[i].alpha=GetAlpha(word,RGBA16);
553
0
          p+=(ptrdiff_t) 2;
554
0
        }
555
0
        break;
556
0
      }
557
0
      case 24:
558
0
      {
559
0
        for (i=0; i < (ssize_t) image->colors; i++)
560
0
        {
561
0
          word = ((unsigned int)* p   )<<0*8 |
562
0
                  ((unsigned int)*(p+1))<<1*8 |
563
0
                  ((unsigned int)*(p+2))<<2*8;
564
565
0
          image->colormap[i].red=GetChannelValue(word,0,RGB24);
566
0
          image->colormap[i].green=GetChannelValue(word,1,RGB24);
567
0
          image->colormap[i].blue=GetChannelValue(word,2,RGB24);
568
0
          p+=(ptrdiff_t) 3;
569
0
        }
570
0
        break;
571
0
      }
572
0
      case 32:
573
0
      {
574
0
        for (i=0; i < (ssize_t) image->colors; i++)
575
0
        {
576
0
          word = ((unsigned int)* p   )<<0*8 |
577
0
                  ((unsigned int)*(p+1))<<1*8 |
578
0
                  ((unsigned int)*(p+2))<<2*8 |
579
0
                  ((unsigned int)*(p+3))<<3*8;
580
581
0
          image->colormap[i].red=GetChannelValue(word,0,RGBA32);
582
0
          image->colormap[i].green=GetChannelValue(word,1,RGBA32);
583
0
          image->colormap[i].blue=GetChannelValue(word,2,RGBA32);
584
0
          image->colormap[i].alpha=GetAlpha(word,RGBA32);
585
0
          p+=(ptrdiff_t) 4;
586
0
        }
587
0
        break;
588
0
      }
589
0
    }
590
0
    clut_data=(unsigned char *) RelinquishMagickMemory(clut_data);
591
    /* CSM: CLUT Storage Mode */
592
0
    switch ((int) header->clut_type>>4)  /* High 4 bits */
593
0
    {
594
0
      case 0:
595
0
        csm=CSM1;
596
0
        break;
597
0
      case 1:
598
0
        csm=CSM2;
599
0
        break;
600
0
      default:
601
0
        ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
602
0
          image_info->filename);
603
0
        break;
604
0
    }
605
0
    if (csm == CSM1)
606
0
      {
607
0
        PixelInfo
608
0
          *oldColormap;
609
610
0
        oldColormap=(PixelInfo *) AcquireQuantumMemory((size_t)(image->colors)+
611
0
          1,sizeof(*image->colormap));
612
0
        if (oldColormap == (PixelInfo *) NULL)
613
0
          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
614
0
            image_info->filename);
615
0
        deshufflePalette(image,oldColormap);
616
0
        RelinquishMagickMemory(oldColormap);
617
0
      }
618
0
  }
619
0
  return(status);
620
0
}
621
622
static Image *ReadTIM2Image(const ImageInfo *image_info,
623
  ExceptionInfo *exception)
624
366
{
625
366
  Image
626
366
    *image;
627
628
366
  MagickBooleanType
629
366
    status;
630
631
366
  ssize_t
632
366
    i;
633
634
366
  TIM2FileHeader
635
366
    file_header;
636
637
  /*
638
   * Open image file.
639
   */
640
366
  assert(image_info != (const ImageInfo *) NULL);
641
366
  assert(image_info->signature == MagickCoreSignature);
642
366
  assert(exception != (ExceptionInfo *) NULL);
643
366
  assert(exception->signature == MagickCoreSignature);
644
366
  if (IsEventLogging() != MagickFalse)
645
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
646
0
      image_info->filename);
647
366
  image=AcquireImage(image_info,exception);
648
366
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
649
366
  if (status == MagickFalse)
650
187
    {
651
187
      image=DestroyImageList(image);
652
187
      return((Image *) NULL);
653
187
    }
654
179
  file_header.magic_num=ReadBlobMSBLong(image);
655
179
  if (file_header.magic_num != 0x54494D32) /* "TIM2" */
656
165
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
657
14
  file_header.format_vers=(unsigned char) ReadBlobByte(image);
658
14
  if (file_header.format_vers != 0x04)
659
13
    ThrowReaderException(CoderError,"ImageTypeNotSupported");
660
13
  file_header.format_type=(unsigned char) ReadBlobByte(image);
661
13
  file_header.image_count=ReadBlobLSBShort(image);
662
13
  if (DiscardBlobBytes(image,8) == MagickFalse) /* reserved */
663
11
    ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
664
11
  if ((file_header.format_type > 0) &&
665
11
      (DiscardBlobBytes(image,112) == MagickFalse))
666
9
    ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
667
  /*
668
   * Process each image. Only one image supported for now
669
   */
670
9
  if (file_header.image_count != 1)
671
7
    ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported");
672
8
  for (i=0; i < (ssize_t) file_header.image_count; i++)
673
7
  {
674
7
    char
675
7
      clut_depth,
676
7
      bits_per_pixel;
677
678
7
    TIM2ImageHeader
679
7
      image_header;
680
681
7
    if (i > 0)
682
0
      {
683
        /*
684
          Proceed to next image.
685
        */
686
0
        if (image_info->number_scenes != 0)
687
0
          if (image->scene >= (image_info->scene+image_info->number_scenes-1))
688
0
            break;
689
        /*
690
          Allocate next image structure.
691
        */
692
0
        AcquireNextImage(image_info,image,exception);
693
0
        if (GetNextImageInList(image) == (Image *) NULL)
694
0
          {
695
0
            status=MagickFalse;
696
0
            break;
697
0
          }
698
0
        image=SyncNextImageInList(image);
699
0
        status=SetImageProgress(image,LoadImagesTag,(MagickOffsetType)
700
0
          image->scene-1,image->scene);
701
0
        if (status == MagickFalse)
702
0
          break;
703
0
      }
704
7
    ReadTIM2ImageHeader(image,&image_header);
705
7
    if (image_header.mipmap_count != 1)
706
6
      ThrowReaderException(CoderError,"NumberOfImagesIsNotSupported");
707
6
    if (image_header.header_size < 48)
708
5
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
709
5
    if ((MagickSizeType) image_header.image_size > GetBlobSize(image))
710
4
      ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
711
4
    if ((MagickSizeType) image_header.clut_size > GetBlobSize(image))
712
3
      ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
713
3
    image->columns=image_header.width;
714
3
    image->rows=image_header.height;
715
3
    clut_depth=0;
716
3
    if (image_header.clut_type !=0)
717
2
      {
718
2
        switch((int) image_header.clut_type&0x0F)  /* Low 4 bits */
719
2
        {
720
0
          case 1:
721
0
            clut_depth=16;
722
0
            break;
723
1
          case 2:
724
1
            clut_depth=24;
725
1
            break;
726
0
          case 3:
727
0
            clut_depth=32;
728
0
            break;
729
1
          default:
730
1
            ThrowReaderException(CorruptImageError,"ImproperImageHeader");
731
0
            break;
732
2
        }
733
2
      }
734
2
    switch ((int) image_header.bpp_type)
735
2
    {
736
0
      case 1:
737
0
        bits_per_pixel=16;
738
0
        break;
739
1
      case 2:
740
1
        bits_per_pixel=24;
741
1
        break;
742
0
      case 3:
743
0
        bits_per_pixel=32;
744
0
        break;
745
0
      case 4:
746
0
        bits_per_pixel=4;  /* Implies CLUT */
747
0
        break;
748
0
      case 5:
749
0
        bits_per_pixel=8;  /* Implies CLUT */
750
0
        break;
751
1
      default:
752
1
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
753
0
        break;
754
2
    }
755
1
    image->depth=(size_t) ((clut_depth != 0) ? clut_depth : bits_per_pixel);
756
1
    if ((image->depth == 16) || (image->depth == 32))
757
0
      image->alpha_trait=BlendPixelTrait;
758
1
    if (image->ping == MagickFalse)
759
0
      {
760
0
        status=ReadTIM2ImageData(image_info,image,&image_header,clut_depth,
761
0
          bits_per_pixel,exception);
762
0
        if (status==MagickFalse)
763
0
          break;
764
0
      }
765
1
    if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
766
0
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
767
0
        break;
768
1
    if ((image->storage_class == PseudoClass) && (EOFBlob(image) != MagickFalse))
769
0
      {
770
0
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
771
0
          image->filename);
772
0
        break;
773
0
      }
774
1
  }
775
1
  if (CloseBlob(image) == MagickFalse)
776
0
    status=MagickFalse;
777
1
  if (status == MagickFalse)
778
0
    return(DestroyImageList(image));
779
1
  return(GetFirstImageInList(image));
780
1
}
781

782
/*
783
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784
%                                                                             %
785
%                                                                             %
786
%                                                                             %
787
%   R e g i s t e r T I M 2 I m a g e                                         %
788
%                                                                             %
789
%                                                                             %
790
%                                                                             %
791
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
792
%
793
%  RegisterTIM2Image() adds attributes for the TIM2 image format to
794
%  the list of supported formats.  The attributes include the image format
795
%  tag, a method to read and/or write the format, whether the format
796
%  supports the saving of more than one frame to the same file or blob,
797
%  whether the format supports native in-memory I/O, and a brief
798
%  description of the format.
799
%
800
%  The format of the RegisterTIM2Image method is:
801
%
802
%      size_t RegisterTIM2Image(void)
803
%
804
*/
805
ModuleExport size_t RegisterTIM2Image(void)
806
9
{
807
9
  MagickInfo
808
9
    *entry;
809
810
9
  entry=AcquireMagickInfo("TIM2","TM2","PS2 TIM2");
811
9
  entry->decoder=(DecodeImageHandler *) ReadTIM2Image;
812
9
  (void) RegisterMagickInfo(entry);
813
9
  return(MagickImageCoderSignature);
814
9
}
815

816
/*
817
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818
%                                                                             %
819
%                                                                             %
820
%                                                                             %
821
%   U n r e g i s t e r T I M 2 I m a g e                                     %
822
%                                                                             %
823
%                                                                             %
824
%                                                                             %
825
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826
%
827
%  UnregisterTIM2Image() removes format registrations made by the
828
%  TIM2 module from the list of supported formats.
829
%
830
%  The format of the UnregisterTIM2Image method is:
831
%
832
%      UnregisterTIM2Image(void)
833
%
834
*/
835
ModuleExport void UnregisterTIM2Image(void)
836
0
{
837
0
  (void) UnregisterMagickInfo("TM2");
838
0
}