1 /* OpenCP Module Player
2 * copyright (c) '10-'21 Stian Skjelstad <stian.skjelstad@gmail.com>
3 *
4 * SIDPlay file type detection routines for the fileselector
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * revision history: (please note changes here)
21 * -kb980717 Tammo Hinrichs <opencp@gmx.net>
22 * -first release
23 */
24
25 #include "config.h"
26 #include <string.h>
27 #include "types.h"
28 extern "C" {
29 #include "boot/plinkman.h"
30 #include "filesel/filesystem.h"
31 #include "filesel/mdb.h"
32 #include "filesel/pfilesel.h"
33 }
34 #include "lzh/lzh.h"
35
36 struct __attribute__((packed)) pymHeader
37 {
38 char id[4]; /* YM2!, YM3!, YM3b, //YM4!, YM5!, YM6! */
39 char mark[8]; /* LeOnArD!, YM5!, YM6! */
40 uint32_t nb_frame; /* YM5!, YM6! */
41 uint32_t attributes; /* YM5!, YM6! */
42 uint16_t nb_drum; /* YM5!, YM6! */
43 uint32_t clock; /* YM5!, YM6! */
44 uint16_t rate; /* YM5!, YM6! */
45 uint32_t loopframe; /* YM5!, YM6! */
46 uint16_t skip; /* YM5!, YM6! */
47 /* skip bytes of data if YM5!, YM6!
48 nb_drums {
49 uint32_t size;
50 size bytes of data;
51 }
52 char *song_name;
53 char *song_author;
54 char *song_comment;
55 data
56 uint32_t loopframe at EOF-4 for YM3b */
57 };
58
59 struct __attribute__((packed)) pmixHeader
60 {
61 char id[4]; /* MIX1 */
62 char mark[8]; /* LeOnArD! */
63 uint32_t attribute;
64 uint32_t sample_size;
65 uint32_t nb_mix_block;
66 /*
67 nb_mix_block {
68 uint32_t samplestart;
69 uint32_t samplelength;
70 uint16_t nbRepeat;
71 uint16_t replayFreq;
72 }
73 char *song_name;
74 char *song_author;
75 char *song_comment;
76 sample_size data;
77 */
78 };
79
80 struct __attribute__((packed)) pymtHeader
81 {
82 char id[4]; /* YMT1, YMT2 */
83 char mark[8]; /* LeOnArD! */
84 uint16_t nb_voices;
85 uint16_t player_rate;
86 uint32_t music_length;
87 uint32_t music_loop;
88 uint16_t nb_digidrum;
89 uint32_t flags;
90 /*
91 char *music_name;
92 char *music_author;
93 char *music_comment;
94 data */
95 };
96
97 struct __attribute__((packed)) lzhHeader
98 {
99 uint8_t size;
100 uint8_t sum;
101 char id[5]; /* -lh5- */
102 uint32_t packed;
103 uint32_t original;
104 char reserved[5];
105 uint8_t level;
106 uint8_t name_length;
107 };
108
ym_strcpy(char * target,int targetsize,const char ** source,int * lenleft)109 static void ym_strcpy(char *target, int targetsize, const char **source, int *lenleft)
110 {
111 int length;
112 int copy;
113
114 if (*lenleft<=0)
115 return;
116
117 for (length=0;;length++)
118 {
119 if (length>=*lenleft)
120 {
121 if (length<targetsize)
122 return;
123 break;
124 }
125 if (!(*source)[length])
126 {
127 length++;
128 break;
129 }
130 }
131 (*lenleft)+=length;
132 if (length>targetsize)
133 copy=targetsize;
134 else
135 copy=length;
136 strncpy(target, *source, copy); /* This does NOT NUL terminate the string.. expected behaviour */
137 (*source)+=length;
138 }
139
ymReadMemInfo2(struct moduleinfostruct * m,const char * buf,size_t len)140 static int ymReadMemInfo2(struct moduleinfostruct *m, const char *buf, size_t len)
141 {
142 struct pymHeader *YM = (struct pymHeader *)buf;
143 struct pymtHeader *YMT = (struct pymtHeader *)buf;
144 struct pmixHeader *MIX = (struct pmixHeader *)buf;
145
146 if (len<4)
147 return 0;
148
149 if (!strncmp(YM->id, "YM2!", 4))
150 {
151 m->modtype=mtYM;
152 m->channels=3;
153 strcpy(m->modname, "Unknown");
154 strcpy(m->composer, "Unknown");
155 strcpy(m->comment, "Converted by Leonard.");
156 strcpy(m->style, "YM 2");
157 m->playtime=0;
158 //#warning Need file size to calculate playtime
159 return 1;
160 }
161 if (!strncmp(YM->id, "YM3!", 4))
162 {
163 m->modtype=mtYM;
164 m->channels=3;
165 strcpy(m->modname, "Unknown");
166 strcpy(m->composer, "Unknown");
167 strcpy(m->comment, "");
168 strcpy(m->style, "YM 3");
169 m->playtime=0;
170 //#warning Need file size to calculate playtime
171 return 1;
172 }
173 if (!strncmp(YM->id, "YM3b", 4))
174 {
175 m->modtype=mtYM;
176 m->channels=3;
177 strcpy(m->modname, "Unknown");
178 strcpy(m->composer, "Unknown");
179 strcpy(m->comment, "");
180 strcpy(m->style, "YM 3b (loop)");
181 m->playtime=0;
182 //#warning Need file size to calculate playtime
183 return 1;
184 }
185 if (!strncmp(YM->id, "YM4!", 4))
186 {
187 m->modtype=mtYM;
188 m->channels=3;
189 strcpy(m->style, "YM 4 not supported");
190 return 0;
191 }
192
193 if (len<12)
194 return 0;
195 if (strncmp(YM->mark, "LeOnArD!", 8))
196 return 0;
197
198 if ((!strncmp(YM->id, "YM5!", 4))||(!strncmp(YM->id, "YM6!", 4)))
199 {
200 m->modtype=mtYM;
201 m->channels=3;
202
203 strcpy(m->modname, "Unknown");
204 strcpy(m->composer, "Unknown");
205 strcpy(m->comment, "");
206 m->playtime=0;
207 if (!strncmp(YM->id, "YM5!", 4))
208 strcpy(m->style, "YM 5");
209 else
210 strcpy(m->style, "YM 6");
211
212 if (len >= sizeof(*YM))
213 {
214 uint_fast32_t drumskip = 0;
215 int i;
216 uint32_t nbFrame = uint32_big(YM->nb_frame);
217 uint16_t nbDrum = uint16_big(YM->nb_drum);
218 /*uint32_t clock = uint32_big(YM->clock);*/
219 uint16_t rate = uint16_big(YM->rate);
220 uint16_t skip = uint16_big(YM->skip);
221 // TODO, use clock, rate and nbFrame to calculate song length */
222 m->playtime = nbFrame/rate;
223
224 for (i=0;i<nbDrum;i++)
225 {
226 if (len >= (sizeof(*YM) + skip + drumskip + 4))
227 {
228 uint32_t drumsize = uint32_big(*(uint32_t *)(buf+skip+drumskip));
229 if (drumsize>=0x1000000) /* don't overflow on big files */
230 drumsize=0xffffff;
231 drumskip+=drumsize+4;
232 } else {
233 drumskip+=4;
234 break;
235 }
236 }
237 const char *ptr = buf+skip+drumskip+sizeof(*YM);
238 int lenleft = len-skip-drumskip-sizeof(*YM);
239
240 ym_strcpy(m->modname, sizeof(m->modname), &ptr, &lenleft);
241 ym_strcpy(m->composer, sizeof(m->composer), &ptr, &lenleft);
242 ym_strcpy(m->comment, sizeof(m->comment), &ptr, &lenleft);
243
244 if (lenleft>=0)
245 return 1;
246 }
247 return 0; /* we wanted more data */
248 }
249
250 if (!strncmp(MIX->id, "MIX1", 4))
251 {
252 m->modtype=mtYM;
253 m->channels=3;
254
255 strcpy(m->modname, "Unknown");
256 strcpy(m->composer, "Unknown");
257 strcpy(m->comment, "");
258 m->playtime=0;
259 strcpy(m->style, "MIX1");
260
261 if (len >= sizeof(*MIX))
262 {
263 uint32_t nbMixBlock = uint32_big(MIX->nb_mix_block);
264 if (nbMixBlock>=0x1000000) /* don't overflow on big files */
265 nbMixBlock=0xffffff;
266 uint32_t skip = nbMixBlock * 12;
267 const char *ptr = buf+skip+sizeof(*MIX);
268 int lenleft = len-skip-sizeof(*MIX);
269
270 ym_strcpy(m->modname, sizeof(m->modname), &ptr, &lenleft);
271 ym_strcpy(m->composer, sizeof(m->composer), &ptr, &lenleft);
272 ym_strcpy(m->comment, sizeof(m->comment), &ptr, &lenleft);
273
274 if (lenleft>=0)
275 return 1;
276 }
277 return 0;
278 }
279
280 if ((!strncmp(YMT->id, "YMT1", 4))||(!strncmp(YMT->id, "YMT2", 4)))
281 {
282 m->modtype=mtYM;
283 m->channels=3;
284
285 strcpy(m->modname, "Unknown");
286 strcpy(m->composer, "Unknown");
287 strcpy(m->comment, "");
288 m->playtime=0;
289 if (!strncmp(YMT->id, "YMT1", 4))
290 strcpy(m->style, "YM-T1");
291 else
292 strcpy(m->style, "YM-T2");
293
294 if (len >= sizeof(*YMT))
295 {
296 /* TODO, time
297 uint16_t Rate = uint16_big(YMT->rate);
298 */
299 const char *ptr = buf+sizeof(*YMT);
300 int lenleft = len-sizeof(*YMT);
301
302 ym_strcpy(m->modname, sizeof(m->modname), &ptr, &lenleft);
303 ym_strcpy(m->composer, sizeof(m->composer), &ptr, &lenleft);
304 ym_strcpy(m->comment, sizeof(m->comment), &ptr, &lenleft);
305
306 if (lenleft>=0)
307 return 1;
308 }
309 return 0;
310 }
311
312 return 0;
313 }
314
ymReadMemInfo(struct moduleinfostruct * m,const char * buf,size_t len)315 static int ymReadMemInfo(struct moduleinfostruct *m, const char *buf, size_t len)
316 {
317 #ifdef HAVE_LZH
318 uint32_t fileSize;
319 uint32_t packedSize;
320 char ex_buf[8192];
321 struct lzhHeader *h= (struct lzhHeader *)buf;
322
323 if (len<sizeof(lzhHeader))
324 return 0; /* no point testing for valid formats at all if we can't even fit this header in */
325
326 if ((h->size==0)||(strncmp(h->id, "-lh5-", 5))||(h->level!=0))
327 return ymReadMemInfo2(m, buf, len);
328
329 fileSize = uint32_little(h->original);
330 if (fileSize>sizeof(ex_buf))
331 fileSize=sizeof(ex_buf);
332
333 packedSize = uint32_little(h->packed)-2;
334 if (packedSize > (len+sizeof(*h)+h->name_length+2))
335 packedSize = len+sizeof(*h)+h->name_length+2;
336
337 memset(ex_buf, 0, fileSize);
338 {
339 CLzhDepacker *pDepacker = new CLzhDepacker;
340 pDepacker->LzUnpack(buf+sizeof(*h)+h->name_length+2,packedSize,ex_buf,fileSize);
341 delete pDepacker;
342 }
343 return ymReadMemInfo2(m, ex_buf, fileSize);
344 #else
345 return ymReadMemInfo2(m, buf, len);
346 #endif
347 }
348
ymReadInfo(struct moduleinfostruct * m,struct ocpfilehandle_t * fp,const char * mem,size_t len)349 static int ymReadInfo(struct moduleinfostruct *m, struct ocpfilehandle_t *fp, const char *mem, size_t len)
350 {
351 return ymReadMemInfo (m, mem, len);
352 }
353
ymEvent(int event)354 static void ymEvent(int event)
355 {
356 switch (event)
357 {
358 case mdbEvInit:
359 {
360 fsRegisterExt("ym");
361 }
362 }
363 }
364 static struct mdbreadinforegstruct ymReadInfoReg = {ymReadMemInfo, ymReadInfo, ymEvent MDBREADINFOREGSTRUCT_TAIL};
365
init(void)366 static void __attribute__((constructor))init(void)
367 {
368 mdbRegisterReadInfo(&ymReadInfoReg);
369 }
370
done(void)371 static void __attribute__((destructor))done(void)
372 {
373 mdbUnregisterReadInfo(&ymReadInfoReg);
374 }
375
376 extern "C" {
377 const char *dllinfo = "";
378 struct linkinfostruct dllextinfo =
379 {
380 "ymtype" /* name */,
381 "OpenCP YM Detection (c) 2010-2021 Stian Skjelstad" /* desc */,
382 DLLVERSION /* ver */
383 };
384 }
385