Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / devs / AHI / Device / database.c
blobee7637b0416be5a0f47a7e62f678112decd709cf
1 /*
2 AHI - Hardware independent audio subsystem
3 Copyright (C) 1996-2005 Martin Blom <martin@blom.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge,
18 MA 02139, USA.
21 #include <config.h>
23 #include <exec/memory.h>
24 #include <exec/semaphores.h>
25 #include <dos/dostags.h>
26 #include <dos/dos.h>
27 #include <libraries/iffparse.h>
29 #include <clib/alib_protos.h>
30 #include <proto/exec.h>
31 #include <proto/dos.h>
32 #include <proto/iffparse.h>
33 #include <proto/utility.h>
34 #define __NOLIBBASE__
35 #define __NOGLOBALIFACE__
36 #include <proto/ahi.h>
37 #undef __NOLIBBASE__
38 #undef __NOGLOBALIFACE__
39 #include <proto/ahi_sub.h>
41 #include <string.h>
43 #include "ahi_def.h"
44 #include "database.h"
45 #include "debug.h"
46 #include "header.h"
47 #include "misc.h"
49 static ULONG AddModeFile ( UBYTE *filename );
51 #ifdef __MORPHOS__
52 #define IS_MORPHOS 1
53 #else
54 #define IS_MORPHOS 0
55 #endif
57 /******************************************************************************
58 ** Audio Database *************************************************************
59 ******************************************************************************/
61 struct AHI_AudioMode
63 struct MinNode ahidbn_MinNode;
64 struct TagItem ahidbn_Tags[0];
66 /* Taglist, mode name and driver name follows.
67 Size variable. Use FreeVec() to free node. */
71 ** Lock the database for read access. Return NULL if database not present.
74 struct AHI_AudioDatabase *
75 LockDatabase(void)
77 struct AHI_AudioDatabase *audiodb;
79 Forbid();
81 audiodb = (struct AHI_AudioDatabase *) FindSemaphore(ADB_NAME);
83 if(audiodb != NULL)
85 ObtainSemaphoreShared((struct SignalSemaphore *) audiodb);
88 Permit();
90 return audiodb;
94 ** Lock the database for write access. Create it if not present.
97 struct AHI_AudioDatabase *
98 LockDatabaseWrite(void)
100 struct AHI_AudioDatabase *audiodb;
102 Forbid();
104 audiodb = (struct AHI_AudioDatabase *) FindSemaphore(ADB_NAME);
106 if(audiodb != NULL)
108 ObtainSemaphore((struct SignalSemaphore *) audiodb);
110 else
112 audiodb = (struct AHI_AudioDatabase *)
113 AllocVec(sizeof(struct AHI_AudioDatabase), MEMF_PUBLIC|MEMF_CLEAR);
115 if(audiodb != NULL)
118 NewList( (struct List *) &audiodb->ahidb_AudioModes);
120 audiodb->ahidb_Semaphore.ss_Link.ln_Name = audiodb->ahidb_Name;
121 audiodb->ahidb_Semaphore.ss_Link.ln_Pri = 20;
122 strcpy(audiodb->ahidb_Semaphore.ss_Link.ln_Name, ADB_NAME);
124 AddSemaphore((struct SignalSemaphore *) audiodb);
125 ObtainSemaphore((struct SignalSemaphore *) audiodb);
128 Permit();
130 return audiodb;
133 void
134 UnlockDatabase ( struct AHI_AudioDatabase *audiodb )
136 if(audiodb)
138 ReleaseSemaphore((struct SignalSemaphore *) audiodb);
142 struct TagItem *
143 GetDBTagList ( struct AHI_AudioDatabase *audiodb,
144 ULONG id )
146 struct AHI_AudioMode *node;
147 struct TagItem *rc = NULL;
149 if((audiodb != NULL) && (id != AHI_INVALID_ID))
151 for(node=(struct AHI_AudioMode *)audiodb->ahidb_AudioModes.mlh_Head;
152 node->ahidbn_MinNode.mln_Succ;
153 node=(struct AHI_AudioMode *)node->ahidbn_MinNode.mln_Succ)
155 if(id == GetTagData(AHIDB_AudioID,AHI_INVALID_ID,node->ahidbn_Tags))
157 rc = node->ahidbn_Tags;
158 break;
163 return rc;
167 /******************************************************************************
168 ** AHI_NextAudioID ************************************************************
169 ******************************************************************************/
171 /****** ahi.device/AHI_NextAudioID ******************************************
173 * NAME
174 * AHI_NextAudioID -- iterate current audio mode identifiers
176 * SYNOPSIS
177 * next_ID = AHI_NextAudioID( last_ID );
178 * D0 D0
180 * ULONG AHI_NextAudioID( ULONG );
182 * FUNCTION
183 * This function is used to iterate through all current AudioIDs in
184 * the audio database.
186 * INPUTS
187 * last_ID - previous AudioID or AHI_INVALID_ID if beginning iteration.
189 * RESULT
190 * next_ID - subsequent AudioID or AHI_INVALID_ID if no more IDs.
192 * EXAMPLE
194 * NOTES
196 * BUGS
198 * SEE ALSO
199 * AHI_GetAudioAttrsA(), AHI_BestAudioIDA()
201 ****************************************************************************
205 ULONG
206 _AHI_NextAudioID( ULONG id,
207 struct AHIBase* AHIBase )
209 struct AHI_AudioDatabase *audiodb;
210 struct AHI_AudioMode *node;
211 ULONG nextid=AHI_INVALID_ID;
213 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
215 Debug_NextAudioID(id);
218 audiodb = LockDatabase();
220 if(audiodb != NULL)
222 node = (struct AHI_AudioMode *) audiodb->ahidb_AudioModes.mlh_Head;
224 if(id != AHI_INVALID_ID)
226 while(node != NULL)
228 ULONG thisid;
230 thisid = GetTagData(AHIDB_AudioID,AHI_INVALID_ID,node->ahidbn_Tags);
231 node = (struct AHI_AudioMode *) node->ahidbn_MinNode.mln_Succ;
233 if(thisid == id)
235 break;
240 while(node && node->ahidbn_MinNode.mln_Succ)
242 if( GetTagData( AHIDB_MultTable, FALSE, node->ahidbn_Tags ) )
244 // Pretend the "Fast" modes are not here
245 node = (struct AHI_AudioMode*) node->ahidbn_MinNode.mln_Succ;
247 else
249 nextid = GetTagData(AHIDB_AudioID, AHI_INVALID_ID, node->ahidbn_Tags);
250 break;
254 UnlockDatabase(audiodb);
257 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
259 KPrintF("=>0x%08lx\n",nextid);
262 return nextid;
266 /******************************************************************************
267 ** AHI_AddAudioMode ***********************************************************
268 ******************************************************************************/
270 /****i* ahi.device/AHI_AddAudioMode *****************************************
272 * NAME
273 * AHI_AddAudioMode -- add an audio mode to the database (V4)
275 * SYNOPSIS
276 * success = AHI_AddAudioMode( DBtags );
277 * D0 A0
279 * ULONG AHI_AddAudioMode( struct TagItem *, UBYTE *, UBYTE *);
281 * FUNCTION
282 * Adds the audio mode described by a taglist to the audio mode
283 * database. If the database does not exists, it will be created.
285 * INPUTS
286 * DBtags - Tags describing the properties of this mode.
288 * RESULT
289 * success - FALSE if the mode could not be added.
291 * EXAMPLE
293 * NOTES
295 * BUGS
297 * SEE ALSO
299 ****************************************************************************
303 ULONG
304 _AHI_AddAudioMode( struct TagItem* DBtags,
305 struct AHIBase* AHIBase )
307 struct AHI_AudioDatabase *audiodb;
308 struct AHI_AudioMode *node;
309 ULONG nodesize = sizeof(struct AHI_AudioMode), tagitems = 0;
310 ULONG datalength = 0, namelength = 0, driverlength = 0, dvrbasenamelength = 0;
311 struct TagItem *tstate = DBtags, *tp, *tag;
312 ULONG rc = FALSE;
314 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
316 Debug_AddAudioMode(DBtags);
319 // Remove old mode if present in database
320 AHI_RemoveAudioMode( GetTagData(AHIDB_AudioID, AHI_INVALID_ID, DBtags));
322 // Now add the new mode
324 audiodb = LockDatabaseWrite();
326 if(audiodb != NULL)
329 // Find total size
331 while( (tag = NextTagItem(&tstate)) != NULL )
334 if(tag->ti_Data) switch(tag->ti_Tag)
336 case AHIDB_Data:
337 datalength = ((ULONG *)tag->ti_Data)[0];
338 nodesize += datalength;
339 break;
341 case AHIDB_Name:
342 namelength = strlen((UBYTE *)tag->ti_Data)+1;
343 nodesize += namelength;
344 break;
346 case AHIDB_Driver:
347 driverlength = strlen((UBYTE *)tag->ti_Data)+1;
348 nodesize += driverlength;
349 break;
351 case AHIDB_DriverBaseName:
352 dvrbasenamelength = strlen((UBYTE *)tag->ti_Data)+1;
353 nodesize += dvrbasenamelength;
354 break;
357 nodesize += sizeof(struct TagItem);
358 tagitems++;
361 nodesize += sizeof(struct TagItem); // The last TAG_END
362 tagitems++;
364 node = AllocVec(nodesize, MEMF_PUBLIC|MEMF_CLEAR);
366 if(node != NULL)
368 tp = node->ahidbn_Tags;
369 tstate = DBtags;
370 while( (tag = NextTagItem(&tstate)) != NULL)
372 if(tag->ti_Data) switch(tag->ti_Tag)
374 case AHIDB_Data:
375 tp->ti_Data = ((IPTR) &node->ahidbn_Tags[tagitems]);
376 CopyMem((APTR)tag->ti_Data, (APTR)tp->ti_Data, datalength);
377 break;
379 case AHIDB_Name:
380 tp->ti_Data = ((IPTR) &node->ahidbn_Tags[tagitems]) + datalength;
381 strcpy((UBYTE *)tp->ti_Data, (UBYTE *)tag->ti_Data);
382 break;
384 case AHIDB_Driver:
385 tp->ti_Data= ((IPTR) &node->ahidbn_Tags[tagitems]) + datalength + namelength;
386 strcpy((UBYTE *)tp->ti_Data, (UBYTE *)tag->ti_Data);
387 break;
389 case AHIDB_DriverBaseName:
390 tp->ti_Data = ((IPTR) &node->ahidbn_Tags[tagitems]) + datalength + namelength + driverlength;
391 strcpy((UBYTE *)tp->ti_Data, (UBYTE *)tag->ti_Data);
392 break;
394 default:
395 tp->ti_Data = tag->ti_Data;
396 break;
398 tp->ti_Tag = tag->ti_Tag;
399 tp++;
401 tp->ti_Tag = TAG_DONE;
403 AddHead((struct List *) &audiodb->ahidb_AudioModes, (struct Node *) node);
404 rc = TRUE;
407 UnlockDatabase(audiodb);
410 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
412 KPrintF("=>%ld\n",rc);
415 return rc;
419 /******************************************************************************
420 ** AHI_RemoveAudioMode ********************************************************
421 ******************************************************************************/
423 /****i* ahi.device/AHI_RemoveAudioMode **************************************
425 * NAME
426 * AHI_RemoveAudioMode -- remove a audio mode to the database (V4)
428 * SYNOPSIS
429 * success = AHI_RemoveAudioMode( ID );
430 * D0 D0
432 * ULONG AHI_RemoveAudioMode( ULONG );
434 * FUNCTION
435 * Removes the audio mode from the audio mode database.
437 * INPUTS
438 * ID - The audio ID of the mode to be removed, or AHI_INVALID_ID.
440 * RESULT
441 * success - FALSE if the mode could not be removed.
443 * EXAMPLE
445 * NOTES
447 * BUGS
449 * SEE ALSO
451 ****************************************************************************
455 ULONG
456 _AHI_RemoveAudioMode( ULONG id,
457 struct AHIBase* AHIBase )
459 struct AHI_AudioMode *node;
460 struct AHI_AudioDatabase *audiodb;
461 ULONG rc=FALSE;
463 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
465 Debug_RemoveAudioMode(id);
469 /* Why ?? */
471 audiodb = LockDatabaseWrite();
473 if(audiodb != NULL)
475 UnlockDatabase(audiodb);
478 audiodb = LockDatabaseWrite();
480 if(audiodb != NULL)
482 if(id != AHI_INVALID_ID)
484 for(node=(struct AHI_AudioMode *)audiodb->ahidb_AudioModes.mlh_Head;
485 node->ahidbn_MinNode.mln_Succ;
486 node=(struct AHI_AudioMode *)node->ahidbn_MinNode.mln_Succ)
488 if(id == GetTagData(AHIDB_AudioID, AHI_INVALID_ID, node->ahidbn_Tags))
490 Remove((struct Node *) node);
491 FreeVec(node);
492 rc = TRUE;
493 break;
497 // Remove the entire database if it's empty
499 Forbid();
501 if(audiodb->ahidb_AudioModes.mlh_Head->mln_Succ == NULL)
503 UnlockDatabase(audiodb);
505 audiodb = (struct AHI_AudioDatabase *) FindSemaphore(ADB_NAME);
507 if(audiodb != NULL)
509 RemSemaphore((struct SignalSemaphore *) audiodb);
510 FreeVec(audiodb);
513 audiodb = NULL;
517 Permit();
520 UnlockDatabase(audiodb);
523 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
525 KPrintF("=>%ld\n",rc);
528 return rc;
532 /******************************************************************************
533 ** AHI_LoadModeFile ***********************************************************
534 ******************************************************************************/
536 /****i* ahi.device/AHI_LoadModeFile *****************************************
538 * NAME
539 * AHI_LoadModeFile -- Add all modes in a mode file to the database (V4)
541 * SYNOPSIS
542 * success = AHI_LoadModeFile( name );
543 * D0 A0
545 * ULONG AHI_LoadModeFile( STRPTR );
547 * FUNCTION
548 * This function takes the name of a file or a directory and either
549 * adds all modes in the file or the modes of all files in the
550 * directory to the audio mode database. Directories inside the
551 * given directory will not be recursed. The file format is IFF-AHIM.
553 * INPUTS
554 * name - A pointer to the name of a file or directory.
556 * RESULT
557 * success - FALSE on error. Check dos.library/IOErr() for more
558 * information.
560 * EXAMPLE
562 * NOTES
564 * BUGS
566 * SEE ALSO
568 ****************************************************************************
572 ULONG
573 _AHI_LoadModeFile( UBYTE* name,
574 struct AHIBase* AHIBase )
576 ULONG rc=FALSE;
577 struct FileInfoBlock *fib;
578 BPTR lock,thisdir;
580 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
582 Debug_LoadModeFile(name);
585 SetIoErr(0);
587 fib = AllocDosObject(DOS_FIB, TAG_DONE);
589 if(fib != NULL)
591 lock = Lock(name, ACCESS_READ);
593 if(lock != 0)
595 if(Examine(lock,fib))
597 if(fib->fib_DirEntryType>0) // Directory?
599 thisdir = CurrentDir(lock);
601 while(ExNext(lock, fib))
603 if(fib->fib_DirEntryType>0)
605 continue; // AHI_LoadModeFile(fib->fib_FileName); for recursion
607 else
609 #if 0
610 rc = AddModeFile(fib->fib_FileName);
612 if(!rc)
614 break;
616 #else
617 // Try to load. Just continue if failing.
618 AddModeFile(fib->fib_FileName);
619 rc = TRUE;
620 #endif
623 if(IoErr() == ERROR_NO_MORE_ENTRIES)
625 SetIoErr(0);
628 CurrentDir(thisdir);
630 else // Plain file
632 rc = AddModeFile(name);
636 UnLock(lock);
639 FreeDosObject(DOS_FIB,fib);
642 if(AHIBase->ahib_DebugLevel >= AHI_DEBUG_HIGH)
644 KPrintF("=>%ld\n",rc);
647 return rc;
650 /* AddModeFile **********************************************************/
652 static ULONG
653 AddModeFile ( UBYTE *filename )
655 struct IFFHandle *iff;
656 struct StoredProperty *name,*data;
657 struct CollectionItem *ci;
658 struct TagItem *tag,*tstate;
659 struct TagItem extratags[]=
661 { AHIDB_Driver, 0 },
662 { AHIDB_Data, 0 },
663 { AHIDB_DriverBaseName, (IPTR)(IS_MORPHOS ? "MOSSYS:DEVS/AHI" : "DEVS:AHI") },
664 { TAG_MORE, 0 }
666 ULONG rc=FALSE;
668 iff = AllocIFF();
670 if(iff != NULL)
673 iff->iff_Stream = (IPTR)Open(filename, MODE_OLDFILE);
675 if(iff->iff_Stream != 0)
677 InitIFFasDOS(iff);
679 if(!OpenIFF(iff, IFFF_READ))
682 if(!(PropChunk(iff, ID_AHIM, ID_AUDN)
683 || PropChunk(iff, ID_AHIM, ID_AUDD)
684 || CollectionChunk(iff, ID_AHIM, ID_AUDM)
685 || StopOnExit(iff, ID_AHIM, ID_FORM)))
687 if(ParseIFF(iff, IFFPARSE_SCAN) == IFFERR_EOC)
689 name = FindProp(iff, ID_AHIM, ID_AUDN);
690 data = FindProp(iff, ID_AHIM, ID_AUDD);
691 ci = FindCollection(iff, ID_AHIM, ID_AUDM);
693 rc = TRUE;
695 if(name != NULL)
697 char driver_name[ 128 ];
698 struct Library* driver_base;
700 rc = FALSE;
702 if( name->sp_Size <= 0 )
704 Req( "%s:\nAUDN chunk has illegal size: %ld.",
705 (IPTR)filename, name->sp_Size );
707 else
709 STRPTR s;
711 // Make sure string is NUL-terminated
713 for( s = (STRPTR) name->sp_Data;
714 (APTR) s < name->sp_Data + name->sp_Size;
715 ++s )
717 if( *s == 0 )
719 rc = TRUE;
720 break;
724 if( !rc )
726 Req( "%s:\nAUDN chunk is not NUL-terminated.",
727 (IPTR)filename );
731 extratags[0].ti_Data = (IPTR) name->sp_Data;
733 // Now verify that the driver can really be opened
735 strcpy( driver_name, IS_MORPHOS ? "MOSSYS:DEVS/AHI/" : "DEVS:AHI/" );
736 strncat( driver_name, name->sp_Data, 100 );
737 strcat( driver_name, ".audio" );
739 driver_base = OpenLibrary( driver_name, DriverVersion );
740 if( driver_base == NULL )
742 if (IS_MORPHOS == 0)
744 rc = FALSE;
746 else
748 // Make it MOSSYS:DEVS:AHI/...
749 // ^
750 driver_name[7 + 4] = ':';
752 // Try "DEVS:AHI/...."
754 driver_base = OpenLibrary( driver_name + 7, DriverVersion );
755 if( driver_base == NULL )
757 rc = FALSE;
759 else
761 // It is a DEVS:AHI driver!
762 extratags[2].ti_Data = (IPTR) "DEVS:AHI";
764 CloseLibrary( driver_base );
768 else
770 CloseLibrary( driver_base );
774 if(data != NULL)
776 if( name->sp_Size <= 0 )
778 Req( "%s:\nAUDD chunk has illegal size: %ld.",
779 (IPTR)filename, name->sp_Size );
781 rc = FALSE;
784 extratags[1].ti_Data = (IPTR) data->sp_Data;
787 while(rc && ci != NULL)
789 // Relocate loaded taglist
791 tstate = (struct TagItem *) ci->ci_Data;
793 while( rc && ( tag = NextTagItem( &tstate ) ) != NULL )
795 EndianSwap( sizeof tag->ti_Tag, &tag->ti_Tag );
796 EndianSwap( sizeof tag->ti_Data, &tag->ti_Data );
798 if(tag->ti_Tag & (AHI_TagBaseR ^ AHI_TagBase))
800 tag->ti_Data += (IPTR)ci->ci_Data;
803 rc = FALSE;
805 switch( tag->ti_Tag )
807 case AHIDB_Name:
809 // Make sure the string is within the chunk and NUL-term.
811 if( tag->ti_Data < (IPTR) ci->ci_Data ||
812 tag->ti_Data >= (IPTR) ci->ci_Data + ci->ci_Size )
814 Req( "%s:\nAUDM chunk contains an invalid string.",
815 (IPTR)filename );
817 else
819 STRPTR s;
821 // Make sure string is NUL-terminated
823 for( s = (STRPTR) tag->ti_Data;
824 (APTR) s < ci->ci_Data + ci->ci_Size;
825 ++s )
827 if( *s == 0 )
829 rc = TRUE;
830 break;
835 break;
838 default:
839 rc = TRUE;
840 break;
843 if( !rc )
845 Req( "%s:\nAUDM chunk contains a string that is not "
846 "NUL-terminated.", (IPTR)filename );
850 if( rc )
852 // Link taglists
854 extratags[3].ti_Data = (IPTR) ci->ci_Data;
856 rc = AHI_AddAudioMode(extratags);
858 ci = ci->ci_Next;
863 CloseIFF(iff);
865 Close((BPTR) iff->iff_Stream);
867 FreeIFF(iff);
870 return rc;