1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/04/2009
6 //
7 // Filename: src-IL/src/il_iwi.c
8 //
9 // Description: Reads from an Infinity Ward Image (.iwi) file from Call of Duty.
10 //
11 //-----------------------------------------------------------------------------
12
13
14 #include "il_internal.h"
15 #ifndef IL_NO_IWI
16 #include "il_dds.h"
17
18 typedef struct IWIHEAD
19 {
20 ILuint Signature;
21 ILubyte Format;
22 ILubyte Flags;
23 ILushort Width;
24 ILushort Height;
25 } IWIHEAD;
26
27 #define IWI_ARGB8 0x01
28 #define IWI_RGB8 0x02
29 #define IWI_ARGB4 0x03
30 #define IWI_A8 0x04
31 #define IWI_JPG 0x07
32 #define IWI_DXT1 0x0B
33 #define IWI_DXT3 0x0C
34 #define IWI_DXT5 0x0D
35
36 ILboolean iIsValidIwi(void);
37 ILboolean iCheckIwi(IWIHEAD *Header);
38 ILboolean iLoadIwiInternal(void);
39 ILboolean IwiInitMipmaps(ILimage *BaseImage, ILuint *NumMips);
40 ILboolean IwiReadImage(ILimage *BaseImage, IWIHEAD *Header, ILuint NumMips);
41 ILenum IwiGetFormat(ILubyte Format, ILubyte *Bpp);
42
43 //! Checks if the file specified in FileName is a valid IWI file.
ilIsValidIwi(ILconst_string FileName)44 ILboolean ilIsValidIwi(ILconst_string FileName)
45 {
46 ILHANDLE IwiFile;
47 ILboolean bIwi = IL_FALSE;
48
49 if (!iCheckExtension(FileName, IL_TEXT("iwi"))) {
50 ilSetError(IL_INVALID_EXTENSION);
51 return bIwi;
52 }
53
54 IwiFile = iopenr(FileName);
55 if (IwiFile == NULL) {
56 ilSetError(IL_COULD_NOT_OPEN_FILE);
57 return bIwi;
58 }
59
60 bIwi = ilIsValidIwiF(IwiFile);
61 icloser(IwiFile);
62
63 return bIwi;
64 }
65
66
67 //! Checks if the ILHANDLE contains a valid IWI file at the current position.
ilIsValidIwiF(ILHANDLE File)68 ILboolean ilIsValidIwiF(ILHANDLE File)
69 {
70 ILuint FirstPos;
71 ILboolean bRet;
72
73 iSetInputFile(File);
74 FirstPos = itell();
75 bRet = iIsValidIwi();
76 iseek(FirstPos, IL_SEEK_SET);
77
78 return bRet;
79 }
80
81
82 //! Checks if Lump is a valid IWI lump.
ilIsValidIwiL(const void * Lump,ILuint Size)83 ILboolean ilIsValidIwiL(const void *Lump, ILuint Size)
84 {
85 iSetInputLump(Lump, Size);
86 return iIsValidIwi();
87 }
88
89
90 // Internal function used to get the IWI header from the current file.
iGetIwiHead(IWIHEAD * Header)91 ILboolean iGetIwiHead(IWIHEAD *Header)
92 {
93 Header->Signature = GetLittleUInt();
94 Header->Format = igetc();
95 Header->Flags = igetc(); //@TODO: Find out what the flags mean.
96 Header->Width = GetLittleUShort();
97 Header->Height = GetLittleUShort();
98
99 // @TODO: Find out what is in the rest of the header.
100 iseek(18, IL_SEEK_CUR);
101
102 return IL_TRUE;
103 }
104
105
106 // Internal function to get the header and check it.
iIsValidIwi(void)107 ILboolean iIsValidIwi(void)
108 {
109 IWIHEAD Header;
110 ILuint Pos = itell();
111
112 if (!iGetIwiHead(&Header))
113 return IL_FALSE;
114 // The length of the header varies, so we just go back to the original position.
115 iseek(Pos, IL_SEEK_CUR);
116
117 return iCheckIwi(&Header);
118 }
119
120
121 // Internal function used to check if the HEADER is a valid IWI header.
iCheckIwi(IWIHEAD * Header)122 ILboolean iCheckIwi(IWIHEAD *Header)
123 {
124 if (Header->Signature != 0x06695749 && Header->Signature != 0x05695749) // 'IWi-' (version 6, and version 5 is the second).
125 return IL_FALSE;
126 if (Header->Width == 0 || Header->Height == 0)
127 return IL_FALSE;
128 // DXT images must have power-of-2 dimensions.
129 if (Header->Format == IWI_DXT1 || Header->Format == IWI_DXT3 || Header->Format == IWI_DXT5)
130 if (Header->Width != ilNextPower2(Header->Width) || Header->Height != ilNextPower2(Header->Height))
131 return IL_FALSE;
132 // 0x0B, 0x0C and 0x0D are DXT formats.
133 if (Header->Format != IWI_ARGB4 && Header->Format != IWI_RGB8 && Header->Format != IWI_ARGB8 && Header->Format != IWI_A8
134 && Header->Format != IWI_DXT1 && Header->Format != IWI_DXT3 && Header->Format != IWI_DXT5)
135 return IL_FALSE;
136
137 return IL_TRUE;
138 }
139
140
141 //! Reads a IWI file
ilLoadIwi(ILconst_string FileName)142 ILboolean ilLoadIwi(ILconst_string FileName)
143 {
144 ILHANDLE IwiFile;
145 ILboolean bIwi = IL_FALSE;
146
147 IwiFile = iopenr(FileName);
148 if (IwiFile == NULL) {
149 ilSetError(IL_COULD_NOT_OPEN_FILE);
150 return bIwi;
151 }
152
153 bIwi = ilLoadIwiF(IwiFile);
154 icloser(IwiFile);
155
156 return bIwi;
157 }
158
159
160 //! Reads an already-opened IWI file
ilLoadIwiF(ILHANDLE File)161 ILboolean ilLoadIwiF(ILHANDLE File)
162 {
163 ILuint FirstPos;
164 ILboolean bRet;
165
166 iSetInputFile(File);
167 FirstPos = itell();
168 bRet = iLoadIwiInternal();
169 iseek(FirstPos, IL_SEEK_SET);
170
171 return bRet;
172 }
173
174
175 //! Reads from a memory "lump" that contains a IWI
ilLoadIwiL(const void * Lump,ILuint Size)176 ILboolean ilLoadIwiL(const void *Lump, ILuint Size)
177 {
178 iSetInputLump(Lump, Size);
179 return iLoadIwiInternal();
180 }
181
182
183 // Internal function used to load the IWI.
iLoadIwiInternal(void)184 ILboolean iLoadIwiInternal(void)
185 {
186 IWIHEAD Header;
187 ILuint NumMips = 0;
188 ILboolean HasMipmaps = IL_TRUE;
189 ILenum Format;
190 ILubyte Bpp;
191
192 if (iCurImage == NULL) {
193 ilSetError(IL_ILLEGAL_OPERATION);
194 return IL_FALSE;
195 }
196
197 // Read the header and check it.
198 if (!iGetIwiHead(&Header))
199 return IL_FALSE;
200 if (!iCheckIwi(&Header)) {
201 ilSetError(IL_INVALID_FILE_HEADER);
202 return IL_FALSE;
203 }
204
205 // From a post by Pointy on http://iwnation.com/forums/index.php?showtopic=27903,
206 // flags ending with 0x3 have no mipmaps.
207 HasMipmaps = ((Header.Flags & 0x03) == 0x03) ? IL_FALSE : IL_TRUE;
208
209 // Create the image, then create the mipmaps, then finally read the image.
210 Format = IwiGetFormat(Header.Format, &Bpp);
211 if (!ilTexImage(Header.Width, Header.Height, 1, Bpp, Format, IL_UNSIGNED_BYTE, NULL))
212 return IL_FALSE;
213 iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
214 if (HasMipmaps)
215 if (!IwiInitMipmaps(iCurImage, &NumMips))
216 return IL_FALSE;
217 if (!IwiReadImage(iCurImage, &Header, NumMips))
218 return IL_FALSE;
219
220 return ilFixImage();
221 }
222
223
224 // Helper function to convert IWI formats to DevIL formats and Bpp.
IwiGetFormat(ILubyte Format,ILubyte * Bpp)225 ILenum IwiGetFormat(ILubyte Format, ILubyte *Bpp)
226 {
227 switch (Format)
228 {
229 case IWI_ARGB8:
230 *Bpp = 4;
231 return IL_BGRA;
232 case IWI_RGB8:
233 *Bpp = 3;
234 return IL_BGR;
235 case IWI_ARGB4:
236 *Bpp = 4;
237 return IL_BGRA;
238 case IWI_A8:
239 *Bpp = 1;
240 return IL_ALPHA;
241 case IWI_DXT1:
242 *Bpp = 4;
243 return IL_RGBA;
244 case IWI_DXT3:
245 *Bpp = 4;
246 return IL_RGBA;
247 case IWI_DXT5:
248 *Bpp = 4;
249 return IL_RGBA;
250 }
251
252 return 0; // Will never reach this.
253 }
254
255
256 // Function to intialize the mipmaps and determine the number of mipmaps.
IwiInitMipmaps(ILimage * BaseImage,ILuint * NumMips)257 ILboolean IwiInitMipmaps(ILimage *BaseImage, ILuint *NumMips)
258 {
259 ILimage *Image;
260 ILuint Width, Height, Mipmap;
261
262 Image = BaseImage;
263 Width = BaseImage->Width; Height = BaseImage->Height;
264 Image->Origin = IL_ORIGIN_UPPER_LEFT;
265
266 for (Mipmap = 0; Width != 1 && Height != 1; Mipmap++) {
267 // 1 is the smallest dimension possible.
268 Width = (Width >> 1) == 0 ? 1 : (Width >> 1);
269 Height = (Height >> 1) == 0 ? 1 : (Height >> 1);
270
271 Image->Mipmaps = ilNewImageFull(Width, Height, 1, BaseImage->Bpp, BaseImage->Format, BaseImage->Type, NULL);
272 if (Image->Mipmaps == NULL)
273 return IL_FALSE;
274 Image = Image->Mipmaps;
275
276 // ilNewImage does not set these.
277 Image->Format = BaseImage->Format;
278 Image->Type = BaseImage->Type;
279 // The origin is in the upper left.
280 Image->Origin = IL_ORIGIN_UPPER_LEFT;
281 }
282
283 *NumMips = Mipmap;
284 return IL_TRUE;
285 }
286
287
IwiReadImage(ILimage * BaseImage,IWIHEAD * Header,ILuint NumMips)288 ILboolean IwiReadImage(ILimage *BaseImage, IWIHEAD *Header, ILuint NumMips)
289 {
290 ILimage *Image;
291 ILuint SizeOfData;
292 ILubyte *CompData = NULL;
293 ILint i, j, k, m;
294
295 for (i = NumMips; i >= 0; i--) {
296 Image = BaseImage;
297 // Go to the ith mipmap level.
298 // The mipmaps go from smallest to the largest.
299 for (j = 0; j < i; j++)
300 Image = Image->Mipmaps;
301
302 switch (Header->Format)
303 {
304 case IWI_ARGB8: // These are all
305 case IWI_RGB8: // uncompressed data,
306 case IWI_A8: // so just read it.
307 if (iread(Image->Data, 1, Image->SizeOfData) != Image->SizeOfData)
308 return IL_FALSE;
309 break;
310
311 case IWI_ARGB4: //@TODO: Find some test images for this.
312 // Data is in ARGB4 format - 4 bits per component.
313 SizeOfData = Image->Width * Image->Height * 2;
314 CompData = ialloc(SizeOfData); // Not really compressed - just in ARGB4 format.
315 if (CompData == NULL)
316 return IL_FALSE;
317 if (iread(CompData, 1, SizeOfData) != SizeOfData) {
318 ifree(CompData);
319 return IL_FALSE;
320 }
321 for (k = 0, m = 0; k < (ILint)Image->SizeOfData; k += 4, m += 2) {
322 // @TODO: Double the image data into the low and high nibbles for a better range of values.
323 Image->Data[k+0] = CompData[m] & 0xF0;
324 Image->Data[k+1] = (CompData[m] & 0x0F) << 4;
325 Image->Data[k+2] = CompData[m+1] & 0xF0;
326 Image->Data[k+3] = (CompData[m+1] & 0x0F) << 4;
327 }
328 break;
329
330 case IWI_DXT1:
331 // DXT1 data has at least 8 bytes, even for one pixel.
332 SizeOfData = IL_MAX(Image->Width * Image->Height / 2, 8);
333 CompData = ialloc(SizeOfData); // Gives a 6:1 compression ratio (or 8:1 for DXT1 with alpha)
334 if (CompData == NULL)
335 return IL_FALSE;
336 if (iread(CompData, 1, SizeOfData) != SizeOfData) {
337 ifree(CompData);
338 return IL_FALSE;
339 }
340
341 // Decompress the DXT1 data into Image (ith mipmap).
342 if (!DecompressDXT1(Image, CompData)) {
343 ifree(CompData);
344 return IL_FALSE;
345 }
346
347 // Keep a copy of the DXTC data if the user wants it.
348 if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) {
349 Image->DxtcSize = SizeOfData;
350 Image->DxtcData = CompData;
351 Image->DxtcFormat = IL_DXT1;
352 CompData = NULL;
353 }
354
355 break;
356
357 case IWI_DXT3:
358 // DXT3 data has at least 16 bytes, even for one pixel.
359 SizeOfData = IL_MAX(Image->Width * Image->Height, 16);
360 CompData = ialloc(SizeOfData); // Gives a 4:1 compression ratio
361 if (CompData == NULL)
362 return IL_FALSE;
363 if (iread(CompData, 1, SizeOfData) != SizeOfData) {
364 ifree(CompData);
365 return IL_FALSE;
366 }
367
368 // Decompress the DXT3 data into Image (ith mipmap).
369 if (!DecompressDXT3(Image, CompData)) {
370 ifree(CompData);
371 return IL_FALSE;
372 }
373 break;
374
375 case IWI_DXT5:
376 // DXT5 data has at least 16 bytes, even for one pixel.
377 SizeOfData = IL_MAX(Image->Width * Image->Height, 16);
378 CompData = ialloc(SizeOfData); // Gives a 4:1 compression ratio
379 if (CompData == NULL)
380 return IL_FALSE;
381 if (iread(CompData, 1, SizeOfData) != SizeOfData) {
382 ifree(CompData);
383 return IL_FALSE;
384 }
385
386 // Decompress the DXT5 data into Image (ith mipmap).
387 if (!DecompressDXT5(Image, CompData)) {
388 ifree(CompData);
389 return IL_FALSE;
390 }
391 break;
392 }
393
394 ifree(CompData);
395 }
396
397 return IL_TRUE;
398 }
399
400
401 #endif//IL_NO_IWI
402