1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/05/2009
6 //
7 // Filename: src-IL/src/il_mp3.c
8 //
9 // MimeType: Reads from an MPEG-1 Audio Layer 3 (.mp3) file.
10 //
11 //-----------------------------------------------------------------------------
12 
13 #include "il_internal.h"
14 #ifndef IL_NO_MP3
15 
16 typedef struct MP3HEAD
17 {
18 	char	Signature[3];
19 	ILubyte	VersionMajor;
20 	ILubyte	VersionMinor;
21 	ILubyte	Flags;
22 	ILuint	Length;
23 } MP3HEAD;
24 
25 #define MP3_NONE 0
26 #define MP3_JPG  1
27 #define MP3_PNG  2
28 
29 ILboolean iLoadMp3Internal(void);
30 ILboolean iIsValidMp3(void);
31 ILboolean iCheckMp3(MP3HEAD *Header);
32 ILboolean iLoadJpegInternal(void);
33 ILboolean iLoadPngInternal(void);
34 
35 
36 //! Checks if the file specified in FileName is a valid MP3 file.
ilIsValidMp3(ILconst_string FileName)37 ILboolean ilIsValidMp3(ILconst_string FileName)
38 {
39 	ILHANDLE	Mp3File;
40 	ILboolean	bMp3 = IL_FALSE;
41 
42 	if (!iCheckExtension(FileName, IL_TEXT("mp3"))) {
43 		ilSetError(IL_INVALID_EXTENSION);
44 		return bMp3;
45 	}
46 
47 	Mp3File = iopenr(FileName);
48 	if (Mp3File == NULL) {
49 		ilSetError(IL_COULD_NOT_OPEN_FILE);
50 		return bMp3;
51 	}
52 
53 	bMp3 = ilIsValidMp3F(Mp3File);
54 	icloser(Mp3File);
55 
56 	return bMp3;
57 }
58 
59 
60 //! Checks if the ILHANDLE contains a valid MP3 file at the current position.
ilIsValidMp3F(ILHANDLE File)61 ILboolean ilIsValidMp3F(ILHANDLE File)
62 {
63 	ILuint		FirstPos;
64 	ILboolean	bRet;
65 
66 	iSetInputFile(File);
67 	FirstPos = itell();
68 	bRet = iIsValidMp3();
69 	iseek(FirstPos, IL_SEEK_SET);
70 
71 	return bRet;
72 }
73 
74 
75 //! Checks if Lump is a valid MP3 lump.
ilIsValidMp3L(const void * Lump,ILuint Size)76 ILboolean ilIsValidMp3L(const void *Lump, ILuint Size)
77 {
78 	iSetInputLump(Lump, Size);
79 	return iIsValidMp3();
80 }
81 
82 
GetSynchInt()83 ILuint GetSynchInt()
84 {
85 	ILuint SynchInt;
86 
87 	SynchInt = GetBigUInt();
88 
89 	SynchInt = ((SynchInt & 0x7F000000) >> 3) | ((SynchInt & 0x7F0000) >> 2)
90 				| ((SynchInt & 0x7F00) >> 1) | (SynchInt & 0x7F);
91 
92 	return SynchInt;
93 }
94 
95 
96 // Internal function used to get the MP3 header from the current file.
iGetMp3Head(MP3HEAD * Header)97 ILboolean iGetMp3Head(MP3HEAD *Header)
98 {
99 	if (iread(Header->Signature, 3, 1) != 1)
100 		return IL_FALSE;
101 	Header->VersionMajor = igetc();
102 	Header->VersionMinor = igetc();
103 	Header->Flags = igetc();
104 	Header->Length = GetSynchInt();
105 
106 	return IL_TRUE;
107 }
108 
109 
110 // Internal function to get the header and check it.
iIsValidMp3(void)111 ILboolean iIsValidMp3(void)
112 {
113 	MP3HEAD		Header;
114 	ILuint		Pos = itell();
115 
116 	if (!iGetMp3Head(&Header))
117 		return IL_FALSE;
118 	// The length of the header varies, so we just go back to the original position.
119 	iseek(Pos, IL_SEEK_CUR);
120 
121 	return iCheckMp3(&Header);
122 }
123 
124 
125 // Internal function used to check if the HEADER is a valid MP3 header.
iCheckMp3(MP3HEAD * Header)126 ILboolean iCheckMp3(MP3HEAD *Header)
127 {
128 	if (strncmp(Header->Signature, "ID3", 3))
129 		return IL_FALSE;
130 	if (Header->VersionMajor != 3 && Header->VersionMinor != 4)
131 		return IL_FALSE;
132 
133 	return IL_TRUE;
134 }
135 
136 
iFindMp3Pic(MP3HEAD * Header)137 ILuint iFindMp3Pic(MP3HEAD *Header)
138 {
139 	char	ID[4];
140 	ILuint	FrameSize;
141 	ILubyte	TextEncoding;
142 	ILubyte	MimeType[65], Description[65];
143 	ILubyte	PicType;
144 	ILuint	i;
145 	ILuint	Type = MP3_NONE;
146 
147 	do {
148 		if (iread(ID, 4, 1) != 1)
149 			return MP3_NONE;
150 		if (Header->VersionMajor == 3)
151 			FrameSize = GetBigUInt();
152 		else
153 			FrameSize = GetSynchInt();
154 
155 		GetBigUShort();  // Skip the flags.
156 
157 		//@TODO: Support multiple APIC entries in an mp3 file.
158 		if (!strncmp(ID, "APIC", 4)) {
159 			//@TODO: Use TextEncoding properly - UTF16 strings starting with FFFE or FEFF.
160 			TextEncoding = igetc();
161 			// Get the MimeType (read until we hit 0).
162 			for (i = 0; i < 65; i++) {
163 				MimeType[i] = igetc();
164 				if (MimeType[i] == 0)
165 					break;
166 			}
167 			// The MimeType must be terminated by 0 in the file by the specs.
168 			if (MimeType[i] != 0)
169 				return MP3_NONE;
170 			if (!strcmp(MimeType, "image/jpeg"))
171 				Type = MP3_JPG;
172 			else if (!strcmp(MimeType, "image/png"))
173 				Type = MP3_PNG;
174 			else
175 				Type = MP3_NONE;
176 
177 			PicType = igetc();  // Whether this is a cover, band logo, etc.
178 
179 			// Skip the description.
180 			for (i = 0; i < 65; i++) {
181 				Description[i] = igetc();
182 				if (Description[i] == 0)
183 					break;
184 			}
185 			if (Description[i] != 0)
186 				return MP3_NONE;
187 			return Type;
188 		}
189 		else {
190 			iseek(FrameSize, IL_SEEK_CUR);
191 		}
192 
193 		//if (!strncmp(MimeType, "
194 	} while (!ieof() && itell() < Header->Length);
195 
196 	return Type;
197 }
198 
199 
200 //! Reads a MP3 file
ilLoadMp3(ILconst_string FileName)201 ILboolean ilLoadMp3(ILconst_string FileName)
202 {
203 	ILHANDLE	Mp3File;
204 	ILboolean	bMp3 = IL_FALSE;
205 
206 	Mp3File = iopenr(FileName);
207 	if (Mp3File == NULL) {
208 		ilSetError(IL_COULD_NOT_OPEN_FILE);
209 		return bMp3;
210 	}
211 
212 	bMp3 = ilLoadMp3F(Mp3File);
213 	icloser(Mp3File);
214 
215 	return bMp3;
216 }
217 
218 
219 //! Reads an already-opened MP3 file
ilLoadMp3F(ILHANDLE File)220 ILboolean ilLoadMp3F(ILHANDLE File)
221 {
222 	ILuint		FirstPos;
223 	ILboolean	bRet;
224 
225 	iSetInputFile(File);
226 	FirstPos = itell();
227 	bRet = iLoadMp3Internal();
228 	iseek(FirstPos, IL_SEEK_SET);
229 
230 	return bRet;
231 }
232 
233 
234 //! Reads from a memory "lump" that contains a MP3
ilLoadMp3L(const void * Lump,ILuint Size)235 ILboolean ilLoadMp3L(const void *Lump, ILuint Size)
236 {
237 	iSetInputLump(Lump, Size);
238 	return iLoadMp3Internal();
239 }
240 
241 
242 // Internal function used to load the MP3.
iLoadMp3Internal(void)243 ILboolean iLoadMp3Internal(void)
244 {
245 	MP3HEAD	Header;
246 	ILuint	Type;
247 
248 	if (iCurImage == NULL) {
249 		ilSetError(IL_ILLEGAL_OPERATION);
250 		return IL_FALSE;
251 	}
252 
253 	if (!iGetMp3Head(&Header))
254 		return IL_FALSE;
255 	if (!iCheckMp3(&Header))
256 		return IL_FALSE;
257 	Type = iFindMp3Pic(&Header);
258 
259 	switch (Type)
260 	{
261 #ifndef IL_NO_JPG
262 		case MP3_JPG:
263 			return iLoadJpegInternal();
264 #endif//IL_NO_JPG
265 
266 #ifndef IL_NO_PNG
267 		case MP3_PNG:
268 			return iLoadPngInternal();
269 #endif//IL_NO_PNG
270 
271 		// Either a picture was not found, or the MIME type was not recognized.
272 		default:
273 			ilSetError(IL_INVALID_FILE_HEADER);
274 	}
275 
276 	return IL_FALSE;
277 }
278 
279 #endif//IL_NO_MP3
280 
281