home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / dos / prg / midas / midp.c < prev    next >
C/C++ Source or Header  |  1994-08-06  |  37KB  |  1,391 lines

  1. /*      MIDP.C
  2.  *
  3.  * MIDAS Module Player v0.42
  4.  *
  5.  * Copyright 1994 Petteri Kangaslampi and Jarno Paananen
  6.  *
  7.  * This file is part of the MIDAS Sound System, and may only be
  8.  * used, modified and distributed under the terms of the MIDAS
  9.  * Sound System license, LICENSE.TXT. By continuing to use,
  10.  * modify or distribute this file you indicate that you have
  11.  * read the license and understand and accept it fully.
  12. */
  13.  
  14.  
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <dir.h>
  18. #include <conio.h>
  19. #include <dos.h>
  20. #include <alloc.h>
  21. #include <string.h>
  22. #include <process.h>
  23. #include <ctype.h>
  24. #include <time.h>
  25.  
  26. #include "midas.h"
  27. #include "vgatext.h"
  28.  
  29. char            *title =
  30. "MIDAS Module Player v0.42, Copyright 1994 Petteri Kangaslampi & "
  31. "Jarno Paananen\n";
  32.  
  33. char            *usage =
  34. "Usage:\tMIDP\t[options] <filename> [options]\n\n"
  35. "Options:\n"
  36. "\t-sx\tForce Sound Device x (1 = GUS, 2 = PAS, 3 = WSS, 4 = SB,\n"
  37. "\t\t5 = No Sound)\n"
  38. "\t-pxxx\tForce I/O port xxx (hex) for Sound Device\n"
  39. "\t-ix\tForce IRQ x for Sound Device\n"
  40. "\t-dx\tForce DMA channel x for Sound Device\n"
  41. "\t-mxxxxx\tSet mixing rate to xxxxx Hz\n"
  42. "\t-e\tDisable EMS usage\n"
  43. "\t-t\tDisable ProTracker BPM tempos\n"
  44. "\t-S\tJump immediately to DOS shell\n"
  45. "\t-u\tEnable Surround\n"
  46. "\t-oxxx\tForce output mode (8 = 8-bit, 1 = 16-bit, s = stereo, m = mono)\n"
  47. "\t-v\tDisable real VU-meters\n"
  48. "\t-c\tDisable screen synchronization\n"
  49. "\t-C\tEnable screen synchronization also in DOS shell\n"
  50. "\t-Lx\tNumber of song loops before next song / restart\n"
  51. "\t-O\tScramble module order\n"
  52. "\t-nxx\tDefault panning";
  53.  
  54. char            *scrtop =
  55. "\xFF\x4FMIDAS Module Player v0.42, Copyright 1994 Petteri Kangaslampi and "
  56. "Jarno Paananen"
  57. "\xFF\x0F▐\xFF\x7F\x7F\x4E▀\xFF\x08▌"
  58. "\xFF\x0F▐\xFF\x78┌\x7F\x4C─\xFF\x7F┐\xFF\x08▌"
  59. "\xFF\x0F▐\xFF\x78│\xFF\x7FModule:\x7F\x1F Type:\x7F\x21 │\xFF\x08▌"
  60. "\xFF\x0F▐\xFF\x78│\xFF\x7FMixing Rate:\x7F\x0C Mixing Mode:\x7F\x1B Time:"
  61. "\x7F\x08 │\xFF\x08▌"
  62. "\xFF\x0F▐\xFF\x78│\xFF\x7FLength:    Position:    Pattern:    Row:    Tempo:"
  63. "     Speed:    Vol:       │\xFF\x08▌"
  64. "\xFF\x0F▐\xFF\x78└\xFF\x7F\x7F\x4C─┘\xFF\x08▌";
  65.  
  66.  
  67. #define MAXNAMES 256                    /* maximum number of file names */
  68.  
  69.  
  70. ulong           free1, free2;
  71. mpModule        *mod;
  72. int             numChans;
  73. ushort          scrSync;
  74. int             immShell = 0;
  75. int             sync = 1, shellSync = 0;
  76. volatile ulong  frameCnt;
  77. mpInformation   *info;
  78. ushort          actch = 0;
  79. char            masterVol = 64;
  80. char            exitFlag = 0;
  81. char            fadeOut;
  82. short           loopCnt = 0;
  83. ushort          defPanning;
  84. int             useDefPanning = 0;
  85. char            *fNames[MAXNAMES];
  86. time_t          startTime, pauseTime = 0, pauseStart;
  87. int             muted = 0, paused = 0;
  88. int             numFNames;
  89. int             fileNumber;
  90. int             noNext;
  91. int             isArchive;              /* is file archive */
  92. int             arcType;                /* archive type */
  93. int             decompressed;           /* 1 if already decompressed */
  94. char            *decompName = NULL;     /* name of decompressed module file */
  95.  
  96. char            *tempDir;               /* temporary directory for
  97.                                            decompression */
  98. int             scrambleOrder = 0;
  99.  
  100. char            *modTypes[2] = {        /* module type strings */
  101.     { "Scream Tracker ]I[" },
  102.     { "Protracker" } };
  103.  
  104. char            *notes[13] = {          /* note strings */
  105.     "C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"};
  106.  
  107. #define DECOMPMEM 350000                /* number of bytes of memory required
  108.                                            for decompression */
  109. #define NUMARCEXTS 3
  110. char            *arcExtensions[NUMARCEXTS] =
  111.     { ".ZIP", ".MDZ", ".S3Z" };
  112.  
  113. char            Channels[32];           /* channel mute flags */
  114. uchar           oldInsts[32];           /* old instrument values for all
  115.                                            channels */
  116. struct text_info  textInfo;             /* text mode information */
  117.  
  118.  
  119. /****************************************************************************\
  120. *
  121. * Function:     void Error(char *msg)
  122. *
  123. * Description:  Prints an error message to stderr, uninitializes MIDAS and
  124. *               exits to DOS
  125. *
  126. * Input:        char *msg               error message
  127. *
  128. \****************************************************************************/
  129.  
  130. void Error(char *msg)
  131. {
  132.     textmode(C80);
  133.     fprintf(stderr, "Error: %s\n", msg);
  134.     midasClose();
  135.     exit(EXIT_FAILURE);
  136. }
  137.  
  138.  
  139.  
  140.  
  141. /****************************************************************************\
  142. *
  143. * Function:     void toggleChannel(char channel)
  144. *
  145. * Description:  Toggles a channel mute on/off
  146. *
  147. * Input:        char channel            channel number
  148. *
  149. \****************************************************************************/
  150.  
  151. void toggleChannel(char channel)
  152. {
  153.     if ( channel < numChans )
  154.     {
  155.         Channels[channel] ^= 1;
  156.         SD->MuteChannel(channel, Channels[channel]);
  157.     }
  158. }
  159.  
  160.  
  161.  
  162.  
  163. /****************************************************************************\
  164. *
  165. * Function:     void prevr(void)
  166. *
  167. * Description:  preVR() routine for Timer, increments frame counter
  168. *
  169. \****************************************************************************/
  170.  
  171. void prevr(void)
  172. {
  173.     frameCnt++;
  174. }
  175.  
  176.  
  177.  
  178.  
  179. /****************************************************************************\
  180. *
  181. * Function:     void showheap(void)
  182. *
  183. * Description:  Displays amount of free memory and amount of memory used,
  184. *               and checks for heap corruption.
  185. *
  186. \****************************************************************************/
  187.  
  188. void showheap(void)
  189. {
  190.     struct heapinfo hinfo;
  191.  
  192.  
  193.     free2 = coreleft();
  194.     cprintf("%lu bytes memory free - %lu bytes used.\r\n",
  195.         free2, free1-free2);
  196.  
  197.     if ( heapcheck() != _HEAPOK )
  198.         cputs("HEAP CORRUPTED - PREPARE FOR SYSTEM CRASH!\r\n");
  199. }
  200.  
  201.  
  202.  
  203.  
  204. /****************************************************************************\
  205. *
  206. * Function:     void dumpheap(void)
  207. *
  208. * Description:  Lists all blocks in heap
  209. *
  210. \****************************************************************************/
  211.  
  212. void dumpheap(void)
  213. {
  214.     struct heapinfo hinfo;
  215.  
  216.     if ( heapcheck() == _HEAPOK )
  217.         cputs("Heap OK\r\n");
  218.     else
  219.         cputs("HEAP CORRUPTED!\r\n");
  220.  
  221.     hinfo.ptr = NULL;
  222.  
  223.     while ( heapwalk(&hinfo) == _HEAPOK )
  224.     {
  225.         cprintf("%p: %06u - ", hinfo.ptr, hinfo.size);
  226.         if ( hinfo.in_use == 1 )
  227.             cprintf("USED\r\n");
  228.         else
  229.             cprintf("FREE\r\n");
  230.     }
  231. }
  232.  
  233.  
  234.  
  235.  
  236. /****************************************************************************\
  237. *
  238. * Function:     void dumpfree(void)
  239. *
  240. * Description:  Lists all free blocks in heap
  241. *
  242. \****************************************************************************/
  243.  
  244. void dumpfree(void)
  245. {
  246.     struct heapinfo hinfo;
  247.  
  248.     if ( heapcheck() == _HEAPOK )
  249.         cputs("Heap OK\r\n");
  250.     else
  251.         cputs("HEAP CORRUPTED!\r\n");
  252.  
  253.     hinfo.ptr = NULL;
  254.  
  255.     while ( heapwalk(&hinfo) == _HEAPOK )
  256.     {
  257.         if ( hinfo.in_use != 1 )
  258.             cprintf("%p: %06u\r\n", hinfo.ptr, hinfo.size);
  259.     }
  260. }
  261.  
  262.  
  263.  
  264. /****************************************************************************\
  265. *
  266. * Function:     void ParseCmdLine(int argc, char *argv[])
  267. *
  268. * Description:  Parses command line options
  269. *
  270. * Input:        int argc                argc from main()
  271. *               char *argv[]            argv from main()
  272. *
  273. \****************************************************************************/
  274.  
  275. void ParseCmdLine(int argc, char *argv[])
  276. {
  277.     int         i, error;
  278.  
  279.     numFNames = 0;
  280.  
  281.     for ( i = 1; i < argc; i++ )
  282.     {
  283.         if ( argv[i][0] == '-' )
  284.         {
  285.             switch ( argv[i][1] )
  286.             {
  287.                 case 'S':
  288.                     immShell = 1;
  289.                     break;
  290.  
  291.                 case 'C':
  292.                     shellSync = 1;
  293.                     break;
  294.  
  295.                 case 'c':
  296.                     sync = 0;
  297.                     break;
  298.  
  299.                 case 'L':
  300.                     loopCnt = atoi(&argv[i][2]);
  301.                     if ( loopCnt == 0 )
  302.                         loopCnt = 1;
  303.                     break;
  304.  
  305.                 case 'O':
  306.                     scrambleOrder = 1;
  307.                     break;
  308.  
  309.                 case 'n':
  310.                     defPanning = atoi(&argv[i][2]);
  311.                     useDefPanning = 1;
  312.                     break;
  313.  
  314.                 default:
  315.                     midasParseOption(&argv[i][1]);
  316.                     break;
  317.             }
  318.         }
  319.         else
  320.         {
  321.             if ( numFNames >= MAXNAMES )
  322.                 Error("Too many file names");
  323.  
  324.             fNames[numFNames] = argv[i];
  325.             numFNames++;
  326.         }
  327.     }
  328. }
  329.  
  330.  
  331.  
  332.  
  333. /****************************************************************************\
  334. *
  335. * Function:     void InitMIDAS(void)
  336. *
  337. * Description:  Initializes MIDAS Sound System
  338. *
  339. \****************************************************************************/
  340.  
  341. void InitMIDAS(void)
  342. {
  343.     int         error;
  344.  
  345.     /* Get screen synchronization value if Timer should be synchronized to
  346.        screen: */
  347.     if ( sync )
  348.     {
  349.         if ( (error = tmrGetScrSync(&scrSync)) != OK )
  350.             midasError(errorMsg[error]);
  351.     }
  352.  
  353.     midasInit();
  354.  
  355.     /* Synchronize timer to screen, if synchronization is used. prevr() will
  356.        be called before each retrace */
  357.     if ( sync )
  358.     {
  359.         if ( (error = tmrSyncScr(scrSync, &prevr, NULL, NULL)) != OK )
  360.             midasError(errorMsg[error]);
  361.     }
  362.  
  363.     cprintf("MIDAS Sound System succesfully initialized.\r\n"
  364.         "Playing through %s,\r\nusing port %Xh, IRQ %i and DMA %i\r\n",
  365.         SD->ID, (unsigned) SD->port, (int) SD->IRQ, (int) SD->DMA);
  366.     showheap();
  367. }
  368.  
  369.  
  370.  
  371.  
  372.  
  373. /****************************************************************************\
  374. *
  375. * Function:     void CloseMIDAS(void)
  376. *
  377. * Description:  Uninitializes MIDAS Sound System
  378. *
  379. \****************************************************************************/
  380.  
  381. void CloseMIDAS(void)
  382. {
  383.     int         error;
  384.  
  385.     /* Remove screen synchronization if used: */
  386.     if ( sync )
  387.     {
  388.         if ( (error = tmrStopScrSync()) != OK )
  389.             midasError(errorMsg[error]);
  390.     }
  391.  
  392.     midasClose();
  393.  
  394.     cprintf("MIDAS Sound System succesfully uninitialized\r\n");
  395. }
  396.  
  397.  
  398.  
  399.  
  400. /****************************************************************************\
  401. *
  402. * Function:     void WaitVR(void)
  403. *
  404. * Description:  Waits for Vertical Retrace
  405. *
  406. \****************************************************************************/
  407.  
  408. void WaitVR(void)
  409. {
  410. asm     mov     dx,03DAh
  411. wvr:
  412. asm {   in      al,dx
  413.         test    al,8
  414.         jz      wvr
  415. }
  416. }
  417.  
  418.  
  419.  
  420.  
  421. /****************************************************************************\
  422. *
  423. * Function:     void WaitDE(void)
  424. *
  425. * Description:  Waits for Display Enable
  426. *
  427. \****************************************************************************/
  428.  
  429. void WaitDE(void)
  430. {
  431. asm     mov     dx,03DAh
  432. wde:
  433. asm {   in      al,dx
  434.         test    al,1
  435.         jnz     wde
  436. }
  437. }
  438.  
  439.  
  440.  
  441.  
  442. /****************************************************************************\
  443. *
  444. * Function:     void SetBorder(uchar color)
  445. *
  446. * Description:  Sets display border color
  447. *
  448. * Input:        uchar color             new border color
  449. *
  450. \****************************************************************************/
  451.  
  452. void SetBorder(uchar color)
  453. {
  454. asm {   mov     dx,03C0h
  455.         mov     al,31h
  456.         out     dx,al
  457.         mov     al,color
  458.         out     dx,al
  459. }
  460. }
  461.  
  462.  
  463.  
  464.  
  465. /****************************************************************************\
  466. *
  467. * Function:     void InitScreen(void)
  468. *
  469. * Description:  Initializes MIDAS Module Player screen
  470. *
  471. \****************************************************************************/
  472.  
  473. void InitScreen(void)
  474. {
  475.     frameCnt = 0;
  476.     textmode(C4350);
  477.     clrscr();
  478.     vgaWriteText(1, 1, scrtop);
  479.     vgaWriteText(1, 8, "\xFF\x0F▐\xFF\x78\x7F\x4E▄\xFF\x08▌");
  480.     window(1, 42, 80, 50);
  481.     gotoxy(1, 1);
  482.     textattr(0x07);
  483. }
  484.  
  485.  
  486.  
  487.  
  488. /****************************************************************************\
  489. *
  490. * Function:     void DrawScreen(void)
  491. *
  492. * Description:  Draws MIDAS Module Player screen. Used after module has
  493. *               been loaded.
  494. *
  495. \****************************************************************************/
  496.  
  497. void DrawScreen(void)
  498. {
  499.     int         i, x, y, len;
  500.     char        c;
  501.     char        str[32];
  502.     char        chstr[18];
  503.     ushort      mode, mixRate;
  504.     int         error;
  505.  
  506.     vgaWriteText(1, 1, scrtop);
  507.     vgaWriteText(1, 8, "\xFF\x0F▐\xFF\x78┌\x7F\x4C─\xFF\x7F┐\xFF\x08▌");
  508.     for ( i = 0; i < numChans; i++ )
  509.         vgaWriteText(1, 9+i, "\xFF\x0F▐\xFF\x78│\xFF\x70   │\x7F\x11 │   │  │"
  510.             "\x7F\x0E │\x7F\x20■\xFF\x7F│\xFF\x08▌");
  511.     y = 9+numChans;
  512.     vgaWriteText(1, y, "\xFF\x0F▐\xFF\x78└\xFF\x7F\x7F\x4C─┘\xFF\x08▌");
  513.     vgaWriteText(1, y+1, "\xFF\x0F▐\xFF\x78┌\x7F\x4C─\xFF\x7F┐\xFF\x08▌");
  514.     y+=2;
  515.     for ( ; y < 40; y++ )
  516.         vgaWriteText(1, y, "\xFF\x0F▐\xFF\x78│\xFF\x70\x7F\x25 │\x7F\x26 "
  517.             "\xFF\x7F│\xFF\x08▌");
  518.     vgaWriteText(1, 40, "\xFF\x0F▐\xFF\x78└\xFF\x7F\x7F\x4C─┘\xFF\x08▌");
  519.     vgaWriteText(1, 41, "\xFF\x0F▐\xFF\x78\x7F\x4E▄\xFF\x08▌");
  520.  
  521.     for ( i = 0; i < numChans; i++ )
  522.     {
  523.         if ( i < 9 )
  524.             c = i + '1';
  525.         else
  526.             c = i + 'A' - 9;
  527.         chstr[i] = c;
  528.     }
  529.     chstr[numChans] = 0;
  530.  
  531.     sprintf(&str[0], "%s Module", modTypes[mod->IDnum]);
  532.     vgaWriteStr(46, 4, str, 0x70, 33);
  533.  
  534.     vgaWriteStr(10, 4, &mod->songName[0], 0x70, 31);
  535.  
  536.     for ( i = 0; i < mod->numInsts; i++ )
  537.     {
  538.         x = 3;
  539.         y = 11 + numChans + i;
  540.         len = 33-numChans;
  541.         if ( y >= 40 )
  542.         {
  543.             x += 38;
  544.             y -= (29 - numChans);
  545.             len++;
  546.         }
  547.         if ( y < 40 )
  548.         {
  549.             vgaWriteStr(x, y, &chstr[0], 0x70, numChans);
  550.             x += numChans+1;
  551.             if(mod->instsUsed[i] == 1)
  552.             {
  553.                 vgaWriteByte(x, y, i+1, 0x70);
  554.                 vgaWriteStr(x+3, y, &mod->insts[i].iname[0], 0x70, len);
  555.             }
  556.             else
  557.             {
  558.                 vgaWriteByte(x, y, i+1, 0x78);
  559.                 vgaWriteStr(x+3, y, &mod->insts[i].iname[0], 0x78, len);
  560.             }
  561.         }
  562.     }
  563.  
  564.     vgaWriteByte(10, 6, mod->songLength, 0x70);
  565.  
  566.     if ( (error = SD->GetMixRate(&mixRate)) != OK )
  567.         midasError(errorMsg[error]);
  568.     sprintf(&str[0], "%uHz", mixRate);
  569.     vgaWriteStr(15, 5, str, 0x70, 7);
  570.  
  571.     if ( (error = SD->GetMode(&mode)) != OK )
  572.         midasError(errorMsg[error]);
  573.  
  574.     if ( mode & sd16bit )
  575.         strcpy(&str[0], "16-bit ");
  576.     else
  577.         strcpy(&str[0], "8-bit ");
  578.  
  579.     if ( mode & sdStereo )
  580.         strcat(&str[0], "Stereo ");
  581.     else
  582.         strcat(&str[0], "Mono ");
  583.  
  584.     if ( mode & sdHighQ )
  585.         strcat(&str[0], "High");
  586.     else
  587.         strcat(&str[0], "Normal");
  588.  
  589.     vgaWriteStr(39, 5, str, 0x70, 26);
  590. }
  591.  
  592.  
  593.  
  594.  
  595. /****************************************************************************\
  596. *
  597. * Function:     void UpdScreen(void)
  598. *
  599. * Description:  Updates MIDAS Module Player screen
  600. *
  601. \****************************************************************************/
  602.  
  603. void UpdScreen(void)
  604. {
  605.     char        str[32];
  606.     int         i, x, y;
  607.     char        *iname;
  608.     mpChanInfo  *chan;
  609.     int         numInsts;
  610.     short       pan;
  611.     ulong       rate;
  612.     int         error;
  613.     ushort      meter, pos;
  614.     time_t      currTime;
  615.     int         hour, min, sec;
  616.  
  617.     vgaWriteByte(23, 6, info->pos, 0x70);
  618.     vgaWriteByte(35, 6, info->pattern, 0x70);
  619.     vgaWriteByte(43, 6, info->row, 0x70);
  620.     sprintf(&str[0], "%3u", (unsigned) info->BPM);
  621.     vgaWriteStr(53, 6, &str[0], 0x70, 3);
  622.     vgaWriteByte(64, 6, info->speed, 0x70);
  623.     sprintf(&str[0], "%2u", masterVol);
  624.     vgaWriteStr(72, 6, &str[0], 0x70, 3);
  625.  
  626.     if ( !paused )
  627.     {
  628.         currTime = time(NULL) - startTime - pauseTime;
  629.         hour = currTime / 3600;
  630.         min = ((currTime - 3600*hour) / 60) % 60;
  631.         sec = currTime % 60;
  632.         sprintf(&str[0], "%2i:%02i:%02i", hour, min, sec);
  633.         vgaWriteStr(71, 5, &str[0], 0x70, 8);
  634.     }
  635.     else
  636.         vgaWriteStr(71, 5, "-PAUSED-", 0x70, 8);
  637.  
  638.     numInsts = mod->numInsts;
  639.  
  640.     for ( i = 0; i < numChans; i++ )
  641.     {
  642.         chan = &info->chans[i];
  643.  
  644.         if ( oldInsts[i] != 0 )
  645.         {
  646.             x = 3+i;
  647.             y = 11 + numChans + (oldInsts[i]-1);
  648.             if ( y >= 40 )
  649.             {
  650.                 x += 38;
  651.                 y -= (29 - numChans);
  652.             }
  653.             if ( y < 40 )
  654.             {
  655.                 str[1] = 0;
  656.                 if ( i < 9 )
  657.                     str[0] = i + '1';
  658.                 else
  659.                     str[0] = (i-9) + 'A';
  660.                 vgaWriteStr(x, y, &str[0], 0x70, 1);
  661.             }
  662.         }
  663.  
  664.         if ( (Channels[i] == 0) && (!muted) && (!paused) )
  665.         {
  666.             if ( (chan->instrument != 0) && (chan->instrument <= numInsts) )
  667.             {
  668.                 x = 3+i;
  669.                 y = 11 + numChans + (chan->instrument-1);
  670.                 if ( y >= 40 )
  671.                 {
  672.                     x += 38;
  673.                     y -= (29 - numChans);
  674.                 }
  675.                 if ( y < 40 )
  676.                 {
  677.                     str[1] = 0;
  678.                     if ( i < 9 )
  679.                         str[0] = i + '1';
  680.                     else
  681.                         str[0] = (i-9) + 'A';
  682.                     vgaWriteStr(x, y, &str[0], 0x7E, 1);
  683.                 }
  684.  
  685.                 vgaWriteByte(7, 9+i, chan->instrument, 0x70);
  686.  
  687.                 iname = &mod->insts[chan->instrument-1].iname[0];
  688.  
  689.                 vgaWriteStr(10, 9+i, iname, 0x70, 14);
  690.  
  691.                 oldInsts[i] = chan->instrument;
  692.  
  693.                 if ( realVU )
  694.                 {
  695.                     if ( (error = SD->GetRate(i, &rate)) != OK )
  696.                         midasError(errorMsg[error]);
  697.  
  698.                     if ( (error = SD->GetPosition(i, &pos)) != OK )
  699.                         midasError(errorMsg[error]);
  700.  
  701.                     if ( rate != 0 )
  702.                     {
  703.                         error = vuMeter(
  704.                             mod->insts[chan->instrument - 1].sdInstHandle,
  705.                             rate, pos, (chan->volume * masterVol) / 64,
  706.                             &meter);
  707.                         if ( error != OK )
  708.                             midasError(errorMsg[error]);
  709.                     }
  710.                     else
  711.                         meter = 0;
  712.                 }
  713.                 else
  714.                     meter = chan->volumebar;
  715.  
  716.                 if ( (chan->note < 254) && ( (chan->note & 15) < 12) )
  717.                 {
  718.                     strcpy(&str[0], notes[chan->note & 15]);
  719.                     str[2] = (chan->note >> 4) + '0';
  720.                     str[3] = 0;
  721.                     vgaWriteStr(25, 9+i, &str[0], 0x70, 3);
  722.                 }
  723.                 else
  724.                     vgaWriteStr(25, 9+i, "", 0x70, 3);
  725.             }
  726.             else
  727.                 meter = 0;
  728.  
  729.             vgaWriteByte(29, 9+i, chan->volume, 0x70);
  730.             if ( chan->commandname[0] != 0 )
  731.             {
  732.                 sprintf(&str[0], "%s %02X", chan->commandname,
  733.                     (int) chan->infobyte);
  734.                 vgaWriteStr(32, 9+i, &str[0], 0x70, 14);
  735.             }
  736.             else
  737.                 vgaWriteStr(32, 9+i, "", 0x70, 14);
  738.  
  739.             vgaDrawMeter(47, 9+i, meter >> 1, 32, '■', 0x7A, 0x70);
  740.         }
  741.         else
  742.             vgaWriteText(7, 9+i,
  743.                 "\xFF\x70\x7F\x11 │   │  │\x7F\x0E │\x7F\x20■");
  744.  
  745.         if ( (error = SD->GetPanning(i, &pan)) != OK )
  746.             midasError(errorMsg[error]);
  747.  
  748.         switch ( pan )
  749.         {
  750.             case panLeft:
  751.                 strcpy(&str[0], "LFT");
  752.                 break;
  753.  
  754.             case panRight:
  755.                 strcpy(&str[0], "RGT");
  756.                 break;
  757.  
  758.             case panMiddle:
  759.                 strcpy(&str[0], "MID");
  760.                 break;
  761.  
  762.             case panSurround:
  763.                 strcpy(&str[0], "SUR");
  764.                 break;
  765.  
  766.             default:
  767.                 sprintf(&str[0], "%3i", pan);
  768.         }
  769.         if ( i != actch )
  770.             vgaWriteStr(3, i+9, &str[0], 0x70, 3);
  771.         else
  772.             vgaWriteStr(3, i+9, &str[0], 0x07, 3);
  773.     }
  774. }
  775.  
  776.  
  777.  
  778. /****************************************************************************\
  779. *
  780. * Function:     void WaitFrame(void)
  781. *
  782. * Description:  Waits for next frame, either by using VGA hardware or by
  783. *               waiting for the frame counter to change, depending on
  784. *               whether screen synchronization is used or not
  785. *
  786. \****************************************************************************/
  787.  
  788. void WaitFrame(void)
  789. {
  790.     ulong       oldcnt = frameCnt;
  791.  
  792.     if ( sync )
  793.         while ( frameCnt == oldcnt );
  794.     else
  795.     {
  796.         WaitDE();
  797.         WaitVR();
  798.     }
  799. }
  800.  
  801.  
  802.  
  803. /****************************************************************************\
  804. *
  805. * Function:     void DOSshell(void)
  806. *
  807. * Description:  Jumps to DOS shell
  808. *
  809. \****************************************************************************/
  810.  
  811. void DOSshell(void)
  812. {
  813.     char        *comspec;
  814.     char        *dir;
  815.     int         disk, error;
  816.  
  817.     if ( (!shellSync) && sync )
  818.         tmrStopScrSync();
  819.  
  820.     /* restore old text mode: */
  821.     textmode(textInfo.currmode);
  822.  
  823.     if ( (error = memAlloc(MAXDIR, (void**) &dir)) != OK )
  824.         Error(errorMsg[error]);
  825.  
  826.     disk = getdisk();
  827.     getcurdir(0, dir);
  828.  
  829.     comspec=getenv("COMSPEC");
  830.     spawnl(P_WAIT, comspec, NULL);
  831.  
  832.     /* save text mode information, including mode: */
  833.     gettextinfo(&textInfo);
  834.  
  835.     InitScreen();
  836.     DrawScreen();
  837.  
  838.     setdisk(disk);
  839.     chdir("\\");
  840.     chdir(dir);
  841.  
  842.     if ( (error = memFree(dir)) != OK )
  843.         Error(errorMsg[error]);
  844.  
  845.     if ( (!shellSync) && sync )
  846.         tmrSyncScr(scrSync, &prevr, NULL, NULL);
  847.     showheap();
  848.  
  849. }
  850.  
  851.  
  852.  
  853.  
  854. /****************************************************************************\
  855. *
  856. * Function:     void prepare(int fNum)
  857. *
  858. * Description:  Prepares for playing a module file, setting variables
  859. *               isArchive and decompressed as necessary.
  860. *
  861. * Input:        int fNum                number of file name
  862. *
  863. \****************************************************************************/
  864.  
  865. void prepare(int fNum)
  866. {
  867.     int         i;
  868.     char        ext[_MAX_EXT];
  869.  
  870.     /* get file name extension: */
  871.     fnsplit(fNames[fNum], NULL, NULL, NULL, &ext[0]);
  872.  
  873.     isArchive = 0;
  874.  
  875.     /* Search through known archive extensions. If a match is found, the file
  876.        is an archive. */
  877.     for ( i = 0; i < NUMARCEXTS; i++ )
  878.         if ( stricmp(&ext[0], arcExtensions[i]) == 0 )
  879.             isArchive = 1;
  880.  
  881.     decompressed = 0;
  882. }
  883.  
  884.  
  885.  
  886.  
  887. /****************************************************************************\
  888. *
  889. * Function:     void decompress(char *fileName)
  890. *
  891. * Description:  Decompresses a file and sets decompName to decompressed file
  892. *               name. The archive is assumed to contain a single file, with
  893. *               the same name as the archive and any extension.
  894. *
  895. * Input:        char *fileName          pointer to archive file name
  896. *
  897. \****************************************************************************/
  898.  
  899. void decompress(char *fileName)
  900. {
  901.     int         error;
  902.     char        name[_MAX_FNAME];
  903.     struct ffblk ffb;
  904.  
  905.     fnsplit(fileName, NULL, NULL, &name[0], NULL);
  906.  
  907.     if ( decompName == NULL )
  908.     {
  909.         if ( (error = memAlloc(_MAX_PATH, (void**) &decompName)) != OK )
  910.             Error(errorMsg[error]);
  911.     }
  912.  
  913.     if ( spawnlp(P_WAIT, "PKUNZIP", "", fileName, tempDir, NULL) != OK )
  914.         Error("PKUNZIP failed");
  915.  
  916.     strcpy(decompName, tempDir);
  917.     strcat(decompName, &name[0]);
  918.     strcat(decompName, ".*");
  919.  
  920.     if ( findfirst(decompName, &ffb, 0) != 0 )
  921.         Error("Unable to find decompressed file");
  922.  
  923.     strcpy(decompName, tempDir);
  924.     strcat(decompName, &ffb.ff_name[0]);
  925.     decompressed = 1;
  926. }
  927.  
  928.  
  929.  
  930.  
  931. /****************************************************************************\
  932. *
  933. * Function:     void HandleKeys(void)
  934. *
  935. * Description:  Handles the keypresses
  936. *
  937. \****************************************************************************/
  938.  
  939. void HandleKeys(void)
  940. {
  941.     char        key;
  942.     short       panning;
  943.     int         error;
  944.     FILE        *sf;
  945.  
  946.     key = getch();
  947.  
  948.     if ( !key )
  949.     {
  950.         switch ( getch() )
  951.         {
  952.             case 45:            /* Alt-X */
  953.                 exitFlag = 1;
  954.                 break;
  955.  
  956.             case 77:            /* Right arrow */
  957.                 MP->SetPosition(info->pos + 1);
  958.                 break;
  959.  
  960.             case 75:            /* Left arrow */
  961.                 MP->SetPosition(info->pos - 1);
  962.                 break;
  963.  
  964.             case 72:            /* Up arrow */
  965.                 if ( actch > 0 )
  966.                     actch--;
  967.                 break;
  968.  
  969.             case 80:            /* Down arrow */
  970.                 if ( actch < (numChans-1) )
  971.                     actch++;
  972.                 break;
  973.         }
  974.     }
  975.     else
  976.     {
  977.         switch ( toupper(key) )
  978.         {
  979.             case 27:
  980.                 fadeOut = 1;
  981.                 noNext = 1;
  982.                 break;
  983.  
  984.             case '+':
  985.                 if ( masterVol != 64 )
  986.                 {
  987.                     masterVol++;
  988.                     MP->SetMasterVolume(masterVol);
  989.                 }
  990.                 break;
  991.  
  992.             case '-':
  993.                 if ( masterVol != 0 )
  994.                 {
  995.                     masterVol--;
  996.                     MP->SetMasterVolume(masterVol);
  997.                 }
  998.                 break;
  999.  
  1000.             case 'D':
  1001.                 DOSshell();
  1002.                 break;
  1003.  
  1004.             case '0': toggleChannel(9); break;
  1005.             case '1': toggleChannel(0); break;
  1006.             case '2': toggleChannel(1); break;
  1007.             case '3': toggleChannel(2); break;
  1008.             case '4': toggleChannel(3); break;
  1009.             case '5': toggleChannel(4); break;
  1010.             case '6': toggleChannel(5); break;
  1011.             case '7': toggleChannel(6); break;
  1012.             case '8': toggleChannel(7); break;
  1013.             case '9': toggleChannel(8); break;
  1014.  
  1015.             case 'T': toggleChannel(actch); break;
  1016.  
  1017.             case ',':
  1018.                 if ( (error = SD->GetPanning(actch, &panning)) != OK )
  1019.                     midasError(errorMsg[error]);
  1020.                 if ( (panning > -64) && (panning <= 64) )
  1021.                     if ( (error = SD->SetPanning(actch, panning-1)) != OK )
  1022.                         midasError(errorMsg[error]);
  1023.                 break;
  1024.  
  1025.             case '.':
  1026.                 if ( (error = SD->GetPanning(actch, &panning)) != OK )
  1027.                     midasError(errorMsg[error]);
  1028.                 if ( (panning < 64) && (panning >= -64) )
  1029.                     if ( (error = SD->SetPanning(actch, panning+1)) != OK )
  1030.                         midasError(errorMsg[error]);
  1031.                 break;
  1032.  
  1033.             case 'M':
  1034.                 if ( (error = SD->SetPanning(actch, panMiddle)) != OK )
  1035.                     midasError(errorMsg[error]);
  1036.                 break;
  1037.  
  1038.             case 'U':
  1039.                 if ( (error = SD->SetPanning(actch, panSurround)) != OK )
  1040.                     midasError(errorMsg[error]);
  1041.                 break;
  1042.  
  1043.             case 'L':
  1044.                 if ( (error = SD->SetPanning(actch, panLeft)) != OK )
  1045.                     midasError(errorMsg[error]);
  1046.                 break;
  1047.  
  1048.             case 'R':
  1049.                 if ( (error = SD->SetPanning(actch, panRight)) != OK )
  1050.                     midasError(errorMsg[error]);
  1051.                 break;
  1052.  
  1053.             case 'F':
  1054.                 dumpfree();
  1055.                 break;
  1056.  
  1057.             case 'H':
  1058.                 dumpheap();
  1059.                 break;
  1060.  
  1061.             case 'N':
  1062.                 fadeOut = 1;
  1063.                 break;
  1064.  
  1065. #ifdef DEBUG
  1066. /*
  1067.             case 'B':
  1068.                 sf = fopen("MIDPSCR.BIN", "wb");
  1069.                 fwrite(MK_FP(0xB800, 0), 8000, 1, sf);
  1070.                 fclose(sf);
  1071.                 break;
  1072. */
  1073. #endif
  1074.             case 'P':
  1075.                 paused ^= 1;
  1076.                 SD->Pause(paused);
  1077.                 if ( paused == 1 )
  1078.                     pauseStart = time(NULL);
  1079.                 else
  1080.                     pauseTime += time(NULL) - pauseStart;
  1081.                 break;
  1082.  
  1083.             case ' ':
  1084.                 muted ^= 1;
  1085.                 SD->Mute(muted);
  1086.                 break;
  1087.         }
  1088.     }
  1089. }
  1090.  
  1091.  
  1092.  
  1093.  
  1094. /****************************************************************************\
  1095. *
  1096. * Function:     void PlayModule(char *fName)
  1097. *
  1098. * Description:  Plays a module file
  1099. *
  1100. * Input:        char *fName
  1101. *
  1102. \****************************************************************************/
  1103.  
  1104. void PlayModule(char *fName)
  1105. {
  1106.     int         stop, i, error, nextf;
  1107.  
  1108.     cprintf("Loading \"%s\"\r\n", fName);
  1109.  
  1110.     /* load module: */
  1111.     mod = midasPlayModule(fName, 0);
  1112.     startTime = time(NULL);
  1113.  
  1114.     numChans = mod->numChans;
  1115.  
  1116.     if ( (useDefPanning) && ((defPanning < 64) || (defPanning == 100))  )
  1117.     {
  1118.         for ( i = 0; i < numChans; i++ )
  1119.         {
  1120.             /* Default panning is used. If 100, set channel to surround: */
  1121.             if ( defPanning == 100 )
  1122.             {
  1123.                 if ( (error = SD->SetPanning(i, panSurround)) != OK )
  1124.                     midasError(errorMsg[error]);
  1125.             }
  1126.             else
  1127.             {
  1128.                 /* If channel is panned to left, set it to -defPanning,
  1129.                    otherwise set it to right */
  1130.                 if ( mod->chanSettings[i] < 0 )
  1131.                 {
  1132.                     if ( (error = SD->SetPanning(i, -defPanning)) != OK )
  1133.                         midasError(errorMsg[error]);
  1134.                 }
  1135.                 else
  1136.                 {
  1137.                     if ( (error = SD->SetPanning(i, defPanning)) != OK )
  1138.                         midasError(errorMsg[error]);
  1139.                 }
  1140.             }
  1141.         }
  1142.     }
  1143.  
  1144.     /* Prepare screen display: */
  1145.     DrawScreen();
  1146.     showheap();
  1147.  
  1148.     for ( i = 0; i < 32; i++ )
  1149.     {
  1150.         oldInsts[i] = 0;
  1151.         Channels[i] = 0;
  1152.     }
  1153.  
  1154.     /* Allocate memory for Module Player information structure and prepare
  1155.        it for use: */
  1156.     if ( (error = memAlloc(sizeof(mpInformation), (void**) &info)) != OK )
  1157.         Error(errorMsg[error]);
  1158.     info->numChannels = numChans;
  1159.     if ( (error = memAlloc(numChans * sizeof(mpChanInfo), (void**)
  1160.         &info->chans)) != OK )
  1161.         Error(errorMsg[error]);
  1162.  
  1163.     stop = 0;
  1164.     fadeOut = 0;
  1165.     masterVol = 64;
  1166.     if ( (error = MP->SetMasterVolume(64)) != OK )
  1167.         midasError(errorMsg[error]);
  1168.  
  1169.     if ( (loopCnt == 0) && (numFNames != 1) )
  1170.         loopCnt = 1;
  1171.  
  1172.     if ( isArchive )
  1173.     {
  1174.         if ( remove(fName) != 0 )
  1175.             Error("Unable to delete file");
  1176.         decompressed = 0;
  1177.     }
  1178.  
  1179.     if ( numFNames != 1 )
  1180.     {
  1181.         if ( fileNumber < (numFNames-1) )
  1182.             nextf = fileNumber + 1;
  1183.         else
  1184.             nextf = 0;
  1185.  
  1186.         prepare(nextf);
  1187.  
  1188.         if ( isArchive )
  1189.         {
  1190.             if ( coreleft() >= DECOMPMEM )
  1191.             {
  1192.                 decompress(fNames[nextf]);
  1193.                 InitScreen();
  1194.                 DrawScreen();
  1195.                 cprintf("Next module file decompressed\r\n");
  1196.             }
  1197.             else
  1198.                 cprintf("Not enough free memory to decompress next module "
  1199.                         "file while playing\r\n");
  1200.         }
  1201.     }
  1202.  
  1203.     cprintf ("Playing %d-channel %s Module \"%s\"\r\n", numChans,
  1204.         modTypes[mod->IDnum], &mod->songName[0]);
  1205.     showheap();
  1206.  
  1207.     while ( (!stop) && (!exitFlag) )
  1208.     {
  1209.         WaitFrame();                    /* wait for next frame */
  1210.  
  1211.         /* Read Module Player information: */
  1212.         if ( (error = MP->GetInformation(info)) != OK )
  1213.             midasError(errorMsg[error]);
  1214.  
  1215.         if ( loopCnt != 0 )
  1216.         {
  1217.             if ( info->loopCnt >= loopCnt )
  1218.                 fadeOut = 1;
  1219.         }
  1220.  
  1221.         UpdScreen();                    /* update screen */
  1222.  
  1223.         if ( fadeOut )
  1224.         {
  1225.             if ( masterVol > 0 )
  1226.             {
  1227.                 masterVol--;
  1228.                 if ( (error = MP->SetMasterVolume(masterVol)) != OK )
  1229.                     midasError(errorMsg[error]);
  1230.             }
  1231.             else
  1232.             {
  1233.                 stop = 1;
  1234.                 if ( (error = MP->SetMasterVolume(0)) != OK )
  1235.                     midasError(errorMsg[error]);
  1236.             }
  1237.         }
  1238.  
  1239.         if( kbhit() )
  1240.             HandleKeys();
  1241.     }
  1242.  
  1243.     /* stop playing: */
  1244.     midasStopModule(mod);
  1245.  
  1246.     /* deallocate info structure: */
  1247.     if ( (error = memFree(info->chans)) != OK )
  1248.         Error(errorMsg[error]);
  1249.     if ( (error = memFree(info)) != OK )
  1250.         Error(errorMsg[error]);
  1251.  
  1252.     showheap();
  1253.  
  1254.     if ( noNext )
  1255.         exitFlag = 1;
  1256. }
  1257.  
  1258.  
  1259.  
  1260.  
  1261.  
  1262. int main(int argc, char *argv[])
  1263. {
  1264.     int         error, i, n;
  1265.     char        *temp;
  1266.  
  1267.     /* save text mode information, including mode: */
  1268.     gettextinfo(&textInfo);
  1269.  
  1270.     puts(title);
  1271.     if  ( argc < 2 )
  1272.     {
  1273.         puts(usage);
  1274.         exit(EXIT_SUCCESS);
  1275.     }
  1276.  
  1277.     printf("Free memory: %lu\n", free1 = coreleft());
  1278.     startTime = time(NULL);
  1279.  
  1280.     midasSetDefaults();
  1281.     midasParseEnvironment();
  1282.     ParseCmdLine(argc, argv);
  1283.     if ( numFNames == 0 )
  1284.     {
  1285.         puts(usage);
  1286.         exit(EXIT_SUCCESS);
  1287.     }
  1288.  
  1289.     if ( scrambleOrder )
  1290.     {
  1291.         randomize();
  1292.         for ( i = 0; i < numFNames; i++ )
  1293.         {
  1294.             n = random(numFNames);
  1295.             temp = fNames[i];
  1296.             fNames[i] = fNames[n];
  1297.             fNames[n] = temp;
  1298.         }
  1299.     }
  1300.  
  1301.     /* allocate memory for decompression directory name string: */
  1302.     if ( (error = memAlloc(MAXPATH, (void*) &tempDir)) != OK )
  1303.         Error(errorMsg[error]);
  1304.  
  1305.     /* if environment variable "TEMP" is set, use it, otherwise use "C:\" */
  1306.     temp = getenv("TEMP");
  1307.     if ( temp != NULL )
  1308.     {
  1309.         /* "TEMP" environment string found. Copy it to tempDir, and if the
  1310.            last character is not '\', append one to the end. */
  1311.         strcpy(tempDir, temp);
  1312.         if ( tempDir[strlen(tempDir)-1] != '\\' )
  1313.             strcat(tempDir, "\\");
  1314.     }
  1315.     else
  1316.     {
  1317.         /* No "TEMP" environment string found - use "C:\" */
  1318.         strcpy(tempDir, "C:\\");
  1319.     }
  1320.  
  1321.  
  1322.     if ( !immShell )
  1323.         InitScreen();
  1324.     InitMIDAS();
  1325.  
  1326.     if ( immShell )
  1327.     {
  1328.         mod = midasPlayModule(fNames[0], 0);
  1329.         DOSshell();
  1330.         midasStopModule(mod);
  1331.         exitFlag = 1;
  1332.     }
  1333.  
  1334.     if ( numFNames == 1 )
  1335.         noNext = 1;
  1336.  
  1337.     fileNumber = 0;
  1338.     prepare(fileNumber);
  1339.  
  1340.     while( !exitFlag )
  1341.     {
  1342.         pauseTime = 0;
  1343.         paused = 0;
  1344.         muted = 0;
  1345.  
  1346.         if ( isArchive )
  1347.         {
  1348.             if ( decompressed )
  1349.                 PlayModule(decompName);
  1350.             else
  1351.             {
  1352.                 decompress(fNames[fileNumber]);
  1353.                 InitScreen();
  1354.                 cprintf("Module file decompressed.\r\n");
  1355.                 showheap();
  1356.                 PlayModule(decompName);
  1357.             }
  1358.         }
  1359.         else
  1360.             PlayModule(fNames[fileNumber]);
  1361.  
  1362.         fileNumber++;
  1363.         if ( fileNumber >= numFNames )
  1364.             fileNumber = 0;
  1365.     }
  1366.  
  1367.     if ( (decompressed) && (decompName != NULL) )
  1368.         if ( remove(decompName) != 0 )
  1369.             Error("Unable to delete file");
  1370.  
  1371.     if ( decompName != NULL )
  1372.         if ( (error = memFree(decompName)) != OK )
  1373.             Error(errorMsg[error]);
  1374.  
  1375.     midasClose();
  1376.  
  1377.     if ( (error = memFree(tempDir)) != OK )
  1378.         Error(errorMsg[error]);
  1379.  
  1380.     /* restore old text mode: */
  1381.     textmode(textInfo.currmode);
  1382.  
  1383.     showheap();
  1384.  
  1385. #ifdef DEBUG
  1386.     errPrintList();
  1387. #endif
  1388.  
  1389.     return 0;
  1390. }
  1391.