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