summaryrefslogtreecommitdiff
path: root/DevIL/src-IL/src/il_rot.cpp
blob: 38cf306758158a14bab0e7a5bad18c8be478ae3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
//-----------------------------------------------------------------------------
//
// ImageLib Sources
// Copyright (C) 2000-2009 by Denton Woods
// Last modified: 02/15/2009
//
// Filename: src-IL/src/il_rot.c
//
// Description: Reads from a Homeworld 2 - Relic Texture (.rot) file.
//
//-----------------------------------------------------------------------------

// @TODO:
// Note: I am not certain about which is DXT3 and which is DXT5.  According to
//  http://forums.relicnews.com/showthread.php?t=20512, DXT3 is 1030, and DXT5
//  is 1029.  However, neither way seems to work quite right for the alpha.

#include "il_internal.h"
#ifndef IL_NO_ROT
#include "il_dds.h"

ILboolean iLoadRotInternal(void);

#define ROT_RGBA32	1024
#define ROT_DXT1	1028
#define ROT_DXT3	1029
#define ROT_DXT5	1030


//! Reads a ROT file
ILboolean ilLoadRot(ILconst_string FileName)
{
	ILHANDLE	RotFile;
	ILboolean	bRot = IL_FALSE;
	
	RotFile = iopenr(FileName);
	if (RotFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return bRot;
	}

	bRot = ilLoadRotF(RotFile);
	icloser(RotFile);

	return bRot;
}


//! Reads an already-opened ROT file
ILboolean ilLoadRotF(ILHANDLE File)
{
	ILuint		FirstPos;
	ILboolean	bRet;
	
	iSetInputFile(File);
	FirstPos = itell();
	bRet = iLoadRotInternal();
	iseek(FirstPos, IL_SEEK_SET);
	
	return bRet;
}


//! Reads from a memory "lump" that contains a ROT
ILboolean ilLoadRotL(const void *Lump, ILuint Size)
{
	iSetInputLump(Lump, Size);
	return iLoadRotInternal();
}


