1 /* OpenCP Module Player
2  * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  * copyright (c) '04-'21 Stian Skjelstad <stian.skjelstad@gmail.com>
4  *
5  * Module information DataBase (and some other related stuff=
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  * revision history: (please note changes here)
22  *  -ss04??????   Stian Skjelstad <stian@nixia.no
23  *    -first release (splited out from pfilesel.c)
24  */
25 
26 #include "config.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include "types.h"
36 #include "boot/plinkman.h"
37 #include "boot/psetting.h"
38 #include "dirdb.h"
39 #include "filesystem.h"
40 #include "mdb.h"
41 #include "pfilesel.h"
42 #include "stuff/compat.h"
43 #include "stuff/imsrtns.h"
44 
45 #ifdef MDB_DEBUG
46 #define DEBUG_PRINT(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
47 #else
48 #define DEBUG_PRINT(...) {}
49 #endif
50 
51 struct __attribute__((packed)) modinfoentry
52 {
53 	uint8_t flags;
54 	union
55 	{
56 		struct __attribute__((packed))
57 		{
58 			uint8_t modtype;    /*  1 */
59 			uint32_t comref;    /*  5 */
60 			uint32_t compref;   /*  9 */
61 			uint32_t futref;    /* 13 */
62 			char name[12];      /* 25 */
63 			uint32_t size;      /* 29 */
64 			char modname[32];   /* 61 */
65 			uint32_t date;      /* 65 */
66 			uint16_t playtime;  /* 67 */
67 			uint8_t channels;   /* 68 */
68 			uint8_t moduleflags;/* 69 */
69 			/* last uint8_t flags2 is up-padding for the top uint8_t and so on.. */
70 		} gen;
71 
72 		struct __attribute__((packed))
73 		{
74 			char unusedfill1[6]; /*  1 */
75 			char comment[63];
76 		} com;
77 
78 		struct __attribute__((packed))
79 		{
80 			char composer[32];
81 			char style[31];
82 		} comp;
83 	} mie;
84 };
85 #define gen mie.gen
86 #define comp mie.comp
87 
88 struct __attribute__((packed)) mdbheader
89 {
90 	char sig[60];
91 	uint32_t entries;
92 };
93 const char mdbsigv1[60] = "Cubic Player Module Information Data Base\x1B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
94 
95 static struct modinfoentry *mdbData;
96 static uint32_t mdbNum;
97 static int mdbDirty;
98 static uint32_t *mdbReloc;
99 static uint32_t mdbGenNum;
100 static uint32_t mdbGenMax;
101 
mdbGetModTypeString(unsigned char type)102 const char *mdbGetModTypeString(unsigned char type)
103 {
104 	return fsTypeNames[type&UINT8_MAX];
105 }
106 
mdbGetModuleType(uint32_t mdb_ref)107 int mdbGetModuleType(uint32_t mdb_ref)
108 {
109 	if (mdb_ref>=mdbNum)
110 		return -1;
111 	if ((mdbData[mdb_ref].flags&(MDB_USED|MDB_BLOCKTYPE))!=(MDB_USED|MDB_GENERAL))
112 		return -1;
113 	return mdbData[mdb_ref].gen.modtype;
114 }
115 
mdbReadModType(const char * str)116 uint8_t mdbReadModType(const char *str)
117 {
118 	int v=255;
119 	int i;
120 	for (i=0; i<256; i++)
121 		if (!strcasecmp(str, fsTypeNames[i]))
122 			v=i;
123 	return v;
124 }
125 
126 
mdbInfoRead(uint32_t mdb_ref)127 int mdbInfoRead(uint32_t mdb_ref)
128 {
129 	if (mdb_ref>=mdbNum)
130 	{
131 		DEBUG_PRINT ("mdbInfoRead(0x%08"PRIx32") => -1 due to out of range\n", mdb_ref);
132 		return -1;
133 	}
134 	if ((mdbData[mdb_ref].flags&(MDB_USED|MDB_BLOCKTYPE))!=(MDB_USED|MDB_GENERAL))
135 	{
136 		DEBUG_PRINT ("mdbInfoRead(0x%08"PRIx32") => -1 due to entry not being USED + GENERAL\n", mdb_ref);
137 		return -1;
138 	}
139 #ifdef MDB_DEBUG
140 	if (mdbData[mdb_ref].gen.modtype==mtUnRead)
141 	{
142 		DEBUG_PRINT ("mdbInfoRead(0x%08"PRIx32") => 0 due to mtUnRead\n", mdb_ref);
143 	} else {
144 		DEBUG_PRINT ("mdbInfoRead(0x%08"PRIx32") => 1 due to modtype != mtUnRead\n", mdb_ref);
145 	}
146 #endif
147 	return mdbData[mdb_ref].gen.modtype!=mtUnRead;
148 }
149 
150 /* This thing will end up with a register of all valid pre-interprators for modules and friends
151  */
152 static struct mdbreadinforegstruct *mdbReadInfos=NULL;
153 
mdbRegisterReadInfo(struct mdbreadinforegstruct * r)154 void mdbRegisterReadInfo(struct mdbreadinforegstruct *r)
155 {
156 	r->next=mdbReadInfos;
157 	mdbReadInfos=r;
158 	if (r->Event)
159 		r->Event(mdbEvInit);
160 }
161 
mdbUnregisterReadInfo(struct mdbreadinforegstruct * r)162 void mdbUnregisterReadInfo(struct mdbreadinforegstruct *r)
163 {
164 	struct mdbreadinforegstruct *root=mdbReadInfos;
165 	if (root==r)
166 	{
167 		mdbReadInfos=r->next;
168 		return;
169 	}
170 	while (root)
171 	{
172 		if (root->next==r)
173 		{
174 			root->next=root->next->next;
175 			return;
176 		}
177 		if (!root->next)
178 			return;
179 		root=root->next;
180 	}
181 }
182 
mdbReadMemInfo(struct moduleinfostruct * m,const char * buf,int len)183 int mdbReadMemInfo(struct moduleinfostruct *m, const char *buf, int len)
184 {
185 	struct mdbreadinforegstruct *rinfos;
186 
187 	DEBUG_PRINT ("mdbReaadMemInfo(buf=%p len=%d)\n", buf, len);
188 
189 	for (rinfos=mdbReadInfos; rinfos; rinfos=rinfos->next)
190 		if (rinfos->ReadMemInfo)
191 			if (rinfos->ReadMemInfo(m, buf, len))
192 				return 1;
193 	return 0;
194 }
195 
mdbReadInfo(struct moduleinfostruct * m,struct ocpfilehandle_t * f)196 int mdbReadInfo(struct moduleinfostruct *m, struct ocpfilehandle_t *f)
197 {
198 	char mdbScanBuf[1084];
199 	struct mdbreadinforegstruct *rinfos;
200 	int maxl;
201 
202 	DEBUG_PRINT ("mdbReadInfo(f=%p)\n", f);
203 
204 	if (f->seek_set (f, 0) < 0)
205 	{
206 		return 1;
207 	}
208 	memset (mdbScanBuf, 0, sizeof (mdbScanBuf));
209 	maxl = f->read (f, mdbScanBuf, sizeof (mdbScanBuf));
210 
211 	{
212 		char *path;
213 		dirdbGetName_internalstr (f->dirdb_ref, &path);
214 		DEBUG_PRINT ("   mdbReadMemInfo(%s %p %d)\n", path, mdbScanBuf, maxl);
215 	}
216 
217 	if (mdbReadMemInfo(m, mdbScanBuf, maxl))
218 		return 1;
219 
220 	for (rinfos=mdbReadInfos; rinfos; rinfos=rinfos->next)
221 		if (rinfos->ReadInfo)
222 			if (rinfos->ReadInfo(m, f, mdbScanBuf, maxl))
223 				return 1;
224 
225 	return m->modtype==mtUnRead;
226 }
227 
mdbGetNew(void)228 static uint32_t mdbGetNew(void)
229 {
230 	uint32_t i;
231 
232 	for (i=0; i<mdbNum; i++)
233 		if (!(mdbData[i].flags&MDB_USED))
234 			break;
235 	if (i==mdbNum)
236 	{
237 		void *t;
238 		uint32_t j;
239 		mdbNum+=64;
240 		if (!(t=realloc(mdbData, mdbNum*sizeof(*mdbData))))
241 			return UINT32_MAX;
242 		mdbData=(struct modinfoentry *)t;
243 		memset(mdbData+i, 0, (mdbNum-i)*sizeof(*mdbData));
244 		for (j=i; j<mdbNum; j++)
245 			mdbData[j].flags|=MDB_DIRTY;
246 	}
247 	mdbDirty=1;
248 
249 	DEBUG_PRINT("mdbGetNew() => 0x%08"PRIx32"\n", i);
250 
251 	return i;
252 }
253 
mdbWriteModuleInfo(uint32_t mdb_ref,struct moduleinfostruct * m)254 int mdbWriteModuleInfo(uint32_t mdb_ref, struct moduleinfostruct *m)
255 {
256 	DEBUG_PRINT("mdbWriteModuleInfo(0x%"PRIx32", %p)\n", mdb_ref, m);
257 
258 	if (mdb_ref>=mdbNum)
259 	{
260 		DEBUG_PRINT ("mdbWriteModuleInfo, mdb_ref(%d)<mdbNum(%d)\n", mdb_ref, mdbNum);
261 		return 0;
262 	}
263 	if ((mdbData[mdb_ref].flags&(MDB_USED|MDB_BLOCKTYPE))!=(MDB_USED|MDB_GENERAL))
264 	{
265 		DEBUG_PRINT ("mdbWriteModuleInfo (mdbData[mdb_ref].flags&(MDB_USED|MDB_BLOCKTYPE))!=(MDB_USED|MDB_GENERAL) Failed\n");
266 		return 0;
267 	}
268 
269 	m->flags1=MDB_USED|MDB_DIRTY|MDB_GENERAL|(m->flags1&(MDB_VIRTUAL|MDB_BIGMODULE|MDB_RESERVED));
270 	m->flags2=MDB_DIRTY|MDB_COMPOSER;
271 	m->flags3=MDB_DIRTY|MDB_COMMENT;
272 	m->flags4=MDB_DIRTY|MDB_FUTURE;
273 
274 	if (*m->composer||*m->style)
275 		m->flags2|=MDB_USED;
276 	if (*m->comment)
277 		m->flags3|=MDB_USED;
278 
279 	/* free the old references */
280 	if (m->comref!=UINT32_MAX)
281 		mdbData[m->comref].flags=MDB_DIRTY;
282 	if (m->compref!=UINT32_MAX)
283 		mdbData[m->compref].flags=MDB_DIRTY;
284 	if (m->futref!=UINT32_MAX)
285 		mdbData[m->futref].flags=MDB_DIRTY;
286 	m->compref=UINT32_MAX;
287 	m->comref=UINT32_MAX;
288 	m->futref=UINT32_MAX;
289 
290 	/* allocate new ones */
291 	if (m->flags2&MDB_USED)
292 	{
293 		m->compref=mdbGetNew();
294 		if (m->compref!=UINT32_MAX)
295 			memcpy(mdbData+m->compref, &m->flags2, sizeof(*mdbData));
296 	}
297 	if (m->flags3&MDB_USED)
298 	{
299 		m->comref=mdbGetNew();
300 		if (m->comref!=UINT32_MAX)
301 			memcpy(mdbData+m->comref, &m->flags3, sizeof(*mdbData));
302 	}
303 	if (m->flags4&MDB_USED)
304 	{
305 		m->futref=mdbGetNew();
306 		if (m->futref!=UINT32_MAX)
307 			memcpy(mdbData+m->futref, &m->flags4, sizeof(*mdbData));
308 	}
309 
310 	memcpy(mdbData+mdb_ref, m, sizeof(*mdbData));
311 	mdbDirty=1;
312 	return 1;
313 }
314 
mdbScan(struct ocpfile_t * file,uint32_t mdb_ref)315 void mdbScan(struct ocpfile_t *file, uint32_t mdb_ref)
316 {
317 	DEBUG_PRINT ("mdbScan(file=%p, mdb_ref=0x%08"PRIx32")\n", file, mdb_ref);
318 	if (!file)
319 	{
320 		return;
321 	}
322 
323 	if (file->is_nodetect)
324 	{
325 		return;
326 	}
327 
328 	if (!mdbInfoRead(mdb_ref)) /* use mdbReadInfo again here ? */
329 	{
330 		struct moduleinfostruct mdbEditBuf;
331 		struct ocpfilehandle_t *f;
332 		if (!(f=file->open(file)))
333 		{
334 			return;
335 		}
336 		mdbGetModuleInfo(&mdbEditBuf, mdb_ref);
337 		mdbReadInfo(&mdbEditBuf, f);
338 		f->unref (f);
339 		mdbWriteModuleInfo(mdb_ref, &mdbEditBuf);
340 	}
341 }
342 
miecmp(const void * a,const void * b)343 static int miecmp(const void *a, const void *b)
344 {
345 	struct modinfoentry *c=&mdbData[*(uint32_t *)a];
346 	struct modinfoentry *d=&mdbData[*(uint32_t *)b];
347 	if (c->gen.size==d->gen.size)
348 		return memcmp(c->gen.name, d->gen.name, 12);
349 	if (c->gen.size<d->gen.size)
350 		return -1;
351 	else
352 		return 1;
353 }
354 
mdbInit(void)355 int mdbInit(void)
356 {
357 	char *path;
358 	int f;
359 	struct mdbheader header;
360 	uint32_t i;
361 
362 	mdbDirty=0;
363 	mdbData=0;
364 	mdbNum=0;
365 	mdbReloc=0;
366 	mdbGenNum=0;
367 	mdbGenMax=0;
368 
369 	makepath_malloc (&path, 0, cfConfigDir, "CPMODNFO.DAT", 0);
370 
371 	if ((f=open(path, O_RDONLY))<0)
372 	{
373 		fprintf (stderr, "open(%s): %s\n", path, strerror (errno));
374 		free (path);
375 		return 1;
376 	}
377 
378 	fprintf(stderr, "Loading %s .. ", path);
379 	free (path); path = 0;
380 
381 	if (read(f, &header, sizeof(header))!=sizeof(header))
382 	{
383 		fprintf(stderr, "No header\n");
384 		close(f);
385 		return 1;
386 	}
387 
388 	if (memcmp(header.sig, mdbsigv1, sizeof(mdbsigv1)))
389 	{
390 		fprintf(stderr, "Invalid header\n");
391 		close(f);
392 		return 1;
393 	}
394 
395 	mdbNum=uint32_little(header.entries);
396 	if (!mdbNum)
397 	{
398 		close(f);
399 		fprintf(stderr, "EOF\n");
400 		return 1;
401 	}
402 
403 	mdbData=malloc(sizeof(struct modinfoentry)*mdbNum);
404 	if (!mdbData)
405 		return 0;
406 	if (read(f, mdbData, mdbNum*sizeof(*mdbData))!=(signed)(mdbNum*sizeof(*mdbData)))
407 	{
408 		mdbNum=0;
409 		free(mdbData);
410 		mdbData=0;
411 		close(f);
412 		fprintf(stderr, "EOF\n");
413 		return 1;
414 	}
415 	close(f);
416 
417 	for (i=0; i<mdbNum; i++)
418 	{
419 		if ((mdbData[i].flags&(MDB_BLOCKTYPE|MDB_USED))==(MDB_USED|MDB_GENERAL))
420 		{
421 			DEBUG_PRINT("0x%08"PRIx32" is USED GENERAL\n", i);
422 			mdbGenMax++;
423 		}
424 	}
425 
426 	if (mdbGenMax)
427 	{
428 		mdbReloc=malloc(sizeof(uint32_t)*mdbGenMax);
429 		if (!mdbReloc)
430 			return 0;
431 		for (i=0; i<mdbNum; i++)
432 			if ((mdbData[i].flags&(MDB_BLOCKTYPE|MDB_USED))==(MDB_USED|MDB_GENERAL))
433 				mdbReloc[mdbGenNum++]=i;
434 
435 		qsort(mdbReloc, mdbGenNum, sizeof(*mdbReloc), miecmp);
436 #ifdef MDB_DEBUG
437 		for (i=0; i<mdbGenMax; i++)
438 		{
439 			DEBUG_PRINT("%5"PRId32" => 0x%08"PRIx32" %"PRId32" %c%c%c%c%c%c%c%c%c%c%c%c\n", i, mdbReloc[i], mdbData[mdbReloc[i]].gen.size, mdbData[mdbReloc[i]].gen.name[0], mdbData[mdbReloc[i]].gen.name[1], mdbData[mdbReloc[i]].gen.name[2], mdbData[mdbReloc[i]].gen.name[3], mdbData[mdbReloc[i]].gen.name[4], mdbData[mdbReloc[i]].gen.name[5], mdbData[mdbReloc[i]].gen.name[6], mdbData[mdbReloc[i]].gen.name[7], mdbData[mdbReloc[i]].gen.name[8], mdbData[mdbReloc[i]].gen.name[9], mdbData[mdbReloc[i]].gen.name[10], mdbData[mdbReloc[i]].gen.name[11]);
440 		}
441 #endif
442 	}
443 
444 	fprintf(stderr, "Done\n");
445 
446 	return 1;
447 }
448 
mdbUpdate(void)449 void mdbUpdate(void)
450 {
451 	char *path;
452 	int f;
453 	uint32_t i, j;
454 	struct mdbheader header;
455 
456 	DEBUG_PRINT("mdbUpdate: mdbDirty=%d fsWriteModInfo=%d\n", mdbDirty, fsWriteModInfo);
457 
458 	if (!mdbDirty||!fsWriteModInfo)
459 		return;
460 	mdbDirty=0;
461 
462 	makepath_malloc (&path, 0, cfConfigDir, "CPMODNFO.DAT", 0);
463 	if ((f=open(path, O_WRONLY|O_CREAT, S_IREAD|S_IWRITE))<0)
464 	{
465 		fprintf (stderr, "open(%s): %s\n", path, strerror (errno));
466 		free (path);
467 		return;
468 	}
469 
470 	lseek(f, 0, SEEK_SET);
471 	memcpy(header.sig, mdbsigv1, sizeof(mdbsigv1));
472 	header.entries = uint32_little(mdbNum);
473 	while (1)
474 	{
475 		ssize_t res;
476 		res = write(f, &header, sizeof(header));
477 		if (res < 0)
478 		{
479 			if (errno==EAGAIN)
480 				continue;
481 			if (errno==EINTR)
482 				continue;
483 			fprintf(stderr, __FILE__ " write() to %s failed: %s\n", path, strerror(errno));
484 			exit(1);
485 		} else if (res != sizeof(header))
486 		{
487 			fprintf(stderr, __FILE__ " write() to %s returned only partial data\n", path);
488 			exit(1);
489 		} else
490 			break;
491 	}
492 	i=0;
493 	while (i<mdbNum)
494 	{
495 		if (!(mdbData[i].flags&MDB_DIRTY))
496 		{
497 			i++;
498 			continue;
499 		}
500 		for (j=i; j<mdbNum; j++)
501 			if (mdbData[j].flags&MDB_DIRTY)
502 				mdbData[j].flags&=~MDB_DIRTY;
503 			else
504 				break;
505 		lseek(f, (uint64_t)64+(uint64_t)i*sizeof(*mdbData), SEEK_SET);
506 		while (1)
507 		{
508 			ssize_t res;
509 
510 			DEBUG_PRINT("  [0x%08"PRIx32" -> 0x%08"PRIx32"] DIRTY\n", i, j);
511 			res = write(f, mdbData+i, (j-i)*sizeof(*mdbData));
512 			if (res < 0)
513 			{
514 				if (errno==EAGAIN)
515 					continue;
516 				if (errno==EINTR)
517 					continue;
518 				fprintf(stderr, __FILE__ " write() to %s failed: %s\n", path, strerror(errno));
519 				exit(1);
520 			} else if (res != (signed)((j-i)*sizeof(*mdbData)))
521 			{
522 				fprintf(stderr, __FILE__ " write() to %s returned only partial data\n", path);
523 				exit(1);
524 			} else
525 				break;
526 		}
527 		i=j;
528 	}
529 	free (path);
530 	lseek(f, 0, SEEK_END);
531 	close(f);
532 }
533 
mdbClose(void)534 void mdbClose(void)
535 {
536 	mdbUpdate();
537 	free(mdbData);
538 	free(mdbReloc);
539 }
540 
mdbGetModuleReference(const char * name,uint32_t size)541 static uint32_t mdbGetModuleReference(const char *name, uint32_t size)
542 {
543 	uint32_t i;
544 
545 	uint32_t *min=mdbReloc;
546 	uint32_t num=mdbGenNum;
547 	uint32_t mn;
548 	struct modinfoentry *m;
549 
550 	/* Iterate fast.. If size to to big, set current to be the minimum, and
551 	 * set the current to point in the new center, else half the current.
552 	 *
553 	 * That code is VERY clever. Took me some minutes to read it :-) nice
554 	 * work guys
555 	 *   - Stian
556 	 */
557 
558 	DEBUG_PRINT("mdbGetModuleReference(%s %"PRId32")\n", name, size);
559 	while (num)
560 	{
561 		struct modinfoentry *m=&mdbData[min[num>>1]];
562 		int ret;
563 #ifdef MDB_DEBUG
564 		{
565 			uint32_t x;
566 			for (x = 0; x < num; x++)
567 			{
568 				DEBUG_PRINT("  %08x %"PRId32" %c%c%c%c%c%c%c%c%c%c%c%c\n",
569 					min[x],
570 					mdbData[min[x]].gen.size,
571 					mdbData[min[x]].gen.name[0],
572 					mdbData[min[x]].gen.name[1],
573 					mdbData[min[x]].gen.name[2],
574 					mdbData[min[x]].gen.name[3],
575 					mdbData[min[x]].gen.name[4],
576 					mdbData[min[x]].gen.name[5],
577 					mdbData[min[x]].gen.name[6],
578 					mdbData[min[x]].gen.name[7],
579 					mdbData[min[x]].gen.name[8],
580 					mdbData[min[x]].gen.name[9],
581 					mdbData[min[x]].gen.name[10],
582 					mdbData[min[x]].gen.name[11]);
583 			}
584 			DEBUG_PRINT("----------\n");
585 		}
586 #endif
587 		if (size==m->gen.size)
588 			ret=memcmp(name, m->gen.name, 12);
589 		else
590 			if (size<m->gen.size)
591 				ret=-1;
592 			else
593 				ret=1;
594 		if (!ret)
595 		{
596 			DEBUG_PRINT("mdbGetModuleReference(%s %"PRId32") => mdbReloc => 0x%08"PRIx32"\n", name, size, min[num>>1]);
597 			return min[num>>1];
598 		}
599 		if (ret<0)
600 			num>>=1;
601 		else {
602 			min+=(num>>1)+1;
603 			num=(num-1)>>1;
604 		}
605 	}
606 	mn=min-mdbReloc;
607 
608 	i=mdbGetNew();
609 	if (i==UINT32_MAX)
610 		return UINT32_MAX;
611 	if (mdbGenNum==mdbGenMax)
612 	{
613 		void *n;
614 		mdbGenMax+=512;
615 		if (!(n=realloc(mdbReloc, sizeof (*mdbReloc)*mdbGenMax)))
616 			return UINT32_MAX;
617 		mdbReloc=(uint32_t *)n;
618 	}
619 
620 	memmovel(mdbReloc+mn+1, mdbReloc+mn, mdbGenNum-mn);
621 	mdbReloc[mn]=(uint32_t)i;
622 	mdbGenNum++;
623 
624 	m=&mdbData[i];
625 	m->flags=MDB_DIRTY|MDB_USED|MDB_GENERAL;
626 	memcpy(m->gen.name, name, 12);
627 	m->gen.size=size;
628 	m->gen.modtype=UINT8_MAX;
629 	m->gen.comref=UINT32_MAX;
630 	m->gen.compref=UINT32_MAX;
631 	m->gen.futref=UINT32_MAX;
632 	memset(m->gen.modname, 0, 32);
633 	m->gen.date=0;
634 	m->gen.playtime=0;
635 	m->gen.channels=0;
636 	m->gen.moduleflags=0;
637 	mdbDirty=1;
638 	DEBUG_PRINT("mdbGetModuleReference(%s %"PRId32") => new => 0x%08"PRIx32"\n", name, size, i);
639 	return (uint32_t)i;
640 }
641 
642 #warning Remake the hash to acculate overflow characters into the character 6 and 7... it will break old databases
mdbGetModuleReference2(uint32_t dirdb_ref,uint32_t size)643 uint32_t mdbGetModuleReference2 (uint32_t dirdb_ref, uint32_t size)
644 {
645 	char shortname[13];
646 	char *temppath;
647 	char *lastdot;
648 	int length;
649 
650 	dirdbGetName_internalstr (dirdb_ref, &temppath);
651 	if (!temppath)
652 	{
653 		return DIRDB_NOPARENT;
654 	}
655 
656 	/* the "hash" is created using the former fs12name() function */
657 	length = strlen (temppath);
658 
659 	shortname[12] = 0;
660 	if ((lastdot=rindex(temppath + 1, '.'))) /* we allow files to start with, hence temppath + 1 */
661 	{
662 		/* delta is the length until the dot */
663 		int delta = lastdot - temppath;
664 
665 		if ((delta) < 8)
666 		{ /* if the text before the dot is shorter than 8, pad it with spaces */
667 			strncpy (shortname,         temppath,   delta);
668 			strncpy (shortname + delta, "        ", 8 - delta);
669 		} else { /* or we take only the first 8 characters */
670 			strncpy (shortname, temppath, 8);
671 		}
672 
673 		/* grab the dot, and upto 3 characters following it */
674 		if (strlen (lastdot) < 4)
675 		{ /* if the text including the dot is shorter than 4, pad it with spaces */
676 			strcpy  (shortname + 8,                    lastdot);
677 			strncpy (shortname + 8 + strlen (lastdot), "   ", 4 - strlen(lastdot));
678 		} else { /* or we take only the first 4 characters,   eg .foo  instead of .foobar, and also accept things like .mod as is */
679 			strncpy (shortname + 8, lastdot, 4);
680 		}
681 	} else { /* we would normally never HASH such a filename */
682 		strncpy(shortname, temppath, 12);
683 		if ((length=strlen(temppath))<12)
684 		{
685 			strncpy(shortname+length, "            ", 12-length);
686 		}
687 	}
688 
689 	return mdbGetModuleReference (shortname, size);
690 }
691 
mdbGetModuleInfo(struct moduleinfostruct * m,uint32_t mdb_ref)692 int mdbGetModuleInfo(struct moduleinfostruct *m, uint32_t mdb_ref)
693 {
694 	memset(m, 0, sizeof(struct moduleinfostruct));
695 	if (mdb_ref>=mdbNum) /* needed, since we else might index mdbData wrong */
696 		goto invalid;
697 	if ((mdbData[mdb_ref].flags&(MDB_USED|MDB_BLOCKTYPE))!=(MDB_USED|MDB_GENERAL))
698 	{
699 invalid:
700 		m->modtype=UINT8_MAX;
701 		m->comref=UINT32_MAX;
702 		m->compref=UINT32_MAX;
703 		m->futref=UINT32_MAX;
704 		return 0;
705 	}
706 	memcpy(m, mdbData+mdb_ref, sizeof(*mdbData));
707 	if (m->compref!=UINT32_MAX)
708 	{
709 		if ((m->compref < mdbNum) && ((mdbData[m->compref].flags & MDB_BLOCKTYPE) == MDB_COMPOSER))
710 		{
711 			memcpy(&m->flags2, mdbData+m->compref, sizeof(*mdbData));
712 		} else {
713 			fprintf (stderr, "[mdb] warning - invalid compref\n");
714 			m->compref=UINT32_MAX;
715 		}
716 	}
717 	if (m->comref!=UINT32_MAX)
718 	{
719 		if ((m->comref < mdbNum) && ((mdbData[m->comref].flags & MDB_BLOCKTYPE) == MDB_COMMENT))
720 		{
721 			memcpy(&m->flags3, mdbData+m->comref, sizeof(*mdbData));
722 		} else {
723 			fprintf (stderr, "[mdb] warning - invalid comref\n");
724 			m->comref=UINT32_MAX;
725 		}
726 	}
727 	if (m->futref!=UINT32_MAX)
728 	{
729 		if ((m->futref < mdbNum) && ((mdbData[m->comref].flags & MDB_BLOCKTYPE) == MDB_FUTURE))
730 		{
731 			memcpy(&m->flags4, mdbData+m->futref, sizeof(*mdbData));
732 		} else {
733 			fprintf (stderr, "[mdb] warning - invalid futref\n");
734 			m->futref=UINT32_MAX;
735 		}
736 	}
737 	return 1;
738 }
739