1 /* OpenCP Module Player
2  * copyright (c) '94-'21 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  *
4  * GMDPlay loader for Oktalyzer modules
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  *  -nb980510   Niklas Beisert <nbeisert@physik.tu-muenchen.de>
22  *    -first release
23  */
24 
25 #include "config.h"
26 #include <sys/types.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include "types.h"
31 #include "boot/plinkman.h"
32 #include "dev/mcp.h"
33 #include "filesel/filesystem.h"
34 #include "gmdplay.h"
35 #include "stuff/err.h"
36 
putcmd(uint8_t ** p,uint8_t c,uint8_t d)37 static inline void putcmd(uint8_t **p, uint8_t c, uint8_t d)
38 {
39 	*(*p)++=c;
40 	*(*p)++=d;
41 }
42 
43 struct LoadOKTResources
44 {
45 	uint8_t *temptrack;
46 	uint8_t *buffer;
47 };
48 
FreeResources(struct LoadOKTResources * r)49 static void FreeResources(struct LoadOKTResources *r)
50 {
51 	if (r->temptrack)
52 	{
53 		free(r->temptrack);
54 		r->temptrack = 0;
55 	}
56 	if (r->buffer)
57 	{
58 		free(r->buffer);
59 		r->buffer = 0;
60 	}
61 }
62 
_mpLoadOKT(struct gmdmodule * m,struct ocpfilehandle_t * file)63 static int _mpLoadOKT(struct gmdmodule *m, struct ocpfilehandle_t *file)
64 {
65 
66 	uint8_t hsig[8];
67 	struct __attribute__((packed))
68 	{
69 		uint8_t sig[4];
70 		uint32_t blen;
71 	} chunk;
72 	uint16_t cflags[4];
73 	uint8_t cflag2[8];
74 	unsigned int i,t;
75 	uint16_t orgticks;
76 	uint16_t pn;
77 	uint16_t ordn;
78 	uint8_t orders[128];
79 	struct gmdpattern *pp;
80 	struct LoadOKTResources r;
81 
82 	r.temptrack = 0;
83 	r.buffer = 0;
84 
85 	mpReset(m);
86 
87 	if (file->read (file, hsig, 8) != 8)
88 	{
89 		fprintf(stderr, __FILE__ ": read failed #1\n");
90 		return errFormSig;
91 	}
92 #ifdef OKT_LOAD_DEBUG
93 	fprintf(stderr, __FILE__ ": comparing sig \"%c%c%c%c%c%c%c%c\" against \"OKTASONG\"\n", hsig[0], hsig[1], hsig[2], hsig[3], hsig[4], hsig[5], hsig[6], hsig[7]);
94 #endif
95 	if (memcmp(hsig, "OKTASONG", 8))
96 	{
97 		fprintf(stderr, __FILE__ ": invalid header\n");
98 		return errFormSig;
99 	}
100 
101 	*m->name=0;
102 	m->message=0;
103 
104 	m->options=MOD_TICK0;
105 
106 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof(chunk))
107 	{
108 		fprintf(stderr, __FILE__ ": read failed #3\n");
109 		return errFormStruc;
110 	}
111 #ifdef OKT_LOAD_DEBUG
112 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"CMOD\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
113 #endif
114 	if (memcmp(chunk.sig, "CMOD", 4))
115 	{
116 		fprintf(stderr, __FILE__ ": invalid \"SAMP\" header\n");
117 		return errFormStruc;
118 	}
119 	chunk.blen=uint32_big(chunk.blen);
120 	if (chunk.blen!=8)
121 	{
122 		fprintf(stderr, __FILE__ ": invalid blen %d, should had been 8\n", (int)chunk.blen);
123 		return errFormStruc;
124 	}
125 	if (file->read (file, cflags, 8) != 8)
126 	{
127 		fprintf(stderr, __FILE__ ": read failed #4\n");
128 		return errFormStruc;
129 	}
130 #ifdef OKT_LOAD_DEBUG
131 	fprintf(stderr, __FILE__ ": chan_flags[4]: 0x%04x 0x%04x 0x%04x 0x%04x\n", uint16_big(cflags[0]), uint16_big(cflags[1]), uint16_big(cflags[2]), uint16_big(cflags[3]));
132 #endif
133 	t=0;
134 	for (i=0; i<4; i++)
135 	{
136 		cflag2[t]=(uint16_big(cflags[i])&1)|((i+i+i)&2);
137 		if (cflag2[t++]&1)
138 			cflag2[t++]=(uint16_big(cflags[i])&1)|((i+i+i)&2);
139 	}
140 	m->channum=t;
141 #ifdef OKT_LOAD_DEBUG
142 	fprintf(stderr, __FILE__ ": channels: %d\n", (int)t);
143 	fprintf(stderr, __FILE__ ": %d %d %d %d %d %d %d %d\n", (int)cflag2[0], (int)cflag2[1], (int)cflag2[2], (int)cflag2[3], (int)cflag2[4], (int)cflag2[5], (int)cflag2[6], (int)cflag2[7]);
144 #endif
145 
146 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
147 	{
148 		fprintf(stderr, __FILE__ ": read failed #5\n");
149 		return errFormStruc;
150 	}
151 #ifdef OKT_LOAD_DEBUG
152 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"SAMP\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
153 #endif
154 	if (memcmp(chunk.sig, "SAMP", 4))
155 	{
156 		fprintf(stderr, __FILE__ ": invalid \"SAMP\" header\n");
157 		return errFormStruc;
158 	}
159 	chunk.blen=uint32_big(chunk.blen);
160 #ifdef OKT_LOAD_DEBUG
161 	fprintf(stderr, __FILE__ ": checking chunk length.. samples=%d module=%d (last should be zero)\n", (int)chunk.blen/32, (int)chunk.blen%32);
162 #endif
163 	if (chunk.blen&31)
164 	{
165 		fprintf(stderr, __FILE__ ": invalid SAMP chunk length (modulo 32 test failed)\n");
166 		return errFormStruc;
167 	}
168 	m->modsampnum=m->sampnum=m->instnum=(chunk.blen)>>5;
169 
170 	if (!mpAllocInstruments(m, m->instnum)||!mpAllocSamples(m, m->sampnum)||!mpAllocModSamples(m, m->modsampnum))
171 		return errAllocMem;
172 
173 	for (i=0; i<m->instnum; i++)
174 	{
175 		struct gmdinstrument *ip;
176 		struct gmdsample *sp;
177 		struct sampleinfo *sip;
178 
179 		struct __attribute__((packed))
180 		{
181 			char name[20];
182 			uint32_t length;
183 			uint16_t repstart;
184 			uint16_t replen;
185 			uint8_t pad1;
186 			uint8_t vol;
187 			uint16_t pad2;
188 		} mi;
189 		if (file->read(file, &mi, sizeof (mi)) != sizeof (mi))
190 		{
191 			fprintf(stderr, __FILE__ ": read failed #7\n");
192 			return errFormStruc;
193 		}
194 		mi.length=uint32_big(mi.length);
195 		mi.repstart=uint16_big(mi.repstart);
196 		mi.replen=uint16_big(mi.replen);
197 #ifdef OKT_LOAD_DEBUG
198 		fprintf(stderr, __FILE__ ": sample[%d]\n", i);
199 		fprintf(stderr, __FILE__ ": length=%d\n", (int)mi.length);
200 		fprintf(stderr, __FILE__ ": repstart=%d\n", (int)mi.repstart);
201 		fprintf(stderr, __FILE__ ": replen=%d\n", (int)mi.replen);
202 		fprintf(stderr, __FILE__ ": pad1=%d\n", (int)mi.pad1);
203 		fprintf(stderr, __FILE__ ": vol=%d\n", (int)mi.vol);
204 		fprintf(stderr, __FILE__ ": pad2=%d\n", (int)mi.pad2);
205 #endif
206 		if (mi.length<4)
207 			mi.length=0;
208 		if (mi.replen<4)
209 			mi.replen=0;
210 		if (!mi.replen||(mi.repstart>=mi.length))
211 			mi.replen=0;
212 		else
213 			if ((mi.repstart+mi.replen)>mi.length)
214 				mi.replen=mi.length-mi.repstart;
215 
216 		ip=&m->instruments[i];
217 		sp=&m->modsamples[i];
218 		sip=&m->samples[i];
219 
220 		memcpy(ip->name, mi.name, 20);
221 		ip->name[20]=0;
222 		if (!mi.length)
223 			continue;
224 
225 		for (t=0; t<128; t++)
226 			ip->samples[t]=i;
227 
228 		*ip->name=0;
229 		sp->handle=i;
230 		sp->normnote=0;
231 		sp->stdvol=(mi.vol>0x3F)?0xFF:(mi.vol<<2);
232 		sp->stdpan=-1;
233 		sp->opt=0;
234 
235 		sip->length=mi.length;
236 		sip->loopstart=mi.repstart;
237 		sip->loopend=mi.repstart+mi.replen;
238 		sip->samprate=8363;
239 		sip->type=mi.replen?mcpSampLoop:0;
240 	}
241 
242 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof(chunk))
243 	{
244 		fprintf(stderr, __FILE__ ": read failed #8\n");
245 		return errFormStruc;
246 	}
247 #ifdef OKT_LOAD_DEBUG
248 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"SPEE\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
249 #endif
250 	if (memcmp(chunk.sig, "SPEE", 4))
251 	{
252 		fprintf(stderr, __FILE__ ": header \"SPEE\" failed\n");
253 		return errFormStruc;
254 	}
255 	chunk.blen=uint32_big(chunk.blen);
256 #ifdef OKT_LOAD_DEBUG
257 	fprintf(stderr, __FILE__ ": chunk length should be 2, checking (%d)\n", (int)chunk.blen);
258 #endif
259 	if (chunk.blen!=2)
260 	{
261 		fprintf(stderr, __FILE__ ": invalid length of sub chunk \"SPEE\": %d\n", (int)chunk.blen);
262 		return errFormStruc;
263 	}
264 
265 	if (ocpfilehandle_read_uint16_be (file, &orgticks))
266 	{
267 		fprintf(stderr, __FILE__ ": read failed #10\n");
268 		return errFormStruc;
269 	}
270 #ifdef OKT_LOAD_DEBUG
271 	fprintf(stderr, __FILE__ ": orgticks: %d\n", (int)orgticks);
272 #endif
273 
274 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
275 	{
276 		fprintf(stderr, __FILE__ ": read failed #11\n");
277 		return errFormStruc;
278 	}
279 #ifdef OKT_LOAD_DEBUG
280 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"SLEN\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
281 #endif
282 	if (memcmp(chunk.sig, "SLEN", 4))
283 	{
284 		fprintf(stderr, __FILE__ ": header \"SLEN\" failed\n");
285 		return errFormStruc;
286 	}
287 	chunk.blen=uint32_big(chunk.blen);
288 #ifdef OKT_LOAD_DEBUG
289 	fprintf(stderr, __FILE__ ": chunk length should be 2, checking (%d)\n", (int)chunk.blen);
290 #endif
291 	if (chunk.blen!=2)
292 	{
293 		fprintf(stderr, __FILE__ ": invalid length of sub chunk \"SPEE\": %d\n", (int)chunk.blen);
294 		return errFormStruc;
295 	}
296 	if (ocpfilehandle_read_uint16_be (file, &pn))
297 	{
298 		fprintf(stderr, __FILE__ ": read failed #13\n");
299 		return errFormStruc;
300 	}
301 #ifdef OKT_LOAD_DEBUG
302 	fprintf(stderr, __FILE__ ":  song length (patterns): %d\n", (int)pn);
303 #endif
304 
305 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
306 	{
307 		fprintf(stderr, __FILE__ ": read failed #14\n");
308 		return errFormStruc;
309 	}
310 #ifdef OKT_LOAD_DEBUG
311 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"PLEN\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
312 #endif
313 	if (memcmp(chunk.sig, "PLEN", 4))
314 	{
315 		fprintf(stderr, __FILE__ ": header \"PLEN\" failed\n");
316 		return errFormStruc;
317 	}
318 	chunk.blen = uint32_big(chunk.blen);
319 #ifdef OKT_LOAD_DEBUG
320 	fprintf(stderr, __FILE__ ": chunk length should be 2, checking (%d)\n", (int)chunk.blen);
321 #endif
322 	if (chunk.blen!=2)
323 	{
324 		fprintf(stderr, __FILE__ ": invalid length of sub chunk \"PLEN\": %d\n", (int)chunk.blen);
325 		return errFormStruc;
326 	}
327 	if (ocpfilehandle_read_uint16_be (file, &ordn))
328 	{
329 		fprintf(stderr, __FILE__ ": warning, read failed #16\n");
330 	}
331 #ifdef OKT_LOAD_DEBUG
332 	fprintf(stderr, __FILE__ ": orders: %d\n", (int)ordn);
333 #endif
334 
335 	if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
336 	{
337 		fprintf(stderr, __FILE__ ": read failed #17\n");
338 		return errFormStruc;
339 	}
340 #ifdef OKT_LOAD_DEBUG
341 	fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"PATT\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
342 #endif
343 	if (memcmp(chunk.sig, "PATT", 4))
344 	{
345 		fprintf(stderr, __FILE__ ": header \"PATT\" failed\n");
346 		return errFormStruc;
347 	}
348 	chunk.blen=uint32_big(chunk.blen);
349 #ifdef OKT_LOAD_DEBUG
350 	fprintf(stderr, __FILE__ ": checking chunk length (should less than or equal to 128): %d\n", (int)chunk.blen);
351 #endif
352 	if (chunk.blen>128)
353 	{
354 		fprintf(stderr, __FILE__ ": invalid length of sub chunk \"PATT\": %d\n", (int)chunk.blen);
355 		return errFormStruc;
356 	}
357 
358 	if (file->read (file, orders, chunk.blen) != chunk.blen)
359 	{
360 		fprintf(stderr, __FILE__ ": read failed #19\n");
361 		return errFormStruc;
362 	}
363 	if (chunk.blen<ordn)
364 		ordn=chunk.blen;
365 	m->loopord=0;
366 
367 	m->patnum=ordn;
368 	m->ordnum=ordn;
369 	m->endord=m->patnum;
370 	m->tracknum=pn*(m->channum+1);
371 
372 	if (!mpAllocPatterns(m, m->patnum)||!mpAllocTracks(m, m->tracknum)||!mpAllocOrders(m, m->ordnum))
373 		return errAllocMem;
374 
375 	for (i=0; i<m->ordnum; i++)
376 		m->orders[i]=i;
377 
378 	for (pp=m->patterns, t=0; t<m->patnum; pp++, t++)
379 	{
380 		for (i=0; i<m->channum; i++)
381 			pp->tracks[i]=orders[t]*(m->channum+1)+i;
382 		pp->gtrack=orders[t]*(m->channum+1)+m->channum;
383 	}
384 
385 	r.temptrack=malloc(sizeof(uint8_t)*3000);
386 	r.buffer=malloc(sizeof(uint8_t)*(1024*m->channum));
387 	if (!r.buffer||!r.temptrack)
388 	{
389 		FreeResources (&r);
390 		return errAllocMem;
391 	}
392 
393 	for (t=0; t<pn; t++)
394 	{
395 		uint16_t patlen;
396 		uint16_t q;
397 		uint8_t *tp;
398 		uint8_t *buf;
399 		uint8_t row;
400 		struct gmdtrack *trk;
401 		uint16_t len;
402 
403 		if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
404 		{
405 			fprintf(stderr, __FILE__ ": read failed #20\n");
406 			FreeResources (&r);
407 			return errFormStruc;
408 		}
409 #ifdef OKT_LOAD_DEBUG
410 		fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"PBOD\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
411 #endif
412 		if (memcmp(chunk.sig, "PBOD", 4))
413 		{
414 			fprintf(stderr, __FILE__ ": header \"PBOD\" failed\n");
415 			FreeResources (&r);
416 			return errFormStruc;
417 		}
418 		chunk.blen=uint32_big(chunk.blen);
419 		if (ocpfilehandle_read_uint16_be (file, &patlen))
420 		{
421 			fprintf(stderr, __FILE__ ": warning, read failed #22\n");
422 		}
423 #ifdef OKT_LOAD_DEBUG
424 		fprintf(stderr, __FILE__ ": patlen red is %d. It should not be bigger than 256, and this should match:\n (blen!=(2+4*m->channum*patlen)\n %d!=2+4*%d*%d\n %d!=%d\n", (int)patlen, (int)chunk.blen, (int)m->channum, (int)patlen, (int)chunk.blen, (int)(2+4*m->channum*patlen));
425 #endif
426 		if ((chunk.blen!=(2+4*m->channum*patlen))||(patlen>256))
427 		{
428 			fprintf(stderr, __FILE__ ": invalid patlen: %d\n", (int)patlen);
429 			FreeResources (&r);
430 			return errFormStruc;
431 		}
432 
433 		for (q=0; q<m->patnum; q++)
434 			if (t==orders[q])
435 				m->patterns[q].patlen=patlen;
436 
437 		if (file->read (file, r.buffer, 4 * m->channum * patlen) != (4 * m->channum * patlen))
438 		{
439 			fprintf(stderr, __FILE__ ": read failed #23\n");
440 			FreeResources (&r);
441 			return errFormStruc;
442 		}
443 		for (q=0; q<m->channum; q++)
444 		{
445 			uint8_t *tp=r.temptrack;
446 			uint8_t *buf=r.buffer+4*q;
447 
448 			struct gmdtrack *trk;
449 			uint16_t len;
450 
451 			uint8_t row;
452 			for (row=0; row<patlen; row++, buf+=m->channum*4)
453 			{
454 				uint8_t *cp=tp+2;
455 
456 				uint8_t command=buf[2];
457 				uint8_t data=buf[3];
458 				int16_t nte=buf[0]?(buf[0]+60-17+4):-1;
459 				int16_t ins=buf[0]?buf[1]:-1;
460 				int16_t pan=-1;
461 				int16_t vol=-1;
462 
463 				if (!row&&(t==orders[0]))
464 					pan=(cflag2[q]&2)?0xFF:0x00;
465 
466 				if ((command==31)&&(data<=0x40))
467 				{
468 					vol=(data>0x3F)?0xFF:(data<<2);
469 					command=0;
470 				}
471 				if ((ins!=-1)||(nte!=-1)||(vol!=-1)||(pan!=-1))
472 				{
473 					unsigned char *act=cp;
474 					*cp++=cmdPlayNote;
475 					if (ins!=-1)
476 					{
477 						*act|=cmdPlayIns;
478 						*cp++=ins;
479 					}
480 					if (nte!=-1)
481 					{
482 						*act|=cmdPlayNte;
483 						*cp++=nte;
484 					}
485 					if (vol!=-1)
486 					{
487 						*act|=cmdPlayVol;
488 						*cp++=vol;
489 					}
490 					if (pan!=-1)
491 					{
492 						*act|=cmdPlayPan;
493 						*cp++=pan;
494 					}
495 				}
496 				switch (command)
497 				{
498 					case 13: /* note down */
499 					case 17: /* note up */
500 					case 21: /* note - */
501 					case 30: /* note + */
502 						break;
503 					case 10: /* LNH */
504 					case 11: /* NHNL */
505 					case 12: /* HHNLL */
506 						break;
507 					case 31:
508 						if (data<=0x50)
509 							putcmd(&cp, cmdVolSlideDown, (data&0xF)<<2);
510 						else
511 							if (data<=0x60)
512 								putcmd(&cp, cmdVolSlideUp, (data&0xF)<<2);
513 							else
514 								if (data<=0x70)
515 									putcmd(&cp, cmdRowVolSlideDown, (data&0xF)<<2);
516 								else
517 									if (data<=0x80)
518 										putcmd(&cp, cmdRowVolSlideUp, (data&0xF)<<2);
519 						break;
520 					case 27: /* release!!! */
521 						putcmd(&cp, cmdSetLoop, 0);
522 						break;
523 					case 0x1:
524 						putcmd(&cp, cmdPitchSlideDown, data);
525 						break;
526 					case 0x2:
527 						putcmd(&cp, cmdPitchSlideUp, data);
528 						break;
529 				}
530 
531 				if (cp!=(tp+2))
532 				{
533 					tp[0]=row;
534 					tp[1]=cp-tp-2;
535 					tp=cp;
536 				}
537 			}
538 			trk=&m->tracks[t*(m->channum+1)+q];
539 			len=tp-r.temptrack;
540 
541 			if (!len)
542 				trk->ptr=trk->end=0;
543 			else {
544 				trk->ptr=malloc(sizeof(uint8_t)*len);
545 				trk->end=trk->ptr+len;
546 				if (!trk->ptr)
547 				{
548 					FreeResources (&r);
549 					return errAllocMem;
550 				}
551 				memcpy(trk->ptr, r.temptrack, len);
552 			}
553 		}
554 
555 		tp=r.temptrack;
556 		buf=r.buffer;
557 		for (row=0; row<patlen; row++)
558 		{
559 			uint8_t *cp=tp+2;
560 
561 			if (!row&&(t==orders[0]))
562 				putcmd(&cp, cmdTempo, orgticks);
563 
564 			for (q=0; q<m->channum; q++, buf+=4)
565 			{
566 				uint8_t command=buf[2];
567 				uint8_t data=buf[3];
568 
569 				switch (command)
570 				{
571 					case 25:
572 						putcmd(&cp, cmdGoto, data);
573 						break;
574 					case 28:
575 						if (data)
576 							putcmd(&cp, cmdTempo, data);
577 						break;
578 				}
579 			}
580 
581 			if (cp!=(tp+2))
582 			{
583 				tp[0]=row;
584 				tp[1]=cp-tp-2;
585 				tp=cp;
586 			}
587 		}
588 
589 		trk=&m->tracks[t*(m->channum+1)+m->channum];
590 		len=tp-r.temptrack;
591 
592 		if (!len)
593 			trk->ptr=trk->end=0;
594 		else {
595 			trk->ptr=malloc(sizeof(uint8_t)*len);
596 			trk->end=trk->ptr+len;
597 			if (!trk->ptr)
598 			{
599 				FreeResources (&r);
600 				return errAllocMem;
601 			}
602 			memcpy(trk->ptr, r.temptrack, len);
603 		}
604 	}
605 	FreeResources (&r);
606 
607 	for (i=0; i<m->instnum; i++)
608 	{
609 /*
610 		struct gmdinstrument *ip=&m->instruments[i];  NOT USED */
611 		struct gmdsample *sp=&m->modsamples[i];
612 		struct sampleinfo *sip=&m->samples[i];
613 		if (sp->handle==0xFFFF)
614 			continue;
615 
616 		if (file->read (file, &chunk, sizeof(chunk)) != sizeof (chunk))
617 		{
618 			fprintf(stderr, __FILE__ ": read failed #24\n");
619 			return errFormStruc;
620 		}
621 #ifdef OKT_LOAD_DEBUG
622 		fprintf(stderr, __FILE__ ": comparing header \"%c%c%c%c\" against \"SBOD\"\n", chunk.sig[0], chunk.sig[1], chunk.sig[2], chunk.sig[3]);
623 #endif
624 		if (memcmp(chunk.sig, "SBOD", 4))
625 			return errFormStruc;
626 		chunk.blen=uint32_big(chunk.blen);
627 
628 		sip->ptr=malloc(sizeof(char)*(chunk.blen+8));
629 		if (!sip->ptr)
630 			return errAllocMem;
631 
632 		if (file->read (file, sip->ptr, chunk.blen) != chunk.blen)
633 		{
634 			fprintf(stderr, __FILE__ ": warning, read failed #26\n");
635 		}
636 		if (sip->length>chunk.blen)
637 			sip->length=chunk.blen;
638 		if (sip->loopend>chunk.blen)
639 			sip->loopend=chunk.blen;
640 		if (sip->loopstart>=sip->loopend)
641 			sip->type&=~mcpSampLoop;
642 	}
643 
644 	return errOk;
645 }
646 
647 struct gmdloadstruct mpLoadOKT = { _mpLoadOKT };
648 
649 struct linkinfostruct dllextinfo = {.name = "gmdlokt", .desc = "OpenCP Module Loader: *.OKT (c) 1994-21 Niklas Beisert, Stian Skjelstad", .ver = DLLVERSION, .size = 0};
650