home *** CD-ROM | disk | FTP | other *** search
/ Large Pack of OldSkool DOS MOD Trackers / goattracker_2.70.zip / src / goattrk2.c < prev    next >
C/C++ Source or Header  |  2010-01-31  |  35KB  |  1,349 lines

  1. //
  2. // GOATTRACKER v2.70
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; either version 2 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program; if not, write to the Free Software
  16. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17. //
  18.  
  19. #define GOATTRK2_C
  20.  
  21. #ifdef __WIN32__
  22. #include <windows.h>
  23. #endif
  24.  
  25. #include "goattrk2.h"
  26. #include "bme.h"
  27.  
  28. int menu = 0;
  29. int editmode = EDIT_PATTERN;
  30. int recordmode = 1;
  31. int followplay = 0;
  32. int hexnybble = -1;
  33. int stepsize = 4;
  34. int autoadvance = 0;
  35. int defaultpatternlength = 64;
  36. int cursorflash = 0;
  37. int cursorcolortable[] = {1,2,7,2};
  38. int exitprogram = 0;
  39. int eacolumn = 0;
  40. int eamode = 0;
  41.  
  42. unsigned keypreset = KEY_TRACKER;
  43. unsigned playerversion = 0;
  44. int fileformat = FORMAT_PRG;
  45. int zeropageadr = 0xfc;
  46. int playeradr = 0x1000;
  47. unsigned sidmodel = 0;
  48. unsigned multiplier = 1;
  49. unsigned adparam = 0x0f00;
  50. unsigned ntsc = 0;
  51. unsigned patternhex = 0;
  52. unsigned sidaddress = 0xd400;
  53. unsigned finevibrato = 1;
  54. unsigned optimizepulse = 1;
  55. unsigned optimizerealtime = 1;
  56. unsigned customclockrate = 0;
  57. unsigned usefinevib = 0;
  58. unsigned b = DEFAULTBUF;
  59. unsigned mr = DEFAULTMIXRATE;
  60. unsigned writer = 0;
  61. unsigned hardsid = 0;
  62. unsigned catweasel = 0;
  63. unsigned interpolate = 0;
  64. unsigned residdelay = 0;
  65. unsigned hardsidbufinteractive = 20;
  66. unsigned hardsidbufplayback = 400;
  67.  
  68. char configbuf[MAX_PATHNAME];
  69. char loadedsongfilename[MAX_FILENAME];
  70. char songfilename[MAX_FILENAME];
  71. char songfilter[MAX_FILENAME];
  72. char songpath[MAX_PATHNAME];
  73. char instrfilename[MAX_FILENAME];
  74. char instrfilter[MAX_FILENAME];
  75. char instrpath[MAX_PATHNAME];
  76. char packedpath[MAX_PATHNAME];
  77.  
  78. char *programname = "$VER: GoatTracker v2.70";
  79.                                       
  80. char textbuffer[MAX_PATHNAME];
  81.  
  82. unsigned char hexkeytbl[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  83.   '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  84.  
  85. extern unsigned char datafile[];
  86.  
  87. int main(int argc, char **argv)
  88. {
  89.   char filename[MAX_PATHNAME];
  90.   FILE *configfile;
  91.   int c,d;
  92.  
  93.   programname += sizeof "$VER:";
  94.   // Open datafile
  95.   io_openlinkeddatafile(datafile);
  96.  
  97.   // Load configuration
  98.   #ifdef __WIN32__
  99.   GetModuleFileName(NULL, filename, MAX_PATHNAME);
  100.   filename[strlen(filename)-3] = 'c';
  101.   filename[strlen(filename)-2] = 'f';
  102.   filename[strlen(filename)-1] = 'g';
  103.   #elif __amigaos__
  104.   strcpy(filename, "PROGDIR:goattrk2.cfg");
  105.   #else
  106.   strcpy(filename, getenv("HOME"));
  107.   strcat(filename, "/.goattrk/goattrk2.cfg");
  108.   #endif
  109.   configfile = fopen(filename, "rt");
  110.   if (configfile)
  111.   {
  112.     getparam(configfile, &b);
  113.     getparam(configfile, &mr);
  114.     getparam(configfile, &hardsid);
  115.     getparam(configfile, &sidmodel);
  116.     getparam(configfile, &ntsc);
  117.     getparam(configfile, (unsigned *)&fileformat);
  118.     getparam(configfile, (unsigned *)&playeradr);
  119.     getparam(configfile, (unsigned *)&zeropageadr);
  120.     getparam(configfile, &playerversion);
  121.     getparam(configfile, &keypreset);
  122.     getparam(configfile, (unsigned *)&stepsize);
  123.     getparam(configfile, &multiplier);
  124.     getparam(configfile, &catweasel);
  125.     getparam(configfile, &adparam);
  126.     getparam(configfile, &interpolate);
  127.     getparam(configfile, &patternhex);
  128.     getparam(configfile, &sidaddress);
  129.     getparam(configfile, &finevibrato);
  130.     getparam(configfile, &optimizepulse);
  131.     getparam(configfile, &optimizerealtime);
  132.     getparam(configfile, &residdelay);
  133.     getparam(configfile, &customclockrate);
  134.     getparam(configfile, &hardsidbufinteractive);
  135.     getparam(configfile, &hardsidbufplayback);
  136.     getfloatparam(configfile, &filterparams.distortionrate);
  137.     getfloatparam(configfile, &filterparams.distortionpoint);
  138.     getfloatparam(configfile, &filterparams.distortioncfthreshold);
  139.     getfloatparam(configfile, &filterparams.type3baseresistance);
  140.     getfloatparam(configfile, &filterparams.type3offset);
  141.     getfloatparam(configfile, &filterparams.type3steepness);
  142.     getfloatparam(configfile, &filterparams.type3minimumfetresistance);
  143.     getfloatparam(configfile, &filterparams.type4k);
  144.     getfloatparam(configfile, &filterparams.type4b);
  145.     getfloatparam(configfile, &filterparams.voicenonlinearity);
  146.     fclose(configfile);
  147.   }
  148.   zeropageadr &= 0xff;
  149.   playeradr &= 0xff00;
  150.   sidaddress &= 0xffff;
  151.   if (!stepsize) stepsize = 4;
  152.   if ((sidaddress < 1) ||(sidaddress > 0xffff)) sidaddress = 0xd400;
  153.  
  154.   if (!initscreen())
  155.   {
  156.     return 1;
  157.   }
  158.  
  159.   // Init pathnames
  160.   initpaths();
  161.  
  162.   // Scan command line
  163.   for (c = 1; c < argc; c++)
  164.   {
  165.     if ((argv[c][0] == '-') || (argv[c][0] == '/'))
  166.     {
  167.       int y = 0;
  168.       switch(toupper(argv[c][1]))
  169.       {
  170.         case '?':
  171.         printtext(0,y++,15,"Usage: GOATTRK2 [songname] [options]");
  172.         printtext(0,y++,15,"Options:");
  173.         printtext(0,y++,15,"/Axx Set ADSR parameter for hardrestart in hex. DEFAULT=0F00");
  174.         printtext(0,y++,15,"/Bxx Set sound buffer length in milliseconds DEFAULT=100");
  175.         printtext(0,y++,15,"/Cxx Use CatWeasel MK3 PCI SID (0 = off, 1 = on)");
  176.         printtext(0,y++,15,"/Dxx Pattern row display (0 = decimal, 1 = hexadecimal)");
  177.         printtext(0,y++,15,"/Exx Set emulated SID model (0 = 6581 1 = 8580) DEFAULT=6581");
  178.         printtext(0,y++,15,"/Fxx Set custom SID clock cycles per second (0 = use PAL/NTSC default)");
  179.         printtext(0,y++,15,"/Hxx Use HardSID (0 = off, 1 = HardSID ID0 2 = HardSID ID1 etc.)");
  180.         printtext(0,y++,15,"/Ixx Set reSID interpolation (0 = off, 1 = on, 2 = distortion, 3 = distortion & on) DEFAULT=off");
  181.         printtext(0,y++,15,"/Kxx Note-entry mode (0 = PROTRACKER 1 = DMC) DEFAULT=PROTRK.");
  182.         printtext(0,y++,15,"/Lxx SID memory location in hex. DEFAULT=D400");
  183.         printtext(0,y++,15,"/Mxx Set sound mixing rate DEFAULT=44100");
  184.         printtext(0,y++,15,"/Oxx Set pulseoptimization/skipping (0 = off, 1 = on) DEFAULT=on");
  185.         printtext(0,y++,15,"/Rxx Set realtime-effect optimization/skipping (0 = off, 1 = on) DEFAULT=on");
  186.         printtext(0,y++,15,"/Sxx Set speed multiplier (0 for 25Hz, 1 for 1x, 2 for 2x etc.)");
  187.         printtext(0,y++,15,"/Txx Set HardSID interactive mode sound buffer length in milliseconds DEFAULT=20, max.buffering=0");
  188.         printtext(0,y++,15,"/Uxx Set HardSID playback mode sound buffer length in milliseconds DEFAULT=400, max.buffering=0");
  189.         printtext(0,y++,15,"/Vxx Set finevibrato conversion (0 = off, 1 = on) DEFAULT=1");
  190.         printtext(0,y++,15,"/Zxx Set random reSID write delay in cycles (0 = off) DEFAULT=0");
  191.         printtext(0,y++,15,"/N   Use NTSC timing");
  192.         printtext(0,y++,15,"/P   Use PAL timing (DEFAULT)");
  193.         printtext(0,y++,15,"/W   Write sound output to a file SIDAUDIO.RAW");
  194.         printtext(0,y++,15,"/?   Show this info again");
  195.         waitkeynoupdate();
  196.         return 0;
  197.  
  198.         case 'Z':
  199.         sscanf(&argv[c][2], "%u", &residdelay);
  200.         break;
  201.  
  202.         case 'A':
  203.         sscanf(&argv[c][2], "%x", &adparam);
  204.         break;
  205.  
  206.         case 'S':
  207.         sscanf(&argv[c][2], "%u", &multiplier);
  208.         break;
  209.  
  210.         case 'B':
  211.         sscanf(&argv[c][2], "%u", &b);
  212.         break;
  213.  
  214.         case 'D':
  215.         sscanf(&argv[c][2], "%u", &patternhex);
  216.         break;
  217.  
  218.         case 'E':
  219.         sscanf(&argv[c][2], "%u", &sidmodel);
  220.         break;
  221.  
  222.         case 'I':
  223.         sscanf(&argv[c][2], "%u", &interpolate);
  224.         break;
  225.  
  226.         case 'K':
  227.         sscanf(&argv[c][2], "%u", &keypreset);
  228.         break;
  229.  
  230.         case 'L':
  231.         sscanf(&argv[c][2], "%x", &sidaddress);
  232.         break;
  233.  
  234.         case 'N':
  235.         ntsc = 1;
  236.         customclockrate = 0;
  237.         break;
  238.  
  239.         case 'P':
  240.         ntsc = 0;
  241.         customclockrate = 0;
  242.         break;
  243.  
  244.         case 'F':
  245.         sscanf(&argv[c][2], "%u", &customclockrate);
  246.         break;
  247.  
  248.         case 'M':
  249.         sscanf(&argv[c][2], "%u", &mr);
  250.         break;
  251.  
  252.         case 'O':
  253.         sscanf(&argv[c][2], "%u", &optimizepulse);
  254.         break;
  255.  
  256.         case 'R':
  257.         sscanf(&argv[c][2], "%u", &optimizerealtime);
  258.         break;
  259.  
  260.         case 'H':
  261.         sscanf(&argv[c][2], "%u", &hardsid);
  262.         break;
  263.  
  264.         case 'V':
  265.         sscanf(&argv[c][2], "%u", &finevibrato);
  266.         break;
  267.         
  268.         case 'T':
  269.         sscanf(&argv[c][2], "%u", &hardsidbufinteractive);
  270.         break;
  271.  
  272.         case 'U':
  273.         sscanf(&argv[c][2], "%u", &hardsidbufplayback);
  274.         break;
  275.  
  276.         case 'W':
  277.         writer = 1;
  278.         break;
  279.  
  280.         case 'C':
  281.         sscanf(&argv[c][2], "%u", &catweasel);
  282.         break;
  283.       }
  284.     }
  285.     else
  286.     {
  287.       char startpath[MAX_PATHNAME];
  288.  
  289.       strcpy(songfilename, argv[c]);
  290.       for (d = strlen(argv[c])-1; d >= 0; d--)
  291.       {
  292.         if ((argv[c][d] == '/') || (argv[c][d] == '\\'))
  293.         {
  294.           strcpy(startpath, argv[c]);
  295.           startpath[d+1] = 0;
  296.           chdir(startpath);
  297.           initpaths();
  298.           strcpy(songfilename, &argv[c][d+1]);
  299.           break;
  300.         }
  301.       }
  302.     }
  303.   }
  304.  
  305.   // Validate parameters
  306.   sidmodel &= 1;
  307.   adparam &= 0xffff;
  308.   if (multiplier > 16) multiplier = 16;
  309.   if ((sidaddress < 1) ||(sidaddress > 0xffff)) sidaddress = 0xd400;
  310.   if (keypreset > 2) keypreset = 0;
  311.   if ((finevibrato == 1) && (multiplier < 2)) usefinevib = 1;
  312.   if (finevibrato > 1) usefinevib = 1;
  313.   if (optimizepulse > 1) optimizepulse = 1;
  314.   if (optimizerealtime > 1) optimizerealtime = 1;
  315.   if (residdelay > 63) residdelay = 63;
  316.   if (customclockrate < 100) customclockrate = 0;
  317.  
  318.   // Reset channels/song
  319.   initchannels();
  320.   clearsong(1,1,1,1,1);
  321.  
  322.   // Init sound
  323.   if (!sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate))
  324.   {
  325.     printtextc(MAX_ROWS/2-1,15,"Sound init failed. Press any key to run without sound (notice that song timer won't start)");
  326.     waitkeynoupdate();
  327.   }
  328.  
  329.   // Load song if applicable
  330.   if (strlen(songfilename)) loadsong();
  331.  
  332.   // Start editor mainloop
  333.   printmainscreen();
  334.   while (!exitprogram)
  335.   {
  336.     waitkeymouse();
  337.     docommand();
  338.   }
  339.   
  340.   // Shutdown sound output now
  341.   sound_uninit();
  342.  
  343.   // Save configuration
  344.   #ifndef __WIN32__
  345.   #ifdef __amigaos__
  346.   strcpy(filename, "PROGDIR:goattrk2.cfg");
  347.   #else
  348.   strcpy(filename, getenv("HOME"));
  349.   strcat(filename, "/.goattrk");
  350.   mkdir(filename, S_IRUSR | S_IWUSR | S_IXUSR);
  351.   strcat(filename, "/goattrk2.cfg");
  352.   #endif
  353.   #endif
  354.   configfile = fopen(filename, "wt");
  355.   if (configfile)
  356.   {
  357.     fprintf(configfile, ";------------------------------------------------------------------------------\n"
  358.                         ";GT2 config file. Rows starting with ; are comments. Hexadecimal parameters are\n"
  359.                         ";to be preceded with $ and decimal parameters with nothing.                    \n"
  360.                         ";------------------------------------------------------------------------------\n"
  361.                         "\n"
  362.                         ";reSID buffer length (in milliseconds)\n%d\n\n"
  363.                         ";reSID mixing rate (in Hz)\n%d\n\n"
  364.                         ";Hardsid device number (0 = off)\n%d\n\n"
  365.                         ";reSID model (0 = 6581, 1 = 8580)\n%d\n\n"
  366.                         ";Timing mode (0 = PAL, 1 = NTSC)\n%d\n\n"
  367.                         ";Packer/relocator fileformat (0 = SID, 1 = PRG, 2 = BIN)\n%d\n\n"
  368.                         ";Packer/relocator player address\n$%04x\n\n"
  369.                         ";Packer/relocator zeropage baseaddress\n$%02x\n\n"
  370.                         ";Packer/relocator player type (0 = standard ... 3 = minimal)\n%d\n\n"
  371.                         ";Key entry mode (0 = Protracker, 1 = DMC)\n%d\n\n"
  372.                         ";Pattern highlight step size\n%d\n\n"
  373.                         ";Speed multiplier (0 = 25Hz, 1 = 1X, 2 = 2X etc.)\n%d\n\n"
  374.                         ";Use CatWeasel SID (0 = off, 1 = on)\n%d\n\n"
  375.                         ";Hardrestart ADSR parameter\n$%04x\n\n"
  376.                         ";reSID interpolation (0 = off, 1 = on, 2 = distortion, 3 = distortion & on)\n%d\n\n"
  377.                         ";Hexadecimal pattern display (0 = off, 1 = on)\n%d\n\n"
  378.                         ";SID baseaddress\n$%04x\n\n"
  379.                         ";Finevibrato mode (0 = off, 1 = on)\n%d\n\n"
  380.                         ";Pulseskipping (0 = off, 1 = on)\n%d\n\n"
  381.                         ";Realtime effect skipping (0 = off, 1 = on)\n%d\n\n"
  382.                         ";Random reSID write delay in cycles (0 = off)\n%d\n\n"
  383.                         ";Custom SID clock cycles per second (0 = use PAL/NTSC default)\n%d\n\n"
  384.                         ";HardSID interactive mode buffer size (in milliseconds, 0 = maximum/no flush)\n%d\n\n"
  385.                         ";HardSID playback mode buffer size (in milliseconds, 0 = maximum/no flush)\n%d\n\n"
  386.                         ";reSID-fp distortion rate\n%f\n\n"
  387.                         ";reSID-fp distortion point\n%f\n\n"
  388.                         ";reSID-fp distortion CF threshold\n%f\n\n"
  389.                         ";reSID-fp type 3 base resistance\n%f\n\n"
  390.                         ";reSID-fp type 3 base offset\n%f\n\n"
  391.                         ";reSID-fp type 3 base steepness\n%f\n\n"
  392.                         ";reSID-fp type 3 minimum FET resistance\n%f\n\n"
  393.                         ";reSID-fp type 4 k\n%f\n\n"
  394.                         ";reSID-fp type 4 b\n%f\n\n"
  395.                         ";reSID-fp voice nonlinearity\n%f\n\n",
  396.     b,
  397.     mr,
  398.     hardsid,
  399.     sidmodel,
  400.     ntsc,
  401.     fileformat,
  402.     playeradr,
  403.     zeropageadr,
  404.     playerversion,
  405.     keypreset,
  406.     stepsize,
  407.     multiplier,
  408.     catweasel,
  409.     adparam,
  410.     interpolate,
  411.     patternhex,
  412.     sidaddress,
  413.     finevibrato,
  414.     optimizepulse,
  415.     optimizerealtime,
  416.     residdelay,
  417.     customclockrate,
  418.     hardsidbufinteractive,
  419.     hardsidbufplayback,
  420.     filterparams.distortionrate,
  421.     filterparams.distortionpoint,
  422.     filterparams.distortioncfthreshold,
  423.     filterparams.type3baseresistance,
  424.     filterparams.type3offset,
  425.     filterparams.type3steepness,
  426.     filterparams.type3minimumfetresistance,
  427.     filterparams.type4k,
  428.     filterparams.type4b,
  429.     filterparams.voicenonlinearity);
  430.     fclose(configfile);
  431.   }
  432.  
  433.   // Exit
  434.   return 0;
  435. }
  436.  
  437. void waitkey(void)
  438. {
  439.   for (;;)
  440.   {
  441.     displayupdate();
  442.     getkey();
  443.     if ((rawkey) || (key)) break;
  444.     if (win_quitted) break;
  445.   }
  446.  
  447.   converthex();
  448. }
  449.  
  450. void waitkeymouse(void)
  451. {
  452.   for (;;)
  453.   {
  454.     displayupdate();
  455.     getkey();
  456.     if ((rawkey) || (key)) break;
  457.     if (win_quitted) break;
  458.     if (mouseb) break;
  459.   }
  460.  
  461.   converthex();
  462. }
  463.  
  464. void waitkeymousenoupdate(void)
  465. {
  466.   for (;;)
  467.   {
  468.       fliptoscreen();
  469.     getkey();
  470.     if ((rawkey) || (key)) break;
  471.     if (win_quitted) break;
  472.     if (mouseb) break;
  473.   }
  474.  
  475.   converthex();
  476. }
  477.  
  478. void waitkeynoupdate(void)
  479. {
  480.   for (;;)
  481.   {
  482.       fliptoscreen();
  483.     getkey();
  484.     if ((rawkey) || (key)) break;
  485.     if ((mouseb) && (!prevmouseb)) break;
  486.     if (win_quitted) break;
  487.   }
  488. }
  489.  
  490. void converthex()
  491. {
  492.   int c;
  493.  
  494.   hexnybble = -1;
  495.   for (c = 0; c < 16; c++)
  496.   {
  497.     if (tolower(key) == hexkeytbl[c])
  498.     {
  499.       if (c >= 10)
  500.       {
  501.         if (!shiftpressed) hexnybble = c;
  502.       }
  503.       else
  504.       {
  505.         hexnybble = c;
  506.       }
  507.     }
  508.   }
  509. }
  510.  
  511.  
  512. void docommand(void)
  513. {
  514.   // "GUI" operation :)
  515.   mousecommands();
  516.  
  517.   // Mode-specific commands
  518.   switch(editmode)
  519.   {
  520.     case EDIT_ORDERLIST:
  521.     orderlistcommands();
  522.     break;
  523.  
  524.     case EDIT_INSTRUMENT:
  525.     instrumentcommands();
  526.     break;
  527.  
  528.     case EDIT_TABLES:
  529.     tablecommands();
  530.     break;
  531.  
  532.     case EDIT_PATTERN:
  533.     patterncommands();
  534.     break;
  535.  
  536.     case EDIT_NAMES:
  537.     namecommands();
  538.     break;
  539.   }
  540.  
  541.   // General commands
  542.   generalcommands();
  543. }
  544.  
  545. void mousecommands(void)
  546. {
  547.   int c;
  548.  
  549.   if (!mouseb) return;
  550.  
  551.   // Pattern editpos & pattern number selection
  552.   for (c = 0; c < MAX_CHN; c++)
  553.   {
  554.     if ((mousey == 2) && (mousex >= 13 + c*15) && (mousex <= 14 + c*15))
  555.     {
  556.         if ((!prevmouseb) || (mouseheld > HOLDDELAY))
  557.         {
  558.         if (mouseb & MOUSEB_LEFT) 
  559.         {
  560.           epchn = c;
  561.           nextpattern();
  562.         }
  563.         if (mouseb & MOUSEB_RIGHT)
  564.         {
  565.           epchn = c;
  566.           prevpattern();
  567.         }
  568.       }
  569.     }
  570.     else
  571.     {
  572.       if ((mousey >= 2) && (mousey <= 34) && (mousex >= 6 + c*15) && (mousex <= 14 + c*15))
  573.       {
  574.         int x = mousex-6-c*15;
  575.         int newpos = mousey-3+epview;
  576.         if (newpos < 0) newpos = 0;
  577.         if (newpos > pattlen[epnum[epchn]]) newpos = pattlen[epnum[epchn]];
  578.  
  579.         editmode = EDIT_PATTERN;
  580.  
  581.         if ((mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) && (!prevmouseb))
  582.         {
  583.           if ((epmarkchn != c) || (newpos != epmarkend))
  584.           {
  585.             epmarkchn = c;
  586.             epmarkstart = epmarkend = newpos;
  587.           }
  588.         }
  589.  
  590.         if (mouseb & MOUSEB_LEFT)
  591.         {
  592.           epchn = c;
  593.           if (x < 4) epcolumn = 0;
  594.           if (x >= 4) epcolumn = x-3;
  595.         }
  596.  
  597.         if (!prevmouseb)
  598.         {
  599.           if (mouseb & MOUSEB_LEFT)
  600.             eppos = newpos;
  601.         }
  602.         else
  603.         {
  604.             if (mouseb & MOUSEB_LEFT)
  605.             {
  606.             if (mousey == 2) eppos--;
  607.             if (mousey == 34) eppos++;
  608.           }
  609.         }
  610.         if (eppos < 0) eppos = 0;
  611.         if (eppos > pattlen[epnum[epchn]]) eppos = pattlen[epnum[epchn]];
  612.  
  613.         if (mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) epmarkend = newpos;
  614.       }
  615.     }
  616.   }
  617.  
  618.   // Song editpos & songnumber selection
  619.   if ((mousey >= 3) && (mousey <= 8) && (mousex >= 40+10))
  620.   {
  621.     int newpos = esview + (mousex-44-10) / 3;
  622.     int newcolumn = (mousex-44-10) % 3;
  623.     int newchn = mousey - 3;
  624.     if (newcolumn < 0) newcolumn = 0;
  625.     if (newcolumn > 1) newcolumn = 1;
  626.     if (newpos < 0)
  627.     {
  628.       newpos = 0;
  629.       newcolumn = 0;
  630.     }
  631.     if (newpos == songlen[esnum][eschn])
  632.     {
  633.       newpos++;
  634.       newcolumn = 0;
  635.     }
  636.     if (newpos > songlen[esnum][eschn]+1)
  637.     {
  638.       newpos = songlen[esnum][eschn] + 1;
  639.       newcolumn = 1;
  640.     }
  641.  
  642.     editmode = EDIT_ORDERLIST;
  643.  
  644.     if ((mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) && (!prevmouseb) && (newpos < songlen[esnum][eschn]))
  645.     {
  646.       if ((esmarkchn != newchn) || (newpos != esmarkend))
  647.       {
  648.         esmarkchn = newchn;
  649.         esmarkstart = esmarkend = newpos;
  650.       }
  651.     }
  652.  
  653.     if (mouseb & MOUSEB_LEFT)
  654.     {
  655.       eschn = newchn;
  656.       eseditpos = newpos;
  657.       escolumn = newcolumn;
  658.     }
  659.  
  660.     if ((mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) && (newpos < songlen[esnum][eschn])) esmarkend = newpos;
  661.   }
  662.   if (((!prevmouseb) || (mouseheld > HOLDDELAY)) && (mousey == 2) && (mousex >= 63+10) && (mousex <= 64+10))
  663.   {
  664.     if (mouseb & MOUSEB_LEFT) nextsong();
  665.     if (mouseb & MOUSEB_RIGHT) prevsong();
  666.   }
  667.  
  668.   // Instrument editpos & instrument number selection
  669.   if ((mousey >= 8) && (mousey <= 12) && (mousex >= 56+10) && (mousex <= 57+10))
  670.   {
  671.     editmode = EDIT_INSTRUMENT;
  672.     eipos = mousey-8;
  673.     eicolumn = mousex-56-10;
  674.   }
  675.   if ((mousey >= 8) && (mousey <= 11) && (mousex >= 76+10) && (mousex <= 77+10))
  676.   {
  677.     editmode = EDIT_INSTRUMENT;
  678.     eipos = mousey-8+5;
  679.     eicolumn = mousex-76-10;
  680.   }
  681.   if ((mousey == 7) && (mousex >= 60+10))
  682.   {
  683.     editmode = EDIT_INSTRUMENT;
  684.     eipos = 9;
  685.   }
  686.   if (((!prevmouseb) || (mouseheld > HOLDDELAY)) && (mousey == 7) && (mousex >= 56+10) && (mousex <= 57+10))
  687.   {
  688.     if (mouseb & MOUSEB_LEFT) nextinstr();
  689.     if (mouseb & MOUSEB_RIGHT) previnstr();
  690.   }
  691.  
  692.  
  693.   // Table editpos
  694.   for (c = 0; c < MAX_TABLES; c++)
  695.   {
  696.     if ((mousey >= 14) && (mousey <= 30) && (mousex >= 43+10+c*10) && (mousex <= 47+10+c*10))
  697.     {
  698.       int newpos = mousey-15+etview[etnum];
  699.       if (newpos < 0) newpos = 0;
  700.       if (newpos >= MAX_TABLELEN) newpos = MAX_TABLELEN-1;
  701.  
  702.       editmode = EDIT_TABLES;
  703.  
  704.       if ((mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) && (!prevmouseb))
  705.       {
  706.         if ((etmarknum != etnum) || (newpos != etmarkend))
  707.         {
  708.           etmarknum = c;
  709.           etmarkstart = etmarkend = newpos;
  710.         }
  711.       }
  712.       if (mouseb & MOUSEB_LEFT)
  713.       {
  714.         etnum = c;
  715.         etpos = mousey-15+etview[etnum];
  716.         etcolumn = mousex-43-10-c*10;
  717.       }
  718.       if (etcolumn >= 2) etcolumn--;
  719.       if (etpos < 0) etpos = 0;
  720.       if (etpos > MAX_TABLELEN-1) etpos = MAX_TABLELEN-1;
  721.  
  722.       if (mouseb & (MOUSEB_RIGHT|MOUSEB_MIDDLE)) etmarkend = newpos;
  723.     }
  724.   }
  725.  
  726.   // Name editpos
  727.   if ((mousey >= 31) && (mousey <= 33) && (mousex >= 47+10))
  728.   {
  729.     editmode = EDIT_NAMES;
  730.     enpos = mousey - 31;
  731.   }
  732.  
  733.   // Status panel
  734.   if ((!prevmouseb) && (mousex == 7) && (mousey == 23+3+9))
  735.   {
  736.     if (mouseb & (MOUSEB_LEFT))
  737.       if (epoctave < 7) epoctave++;
  738.     if (mouseb & (MOUSEB_RIGHT))
  739.       if (epoctave > 0) epoctave--;
  740.   }
  741.   if ((!prevmouseb) && (mousex <= 7) && (mousey == 24+3+9))
  742.   {
  743.     recordmode ^= 1;
  744.   }
  745.   for (c = 0; c < MAX_CHN; c++)
  746.   {
  747.     if ((!prevmouseb) && (mousey >= 23+3+9) && (mousex >= 80 + 7*c) && (mousex <= 85 + 7*c))
  748.       mutechannel(c);
  749.   }
  750.  
  751.   // Titlebar actions
  752.   if (!menu)
  753.   {
  754.     if ((mousey == 0) && (!prevmouseb) && (mouseb == MOUSEB_LEFT))
  755.     {
  756.       if ((mousex >= 40+10) && (mousex <= 41+10))
  757.       {
  758.         usefinevib ^= 1;
  759.       }
  760.       if ((mousex >= 43+10) && (mousex <= 44+10))
  761.       {
  762.         optimizepulse ^= 1;
  763.       }
  764.       if ((mousex >= 46+10) && (mousex <= 47+10))
  765.       {
  766.         optimizerealtime ^= 1;
  767.       }
  768.       if ((mousex >= 49+10) && (mousex <= 52+10))
  769.       {
  770.         ntsc ^= 1;
  771.         sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate);
  772.       }
  773.       if ((mousex >= 54+10) && (mousex <= 57+10))
  774.       {
  775.         sidmodel ^= 1;
  776.         sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate);
  777.       }
  778.       if ((mousex >= 62+10) && (mousex <= 65+10)) editadsr();
  779.       if ((mousex >= 67+10) && (mousex <= 68+10)) prevmultiplier();
  780.       if ((mousex >= 69+10) && (mousex <= 70+10)) nextmultiplier();
  781.     }
  782.   }
  783.   else
  784.   {
  785.     if ((!mousey) && (mouseb & MOUSEB_LEFT) && (!(prevmouseb & MOUSEB_LEFT)))
  786.     {
  787.       if ((mousex >= 0) && (mousex <= 5))
  788.       {
  789.         initsong(esnum, PLAY_BEGINNING);
  790.         followplay = shiftpressed;
  791.       }
  792.       if ((mousex >= 7) && (mousex <= 15))
  793.       {
  794.         initsong(esnum, PLAY_POS);
  795.         followplay = shiftpressed;
  796.       }
  797.       if ((mousex >= 17) && (mousex <= 26))
  798.       {
  799.         initsong(esnum, PLAY_PATTERN);
  800.         followplay = shiftpressed;
  801.       }
  802.       if ((mousex >= 28) && (mousex <= 33))
  803.         stopsong();
  804.       if ((mousex >= 35) && (mousex <= 40))
  805.         load();
  806.       if ((mousex >= 42) && (mousex <= 47))
  807.         save();
  808.       if ((mousex >= 49) && (mousex <= 57))
  809.         relocator();
  810.       if ((mousex >= 59) && (mousex <= 64))
  811.         onlinehelp();
  812.       if ((mousex >= 66) && (mousex <= 72))
  813.         clear();
  814.       if ((mousex >= 74) && (mousex <= 79))
  815.         quit();
  816.     }
  817.   }
  818. }
  819.  
  820. void generalcommands(void)
  821. {
  822.   int c;
  823.  
  824.   switch(key)
  825.   {
  826.     case '?':
  827.     case '-':
  828.     if ((editmode != EDIT_NAMES) && (editmode != EDIT_ORDERLIST))
  829.     {
  830.       if (!((editmode == EDIT_INSTRUMENT) && (eipos == 9))) previnstr();
  831.     }
  832.     break;
  833.  
  834.     case '+':
  835.     case '_':
  836.     if ((editmode != EDIT_NAMES) && (editmode != EDIT_ORDERLIST))
  837.     {
  838.       if (!((editmode == EDIT_INSTRUMENT) && (eipos >= 9))) nextinstr();
  839.  
  840.     }
  841.     break;
  842.  
  843.     case '*':
  844.     if (editmode != EDIT_NAMES)
  845.     {
  846.       if (!((editmode == EDIT_INSTRUMENT) && (eipos >= 9)))
  847.       {
  848.         if (epoctave < 7) epoctave++;
  849.       }
  850.     }
  851.     break;
  852.  
  853.     case '/':
  854.     case '\'':
  855.     if (editmode != EDIT_NAMES)
  856.     {
  857.       if (!((editmode == EDIT_INSTRUMENT) && (eipos >= 9)))
  858.       {
  859.         if (epoctave > 0) epoctave--;
  860.       }
  861.     }
  862.     break;
  863.  
  864.     case '<':
  865.     if (((editmode == EDIT_INSTRUMENT) && (eipos != 9)) || (editmode == EDIT_TABLES))
  866.       previnstr();
  867.     break;
  868.  
  869.     case '>':
  870.     if (((editmode == EDIT_INSTRUMENT) && (eipos != 9)) || (editmode == EDIT_TABLES))
  871.       nextinstr();
  872.     break;
  873.  
  874.     case ';':
  875.     for (c = 0; c < MAX_CHN; c++)
  876.     {
  877.       if (espos[c]) espos[c]--;
  878.       if (espos[c] < esview)
  879.       {
  880.         esview = espos[c];
  881.         eseditpos = espos[c];
  882.       }
  883.     }
  884.     updateviewtopos();
  885.     rewindsong();
  886.     break;
  887.  
  888.     case ':':
  889.     for (c = 0; c < MAX_CHN; c++)
  890.     {
  891.       if (espos[c] < songlen[esnum][c]-1)
  892.         espos[c]++;
  893.       if (espos[c] - esview >= VISIBLEORDERLIST)
  894.       {
  895.         esview = espos[c] - VISIBLEORDERLIST + 1;
  896.         eseditpos = espos[c];
  897.       }
  898.     }
  899.     updateviewtopos();
  900.     rewindsong();
  901.     break;
  902.  
  903.   }
  904.   if (win_quitted) exitprogram = 1;
  905.   switch(rawkey)
  906.   {
  907.     case KEY_ESC:
  908.     if (!shiftpressed)
  909.       quit();
  910.     else
  911.       clear();
  912.     break;
  913.  
  914.     case KEY_KPMULTIPLY:
  915.     if ((editmode != EDIT_NAMES) && (!key))
  916.     {
  917.       if (!((editmode == EDIT_INSTRUMENT) && (eipos >= 9)))
  918.       {
  919.         if (epoctave < 7) epoctave++;
  920.       }
  921.     }
  922.     break;
  923.  
  924.     case KEY_KPDIVIDE:
  925.     if ((editmode != EDIT_NAMES) && (!key))
  926.     {
  927.       if (!((editmode == EDIT_INSTRUMENT) && (eipos >= 9)))
  928.       {
  929.         if (epoctave > 0) epoctave--;
  930.       }
  931.     }
  932.     break;
  933.  
  934.     case KEY_F12:
  935.     onlinehelp();
  936.     break;
  937.  
  938.     case KEY_TAB:
  939.     if (!shiftpressed) editmode++;
  940.     else editmode--;
  941.     if (editmode > EDIT_NAMES) editmode = EDIT_PATTERN;
  942.     if (editmode < EDIT_PATTERN) editmode = EDIT_NAMES;
  943.     break;
  944.  
  945.     case KEY_F1:
  946.     initsong(esnum, PLAY_BEGINNING);
  947.     followplay = shiftpressed;
  948.     break;
  949.  
  950.     case KEY_F2:
  951.     initsong(esnum, PLAY_POS);
  952.     followplay = shiftpressed;
  953.     break;
  954.  
  955.     case KEY_F3:
  956.     initsong(esnum, PLAY_PATTERN);
  957.     followplay = shiftpressed;
  958.     break;
  959.  
  960.     case KEY_F4:
  961.     if (shiftpressed) 
  962.       mutechannel(epchn);
  963.     else
  964.     stopsong();
  965.     break;
  966.  
  967.     case KEY_F5:
  968.     if (!shiftpressed)
  969.       editmode = EDIT_PATTERN;
  970.     else prevmultiplier();
  971.     break;
  972.  
  973.     case KEY_F6:
  974.     if (!shiftpressed)
  975.       editmode = EDIT_ORDERLIST;
  976.     else nextmultiplier();
  977.     break;
  978.  
  979.     case KEY_F7:
  980.     if (!shiftpressed)
  981.     {
  982.       if (editmode == EDIT_INSTRUMENT)
  983.         editmode = EDIT_TABLES;
  984.       else
  985.         editmode = EDIT_INSTRUMENT;
  986.     }
  987.     else editadsr();
  988.     break;
  989.  
  990.     case KEY_F8:
  991.     if (!shiftpressed)
  992.       editmode = EDIT_NAMES;
  993.     else
  994.     {
  995.       sidmodel ^= 1;
  996.       sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate);
  997.     }
  998.     break;
  999.  
  1000.     case KEY_F9:
  1001.     relocator();
  1002.     break;
  1003.  
  1004.     case KEY_F10:
  1005.     load();
  1006.     break;
  1007.  
  1008.     case KEY_F11:
  1009.     save();
  1010.     break;
  1011.   }
  1012. }
  1013.  
  1014. void load(void)
  1015. {
  1016.   if ((editmode != EDIT_INSTRUMENT) && (editmode != EDIT_TABLES))
  1017.   {
  1018.     if (fileselector(songfilename, songpath, songfilter, "LOAD SONG", 0))
  1019.       loadsong();
  1020.   }
  1021.   else
  1022.   {
  1023.     if (einum)
  1024.     {
  1025.       if (fileselector(instrfilename, instrpath, instrfilter, "LOAD INSTRUMENT", 0))
  1026.         loadinstrument();
  1027.     }
  1028.   }
  1029.   key = 0;
  1030.   rawkey = 0;
  1031. }
  1032.  
  1033. void save(void)
  1034. {
  1035.   if ((editmode != EDIT_INSTRUMENT) && (editmode != EDIT_TABLES))
  1036.   {
  1037.     int done = 0;
  1038.  
  1039.     // Repeat until quit or save successful
  1040.     while (!done)
  1041.     {
  1042.       if (strlen(loadedsongfilename)) strcpy(songfilename, loadedsongfilename);
  1043.       if (fileselector(songfilename, songpath, songfilter, "SAVE SONG", 3))
  1044.         done = savesong();
  1045.       else done = 1;
  1046.     }
  1047.   }
  1048.   else
  1049.   {
  1050.     if (einum)
  1051.     {
  1052.       int done = 0;
  1053.       int useinstrname = 0;
  1054.       char tempfilename[MAX_FILENAME];
  1055.  
  1056.       // Repeat until quit or save successful
  1057.       while (!done)
  1058.       {
  1059.         if ((!strlen(instrfilename)) && (strlen(instr[einum].name)))
  1060.         {
  1061.           useinstrname = 1;
  1062.           strcpy(instrfilename, instr[einum].name);
  1063.           strcat(instrfilename, ".ins");
  1064.           strcpy(tempfilename, instrfilename);
  1065.         }
  1066.  
  1067.         if (fileselector(instrfilename, instrpath, instrfilter, "SAVE INSTRUMENT", 3))
  1068.           done = saveinstrument();
  1069.         else done = 1;
  1070.  
  1071.         if (useinstrname)
  1072.         {
  1073.           if (!strcmp(tempfilename, instrfilename))
  1074.             memset(instrfilename, 0, sizeof instrfilename);
  1075.         }
  1076.       }
  1077.     }
  1078.   }
  1079.   key = 0;
  1080.   rawkey = 0;
  1081. }
  1082.  
  1083. void quit(void)
  1084. {
  1085.   if ((!shiftpressed) || (mouseb))
  1086.   {
  1087.     printtextcp(49, 36, 15, "Really Quit (y/n)?");
  1088.     waitkey();
  1089.     printblank(20, 36, 58);
  1090.     if ((key == 'y') || (key == 'Y')) exitprogram = 1;
  1091.   }
  1092.   key = 0;
  1093.   rawkey = 0;
  1094. }
  1095.  
  1096. void clear(void)
  1097. {
  1098.   int cs = 0;
  1099.   int cp = 0;
  1100.   int ci = 0;
  1101.   int ct = 0;
  1102.   int cn = 0;
  1103.  
  1104.   printtextcp(49, 36, 15, "Optimize everything (y/n)?");
  1105.   waitkey();
  1106.   printblank(20, 36, 58);
  1107.   if ((key == 'y') || (key == 'Y'))
  1108.   {
  1109.     optimizeeverything(1, 1);
  1110.     key = 0;
  1111.     rawkey = 0;
  1112.     return;
  1113.   }
  1114.  
  1115.   printtextcp(49, 36, 15, "Clear orderlists (y/n)?");
  1116.   waitkey();
  1117.   printblank(20, 36, 58);
  1118.   if ((key == 'y') || (key == 'Y')) cs = 1;
  1119.  
  1120.   printtextcp(49, 36, 15, "Clear patterns (y/n)?");
  1121.   waitkey();
  1122.   printblank(20, 36, 58);
  1123.   if ((key == 'y') || (key == 'Y')) cp = 1;
  1124.  
  1125.   printtextcp(49, 36, 15, "Clear instruments (y/n)?");
  1126.   waitkey();
  1127.   printblank(20, 36, 58);
  1128.   if ((key == 'y') || (key == 'Y')) ci = 1;
  1129.  
  1130.   printtextcp(49, 36, 15, "Clear tables (y/n)?");
  1131.   waitkey();
  1132.   printblank(20, 36, 58);
  1133.   if ((key == 'y') || (key == 'Y')) ct = 1;
  1134.  
  1135.   printtextcp(49, 36, 15, "Clear songname (y/n)?");
  1136.   waitkey();
  1137.   printblank(20, 36, 58);
  1138.   if ((key == 'y') || (key == 'Y')) cn = 1;
  1139.  
  1140.   if (cp == 1)
  1141.   {
  1142.     int selectdone = 0;
  1143.     int olddpl = defaultpatternlength;
  1144.  
  1145.     printtext(40, 36, 15,"Pattern length:");
  1146.     while (!selectdone)
  1147.     {
  1148.       sprintf(textbuffer, "%02d ", defaultpatternlength);
  1149.       printtext(55, 36, 15, textbuffer);
  1150.       waitkey();
  1151.       switch(rawkey)
  1152.       {
  1153.         case KEY_LEFT:
  1154.         defaultpatternlength -= 7;
  1155.         case KEY_DOWN:
  1156.         defaultpatternlength--;
  1157.         if (defaultpatternlength < 1) defaultpatternlength = 1;
  1158.         break;
  1159.  
  1160.         case KEY_RIGHT:
  1161.         defaultpatternlength += 7;
  1162.         case KEY_UP:
  1163.         defaultpatternlength++;
  1164.         if (defaultpatternlength > MAX_PATTROWS) defaultpatternlength = MAX_PATTROWS;
  1165.         break;
  1166.  
  1167.         case KEY_ESC:
  1168.         defaultpatternlength = olddpl;
  1169.         selectdone = 1;
  1170.         break;
  1171.  
  1172.         case KEY_ENTER:
  1173.         selectdone = 1;
  1174.         break;
  1175.       }
  1176.     }
  1177.     printblank(20, 36, 58);
  1178.   }
  1179.  
  1180.   if (cs | cp | ci | ct | cn)
  1181.     memset(songfilename, 0, sizeof songfilename);
  1182.   clearsong(cs, cp, ci, ct, cn);
  1183.  
  1184.   key = 0;
  1185.   rawkey = 0;
  1186. }
  1187.  
  1188. void editadsr(void)
  1189. {
  1190.   eamode = 1;
  1191.   eacolumn = 0;
  1192.  
  1193.   for (;;)
  1194.   {
  1195.     waitkeymouse();
  1196.  
  1197.     if (win_quitted)
  1198.     {
  1199.       exitprogram = 1;
  1200.       key = 0;
  1201.       rawkey = 0;
  1202.       return;
  1203.     }
  1204.  
  1205.     if (hexnybble >= 0)
  1206.     {
  1207.       switch(eacolumn)
  1208.       {
  1209.         case 0:
  1210.         adparam &= 0x0fff;
  1211.         adparam |= hexnybble << 12;
  1212.         break;
  1213.  
  1214.         case 1:
  1215.         adparam &= 0xf0ff;
  1216.         adparam |= hexnybble << 8;
  1217.         break;
  1218.  
  1219.         case 2:
  1220.         adparam &= 0xff0f;
  1221.         adparam |= hexnybble << 4;
  1222.         break;
  1223.  
  1224.         case 3:
  1225.         adparam &= 0xfff0;
  1226.         adparam |= hexnybble;
  1227.         break;
  1228.       }
  1229.       eacolumn++;
  1230.     }
  1231.  
  1232.     switch(rawkey)
  1233.     {
  1234.       case KEY_F7:
  1235.       if (!shiftpressed) break;
  1236.  
  1237.       case KEY_ESC:
  1238.       case KEY_ENTER:
  1239.       case KEY_TAB:
  1240.       eamode = 0;
  1241.       key = 0;
  1242.       rawkey = 0;
  1243.       return;
  1244.  
  1245.       case KEY_BACKSPACE:
  1246.       if (!eacolumn) break;
  1247.       case KEY_LEFT:
  1248.       eacolumn--;
  1249.       break;
  1250.  
  1251.       case KEY_RIGHT:
  1252.       eacolumn++;
  1253.     }
  1254.     eacolumn &= 3;
  1255.  
  1256.     if ((mouseb) && (!prevmouseb))
  1257.     {
  1258.       eamode = 0;
  1259.       return;
  1260.     }
  1261.   }
  1262. }
  1263.  
  1264. void getparam(FILE *handle, unsigned *value)
  1265. {
  1266.   char *configptr;
  1267.  
  1268.   for (;;)
  1269.   {
  1270.     if (feof(handle)) return;
  1271.     fgets(configbuf, MAX_PATHNAME, handle);
  1272.     if ((configbuf[0]) && (configbuf[0] != ';') && (configbuf[0] != ' ') && (configbuf[0] != 13) && (configbuf[0] != 10)) break;
  1273.   }
  1274.  
  1275.   configptr = configbuf;
  1276.   if (*configptr == '$')
  1277.   {
  1278.     *value = 0;
  1279.     configptr++;
  1280.     for (;;)
  1281.     {
  1282.       char c = tolower(*configptr++);
  1283.       int h = -1;
  1284.  
  1285.       if ((c >= 'a') && (c <= 'f')) h = c - 'a' + 10;
  1286.       if ((c >= '0') && (c <= '9')) h = c - '0';
  1287.  
  1288.       if (h >= 0)
  1289.       {
  1290.         *value *= 16;
  1291.         *value += h;
  1292.       }
  1293.       else break;
  1294.     }
  1295.   }
  1296.   else
  1297.   {
  1298.     *value = 0;
  1299.     for (;;)
  1300.     {
  1301.       char c = tolower(*configptr++);
  1302.       int d = -1;
  1303.  
  1304.       if ((c >= '0') && (c <= '9')) d = c - '0';
  1305.  
  1306.       if (d >= 0)
  1307.       {
  1308.         *value *= 10;
  1309.         *value += d;
  1310.       }
  1311.       else break;
  1312.     }
  1313.   }
  1314. }
  1315.  
  1316. void getfloatparam(FILE *handle, float *value)
  1317. {
  1318.   char *configptr;
  1319.  
  1320.   for (;;)
  1321.   {
  1322.     if (feof(handle)) return;
  1323.     fgets(configbuf, MAX_PATHNAME, handle);
  1324.     if ((configbuf[0]) && (configbuf[0] != ';') && (configbuf[0] != ' ') && (configbuf[0] != 13) && (configbuf[0] != 10)) break;
  1325.   }
  1326.  
  1327.   configptr = configbuf;
  1328.   *value = 0.0f;
  1329.   sscanf(configptr, "%f", value);
  1330. }
  1331.  
  1332. void prevmultiplier(void)
  1333. {
  1334.   if (multiplier > 0)
  1335.   {
  1336.     multiplier--;
  1337.     sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate);
  1338.   }
  1339. }
  1340.  
  1341. void nextmultiplier(void)
  1342. {
  1343.   if (multiplier < 16)
  1344.   {
  1345.     multiplier++;
  1346.     sound_init(b, mr, writer, hardsid, sidmodel, ntsc, multiplier, catweasel, interpolate, customclockrate);
  1347.   }
  1348. }
  1349.