home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 106 / EnigmaAmiga106CD.iso / software / sviluppo / ahisrc / ahi / ahi.c next >
Encoding:
C/C++ Source or Header  |  1999-08-29  |  16.4 KB  |  623 lines

  1. /*
  2.      AHI - The AHI preferences program
  3.      Copyright (C) 1996-1999 Martin Blom <martin@blom.org>
  4.      
  5.      This program is free software; you can redistribute it and/or
  6.      modify it under the terms of the GNU General Public License
  7.      as published by the Free Software Foundation; either version 2
  8.      of the License, or (at your option) any later version.
  9.      
  10.      This program 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
  13.      GNU General Public License for more details.
  14.      
  15.      You should have received a copy of the GNU General Public License
  16.      along with this program; if not, write to the Free Software
  17.      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19.  
  20. /* $Id: ahi.c,v 4.11 1999/08/29 23:43:48 lcs Exp $
  21.  * $Log: ahi.c,v $
  22.  * Revision 4.11  1999/08/29 23:43:48  lcs
  23.  * Added support for ahigp_AntiClickTime.
  24.  *
  25.  * Revision 4.10  1999/04/22 19:41:08  lcs
  26.  * Removed SAS/C smakefile.
  27.  * I had the copyright date screwed up: Changed to 1996-1999 (which is only
  28.  * partly correct, but still better than 1997-1999....)
  29.  *
  30.  * Revision 4.9  1999/03/28 22:30:38  lcs
  31.  * AHI is now GPL/LGPL software.
  32.  * Make target bindist work correctly when using a separate build directory.
  33.  * Small first steps towards a WarpOS PPC version.
  34.  *
  35.  * Revision 4.8  1999/01/09 23:14:06  lcs
  36.  * Switched from SAS/C to gcc
  37.  *
  38.  * Revision 4.7  1997/06/24 21:49:49  lcs
  39.  * Increased version number to match the catalogs (4.5).
  40.  *
  41.  * Revision 4.6  1997/05/09 14:02:17  lcs
  42.  * Program version 4.4
  43.  *
  44.  * Revision 4.5  1997/05/04 22:13:29  lcs
  45.  * Version label now public
  46.  *
  47.  * Revision 4.4  1997/04/27 16:16:06  lcs
  48.  * Added "Mastervolume with(out) clipping".
  49.  *
  50.  * Revision 4.3  1997/04/09 03:00:06  lcs
  51.  * Fixed globaloptions and "Restore"
  52.  *
  53.  * Revision 4.2  1997/04/07 01:36:51  lcs
  54.  * Localized it, bug fixes
  55.  *
  56.  */
  57.  
  58. #include <config.h>
  59. #include <CompilerSpecific.h>
  60.  
  61. #include <devices/ahi.h>
  62. #include <workbench/startup.h>
  63. #include <proto/ahi.h>
  64. #include <proto/exec.h>
  65. #include <proto/dos.h>
  66. #include <proto/icon.h>
  67. #include <math.h>
  68. #include <stdio.h>
  69. #include <string.h>
  70.  
  71. #include "ahi.h"
  72. #include "ahiprefs_Cat.h"
  73. #include "support.h"
  74. #include "gui.h"
  75. #include "version.h"
  76.  
  77. #define DBSTEP      0.5
  78.  
  79. const char *Version = "$VER: AHI preferences " VERS "\r\n";
  80.  
  81. #define TEMPLATE "FROM,EDIT/S,USE/S,SAVE/S,PUBSCREEN/K"
  82.  
  83. struct List      *UnitList   = NULL;
  84. struct List      *ModeList   = NULL;
  85. char            **Units      = NULL;
  86. char            **Modes      = NULL;
  87. char            **Inputs     = NULL;
  88. char            **Outputs    = NULL;
  89.  
  90. struct state state = { 0 };
  91. struct args  args =  {NULL, FALSE, FALSE, FALSE, NULL };
  92.  
  93. BOOL SaveIcons;
  94.  
  95. static char freqBuffer[16];
  96. static char chanBuffer[16];
  97. static char outvolBuffer[16];
  98. static char monvolBuffer[16];
  99. static char gainBuffer[16];
  100.  
  101. static char authorBuffer[32];
  102. static char copyrightBuffer[32];
  103. static char driverBuffer[32];
  104. static char versionBuffer[32];
  105.  
  106.  
  107. /******************************************************************************
  108. **** main() function **********************************************************
  109. ******************************************************************************/
  110.  
  111. int main(int argc, char **argv) {
  112.   struct RDArgs *rdargs = NULL;
  113.   int i;
  114.   char pubscreen[32];
  115.  
  116.   if(argc) {
  117.     rdargs=ReadArgs( TEMPLATE , (LONG *) &args, NULL);
  118.     SaveIcons  = FALSE;
  119.   }
  120.   else {
  121.     struct WBStartup *WBenchMsg = (struct WBStartup *)argv;
  122.     struct WBArg *wbarg;
  123.     LONG olddir;
  124.     struct DiskObject *dobj;
  125.     char **toolarray;
  126.     char *s;
  127.  
  128.     SaveIcons  = TRUE;
  129.  
  130.     for(i=0, wbarg=WBenchMsg->sm_ArgList;
  131.       i < WBenchMsg->sm_NumArgs;
  132.       i++, wbarg++) {
  133.  
  134.       olddir = -1;
  135.       if((wbarg->wa_Lock)&&(*wbarg->wa_Name))
  136.           olddir = CurrentDir(wbarg->wa_Lock);
  137.  
  138.  
  139.       if((*wbarg->wa_Name) && (dobj=GetDiskObject(wbarg->wa_Name))) {
  140.         toolarray = (char **)dobj->do_ToolTypes;
  141.  
  142.         s = (char *) FindToolType(toolarray,"CREATEICONS");
  143.  
  144.         if( s != NULL ) {
  145.           if( MatchToolValue(s,"NO") ||
  146.               MatchToolValue(s,"FALSE")) {
  147.             SaveIcons = FALSE;
  148.           }
  149.         }
  150.  
  151.         s = (char *) FindToolType(toolarray,"PUBSCREEN");
  152.  
  153.         if( s != NULL ) {
  154.           strncpy(pubscreen, s, sizeof pubscreen);
  155.           args.pubscreen = pubscreen;
  156.         }
  157.  
  158.         s = (char *) FindToolType(toolarray,"ACTION");
  159.  
  160.         if( s != NULL ) {
  161.           if(MatchToolValue(s,"EDIT")) {
  162.             args.edit = TRUE;
  163.           }
  164.           else if(MatchToolValue(s,"USE")) {
  165.             args.use = TRUE;
  166.           }
  167.           else if(MatchToolValue(s,"SAVE")) {
  168.             args.save = TRUE;
  169.           }
  170.         }
  171.  
  172.         FreeDiskObject(dobj);
  173.       }
  174.  
  175.       if((i>0)&&(*wbarg->wa_Name)) {
  176.         args.from = wbarg->wa_Name;
  177.       }
  178.  
  179.       if(olddir != -1) {
  180.         CurrentDir(olddir); /* CD back where we were */
  181.       }
  182.     }
  183.   
  184.   }
  185.  
  186.   if(args.from == NULL) {
  187.     args.from = ENVFILE;
  188.   }
  189.  
  190.   if(args.edit) {
  191.     args.use  = FALSE;
  192.     args.save = FALSE;
  193.   }
  194.  
  195.   if((SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) == 0) {
  196.     if(Initialize()) {
  197.  
  198.       NewSettings(args.from);
  199.  
  200.       if((!args.use && !args.save) || args.edit) {
  201.         if(BuildGUI(args.pubscreen)) {
  202.           EventLoop();
  203.         }
  204.         CloseGUI();
  205.       }
  206.  
  207.       if(args.use || args.save) {
  208.         SaveSettings(ENVFILE, UnitList);
  209.       }
  210.  
  211.       if(args.save) {
  212.         SaveSettings(ENVARCFILE, UnitList);
  213.       }
  214.     }
  215.   }
  216.  
  217.   if(rdargs) {
  218.     FreeArgs(rdargs);
  219.   }
  220.  
  221.   FreeVec(Units);
  222.   FreeVec(Modes);
  223.   FreeVec(Outputs);
  224.   FreeVec(Inputs);
  225.   FreeList(ModeList);
  226.   FreeList(UnitList);
  227.  
  228.   CleanUp();
  229.  
  230.   return 0;
  231. }
  232.  
  233. /******************************************************************************
  234. **** Given a file name, load it and fill all structures ***********************
  235. ******************************************************************************/
  236.  
  237. void NewSettings(char *name) {
  238.   FreeVec(Units);
  239.   FreeList(UnitList);
  240.  
  241.   globalprefs.ahigp_DebugLevel       = AHI_DEBUG_NONE;
  242.   globalprefs.ahigp_DisableSurround  = FALSE;
  243.   globalprefs.ahigp_DisableEcho      = FALSE;
  244.   globalprefs.ahigp_FastEcho         = FALSE;
  245.   globalprefs.ahigp_MaxCPU           = (90 << 16) / 100;
  246.   globalprefs.ahigp_ClipMasterVolume = FALSE;
  247.   globalprefs.ahigp_AntiClickTime    = 0;
  248.  
  249.   UnitList = GetUnits(name);
  250.   Units = List2Array((struct List *) UnitList);
  251.  
  252.   NewUnit(0);
  253. }
  254.  
  255. /******************************************************************************
  256. **** Given a unit, fill the state structure ***********************************
  257. ******************************************************************************/
  258.  
  259. void NewUnit(int selectedunit) {
  260.   struct UnitNode *unit;
  261.   ULONG id, modeselected;
  262.   struct ModeNode *mode;
  263.  
  264.   unit = (struct UnitNode *) GetNode(selectedunit, UnitList);
  265.  
  266.   state.UnitSelected     = selectedunit;
  267.  
  268.   id   = unit->prefs.ahiup_AudioMode;
  269.  
  270.   FreeVec(Modes);
  271.   FreeList(ModeList);
  272.   ModeList = GetModes( &unit->prefs);
  273.   Modes = List2Array((struct List *) ModeList);
  274.  
  275.   modeselected = 0;
  276.   mode = (struct ModeNode *) ModeList->lh_Head;
  277.   while(mode->node.ln_Succ) {
  278.     if(id == mode->ID)
  279.       break;
  280.     modeselected++;
  281.     mode = (struct ModeNode *) mode->node.ln_Succ;
  282.   }
  283.  
  284.   if(mode->node.ln_Succ == NULL) {
  285.     modeselected = 0;
  286.   }
  287.   
  288.   NewMode(modeselected);
  289. }
  290.  
  291.  
  292. /******************************************************************************
  293. **** Given a mode, fill all that depends on it in the state structure *********
  294. ******************************************************************************/
  295.  
  296. void NewMode(int selectedmode) {
  297.   struct UnitNode *unit;
  298.   ULONG id;
  299.   Fixed MinOutVol = 0, MaxOutVol = 0, MinMonVol = 0, MaxMonVol = 0;
  300.   Fixed MinGain = 0, MaxGain = 0;
  301.   double Min, Max, Current;
  302.   int offset;
  303.  
  304.   state.ModeSelected = selectedmode;
  305.  
  306.   unit = (struct UnitNode *) GetNode(state.UnitSelected, UnitList);
  307.  
  308.   id = ((struct ModeNode *) GetNode(selectedmode, ModeList))->ID;
  309.  
  310.   AHI_GetAudioAttrs(id, NULL,
  311.       AHIDB_IndexArg,         unit->prefs.ahiup_Frequency,
  312.       AHIDB_Index,            (ULONG) &state.FreqSelected,
  313.       AHIDB_Frequencies,      (ULONG) &state.Frequencies,
  314.       AHIDB_MaxChannels,      (ULONG) &state.Channels,
  315.       AHIDB_Inputs,           (ULONG) &state.Inputs,
  316.       AHIDB_Outputs,          (ULONG) &state.Outputs,
  317.       AHIDB_MinOutputVolume,  (ULONG) &MinOutVol,
  318.       AHIDB_MaxOutputVolume,  (ULONG) &MaxOutVol,
  319.       AHIDB_MinMonitorVolume, (ULONG) &MinMonVol,
  320.       AHIDB_MaxMonitorVolume, (ULONG) &MaxMonVol,
  321.       AHIDB_MinInputGain,     (ULONG) &MinGain,
  322.       AHIDB_MaxInputGain,     (ULONG) &MaxGain,
  323.  
  324.       AHIDB_BufferLen,        32,
  325.       AHIDB_Author,           (ULONG) authorBuffer,
  326.       AHIDB_Copyright,        (ULONG) copyrightBuffer,
  327.       AHIDB_Driver,           (ULONG) driverBuffer,
  328.       AHIDB_Version,          (ULONG) versionBuffer,
  329.       TAG_DONE);
  330.  
  331.   state.ChannelsSelected = unit->prefs.ahiup_Channels;
  332.   state.InputSelected    = unit->prefs.ahiup_Input;
  333.   state.OutputSelected   = unit->prefs.ahiup_Output;
  334.  
  335.   // Limit channels
  336.   state.Channels = min(state.Channels, 32);
  337.  
  338.   if(unit->prefs.ahiup_Unit == AHI_NO_UNIT) {
  339.     state.ChannelsDisabled = TRUE;
  340.   }
  341.   else {
  342.     state.ChannelsDisabled = FALSE;
  343.   }
  344.  
  345.   if(MinOutVol == 0) {
  346.     MinOutVol = 1;
  347.     state.OutVolMute = TRUE;
  348.     state.OutVols    = 1;
  349.   }
  350.   else {
  351.     state.OutVolMute = FALSE;
  352.     state.OutVols    = 0;
  353.   }
  354.  
  355.   if(MinMonVol == 0) {
  356.     MinMonVol = 1;
  357.     state.MonVolMute = TRUE;
  358.     state.MonVols    = 1;
  359.   }
  360.   else {
  361.     state.MonVolMute = FALSE;
  362.     state.MonVols    = 0;
  363.   }
  364.  
  365.  
  366.   if(MinGain == 0) {
  367.     MinGain = 1;
  368.     state.GainMute = TRUE;
  369.     state.Gains    = 1;
  370.   }
  371.   else {
  372.     state.GainMute = FALSE;
  373.     state.Gains    = 0;
  374.   }
  375.  
  376.   if(MaxOutVol == 0) {
  377.     state.OutVolSelected = 0;
  378.     state.OutVolOffset   = 0;
  379.   }
  380.   else {
  381.     Current = 20 * log10( unit->prefs.ahiup_OutputVolume / 65536.0 );
  382.     Min = floor(20 * log10( MinOutVol / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  383.     Max = floor(20 * log10( MaxOutVol / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  384.     state.OutVolSelected = (Current - Min) / DBSTEP + 0.5 + state.OutVols;
  385.     state.OutVols += ((Max - Min) / DBSTEP) + 1;
  386.     state.OutVolOffset = Min;
  387.   }
  388.  
  389.   if(MaxMonVol == 0) {
  390.     state.MonVolSelected = 0;
  391.     state.MonVolOffset   = 0;
  392.   }
  393.   else {
  394.     Current = 20 * log10( unit->prefs.ahiup_MonitorVolume / 65536.0 );
  395.     Min = floor(20 * log10( MinMonVol / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  396.     Max = floor(20 * log10( MaxMonVol / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  397.     state.MonVolSelected = (Current - Min) / DBSTEP + 0.5 + state.MonVols;
  398.     state.MonVols += ((Max - Min) / DBSTEP) + 1;
  399.     state.MonVolOffset = Min;
  400.   }
  401.  
  402.   if(MaxGain == 0) {
  403.     state.GainSelected = 0;
  404.     state.GainOffset   = 0;
  405.   }
  406.   else {
  407.     Current = 20 * log10( unit->prefs.ahiup_InputGain / 65536.0 );
  408.     Min = floor(20 * log10( MinGain / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  409.     Max = floor(20 * log10( MaxGain / 65536.0 ) / DBSTEP + 0.5) * DBSTEP;
  410.     state.GainSelected = (Current - Min) / DBSTEP + 0.5 + state.Gains;
  411.     state.Gains += ((Max - Min) / DBSTEP) + 1;
  412.     state.GainOffset = Min;
  413.   }
  414.  
  415.   // Make sure everything is within bounds!
  416.  
  417.   state.FreqSelected = max(state.FreqSelected, 0);  
  418.   state.FreqSelected = min(state.FreqSelected, state.Frequencies);
  419.  
  420.   state.ChannelsSelected = max(state.ChannelsSelected, 1);
  421.   state.ChannelsSelected = min(state.ChannelsSelected, state.Channels);
  422.   
  423.   state.OutVolSelected = max(state.OutVolSelected, 0);
  424.   state.OutVolSelected = min(state.OutVolSelected, state.OutVols);
  425.   
  426.   state.MonVolSelected = max(state.MonVolSelected, 0);
  427.   state.MonVolSelected = min(state.MonVolSelected, state.MonVols);
  428.   
  429.   state.GainSelected = max(state.GainSelected, 0);
  430.   state.GainSelected = min(state.GainSelected, state.Gains);
  431.  
  432.   state.InputSelected = max(state.InputSelected, 0);
  433.   state.InputSelected = min(state.InputSelected, state.Inputs);
  434.  
  435.   state.OutputSelected = max(state.OutputSelected, 0);
  436.   state.OutputSelected = min(state.OutputSelected, state.Outputs);
  437.  
  438.   // Remove any \r's or \n's from version string
  439.  
  440.   offset = strlen(versionBuffer);
  441.   while((offset > 0) &&
  442.         ((versionBuffer[offset-1] == '\r') ||
  443.          (versionBuffer[offset-1] == '\n'))) {
  444.     versionBuffer[offset-1] = '\0';
  445.     offset--;
  446.   }
  447.  
  448.   FreeVec(Inputs);
  449.   FreeVec(Outputs);
  450.   Inputs   = GetInputs(id);
  451.   Outputs  = GetOutputs(id);
  452. }
  453.  
  454.  
  455. /******************************************************************************
  456. **** Fill the AHIUnitPrefs structure from state *******************************
  457. ******************************************************************************/
  458.  
  459. void FillUnit() {
  460.   struct UnitNode *unit;
  461.   ULONG id;
  462.   double db;
  463.  
  464.   unit = (struct UnitNode *) GetNode(state.UnitSelected, UnitList);
  465.  
  466.   if(unit->prefs.ahiup_Unit != AHI_NO_UNIT) {
  467.     unit->prefs.ahiup_Channels    = state.ChannelsSelected;
  468.   }
  469.   else {
  470.     unit->prefs.ahiup_Channels    = 0;
  471.   }
  472.  
  473.   id = ((struct ModeNode *) GetNode(state.ModeSelected, ModeList))->ID;
  474.  
  475.   unit->prefs.ahiup_AudioMode     = id;
  476.  
  477.   AHI_GetAudioAttrs(id, NULL,
  478.       AHIDB_FrequencyArg, state.FreqSelected,
  479.       AHIDB_Frequency,    (ULONG) &unit->prefs.ahiup_Frequency,
  480.       TAG_DONE);
  481.  
  482.   db = state.OutVolOffset + DBSTEP * 
  483.       (state.OutVolSelected - (state.OutVolMute ? 1 : 0) );
  484.   unit->prefs.ahiup_OutputVolume = pow(10.0, db/20) * 65536 + 0.5;
  485.   if(state.OutVolMute && (state.OutVolSelected == 0))
  486.     unit->prefs.ahiup_OutputVolume = 0;
  487.  
  488.   db = state.MonVolOffset + DBSTEP * 
  489.       (state.MonVolSelected - (state.MonVolMute ? 1 : 0) );
  490.   unit->prefs.ahiup_MonitorVolume = pow(10.0, db/20) * 65536 + 0.5;
  491.   if(state.MonVolMute && (state.MonVolSelected == 0))
  492.     unit->prefs.ahiup_MonitorVolume = 0;
  493.  
  494.   db = state.GainOffset + DBSTEP * 
  495.       (state.GainSelected - (state.GainMute ? 1 : 0) );
  496.   unit->prefs.ahiup_InputGain = pow(10.0, db/20) * 65536 + 0.5;
  497.   if(state.GainMute && (state.GainSelected == 0))
  498.     unit->prefs.ahiup_InputGain = 0;
  499.  
  500.   unit->prefs.ahiup_Input         = state.InputSelected;
  501.   unit->prefs.ahiup_Output        = state.OutputSelected;
  502. }
  503.  
  504.  
  505. /******************************************************************************
  506. **** Routines to show state in human readable from ****************************
  507. ******************************************************************************/
  508.  
  509. char *getFreq(void) {
  510.   LONG freq = 0;
  511.  
  512.   AHI_GetAudioAttrs(
  513.       ((struct ModeNode *) GetNode(state.ModeSelected, ModeList))->ID, NULL,
  514.       AHIDB_FrequencyArg, state.FreqSelected,
  515.       AHIDB_Frequency,    (ULONG) &freq,
  516.       TAG_DONE);
  517.  
  518.   sprintf(freqBuffer, "%ld Hz", freq);
  519.   return freqBuffer;
  520. }
  521.  
  522.  
  523. char *getChannels(void) {
  524.   if(state.ChannelsDisabled) {
  525.     sprintf(chanBuffer, (char *) msgOptNoChannels);
  526.   }
  527.   else {
  528.     sprintf(chanBuffer, "%ld", state.ChannelsSelected);
  529.   }
  530.   return chanBuffer;
  531. }
  532.  
  533. char *getOutVol(void) {
  534.   int selected = state.OutVolSelected;
  535.  
  536.   if(state.OutVolMute) {
  537.     if(selected == 0) {
  538.       sprintf(outvolBuffer, (char *) msgOptMuted);
  539.       return outvolBuffer;
  540.     }
  541.     else {
  542.       selected--;
  543.     }
  544.   }
  545.  
  546.   sprintf(outvolBuffer, "%+4.1f dB", state.OutVolOffset + (selected * DBSTEP));
  547.   return outvolBuffer;
  548. }
  549.  
  550. char *getMonVol(void) {
  551.   int selected = state.MonVolSelected;
  552.  
  553.   if(state.MonVolMute) {
  554.     if(selected == 0) {
  555.       sprintf(monvolBuffer, (char *) msgOptMuted);
  556.       return monvolBuffer;
  557.     }
  558.     else {
  559.       selected--;
  560.     }
  561.   }
  562.  
  563.   sprintf(monvolBuffer, "%+4.1f dB", state.MonVolOffset + (selected * DBSTEP));
  564.   return monvolBuffer;
  565. }
  566.  
  567. char *getGain(void) {
  568.   int selected = state.GainSelected;
  569.  
  570.   sprintf(gainBuffer, "%+4.1f dB", state.GainOffset + (selected * DBSTEP));
  571.   return gainBuffer;
  572. }
  573.  
  574. char *getInput(void) {
  575.  
  576.   if(Inputs[0]) {
  577.     return Inputs[state.InputSelected];
  578.   }
  579.   return (char *) msgOptNoInputs;
  580. }
  581.  
  582. char *getOutput(void) {
  583.  
  584.   if(Outputs[0]) {
  585.     return Outputs[state.OutputSelected];
  586.   }
  587.   return (char *) msgOptNoOutputs;
  588. }
  589.  
  590. ULONG getAudioMode(void) {
  591.  
  592.   return ((struct ModeNode *) GetNode(state.ModeSelected, ModeList))->ID;
  593. }
  594.  
  595. char *getRecord(void) {
  596.   ULONG record = FALSE, fullduplex = FALSE;
  597.  
  598.   AHI_GetAudioAttrs(
  599.       ((struct ModeNode *) GetNode(state.ModeSelected, ModeList))->ID, NULL,
  600.       AHIDB_Record,     (ULONG) &record,
  601.       AHIDB_FullDuplex, (ULONG) &fullduplex,
  602.       TAG_DONE);
  603.   
  604.   return (char *) (record ? (fullduplex ? msgPropRecordFull : msgPropRecordHalf )
  605.                           : msgPropRecordNone);
  606. }
  607.  
  608. char *getAuthor(void) {
  609.   return authorBuffer;
  610. }
  611.  
  612. char *getCopyright(void) {
  613.   return copyrightBuffer;
  614. }
  615.  
  616. char *getDriver(void) {
  617.   return driverBuffer;
  618. }
  619.  
  620. char *getVersion(void) {
  621.   return versionBuffer;
  622. }
  623.