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