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_dcx.c
8 //
9 // Description: Reads from a .dcx file.
10 //
11 //-----------------------------------------------------------------------------
12 
13 
14 #include "il_internal.h"
15 #ifndef IL_NO_DCX
16 #include "il_dcx.h"
17 #include "il_manip.h"
18 
19 
20 //! Checks if the file specified in FileName is a valid .dcx file.
ilIsValidDcx(ILconst_string FileName)21 ILboolean ilIsValidDcx(ILconst_string FileName)
22 {
23 	ILHANDLE	DcxFile;
24 	ILboolean	bDcx = IL_FALSE;
25 
26 	if (!iCheckExtension(FileName, IL_TEXT("dcx"))) {
27 		ilSetError(IL_INVALID_EXTENSION);
28 		return bDcx;
29 	}
30 
31 	DcxFile = iopenr(FileName);
32 	if (DcxFile == NULL) {
33 		ilSetError(IL_COULD_NOT_OPEN_FILE);
34 		return bDcx;
35 	}
36 
37 	bDcx = ilIsValidDcxF(DcxFile);
38 	icloser(DcxFile);
39 
40 	return bDcx;
41 }
42 
43 
44 //! Checks if the ILHANDLE contains a valid .dcx file at the current position.
ilIsValidDcxF(ILHANDLE File)45 ILboolean ilIsValidDcxF(ILHANDLE File)
46 {
47 	ILuint		FirstPos;
48 	ILboolean	bRet;
49 
50 	iSetInputFile(File);
51 	FirstPos = itell();
52 	bRet = iIsValidDcx();
53 	iseek(FirstPos, IL_SEEK_SET);
54 
55 	return bRet;
56 }
57 
58 
59 //! Checks if Lump is a valid .dcx lump.
ilIsValidDcxL(const void * Lump,ILuint Size)60 ILboolean ilIsValidDcxL(const void *Lump, ILuint Size)
61 {
62 	iSetInputLump(Lump, Size);
63 	return iIsValidDcx();
64 }
65 
66 
67 // Internal function obtain the .dcx header from the current file.
iGetDcxHead(DCXHEAD * Head)68 ILboolean iGetDcxHead(DCXHEAD *Head)
69 {
70 	Head->Xmin = GetLittleUShort();
71 	Head->Ymin = GetLittleUShort();
72 	Head->Xmax = GetLittleUShort();
73 	Head->Ymax = GetLittleUShort();
74 	Head->HDpi = GetLittleUShort();
75 	Head->VDpi = GetLittleUShort();
76 	Head->Bps = GetLittleUShort();
77 	Head->PaletteInfo = GetLittleUShort();
78 	Head->HScreenSize = GetLittleUShort();
79 	Head->VScreenSize = GetLittleUShort();
80 
81 	return IL_TRUE;
82 }
83 
84 
85 // Internal function to get the header and check it.
iIsValidDcx()86 ILboolean iIsValidDcx()
87 {
88 	ILuint Signature;
89 
90 	if (iread(&Signature, 1, 4) != 4)
91 		return IL_FALSE;
92 	iseek(-4, IL_SEEK_CUR);
93 
94 	return (Signature == 987654321);
95 }
96 
97 
98 // Internal function used to check if the HEADER is a valid .dcx header.
99 // Should we also do a check on Header->Bpp?
iCheckDcx(DCXHEAD * Header)100 ILboolean iCheckDcx(DCXHEAD *Header)
101 {
102 	ILuint	Test, i;
103 
104 	// There are other versions, but I am not supporting them as of yet.
105 	//	Got rid of the Reserved check, because I've seen some .dcx files with invalid values in it.
106 	if (Header->Manufacturer != 10 || Header->Version != 5 || Header->Encoding != 1/* || Header->Reserved != 0*/)
107 		return IL_FALSE;
108 
109 	// See if the padding size is correct
110 	Test = Header->Xmax - Header->Xmin + 1;
111 	/*if (Header->Bpp >= 8) {
112 		if (Test & 1) {
113 			if (Header->Bps != Test + 1)
114 				return IL_FALSE;
115 		}
116 		else {
117 			if (Header->Bps != Test)  // No padding
118 				return IL_FALSE;
119 		}
120 	}*/
121 
122 	for (i = 0; i < 54; i++) {
123 		if (Header->Filler[i] != 0)
124 			return IL_FALSE;
125 	}
126 
127 	return IL_TRUE;
128 }
129 
130 
131 //! Reads a .dcx file
ilLoadDcx(ILconst_string FileName)132 ILboolean ilLoadDcx(ILconst_string FileName)
133 {
134 	ILHANDLE	DcxFile;
135 	ILboolean	bDcx = IL_FALSE;
136 
137 	DcxFile = iopenr(FileName);
138 	if (DcxFile == NULL) {
139 		ilSetError(IL_COULD_NOT_OPEN_FILE);
140 		return bDcx;
141 	}
142 
143 	bDcx = ilLoadDcxF(DcxFile);
144 	icloser(DcxFile);
145 
146 	return bDcx;
147 }
148 
149 
150 //! Reads an already-opened .dcx file
ilLoadDcxF(ILHANDLE File)151 ILboolean ilLoadDcxF(ILHANDLE File)
152 {
153 	ILuint		FirstPos;
154 	ILboolean	bRet;
155 
156 	iSetInputFile(File);
157 	FirstPos = itell();
158 	bRet = iLoadDcxInternal();
159 	iseek(FirstPos, IL_SEEK_SET);
160 
161 	return bRet;
162 }
163 
164 
165 //! Reads from a memory "lump" that contains a .dcx
ilLoadDcxL(const void * Lump,ILuint Size)166 ILboolean ilLoadDcxL(const void *Lump, ILuint Size) {
167 	iSetInputLump(Lump, Size);
168 	return iLoadDcxInternal();
169 }
170 
171 
172 // Internal function used to load the .dcx.
iLoadDcxInternal()173 ILboolean iLoadDcxInternal()
174 {
175 	DCXHEAD	Header;
176 	ILuint	Signature, i, Entries[1024], Num = 0;
177 	ILimage	*Image, *Base;
178 
179 	if (iCurImage == NULL) {
180 		ilSetError(IL_ILLEGAL_OPERATION);
181 		return IL_FALSE;
182 	}
183 
184 	if (!iIsValidDcx())
185 		return IL_FALSE;
186 	iread(&Signature, 1, 4);
187 
188 	do {
189 		if (iread(&Entries[Num], 1, 4) != 4)
190 			return IL_FALSE;
191 		Num++;
192 	} while (Entries[Num-1] != 0);
193 
194 	for (i = 0; i < Num; i++) {
195 		iseek(Entries[i], IL_SEEK_SET);
196 		iGetDcxHead(&Header);
197 		/*if (!iCheckDcx(&Header)) {
198 			ilSetError(IL_INVALID_FILE_HEADER);
199 			return IL_FALSE;
200 		}*/
201 
202 		Image = iUncompressDcx(&Header);
203 		if (Image == NULL)
204 			return IL_FALSE;
205 
206 		if (i == 0) {
207 			ilTexImage(Image->Width, Image->Height, 1, Image->Bpp, Image->Format, Image->Type, Image->Data);
208 			Base = iCurImage;
209 			Base->Origin = IL_ORIGIN_UPPER_LEFT;
210 			ilCloseImage(Image);
211 		}
212 		else {
213 			iCurImage->Next = Image;
214 			iCurImage = iCurImage->Next;
215 		}
216 	}
217 
218 	return ilFixImage();
219 }
220 
221 
222 // Internal function to uncompress the .dcx (all .dcx files are rle compressed)
iUncompressDcx(DCXHEAD * Header)223 ILimage *iUncompressDcx(DCXHEAD *Header)
224 {
225 	ILubyte		ByteHead, Colour, *ScanLine = NULL /* Only one plane */;
226 	ILuint		c, i, x, y;//, Read = 0;
227 	ILimage		*Image = NULL;
228 
229 	if (Header->Bpp < 8) {
230 		/*ilSetError(IL_FORMAT_NOT_SUPPORTED);
231 		return IL_FALSE;*/
232 		return iUncompressDcxSmall(Header);
233 	}
234 
235 	Image = ilNewImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 1);
236 	if (Image == NULL)
237 		return NULL;
238 	/*if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 0, IL_UNSIGNED_BYTE, NULL)) {
239 		return IL_FALSE;
240 	}*/
241 	Image->Origin = IL_ORIGIN_UPPER_LEFT;
242 
243 	ScanLine = (ILubyte*)ialloc(Header->Bps);
244 	if (ScanLine == NULL)
245 		goto dcx_error;
246 
247 	switch (Image->Bpp)
248 	{
249 		case 1:
250 			Image->Format = IL_COLOUR_INDEX;
251 			Image->Pal.PalType = IL_PAL_RGB24;
252 			Image->Pal.PalSize = 256 * 3; // Need to find out for sure...
253 			Image->Pal.Palette = (ILubyte*)ialloc(Image->Pal.PalSize);
254 			if (Image->Pal.Palette == NULL)
255 				goto dcx_error;
256 			break;
257 		//case 2:  // No 16-bit images in the dcx format!
258 		case 3:
259 			Image->Format = IL_RGB;
260 			Image->Pal.Palette = NULL;
261 			Image->Pal.PalSize = 0;
262 			Image->Pal.PalType = IL_PAL_NONE;
263 			break;
264 		case 4:
265 			Image->Format = IL_RGBA;
266 			Image->Pal.Palette = NULL;
267 			Image->Pal.PalSize = 0;
268 			Image->Pal.PalType = IL_PAL_NONE;
269 			break;
270 
271 		default:
272 			ilSetError(IL_ILLEGAL_FILE_VALUE);
273 			goto dcx_error;
274 	}
275 
276 
277 		/*StartPos = itell();
278 		Compressed = (ILubyte*)ialloc(Image->SizeOfData * 4 / 3);
279 		iread(Compressed, 1, Image->SizeOfData * 4 / 3);
280 
281 		for (y = 0; y < Image->Height; y++) {
282 			for (c = 0; c < Image->Bpp; c++) {
283 				x = 0;
284 				while (x < Header->Bps) {
285 					ByteHead = Compressed[Read++];
286 					if ((ByteHead & 0xC0) == 0xC0) {
287 						ByteHead &= 0x3F;
288 						Colour = Compressed[Read++];
289 						for (i = 0; i < ByteHead; i++) {
290 							ScanLine[x++] = Colour;
291 						}
292 					}
293 					else {
294 						ScanLine[x++] = ByteHead;
295 					}
296 				}
297 
298 				for (x = 0; x < Image->Width; x++) {  // 'Cleverly' ignores the pad bytes ;)
299 					Image->Data[y * Image->Bps + x * Image->Bpp + c] = ScanLine[x];
300 				}
301 			}
302 		}
303 
304 		ifree(Compressed);
305 		iseek(StartPos + Read, IL_SEEK_SET);*/
306 
307 	//changed 2003-09-01
308 	if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
309 		iPreCache(iCurImage->SizeOfData);
310 
311 	//TODO: because the .pcx-code was broken this
312 	//code is probably broken, too
313 	for (y = 0; y < Image->Height; y++) {
314 		for (c = 0; c < Image->Bpp; c++) {
315 			x = 0;
316 			while (x < Header->Bps) {
317 				if (iread(&ByteHead, 1, 1) != 1) {
318 					iUnCache();
319 					goto dcx_error;
320 				}
321 				if ((ByteHead & 0xC0) == 0xC0) {
322 					ByteHead &= 0x3F;
323 					if (iread(&Colour, 1, 1) != 1) {
324 						iUnCache();
325 						goto dcx_error;
326 					}
327 					for (i = 0; i < ByteHead; i++) {
328 						ScanLine[x++] = Colour;
329 					}
330 				}
331 				else {
332 					ScanLine[x++] = ByteHead;
333 				}
334 			}
335 
336 			for (x = 0; x < Image->Width; x++) {  // 'Cleverly' ignores the pad bytes ;)
337 				Image->Data[y * Image->Bps + x * Image->Bpp + c] = ScanLine[x];
338 			}
339 		}
340 	}
341 
342 	iUnCache();
343 
344 
345 	ifree(ScanLine);
346 
347 	// Read in the palette
348 	if (Image->Bpp == 1) {
349 		ByteHead = igetc();	// the value 12, because it signals there's a palette for some reason...
350 							//	We should do a check to make certain it's 12...
351 		if (ByteHead != 12)
352 			iseek(-1, IL_SEEK_CUR);
353 		if (iread(Image->Pal.Palette, 1, Image->Pal.PalSize) != Image->Pal.PalSize) {
354 			ilCloseImage(Image);
355 			return NULL;
356 		}
357 	}
358 
359 	return Image;
360 
361 dcx_error:
362 	ifree(ScanLine);
363 	ilCloseImage(Image);
364 	return NULL;
365 }
366 
367 
iUncompressDcxSmall(DCXHEAD * Header)368 ILimage *iUncompressDcxSmall(DCXHEAD *Header)
369 {
370 	ILuint	i = 0, j, k, c, d, x, y, Bps;
371 	ILubyte	HeadByte, Colour, Data = 0, *ScanLine = NULL;
372 	ILimage	*Image;
373 
374 	Image = ilNewImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 1);
375 	if (Image == NULL)
376 		return NULL;
377 
378 	/*if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, 1, 0, IL_UNSIGNED_BYTE, NULL)) {
379 		return IL_FALSE;
380 	}*/
381 	Image->Origin = IL_ORIGIN_UPPER_LEFT;
382 
383 	switch (Header->NumPlanes)
384 	{
385 		case 1:
386 			Image->Format = IL_LUMINANCE;
387 			break;
388 		case 4:
389 			Image->Format = IL_COLOUR_INDEX;
390 			break;
391 		default:
392 			ilSetError(IL_ILLEGAL_FILE_VALUE);
393 			ilCloseImage(Image);
394 			return NULL;
395 	}
396 
397 	if (Header->NumPlanes == 1) {
398 		for (j = 0; j < Image->Height; j++) {
399 			i = 0;
400 			while (i < Image->Width) {
401 				if (iread(&HeadByte, 1, 1) != 1)
402 					goto file_read_error;
403 				if (HeadByte >= 192) {
404 					HeadByte -= 192;
405 					if (iread(&Data, 1, 1) != 1)
406 						goto file_read_error;
407 
408 					for (c = 0; c < HeadByte; c++) {
409 						k = 128;
410 						for (d = 0; d < 8 && i < Image->Width; d++) {
411 							Image->Data[j * Image->Width + i++] = (!!(Data & k) == 1 ? 255 : 0);
412 							k >>= 1;
413 						}
414 					}
415 				}
416 				else {
417 					k = 128;
418 					for (c = 0; c < 8 && i < Image->Width; c++) {
419 						Image->Data[j * Image->Width + i++] = (!!(HeadByte & k) == 1 ? 255 : 0);
420 						k >>= 1;
421 					}
422 				}
423 			}
424 			if (Data != 0)
425 				igetc();  // Skip pad byte if last byte not a 0
426 		}
427 	}
428 	else {   // 4-bit images
429 		Bps = Header->Bps * Header->NumPlanes * 2;
430 		Image->Pal.Palette = (ILubyte*)ialloc(16 * 3);  // Size of palette always (48 bytes).
431 		Image->Pal.PalSize = 16 * 3;
432 		Image->Pal.PalType = IL_PAL_RGB24;
433 		ScanLine = (ILubyte*)ialloc(Bps);
434 		if (Image->Pal.Palette == NULL || ScanLine == NULL) {
435 			ifree(ScanLine);
436 			ilCloseImage(Image);
437 			return NULL;
438 		}
439 
440 		memcpy(Image->Pal.Palette, Header->ColMap, 16 * 3);
441 		imemclear(Image->Data, Image->SizeOfData);  // Since we do a += later.
442 
443 		for (y = 0; y < Image->Height; y++) {
444 			for (c = 0; c < Header->NumPlanes; c++) {
445 				x = 0;
446 				while (x < Bps) {
447 					if (iread(&HeadByte, 1, 1) != 1)
448 						goto file_read_error;
449 					if ((HeadByte & 0xC0) == 0xC0) {
450 						HeadByte &= 0x3F;
451 						if (iread(&Colour, 1, 1) != 1)
452 							goto file_read_error;
453 						for (i = 0; i < HeadByte; i++) {
454 							k = 128;
455 							for (j = 0; j < 8; j++) {
456 								ScanLine[x++] = !!(Colour & k);
457 								k >>= 1;
458 							}
459 						}
460 					}
461 					else {
462 						k = 128;
463 						for (j = 0; j < 8; j++) {
464 							ScanLine[x++] = !!(HeadByte & k);
465 							k >>= 1;
466 						}
467 					}
468 				}
469 
470 				for (x = 0; x < Image->Width; x++) {  // 'Cleverly' ignores the pad bytes. ;)
471 					Image->Data[y * Image->Width + x] += ScanLine[x] << c;
472 				}
473 			}
474 		}
475 		ifree(ScanLine);
476 	}
477 
478 	return Image;
479 
480 file_read_error:
481 	ifree(ScanLine);
482 	ilCloseImage(Image);
483 	return NULL;
484 }
485 
486 #endif//IL_NO_DCX
487