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