// Internal function used to load the ROT.
ILboolean iLoadRotInternal(void)
{
	ILubyte		Form[4], FormName[4];
	ILuint		FormLen, Width, Height, Format, Channels, CompSize;
	ILuint		MipSize, MipLevel, MipWidth, MipHeight;
	ILenum		FormatIL;
	ILimage		*Image;
	ILboolean	BaseCreated = IL_FALSE;
	ILubyte		*CompData = NULL;

	if (iCurImage == NULL) {
		ilSetError(IL_ILLEGAL_OPERATION);
		return IL_FALSE;
	}

	// The first entry in the file must be 'FORM', 0x20 in a big endian integer and then 'HEAD'.
	iread(Form, 1, 4);
	FormLen = GetBigUInt();
	iread(FormName, 1, 4);
	if (strncmp(Form, "FORM", 4) || FormLen != 0x14 || strncmp(FormName, "HEAD", 4)) {
		ilSetError(IL_INVALID_FILE_HEADER);
		return IL_FALSE;
	}

	// Next follows the width, height and format in the header.
	Width = GetLittleUInt();
	Height = GetLittleUInt();
	Format = GetLittleUInt();

	//@TODO: More formats.
	switch (Format)
	{
		case ROT_RGBA32:  // 32-bit RGBA format
			Channels = 4;
			FormatIL = IL_RGBA;
			break;

		case ROT_DXT1:  // DXT1 (no alpha)
			Channels = 4;
			FormatIL = IL_RGBA;
			break;

		case ROT_DXT3:  // DXT3
		case ROT_DXT5:  // DXT5
			Channels = 4;
			FormatIL = IL_RGBA;
			// Allocates the maximum needed (the first width/height given in the file).
			CompSize = ((Width + 3) / 4) * ((Height + 3) / 4) * 16;
			CompData = ialloc(CompSize);
			if (CompData == NULL)
				return IL_FALSE;
			break;

		default:
			ilSetError(IL_INVALID_FILE_HEADER);
			return IL_FALSE;
	}

	if (Width == 0 || Height == 0) {
		ilSetError(IL_INVALID_FILE_HEADER);
		return IL_FALSE;
	}

	//@TODO: Find out what this is.
	GetLittleUInt();  // Skip this for the moment.  This appears to be the number of channels.

	// Next comes 'FORM', a length and 'MIPS'.
	iread(Form, 1, 4);
	FormLen = GetBigUInt();
	iread(FormName, 1, 4);
	//@TODO: Not sure if the FormLen has to be anything specific here.
	if (strncmp(Form, "FORM", 4) || strncmp(FormName, "MIPS", 4)) {
		ilSetError(IL_INVALID_FILE_HEADER);
		return IL_FALSE;
	}

	//@TODO: Can these mipmap levels be in any order?  Some things may be easier if the answer is no.
	Image = iCurImage;
	do {
		// Then we have 'FORM' again.
		iread(Form, 1, 4);
		// This is the size of the mipmap data.
		MipSize = GetBigUInt();
		iread(FormName, 1, 4);
		if (strncmp(Form, "FORM", 4)) {
			if (!BaseCreated) {  // Our file is malformed.
				ilSetError(IL_INVALID_FILE_HEADER);
				return IL_FALSE;
			}
			// We have reached the end of the mipmap data.
			break;
		}
		if (strncmp(FormName, "MLVL", 4)) {
			ilSetError(IL_INVALID_FILE_HEADER);
			return IL_FALSE;
		}

		// Next is the mipmap attributes (level number, width, height and length)
		MipLevel = GetLittleUInt();
		MipWidth = GetLittleUInt();
		MipHeight = GetLittleUInt();
		MipSize = GetLittleUInt();  // This is the same as the previous size listed -20 (for attributes).

		// Lower level mipmaps cannot be larger than the main image.
		if (MipWidth > Width || MipHeight > Height || MipSize > CompSize) {
			ilSetError(IL_INVALID_FILE_HEADER);
			return IL_FALSE;
		}

		// Just create our images here.
		if (!BaseCreated) {
			if (!ilTexImage(MipWidth, MipHeight, 1, Channels, FormatIL, IL_UNSIGNED_BYTE, NULL))
				return IL_FALSE;
			BaseCreated = IL_TRUE;
		}
		else {
			Image->Mipmaps = ilNewImageFull(MipWidth, MipHeight, 1, Channels, FormatIL, IL_UNSIGNED_BYTE, NULL);
			Image = Image->Mipmaps;
		}

		switch (Format)
		{
			case ROT_RGBA32:  // 32-bit RGBA format
				if (iread(Image->Data, Image->SizeOfData, 1) != 1)
					return IL_FALSE;
				break;

			case ROT_DXT1:
				// Allocates the size of the compressed data.
				CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 8;
				if (CompSize != MipSize) {
					ilSetError(IL_INVALID_FILE_HEADER);
					return IL_FALSE;
				}
				CompData = ialloc(CompSize);
				if (CompData == NULL)
					return IL_FALSE;

				// Read in the DXT1 data...
				if (iread(CompData, CompSize, 1) != 1)
					return IL_FALSE;
				// ...and decompress it.
				if (!DecompressDXT1(Image, CompData)) {
					ifree(CompData);
					return IL_FALSE;
				}
				if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) {
					Image->DxtcSize = CompSize;
					Image->DxtcData = CompData;
					Image->DxtcFormat = IL_DXT1;
					CompData = NULL;
				}
				break;

			case ROT_DXT3:
				// Allocates the size of the compressed data.
				CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 16;
				if (CompSize != MipSize) {
					ilSetError(IL_INVALID_FILE_HEADER);
					return IL_FALSE;
				}
				CompData = ialloc(CompSize);
				if (CompData == NULL)
					return IL_FALSE;

				// Read in the DXT3 data...
				if (iread(CompData, MipSize, 1) != 1)
					return IL_FALSE;
				// ...and decompress it.
				if (!DecompressDXT3(Image, CompData)) {
					ifree(CompData);
					return IL_FALSE;
				}
				if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) {
					Image->DxtcSize = CompSize;
					Image->DxtcData = CompData;
					Image->DxtcFormat = IL_DXT3;
					CompData = NULL;
				}
				break;

			case ROT_DXT5:
				// Allocates the size of the compressed data.
				CompSize = ((MipWidth + 3) / 4) * ((MipHeight + 3) / 4) * 16;
				if (CompSize != MipSize) {
					ilSetError(IL_INVALID_FILE_HEADER);
					return IL_FALSE;
				}
				CompData = ialloc(CompSize);
				if (CompData == NULL)
					return IL_FALSE;

				// Read in the DXT5 data...
				if (iread(CompData, MipSize, 1) != 1)
					return IL_FALSE;
				// ...and decompress it.
				if (!DecompressDXT5(Image, CompData)) {
					ifree(CompData);
					return IL_FALSE;
				}
				// Keeps a copy
				if (ilGetInteger(IL_KEEP_DXTC_DATA) == IL_TRUE) {
					Image->DxtcSize = CompSize;
					Image->DxtcData = CompData;
					Image->DxtcFormat = IL_DXT5;
					CompData = NULL;
				}
				break;
		}
		ifree(CompData);  // Free it if it was not saved.
	} while (!ieof());  //@TODO: Is there any other condition that should end this?

	return ilFixImage();
}

#endif//IL_NO_ROT