code:
/***************************************************************************
 *   TEKPREP.C -                                                           *
 *                                                                         *
 *                                                                         *
 ***************************************************************************/

#include "build.h"
#include "names.h"
#include "pragmas.h"
#include "mmulti.h"
#include "cache1d.h"
#include "baselayer.h"

#include "tekwar.h"

sectortype          *sectptr[MAXSECTORS];
spritetype          *sprptr[MAXSPRITES];
walltype            *wallptr[MAXWALLS];
struct    spriteextension     spriteXT[MAXSPRITES];
struct    spriteextension     *sprXTptr[MAXSPRITES];
int      startx,starty,startz;
short starta,starts;

#define   MAXSTARTSPOTS  16
int       startspotcnt;

struct startspottype
{
    int   x, y, z;
    short sectnum;
};
startspottype startspot[MAXSTARTSPOTS];

int firsttimethru=1;
int subwaysound[4];

void prepareboard(char *daboardfilename)
{
     short     startwall, endwall, dasector;
     int      i, j, k, s, dax, day, daz, dax2, day2;
     int       l;

     initsprites();

     if( firsttimethru ) {
          getmessageleng = 0;
          typemessageleng = 0;
          randomseed = 17L;
     }

     // clear (do)animation's list
     animatecnt = 0;
     typemode = 0;
     locselectedgun=1;
     locselectedgun2=1;

     vec3_t startPos;
     int status = engineLoadBoard(daboardfilename, 0, &startPos, &ang[0], &cursectnum[0]);
     if (status == -2)
         status = engineLoadBoardV5V6(daboardfilename, 0, &startPos, &ang[0], &cursectnum[0]);

     if (status == -1)
     {
         crash("Board not found");
     }

     posx[0] = startPos.x;
     posy[0] = startPos.y;
     posz[0] = startPos.z;

     //if (loadoldboard(daboardfilename,0,&posx[0],&posy[0],&posz[0],&ang[0],&cursectnum[0]) == -1)
     {
         #if 0 // TODO
          musicoff();
          uninitmultiplayers();
          //uninittimer();
          uninitinput();
          engineUnInit();
          uninitsb();
          printf("Board not found\n");
          exit(0);
          #endif
     }

     startx=posx[0];
     starty=posy[0];
     startz=posz[0];
     starta=ang[0];
     starts=cursectnum[0];

#ifdef YAX_ENABLE
     yax_update(1);
#endif

     psky_t* pSky = tileSetupSky(0);

     pSky->tileofs[0] = 0;
     pSky->tileofs[1] = 0;
     pSky->tileofs[2] = 0;
     pSky->tileofs[3] = 0;
     pSky->yoffs = 112;
     pSky->lognumtiles = 2;
     pSky->horizfrac = 65536;
     pSky->yscale = 65536;
     parallaxtype = 1;
     g_visibility = 2048;

     for(i=0;i<MAXPLAYERS;i++)
     {
          posx[i] = posx[0];
          posy[i] = posy[0];
          posz[i] = posz[0];
          ang[i] = ang[0];
          cursectnum[i] = cursectnum[0];
          ocursectnum[i] = cursectnum[0];
          horiz[i] = 100;
          lastchaingun[i] = 0;
          health[i] = 100;
         #ifdef RESETSCORE
          score[i] = 0L;
         #endif
          dimensionmode[i] = 3;
          numbombs[i] = -1;
          zoom[i] = 768L;
          deaths[i] = 0L;
          playersprite[i] = -1;
          saywatchit[i] = -1;
          oposx[i] = posx[0];
          oposy[i] = posy[0];
          oposz[i] = posz[0];
          ohoriz[i] = horiz[0];
          ozoom[i] = zoom[0];
          oang[i] = ang[0];
     }

     setup3dscreen();

     if( firsttimethru ) {
          olocvel = 0; olocvel2 = 0;
          olocsvel = 0; olocsvel2 = 0;
          olocangvel = 0; olocangvel2 = 0;
          olocbits = 0; olocbits2 = 0;
          for(i=0;i<MAXPLAYERS;i++) {
               fsyncvel[i] = syncvel[i] = osyncvel[i] = 0;
               fsyncsvel[i] = syncsvel[i] = osyncsvel[i] = 0;
               fsyncangvel[i] = syncangvel[i] = osyncangvel[i] = 0;
               fsyncbits[i] = syncbits[i] = osyncbits[i] = 0;
          }
     }

     for(i=0;i<MAXPLAYERS;i++)
     {
          waterfountainwall[i] = -1;
          waterfountaincnt[i] = 0;
          slimesoundcnt[i] = 0;
     }

     warpsectorcnt = 0;      //Make a list of warping sectors
     xpanningsectorcnt = 0;  //Make a list of wall x-panning sectors
     floorpanningcnt = 0;    //Make a list of slime sectors
     dragsectorcnt = 0;      //Make a list of moving platforms
     swingcnt = 0;           //Make a list of swinging doors
     revolvecnt = 0;         //Make a list of revolving doors
     subwaytrackcnt = 0;     //Make a list of subways

     // intitialize subwaysound[]s
     for( i=0; i<4; i++ ) {
          subwaysound[i]=-1;
     }

     // scan sector tags
     for(i=0;i<numsectors;i++)
     {
          switch(sector[i].lotag)
          {
               case 4:
                    floorpanninglist[floorpanningcnt++] = i;
                    break;
               case 5060:
                    if( option[4] != 0 ) {
                         sector[i].lotag=0;
                    }
                    break;
               case 25:
                    if( (singlemapmode != 0) || (generalplay != 0) || (option[4] != 0) ) {
                         sector[i].lotag=0;
                    }
                    break;
               case 10:
                    if( (generalplay == 0) && (option[4] == 0) && (warpsectorcnt < 64) ) {
                         warpsectorlist[warpsectorcnt++] = i;
                    }
                    break;
               case 11:
                    xpanningsectorlist[xpanningsectorcnt++] = i;
                    break;
               case 12:
                    dasector = i;
                    dax = 0x7fffffff;
                    day = 0x7fffffff;
                    dax2 = 0x80000000;
                    day2 = 0x80000000;
                    startwall = sector[i].wallptr;
                    endwall = startwall+sector[i].wallnum-1;
                    for(j=startwall;j<=endwall;j++)
                    {
                         if (wall[j].x < dax) dax = wall[j].x;
                         if (wall[j].y < day) day = wall[j].y;
                         if (wall[j].x > dax2) dax2 = wall[j].x;
                         if (wall[j].y > day2) day2 = wall[j].y;
                         if (wall[j].lotag == 3) k = j;
                    }
                    if (wall[k].x == dax) dragxdir[dragsectorcnt] = -16;
                    if (wall[k].y == day) dragydir[dragsectorcnt] = -16;
                    if (wall[k].x == dax2) dragxdir[dragsectorcnt] = 16;
                    if (wall[k].y == day2) dragydir[dragsectorcnt] = 16;

                    dasector = wall[startwall].nextsector;
                    dragx1[dragsectorcnt] = 0x7fffffff;
                    dragy1[dragsectorcnt] = 0x7fffffff;
                    dragx2[dragsectorcnt] = 0x80000000;
                    dragy2[dragsectorcnt] = 0x80000000;
                    startwall = sector[dasector].wallptr;
                    endwall = startwall+sector[dasector].wallnum-1;
                    for(j=startwall;j<=endwall;j++)
                    {
                         if (wall[j].x < dragx1[dragsectorcnt]) dragx1[dragsectorcnt] = wall[j].x;
                         if (wall[j].y < dragy1[dragsectorcnt]) dragy1[dragsectorcnt] = wall[j].y;
                         if (wall[j].x > dragx2[dragsectorcnt]) dragx2[dragsectorcnt] = wall[j].x;
                         if (wall[j].y > dragy2[dragsectorcnt]) dragy2[dragsectorcnt] = wall[j].y;
                    }

                    dragx1[dragsectorcnt] += (wall[sector[i].wallptr].x-dax);
                    dragy1[dragsectorcnt] += (wall[sector[i].wallptr].y-day);
                    dragx2[dragsectorcnt] -= (dax2-wall[sector[i].wallptr].x);
                    dragy2[dragsectorcnt] -= (day2-wall[sector[i].wallptr].y);

                    dragfloorz[dragsectorcnt] = sector[i].floorz;

                    dragsectorlist[dragsectorcnt++] = i;
                    break;
               case 13:
                    startwall = sector[i].wallptr;
                    endwall = startwall+sector[i].wallnum-1;
                    for(j=startwall;j<=endwall;j++)
                    {
                         if (wall[j].lotag == 4)
                         {
                              k = wall[wall[wall[wall[j].point2].point2].point2].point2;
                              if ((wall[j].x == wall[k].x) && (wall[j].y == wall[k].y))
                              {     //Door opens counterclockwise
                                   swingwall[swingcnt][0] = j;
                                   swingwall[swingcnt][1] = wall[j].point2;
                                   swingwall[swingcnt][2] = wall[wall[j].point2].point2;
                                   swingwall[swingcnt][3] = wall[wall[wall[j].point2].point2].point2;
                                   swingangopen[swingcnt] = 1536;
                                   swingangclosed[swingcnt] = 0;
                                   swingangopendir[swingcnt] = -1;
                              }
                              else
                              {     //Door opens clockwise
                                   swingwall[swingcnt][0] = wall[j].point2;
                                   swingwall[swingcnt][1] = j;
                                   swingwall[swingcnt][2] = lastwall(j);
                                   swingwall[swingcnt][3] = lastwall(swingwall[swingcnt][2]);
                                   swingwall[swingcnt][4] = lastwall(swingwall[swingcnt][3]);
                                   swingangopen[swingcnt] = 512;
                                   swingangclosed[swingcnt] = 0;
                                   swingangopendir[swingcnt] = 1;
                              }
                              for(k=0;k<4;k++)
                              {
                                   swingx[swingcnt][k] = wall[swingwall[swingcnt][k]].x;
                                   swingy[swingcnt][k] = wall[swingwall[swingcnt][k]].y;
                              }

                              swingsector[swingcnt] = i;
                              swingang[swingcnt] = swingangclosed[swingcnt];
                              swinganginc[swingcnt] = 0;
                              swingcnt++;
                         }
                    }
                    break;
               case 14:
                    startwall = sector[i].wallptr;
                    endwall = startwall+sector[i].wallnum-1;
                    dax = 0L;
                    day = 0L;
                    for(j=startwall;j<=endwall;j++)
                    {
                         dax += wall[j].x;
                         day += wall[j].y;
                    }
                    revolvepivotx[revolvecnt] = dax / (endwall-startwall+1);
                    revolvepivoty[revolvecnt] = day / (endwall-startwall+1);

                    k = 0;
                    for(j=startwall;j<=endwall;j++)
                    {
                         revolvex[revolvecnt][k] = wall[j].x;
                         revolvey[revolvecnt][k] = wall[j].y;
                         k++;
                    }
                    revolvesector[revolvecnt] = i;
                    revolveang[revolvecnt] = 0;

                    revolvecnt++;
                    break;
               case 15:
                    subwaytracksector[subwaytrackcnt][0] = i;
                    subwaystopcnt[subwaytrackcnt] = 0;
                    dax = 0x7fffffff;
                    day = 0x7fffffff;
                    dax2 = 0x80000000;
                    day2 = 0x80000000;
                    startwall = sector[i].wallptr;
                    endwall = startwall+sector[i].wallnum-1;
                    for(j=startwall;j<=endwall;j++)
                    {
                         if (wall[j].x < dax) dax = wall[j].x;
                         if (wall[j].y < day) day = wall[j].y;
                         if (wall[j].x > dax2) dax2 = wall[j].x;
                         if (wall[j].y > day2) day2 = wall[j].y;
                    }
                    for(j=startwall;j<=endwall;j++)
                    {
                         if (wall[j].lotag == 5)
                         {
                              if( (wall[j].x > dax) && (wall[j].y > day) && (wall[j].x < dax2) && (wall[j].y < day2) ) {
                                   subwayx[subwaytrackcnt] = wall[j].x;
                              }
                              else {
                                   subwaystop[subwaytrackcnt][subwaystopcnt[subwaytrackcnt]] = wall[j].x;
                                   if( accessiblemap(wall[j].hitag) == 0 ) {
                                        subwaystop[subwaytrackcnt][subwaystopcnt[subwaytrackcnt]]=0;
                                   }
                                   subwaystopcnt[subwaytrackcnt]++;
                              }
                         }
                    }
                    // de-sparse stoplist but keep increasing x order
                    for( j=0; j<subwaystopcnt[subwaytrackcnt]; j++ ) {
                         if( subwaystop[subwaytrackcnt][j] == 0 ) {
                              for( l=j+1; l<subwaystopcnt[subwaytrackcnt]; l++ ) {
                                   if( subwaystop[subwaytrackcnt][l] != 0 ) {
                                        subwaystop[subwaytrackcnt][j]=subwaystop[subwaytrackcnt][l];
                                        subwaystop[subwaytrackcnt][l]=0;
                                        l=subwaystopcnt[subwaytrackcnt];
                                   }
                              }
                         }
                    }
                    // recount stopcnt
                    subwaystopcnt[subwaytrackcnt]=0;
                    while( subwaystop[subwaytrackcnt][subwaystopcnt[subwaytrackcnt]] != 0 ) {
                         subwaystopcnt[subwaytrackcnt]++;
                    }

                    for(j=1;j<subwaystopcnt[subwaytrackcnt];j++)
                         for(k=0;k<j;k++)
                              if (subwaystop[subwaytrackcnt][j] < subwaystop[subwaytrackcnt][k])
                              {
                                   s = subwaystop[subwaytrackcnt][j];
                                   subwaystop[subwaytrackcnt][j] = subwaystop[subwaytrackcnt][k];
                                   subwaystop[subwaytrackcnt][k] = s;
                              }

                    subwaygoalstop[subwaytrackcnt] = 0;
                    for(j=0;j<subwaystopcnt[subwaytrackcnt];j++)
                         if (labs(subwaystop[subwaytrackcnt][j]-subwayx[subwaytrackcnt]) < labs(subwaystop[subwaytrackcnt][subwaygoalstop[subwaytrackcnt]]-subwayx[subwaytrackcnt]))
                              subwaygoalstop[subwaytrackcnt] = j;

                    subwaytrackx1[subwaytrackcnt] = dax;
                    subwaytracky1[subwaytrackcnt] = day;
                    subwaytrackx2[subwaytrackcnt] = dax2;
                    subwaytracky2[subwaytrackcnt] = day2;

                    subwaynumsectors[subwaytrackcnt] = 1;
                    for(j=0;j<numsectors;j++)
                         if (j != i)
                         {
                              startwall = sector[j].wallptr;
                              if (wall[startwall].x > subwaytrackx1[subwaytrackcnt])
                                   if (wall[startwall].y > subwaytracky1[subwaytrackcnt])
                                        if (wall[startwall].x < subwaytrackx2[subwaytrackcnt])
                                             if (wall[startwall].y < subwaytracky2[subwaytrackcnt])
                                             {
                                                  if (sector[j].lotag == 16)
                                                       sector[j].lotag = 17;   //Make special subway door

                                                  if (sector[j].floorz != sector[i].floorz)
                                                  {
                                                       sector[j].ceilingstat |= 64;
                                                       sector[j].floorstat |= 64;
                                                  }
                                                  subwaytracksector[subwaytrackcnt][subwaynumsectors[subwaytrackcnt]] = j;
                                                  subwaynumsectors[subwaytrackcnt]++;
                                             }
                         }

                    subwayvel[subwaytrackcnt] = 32;  // orig 64
                    subwaypausetime[subwaytrackcnt] = 720;
                    subwaytrackcnt++;
                    break;
          }
     }

     // scan wall tags
     ypanningwallcnt = 0;
     for(i=0;i<numwalls;i++)
     {
          if (wall[i].lotag == 1) ypanningwalllist[ypanningwallcnt++] = i;
     }

     // scan sprite tags&picnum's
     rotatespritecnt = 0;
     startspotcnt=0;
     for(i=0;i<MAXSPRITES;i++)
     {
          if( sprite[i].picnum == STARTPOS ) {
               if( startspotcnt < MAXSTARTSPOTS ) {
                    startspot[startspotcnt].x=sprite[i].x;
                    startspot[startspotcnt].y=sprite[i].y;
                    startspot[startspotcnt].z=sprite[i].z;
                    startspot[startspotcnt].sectnum=sprite[i].sectnum;
                    startspotcnt++;
               }
               jsdeletesprite(i);
          }
          else if( sprite[i].lotag == 3 ) {
               rotatespritelist[rotatespritecnt++] = i;
          }
          else if( option[4] != 0 ) {
               if( sprite[i].lotag == 1009 ) {
                    jsdeletesprite(i);
               }
          }
     }
     if( (startspotcnt == 0) && (option[4] != 0) ) {
          crash("no net startspots");
     }

     for(i=0;i<(MAXSECTORS>>3);i++) show2dsector[i] = 0xff;
     for(i=0;i<(MAXWALLS>>3);i++) show2dwall[i] = 0xff;
     automapping = 0;
     // tags that make wall/sector not show up on 2D map
     for( i=0; i < MAXSECTORS; i++ ) {
          if( sector[i].lotag == 9901 ) {
               show2dsector[i>>3] &= ~(1<<(i&7));
               startwall = sector[i].wallptr;
               endwall = startwall+sector[i].wallnum-1;
               for( j=startwall; j<=endwall; j++) {
                    show2dwall[j>>3] &= ~(1<<(j&7));
                    show2dwall[(wall[j].nextwall)>>3] &= ~(1<<((wall[j].nextwall)&7));
               }
          }
     }
     for( i=0; i < MAXWALLS; i++ ) {
          if( wall[i].lotag == 9900 ) {
               show2dwall[i>>3] &= ~(1<<(i&7));
          }
     }

     if( firsttimethru ) {
          lockclock = 0;
          ototalclock = 0;
          gotlastpacketclock = 0;
          masterslavetexttime = 0;
     }

     if( option[4] != 0 ) {
          firsttimethru=0;
     }

     tekpreptags();
     initspriteXTs();

     // put guns somewhere on map
     if( option[4] > 0 ) {
          placerandompic(3094L);
          placerandompic(3093L);
          placerandompic(3095L);
          placerandompic(3091L);
          placerandompic(3090L);
     }
}

void
findrandomspot(int *x, int *y, short *sectnum)
{
     short startwall, endwall, s;
     int dax, day, minx, maxx, miny, maxy, cnt, k;

     cnt = 256;
     while (cnt > 0)
     {
          do
          {
            k = mulscale(krand_intercept("PREP 521"),numsectors,16);
          } while ((sector[k].ceilingz >= sector[k].floorz) || (sector[k].lotag != 0) || ((sector[k].floorstat&2) != 0));

          startwall = sector[k].wallptr;
          endwall = startwall+sector[k].wallnum-1;
          if (endwall > startwall)
          {
               dax = 0L;
               day = 0L;
               minx = 0x7fffffff; maxx = 0x80000000;
               miny = 0x7fffffff; maxy = 0x80000000;

               for(s=startwall;s<=endwall;s++)
               {
                    dax += wall[s].x;
                    day += wall[s].y;
                    if (wall[s].x < minx) minx = wall[s].x;
                    if (wall[s].x > maxx) maxx = wall[s].x;
                    if (wall[s].y < miny) miny = wall[s].y;
                    if (wall[s].y > maxy) maxy = wall[s].y;
               }
               if ((maxx-minx > 256) && (maxy-miny > 256))
               {
                    dax /= (endwall-startwall+1);
                    day /= (endwall-startwall+1);
                    if (inside(dax,day,k) == 1)
                    {
                         *x = dax;
                         *y = day;
                         *sectnum = k;
                         return;
                    }
               }
          }
          cnt--;
     }
}

void
netstartspot(int *x, int *y,short *sectnum)
{
     int       rv;

     rv=( int)((krand_intercept("PREP 564"))&15L);
     if (rv >= startspotcnt) {
          rv=0;
     }
     *x=startspot[rv].x;
     *y=startspot[rv].y;
     *sectnum=startspot[rv].sectnum;
}

void
placerandompic(int picnum)
{
     short     startwall, endwall, s;
     int      dax, day, minx, maxx, miny, maxy, cnt, k;
     int       j;

     cnt = 256;
     while (cnt > 0)
     {
          do
          {
            k = mulscale(krand_intercept("PREP 585"),numsectors,16);
          } while ((sector[k].ceilingz >= sector[k].floorz) || (sector[k].lotag != 0) || ((sector[k].floorstat&2) != 0));

          startwall = sector[k].wallptr;
          endwall = startwall+sector[k].wallnum-1;
          if (endwall > startwall)
          {
               dax = 0L;
               day = 0L;
               minx = 0x7fffffff; maxx = 0x80000000;
               miny = 0x7fffffff; maxy = 0x80000000;

               for(s=startwall;s<=endwall;s++)
               {
                    dax += wall[s].x;
                    day += wall[s].y;
                    if (wall[s].x < minx) minx = wall[s].x;
                    if (wall[s].x > maxx) maxx = wall[s].x;
                    if (wall[s].y < miny) miny = wall[s].y;
                    if (wall[s].y > maxy) maxy = wall[s].y;
               }
               if ((maxx-minx > 256) && (maxy-miny > 256))
               {
                    dax /= (endwall-startwall+1);
                    day /= (endwall-startwall+1);
                    if (inside(dax,day,k) == 1)
                    {
                         j=jsinsertsprite(k,0);
                         if( j != -1 ) {
                              fillsprite(j,dax,day,sector[k].floorz,0,-8,0,
                                         12,16,16,0,0,0,0,
                                         0,0,0,0,k,0,0,0,-1);
                              sprite[j].z-=((tilesiz[sprite[j].picnum].y*sprite[j].yrepeat)<<1);
                              switch( picnum ) {
                              }
                              sprite[j].picnum=picnum;
                         }
                         return;
                    }
               }
          }
          cnt--;
     }
}

void
tekrestoreplayer(short snum)
{
    vec3_t pos;
    pos.x = posx[snum]; pos.y = posy[snum]; pos.z = posz[snum] + (KENSPLAYERHEIGHT << 8);
     setsprite(playersprite[snum],&pos);
     sprite[playersprite[snum]].ang = ang[snum];
     sprite[playersprite[snum]].xrepeat = 24;
     sprite[playersprite[snum]].yrepeat = 24;
     horiz[snum] = 100;
     health[snum]=MAXHEALTH;
     fireseq[snum]=0;
     restockammo(snum);
     stun[snum]=MAXSTUN;
     fallz[snum]=0;
     drawweap[snum]=0;
     invredcards[snum]=0;
     invbluecards[snum]=0;
     invaccutrak[snum]=0;
     dieframe[snum]=0;
     if( option[4] != 0 ) {
          weapons[snum]=0;
          weapons[snum]=flags32[GUN2FLAG]|flags32[GUN3FLAG];
     }
     else {
          weapons[snum]=0;
          weapons[snum]=flags32[GUN1FLAG]|flags32[GUN2FLAG]|flags32[GUN3FLAG];
     }
}

void initplayersprite(short snum)
{
    int i;

    if (playersprite[snum] >= 0)
        return;

    i = jsinsertsprite(cursectnum[snum], 8);
    if (i == -1) {
        crash("initplayersprite: jsinsertsprite on player %d failed", snum);
    }

    playersprite[snum] = i;

    sprite[i].x = posx[snum];
    sprite[i].y = posy[snum];
    sprite[i].z = posz[snum] + (KENSPLAYERHEIGHT << 8);
    sprite[i].cstat = 0x101;
    sprite[i].shade = 0;

    if (option[4] == 0) {
        sprite[i].pal = 0;
    }
    else {
        sprite[i].pal = snum + 16;
    }

    sprite[i].clipdist = 32;
    sprite[i].xrepeat = 24;
    sprite[i].yrepeat = 24;
    sprite[i].xoffset = 0;
    sprite[i].yoffset = 0;
    sprite[i].picnum = DOOMGUY;
    sprite[i].ang = ang[snum];
    sprite[i].xvel = 0;
    sprite[i].yvel = 0;
    sprite[i].zvel = 0;
    sprite[i].owner = snum + 4096;
    sprite[i].sectnum = cursectnum[snum];
    sprite[i].statnum = 8;
    sprite[i].lotag = 0;
    sprite[i].hitag = 0;
    // important to set extra = -1
    sprite[i].extra = -1;

    tekrestoreplayer(snum);
}

int initptrlists()
{
    int i;

    for (i = 0; i < MAXSPRITES; i++)
    {
        sprptr[i] = &sprite[i];
        sprXTptr[i] = &spriteXT[i];
    }
    for (i = 0; i < MAXSECTORS; i++)
    {
        sectptr[i] = &sector[i];
    }
    for (i = 0; i < MAXWALLS; i++)
    {
        wallptr[i] = &wall[i];
    }

    return 0;
}

int tekpreinit(void)
{
    uint8_t palbuf[256];
    int fh;

    cdpreinit();

    if ((fh = kopen4loadfrommod("lookup.dat", 0)) >= 0)
    {
        uint8_t num_tables = 0;
        kread(fh, &num_tables, 1);

        for (int j = 0; j < num_tables; j++)
        {
            char lookup_num;
            kread(fh, &lookup_num, 1);
            kread(fh, palbuf, 256);

            paletteMakeLookupTable(lookup_num, (const char*)palbuf, 0, 0, 0, 1);
        }
        kclose(fh);
    }

    if ((option[4] != 0) && ((fh = kopen4load("nlookup.dat", 0)) >= 0))
    {
        uint8_t num_tables = 0;
        kread(fh, &num_tables, 1);

        for (int j = 0; j < num_tables; j++)
        {
            char lookup_num;
            kread(fh, &lookup_num, 1);
            kread(fh, palbuf, 256);

            paletteMakeLookupTable(lookup_num + 15, (const char*)palbuf, 0, 0, 0, 1);
        }
        kclose(fh);
    }

    paletteMakeLookupTable(255, (const char*)palbuf, 60, 60, 60, 1);

    #if 0// TODO
    pskyoff[0] = 0;  // 2 tiles
    pskyoff[1] = 1;
    pskyoff[2] = 2;
    pskyoff[3] = 3;
    pskybits = 2;    // tile 4 times, every 90 deg.
    parallaxtype = 1;
    parallaxyoffs = 112;
    #endif

    initptrlists();
    initpaletteshifts();
    initmenu();
    initmoreoptions();

    if (screensize == 0) {
        screensize = MAXXDIM;
    }

    activemenu = 0;
    return 0;
}

void
tekinitmultiplayers(int argc, char const * const argv[])
{
      initmultiplayersparms(argc, argv);
}

short mappic[]={
     1608,1609,1610,1611,1612,1613,1614
};

int  coopmode,
     switchlevelsflag;

void
teknetpickmap(void)
{
//     short i,map=0;
//     int lastclock=0L,rotangle=0L,zoom=0L;

     noenemiesflag=1;
     if (coopmode) {
          noenemiesflag=0;
     }
     if (switchlevelsflag) {
          strcpy(boardfilename,"NET1.MAP");
     }

// TODO     setgamemode(fullscreen, xdimgame, ydimgame, bppgame);
     initpaletteshifts();
//     memcpy(palette1, palette, 768);
/*     memset(palette, 0, 768);
     clearview(0);
     if (switchlevelsflag) {
          goto skippick;
     }
     lastclock=totalclock;
     do {
          if (keystatus[0x49] || keystatus[0x4B] || keystatus[0xC9] || keystatus[0xCB]) {
               if (map > 0) {
                    map--;
               }
               else {
                    map=6;
               }
               keystatus[0x49]=keystatus[0x4B]=0;
               keystatus[0xC9]=keystatus[0xCB]=0;
               zoom=0L;
          }
          if (keystatus[0x51] || keystatus[0x4D] || keystatus[0xD1] || keystatus[0xCD]) {
               if (map < 6) {
                    map++;
               }
               else {
                    map=0;
               }
               keystatus[0x51]=keystatus[0x4D]=0;
               keystatus[0xD1]=keystatus[0xCD]=0;
               zoom=0L;
          }
          zoom+=(totalclock-lastclock)<<10;
          rotangle+=((totalclock-lastclock)<<5);
          lastclock=totalclock;
          if (zoom > 65536L) {
               zoom=65536L;
               rotangle=0;
          }
          clearview(0);
          rotatesprite(xdim<<15,ydim<<15,zoom,rotangle,mappic[map],0,0,0, 0, 0, xdim-1, ydim-1);
          overwritesprite((xdim>>1)-160,0,408,0,0,0);
          sprintf(tempbuf,"MULTIPLAYER MAP %d",map+1);
          printext((xdim>>1)-(strlen(tempbuf)<<2),ydim-16,tempbuf,ALPHABET2,255);
          nextpage();
     } while (keystatus[0x1C] == 0 && keystatus[0x9C] == 0 && keystatus[0x01] == 0);
     if (keystatus[0x1C] || keystatus[0x9C]) {
          keystatus[0x1C]=0;
          sprintf(boardfilename,"NET%d.MAP",map+1);
     }
     else if (keystatus[0x01]) {
          keystatus[0x01]=0;
          crash("Multiplayer game aborted!");
     }
skippick:*/
     prepareboard(boardfilename);
     precache();
     videoClearViewableArea(0);
//     memcpy(palette, palette1, 768);
     fadein(0,255,16);
}

void
tekloadsetup()
{
     /*
     int  fil;

     if ((fil = open("setup.dat",O_BINARY|O_RDWR,S_IREAD)) != -1)
     {
          read(fil,&option[0],NUMOPTIONS);
          read(fil,&keys[0],NUMGAMEKEYS);
          tekloadmoreoptions(fil);
          close(fil);
     }
     */
//    loadsetup("tekwar.ini");
}

void
teksavesetup(void)
{
     /*
     int  fil;

     if ((fil=open("setup.dat",O_BINARY|O_RDWR,S_IREAD)) != -1) {
          if (option[4] < 5) {
               option[4]=0;
          }
          write(fil,&option[0],NUMOPTIONS);
          write(fil,&keys[0],NUMGAMEKEYS);
          teksavemoreoptions(fil);
          close(fil);
     }
     */
//     writesetup("tekwar.ini");
}

void
tekview(int * UNUSED(x1),int *y1, int * UNUSED(x2),int *y2)
{
     if( screensize <= xdim ) {
          *y1+=16;
          *y2+=16;
     }
}

repo_name: orochi/nblood

license: mit


code:
/***************************************************************************
 *   TEKSMK.C  -   smack flic stuff etc. for Tekwar                        *
 *                                                                         *
 *                                                                         *
 ***************************************************************************/

#include "build.h"
#include "compat.h"
#include "baselayer.h"
#include "pragmas.h"
#include "cache1d.h"
#include "tekwar.h"
#include "keyboard.h"
#include "SmackerDecoder.h"

extern int nobriefflag;

SmackerHandle hSMK;
uint8_t* pFrame = nullptr;
uint8_t* pMenuBackground = nullptr;
uint32_t nWidth, nHeight;

#define kSMKPal   5
#define kSMKTile  (MAXTILES-1)

void smkplayseq(const char *name)
{
    char filename[BMAX_PATH];

    if ((generalplay != 0) || (nobriefflag != 0)) {
        return;
    }

    strcpy(filename, "/SMK/");
    strcat(filename, name);
    strcat(filename, ".SMK");

    hSMK = Smacker_Open(filename);
    if (!hSMK.isValid)
    {
        return;
    }

    Smacker_GetFrameSize(hSMK, nWidth, nHeight);
    pFrame = (uint8_t*)Xmalloc(nWidth * nHeight);
    walock[kSMKTile] = CACHE1D_PERMANENT;
    waloff[kSMKTile] = (intptr_t)pFrame;
    tileSetSize(kSMKTile, nHeight, nWidth);

    if (!pFrame)
    {
        Smacker_Close(hSMK);
        return;
    }

    int nFrameRate = Smacker_GetFrameRate(hSMK);
    int nFrames = Smacker_GetNumFrames(hSMK);

    auto const oyxaspect = yxaspect;

    int nScale = tabledivide32(scale(65536, ydim << 2, xdim * 3), ((max(nHeight, 240 + 1u) + 239) / 240));
    int nStat = 2|4|8|64|1024;
    renderSetAspect(viewingrange, 65536);

    #if 0
    Smacker_GotoFrame(hSMK, 16);

    rgb24_t quickpal[256];

    Smacker_GetPalette(hSMK, (uint8_t*)&quickpal);
    Smacker_GetFrame(hSMK, pFrame);

    FILE* fw = fopen("c:/temp/wilshart.raw", "wb");
    if (fw)
    {
        char pixel[3];
        for (int i = 0; i < nWidth * nHeight; i++)
        {
            memcpy(pixel, &quickpal[pFrame[i]], 3);
            fwrite(pixel, 3, 1, fw);
        }

        fclose(fw);
    }
    #endif

//    paletteSetColorTable(kSMKPal, palette);
//    videoSetPalette(/*gBrightness >> 2*/8, kSMKPal, 8 + 2);

    ClockTicks nStartTime = totalclock;

    int nFrame = 0;
    do
    {
        handleevents();
        if (scale((int)(totalclock - nStartTime), nFrameRate, CLKIPS) < nFrame)
            continue;

        if (KB_KeyPressed(sc_Escape) || KB_KeyPressed(sc_Enter))
            break;

        Smacker_GetPalette(hSMK, palette);
        paletteSetColorTable(kSMKPal, palette);
        videoSetPalette(0, kSMKPal, 8+2);

        Smacker_GetFrame(hSMK, pFrame);
        tileInvalidate(kSMKTile, -1, -1);

        rotatesprite_fs(160 << 16, 100 << 16, nScale, 512, kSMKTile, 0, 0, nStat);

        videoNextPage();

        nFrame++;
        Smacker_GetNextFrame(hSMK);
    } while (nFrame < nFrames);

    KB_FlushKeyboardQueue();
    KB_ClearKeysDown();

    Smacker_Close(hSMK);
    renderSetAspect(viewingrange, oyxaspect);
    videoSetPalette(0, 0, 8 + 2);
    walock[kSMKTile] = 0;
    waloff[kSMKTile] = 0;
    tileSetSize(kSMKTile, 0, 0);
    Xfree(pFrame);
    pFrame = nullptr;

//    debugprintf("smkplayseq(\"%s\")\n", name);
}

void smkopenmenu(const char *name)
{
    hSMK = Smacker_Open(name);
    if (!hSMK.isValid)
    {
        return;
    }

    Smacker_GetFrameSize(hSMK, nWidth, nHeight);
    Smacker_GotoFrame(hSMK, 0);

    Smacker_GetPalette(hSMK, palette);
    paletteSetColorTable(kSMKPal, palette);
    videoSetPalette(/*gBrightness >> 2*/8, kSMKPal, 8 + 2);

    pFrame = (uint8_t*)Xmalloc(nWidth * nHeight);
    pMenuBackground = (uint8_t*)Xmalloc(nWidth * nHeight);

    if (!pFrame || !pMenuBackground)
    {
        Smacker_Close(hSMK);
        return;
    }

    walock[kSMKTile] = CACHE1D_PERMANENT;
    waloff[kSMKTile] = (intptr_t)pFrame;
    tileSetSize(kSMKTile, nHeight, nWidth);
    tileInvalidate(kSMKTile, 0, 1 << 4);  // JBF 20031228

    // first frame is menu background - keep a copy around for later
    Smacker_GetFrame(hSMK, pMenuBackground);
    memcpy(pFrame, pMenuBackground, nWidth * nHeight);
}

void smkmenuframe(int fn)
{
    if (!hSMK.isValid)
        return;

    Smacker_GetPalette(hSMK, palette);
    paletteSetColorTable(kSMKPal, palette);
    videoSetPalette(/*gBrightness >> 2*/8, kSMKPal, 8 + 2);

    tileInvalidate(kSMKTile, 0, 1 << 4);  // JBF 20031228

    Smacker_GotoFrame(hSMK, fn - 1);
    Smacker_GetFrame(hSMK, pFrame);

    // replace transparent pixels on overlay with background pixel
    for (int y = 0; y < nHeight; y++)
    {
        for (int x = 0; x < nWidth; x++)
        {
            uint32_t nPos = (y * nWidth) + x;
            if (pFrame[nPos] == 232) {
                pFrame[nPos] = pMenuBackground[nPos];
            }
        }
    }
}

void smkshowmenu()
{
    if (!hSMK.isValid)
        return;

    videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
    videoClearViewableArea(0);
    rotatesprite(160 << 16, 100 << 16, divscale16(200, tilesiz[kSMKTile].x),
        512, kSMKTile, 0, 0, 2 + 4, 0, 0, xdim - 1, ydim - 1);

    videoNextPage();
}

void smkclosemenu()
{
    if (!hSMK.isValid)
        return;

    Smacker_Close(hSMK);

    walock[kSMKTile] = 0;
    waloff[kSMKTile] = 0;
    tileSetSize(kSMKTile, 0, 0);
    Xfree(pFrame);
    Xfree(pMenuBackground);
}


code:
#include "compat.h"
#include "baselayer.h"
#include "fx_man.h"
#include "hmpplay.h"
#include "keyboard.h"
#include "control.h"
#include "tekwar.h"
#include "config.h"

#if 0

/***************************************************************************
 *   TEKSND.C  - HMI library replaces Kens sound stuff                     *
 *               Also timer routine and keytimerstuff is here              *
 *                                                                         *
 ***************************************************************************/

#define  AMBUPDATEDIST  4000L

//defines for looping sound variables
extern    int       loopinsound;
extern    int       baydoorloop;
extern    int       ambsubloop;

// a 1-1 map from sounds in sound file to this array
struct    soundbuffertype {
     int       users;
     long      offset;
     long      cache_ptr;
     long      cache_length;
     char      cache_lock;
};
struct    soundbuffertype    sbuf[TOTALSOUNDS];
struct    soundbuffertype    *sbufptr[TOTALSOUNDS];
struct    soundbuffertype    loopbuf[MAXLOOPS];
struct    soundbuffertype    *loopbufptr[MAXLOOPS];

struct    soundtype {
     int       handle;
     int       sndnum;
     int       plevel;
     long      x,y;
     short     type;
};
struct    soundtype     dsound[MAXSOUNDS];
struct    soundtype     *dsoundptr[MAXSOUNDS];
struct    soundtype     lsound[MAXLOOPS];
struct    soundtype     *lsoundptr[MAXLOOPS];
DWORD    *LoopList;

#define   BASESONG            0
#define   MAXBASESONGLENGTH   44136
#define   AVAILMODES          3
#define   SONGSPERLEVEL       3
#define   NUMLEVELS       7

int       totalsongsperlevel;
char      basesongdata[MAXBASESONGLENGTH];
char      secondsongdata[MAXBASESONGLENGTH];
char      thirdsongdata[MAXBASESONGLENGTH];

struct    songtype  {
     int       handle;
     int       offset;
     int       playing;
     int       pending;
     char     *buffer;
     long      length;
};
struct    songtype  song[SONGSPERLEVEL];
struct    songtype  *songptr[SONGSPERLEVEL];

int       songlist[4096];

initaudioptrs()
{
     int         i;

     for( i=0; i<TOTALSOUNDS; i++ ) {
          sbufptr[i]=&sbuf[i];
          sbufptr[i]->users=0;
          sbufptr[i]->offset=0;
          sbufptr[i]->cache_ptr=0L;
          sbufptr[i]->cache_length=0;
          sbufptr[i]->cache_lock=0x00;
     }
     for( i=0; i<MAXSOUNDS; i++ ) {
          dsoundptr[i]=&dsound[i];
          dsoundptr[i]->handle=NULL_HANDLE;
          dsoundptr[i]->sndnum=0;
          dsoundptr[i]->plevel=0;
          dsoundptr[i]->x=0L;
          dsoundptr[i]->y=0L;
          dsoundptr[i]->type=ST_UPDATE;
          dsoundptr[i]->sndnum=-1;
          sampleptr[i]=(_SOS_START_SAMPLE   _far *)&sample[i];
          sampleptr[i]->wSamplePitchFraction=0;
          sampleptr[i]->wSamplePanDirection=0;
          sampleptr[i]->wSamplePanStart=0;
          sampleptr[i]->wSamplePanEnd=0;
          sampleptr[i]->wSampleDelayBytes=0;
          sampleptr[i]->wSampleDelayRepeat=0;
          sampleptr[i]->dwSampleADPCMPredicted=0;
          sampleptr[i]->wSampleADPCMIndex=0;
          sampleptr[i]->wSampleRootNoteMIDI=0;
          sampleptr[i]->dwSampleTemp1=0;
          sampleptr[i]->dwSampleTemp2=0;
          sampleptr[i]->dwSampleTemp3=0;
     }
//jsa venom
     for( i=0; i<SONGSPERLEVEL; i++ ) {
          songptr[i]=&song[i];
          songptr[i]->handle=NULL_HANDLE;
          songptr[i]->offset=0;
          songptr[i]->playing= 0;
          songptr[i]->pending=0;
          songptr[i]->length=0L;
     }
     songptr[0]->buffer=&basesongdata;
     songptr[1]->buffer=&secondsongdata;
     songptr[2]->buffer=&thirdsongdata;

     for( i=0; i<MAXLOOPS; i++ ) {
          loopbufptr[i]=&loopbuf[i];
          loopbufptr[i]->users=0;
          loopbufptr[i]->offset=0;
          loopbufptr[i]->cache_ptr=0L;
          loopbufptr[i]->cache_length=0;
          loopbufptr[i]->cache_lock=0x00;
          lsoundptr[i]=&lsound[i];
          lsoundptr[i]->handle=NULL_HANDLE;
          lsoundptr[i]->sndnum=0;
          lsoundptr[i]->plevel=0;
          lsoundptr[i]->x=0L;
          lsoundptr[i]->y=0L;
          lsoundptr[i]->type=ST_UPDATE;
          lsoundptr[i]->sndnum=-1;
          loopsampleptr[i]=(_SOS_START_SAMPLE   _far *)&loopsampledata[i];
     }
}

setupdigi()
{
     int       i;
     DWORD     *digilist;

     if( soundmode == SM_NOHARDWARE ) {
          return;
     }

     digilist=( DWORD *)malloc(( size_t)4096);
     if( digilist == ( DWORD *)NULL ) {
          crash("setupdigi: digilist malloc failed");
     }

     fhsounds=open("sounds",O_RDONLY|O_BINARY);
     if( fhsounds == -1 ) {
          crash("setupdigi: cant open sounds");
     }
     memset(digilist,0, 4096);
     lseek(fhsounds,-4096L,SEEK_END);
     i=read(fhsounds,( void *)digilist, 4096);
     if( i != 4096 ) {
          crash("setupdigi: bad read of digilist");
     }

     for( i=0; i<TOTALSOUNDS; i++ ) {
          sbufptr[i]=&sbuf[i];
          sbufptr[i]->users=0;
          sbufptr[i]->offset=(digilist[i*3]*4096L);
          sbufptr[i]->cache_ptr=0L;
          sbufptr[i]->cache_length=( WORD)(digilist[i*3+1]);
          sbufptr[i]->cache_lock=0x00;
     }
     for( i=0; i<MAXSOUNDS; i++ ) {
          dsoundptr[i]=&dsound[i];
          dsoundptr[i]->handle=NULL_HANDLE;
          dsoundptr[i]->sndnum=0;
          dsoundptr[i]->plevel=0;
          dsoundptr[i]->x=0L;
          dsoundptr[i]->y=0L;
          dsoundptr[i]->type=ST_UPDATE;
          dsoundptr[i]->sndnum=-1;
          sampleptr[i]=(_SOS_START_SAMPLE   _far *)&sample[i];
          sampleptr[i]->wSamplePitchFraction=0;
          sampleptr[i]->wSamplePanDirection=0;
          sampleptr[i]->wSamplePanStart=0;
          sampleptr[i]->wSamplePanEnd=0;
          sampleptr[i]->wSampleDelayBytes=0;
          sampleptr[i]->wSampleDelayRepeat=0;
          sampleptr[i]->dwSampleADPCMPredicted=0;
          sampleptr[i]->wSampleADPCMIndex=0;
          sampleptr[i]->wSampleRootNoteMIDI=0;
          sampleptr[i]->dwSampleTemp1=0;
          sampleptr[i]->dwSampleTemp2=0;
          sampleptr[i]->dwSampleTemp3=0;
     }

     free(digilist);
}

setupmidi()
{
     int       fh,dl,rv,i;

     if( musicmode == MM_NOHARDWARE ) {
          return;
     }

     melodicbankptr=( LPSTR)0;
     drumbankptr=( LPSTR)0;
     digitalbankptr=( LPSTR)0;

     if( (musicmode != MM_MIDIFM) && (musicmode != MM_MIDIDIGI) )
          goto nobanks;

     melodicbankptr=( LPSTR)malloc(( size_t)MELODICBANKLENGTH);
     drumbankptr=( LPSTR)malloc(( size_t)DRUMBANKLENGTH);
     if( (melodicbankptr == ( LPSTR)NULL) || (drumbankptr == ( LPSTR)NULL) ) {
          crash("setupmidi: failed malloc");
     }
     if( (fh=open("melodic.bnk",O_RDONLY)) == -1 ) {
          crash("setupmidi: cant open melodic.bnk");
     }
     read(fh, ( void * )melodicbankptr, MELODICBANKLENGTH);
     close(fh);
     rv=sosMIDISetInsData(*fhmididriverptr, melodicbankptr, 1);
     if( rv != _ERR_NO_ERROR ) {
          crash("setupmidi: bad SetInsData");
     }
     if( (fh=open("drum.bnk",O_RDONLY)) == -1 ) {
          crash("setupmidi: cant open drum.bnk");
     }
     read(fh, ( void * )drumbankptr, DRUMBANKLENGTH);
     close(fh);
     rv=sosMIDISetInsData(*fhmididriverptr, drumbankptr, 1);
     if( rv != _ERR_NO_ERROR ) {
          crash("setupmidi: bad SetInsData");
     }

     if( (musicmode == MM_MIDIDIGI) && (midihardwareptr->wPort == 0x388) ) {
          if( (fh=open("test.dig",O_BINARY|O_RDWR)) == -1 ) {
               crash("setupmidi: cant open test.dig");
          }
          dl=lseek(fh, 0L, SEEK_END);
          lseek(fh, 0L, SEEK_SET);
          digitalbankptr=( LPSTR)malloc(( size_t)dl);
          if( digitalbankptr == ( LPSTR)NULL ) {
               crash("setupmidi: failed malloc digbnkptr");
          }
          rv=read(fh, ( void * )digitalbankptr, dl);
          if( rv != dl ) {
               crash("setupmidi: bad .dig read");
          }
          close(fh);
          rv=sosMIDISetInsData(*fhmididigidriverptr, digitalbankptr, 1);
          if( rv != _ERR_NO_ERROR ) {
               crash("setupmidi: bad SetInsData");
          }
     }

nobanks:

     if( musicmode != MM_NOHARDWARE ) {
          if( (fhsongs=open("SONGS",O_RDONLY | O_BINARY)) == -1 ) {
               crash("setupmidi: cant open songs");
          }
          lseek(fhsongs, 0, SEEK_SET);
          lseek(fhsongs, -4096, SEEK_END);
          read(fhsongs, ( void *)songlist, 4096);
     }

//jsa venom
     for( i=0; i<SONGSPERLEVEL; i++ ) {
          songptr[i]=&song[i];
          songptr[i]->handle=NULL_HANDLE;
          songptr[i]->offset=0;
          songptr[i]->playing= 0;
          songptr[i]->pending=0;
          songptr[i]->length=0L;
     }
     songptr[0]->buffer=&basesongdata;
     songptr[1]->buffer=&secondsongdata;
     songptr[2]->buffer=&thirdsongdata;

      totalsongsperlevel=SONGSPERLEVEL*AVAILMODES;

}

VOID _far cdecl
soundcallback(WORD fhdriver, WORD action, WORD fhsample)
{
     int  i;

     switch( action ) {
     case _SAMPLE_PROCESSED:
          return;
     case _SAMPLE_LOOPING:
          return;
     case _SAMPLE_DONE:
          for( i=0; i<MAXSOUNDS; i++ ) {
               if( dsoundptr[i]->handle == fhsample ) {
                    sbufptr[dsoundptr[i]->sndnum]->users--;
                    if( sbufptr[dsoundptr[i]->sndnum]->users == 0 ) {
                         sbufptr[dsoundptr[i]->sndnum]->cache_lock=0x00;
                    }
                    dsoundptr[i]->handle=NULL_HANDLE;
                    dsoundptr[i]->plevel=0;
                    dsoundptr[i]->sndnum=-1;
                    break;
               }
          }
          break;
     }
     return;
}

VOID _far cdecl
digiloopcallback(WORD fhdriver, WORD action, WORD fhsample)
{
     if ( action == _SAMPLE_LOOPING ) {
               if(LoopPending) {
                    SND_SwapLoops();
                    LoopPending = 0;
               }
     }
}

int
playsound(int sn, long sndx,long sndy, int loop, short type)
{
     int       i,nr=0;
     long      dist=0L,vol=0L,pan=0L;

     if( (toggles[TOGGLE_SOUND] == 0) || (soundmode == SM_NOHARDWARE) || (sn < 0) || (sn >= TOTALSOUNDS) )
          return(-1);

     if( type&(ST_UNIQUE|ST_AMBUPDATE|ST_TOGGLE) ) {
          for( i=0; i<MAXSOUNDS; i++ ) {
               if( dsoundptr[i]->handle == NULL_HANDLE ) {
                    continue;
               }
               else if( dsoundptr[i]->sndnum == sn ) {
                    if( (type&ST_TOGGLE) != 0 ) {
                         stopsound(i);
                    }
                    return(-1);
               }
          }
     }

     for( i=0; i<MAXSOUNDS; i++ ) {
          if( dsoundptr[i]->handle == NULL_HANDLE )
               break;
     }
     if( i == MAXSOUNDS ) {
          // add plevel and multiple occurrence replacement
          return(-1);
     }

     dsoundptr[i]->type=type;
     dsoundptr[i]->x=sndx; dsoundptr[i]->y=sndy;

     sbufptr[sn]->cache_lock=1;

     if( sbufptr[sn]->cache_ptr == 0L ) {   // no longer in cache
          allocache(&(sbufptr[sn]->cache_ptr), sbufptr[sn]->cache_length, &(sbufptr[sn]->cache_lock));
          if( sbufptr[sn]->cache_ptr == 0L ) {
               sbufptr[sn]->cache_lock=0x00;
               return(-1);
          }
          lseek(fhsounds, sbufptr[sn]->offset, SEEK_SET);
          nr=read(fhsounds,( void *)(sbufptr[sn]->cache_ptr),sbufptr[sn]->cache_length);
          if( nr != sbufptr[sn]->cache_length ) {
               sbufptr[sn]->cache_ptr=0L;
               sbufptr[sn]->cache_lock=0x00;
               return(-1);
          }
     }
     else {
     }

     if( (type&ST_IMMEDIATE) ) {
          vol=0x7fff;
          pan=13;
     }
     else {
          dist=labs(posx[screenpeek]-sndx)+labs(posy[screenpeek]-sndy);
           if( (type&ST_AMBUPDATE) || (type&ST_VEHUPDATE) ) {
               if( dist < AMBUPDATEDIST ) {
                   vol = (AMBUPDATEDIST<<3)-(dist<<3);
               }
               else {
                   vol=0;
               }
           }
           else {
               if(dist < 1500L)
                    vol = 0x7fff;
               else if(dist > 8500L) {
                    if(sn >= S_MALE_COMEONYOU)
                         vol = 0x0000;
                    else
                         vol = 0x1f00;
               }
               else
                    vol = 39000L-(dist<<2);
           }
           pan=((getangle(posx[screenpeek]-dsoundptr[i]->x,posy[screenpeek]-dsoundptr[i]->y)+(2047-ang[screenpeek]))&2047) >> 6;
           if( (pan < 0) || (pan > 35) )
               pan=13;
     }
     if( (vol < 0) )
         vol=0;
     if( (vol > 0x7fff) )
         vol=0x7fff;

     sampleptr[i]->lpSamplePtr=( LPSTR)sbufptr[sn]->cache_ptr;
     sampleptr[i]->dwSampleSize=sbufptr[sn]->cache_length;
     sampleptr[i]->wLoopCount=loop;
     sampleptr[i]->wChannel=_CENTER_CHANNEL;
     sampleptr[i]->wVolume=vol;
     sampleptr[i]->wSampleID=sn;
     sampleptr[i]->lpCallback=soundcallback;
     sampleptr[i]->wSamplePort=0;
     sampleptr[i]->wSampleFlags=_LOOPING|_VOLUME|_PANNING;
     sampleptr[i]->dwSampleLoopPoint=0;
     sampleptr[i]->dwSampleLoopLength=0;
     sampleptr[i]->dwSamplePitchAdd=0;
     sampleptr[i]->dwSampleByteLength=sbufptr[sn]->cache_length;
     sampleptr[i]->wSamplePanLocation=PanArray[pan];
     if( sampleptr[i]->wSamplePanLocation > 0xffff )
          sampleptr[i]->wSamplePanLocation=0x8000;
     sampleptr[i]->wSamplePanSpeed=0;

     dsoundptr[i]->handle=sosDIGIStartSample(*fhdigidriverptr,sampleptr[i]);
     if( dsoundptr[i]->handle == _ERR_NO_SLOTS ) {
          dsoundptr[i]->handle=NULL_HANDLE;
          dsoundptr[i]->plevel=0;
          dsoundptr[i]->sndnum=-1;
          if( sbufptr[sn]->users == 0 ) {
               sbufptr[sn]->cache_lock=0;
          }
          return(-1);
     }
     else {
          sbufptr[sn]->users++;
         #ifdef SNDDEBUG
          showmessage("SND %03d ADDR %08ld USRS %02d", sn, sbufptr[sn]->cache_ptr, sbufptr[sn]->users);
         #endif
         dsoundptr[i]->sndnum=sn;
     }

     return(i);
}

stopsound(int i)
{
     if( soundmode == SM_NOHARDWARE )
          return;
     if( (i < 0) || (i >= MAXSOUNDS) ) {
          return;
     }
     if( dsoundptr[i]->handle == NULL_HANDLE )
          return;

     sosDIGIStopSample(*fhdigidriverptr, dsoundptr[i]->handle);
     sbufptr[dsoundptr[i]->sndnum]->users--;
     if( sbufptr[dsoundptr[i]->sndnum]->users < 0 )
              sbufptr[dsoundptr[i]->sndnum]->users=0;
     if( sbufptr[dsoundptr[i]->sndnum]->users == 0 ) {
         sbufptr[dsoundptr[i]->sndnum]->cache_lock=0x00;
     }
     dsoundptr[i]->handle=NULL_HANDLE;
     dsoundptr[i]->plevel=0;
     dsoundptr[i]->sndnum=-1;
}

void
updatesounds(int    snum)
{
     long      dist=0L,vol=0L,pan=0L;
     int       i,bufnum,panindx;

     if( (toggles[TOGGLE_SOUND] == 0) || (soundmode == SM_NOHARDWARE) )
          return;

     for( i=0; i<MAXSOUNDS; i++ ) {
          if( dsoundptr[i]->handle == NULL_HANDLE ) {
               continue;
          }
          if( (dsoundptr[i]->type&(ST_IMMEDIATE|ST_NOUPDATE|ST_VEHUPDATE)) != 0 ) {
               continue;
          }
          dist=labs(posx[snum]-dsoundptr[i]->x)+labs(posy[snum]-dsoundptr[i]->y);

          if(dsoundptr[i]->type==ST_AMBUPDATE) {
               if( dist < AMBUPDATEDIST ) {
                    vol = (AMBUPDATEDIST<<3)-(dist<<3);
               }
               else {
                    vol=0;
               }
          }
          else {
               if(dist < 1500L)
                    vol = 0x7fff;
               else if(dist > 8500L)
                    vol = 0x1f00;
               else
                    vol = 39000L-(dist<<2);
          }

          if( (vol < 0) )
              vol=0;
          if( (vol > 0x7fff) )
              vol=0x7fff;

          if( dsoundptr[i]->handle != NULL_HANDLE ) {   // safeguard on int level
               sosDIGISetSampleVolume(*fhdigidriverptr, dsoundptr[i]->handle, vol);
          }
         #ifdef DYNAMICPANPERFECT
          panindx=((getangle(posx[snum]-dsoundptr[i]->x,posy[snum]-dsoundptr[i]->y)+(2047-ang[snum]))&2047) >> 6;
          if( (panindx < 0) || (panindx > 35) )
               panindx=13;
          pan=PanArray[panindx];
          if( pan > 0xffff )
               pan=0xffff;
          if( dsoundptr[i]->handle != NULL_HANDLE ) {   // safeguard on int level
               sosDIGISetPanLocation(*fhdigidriverptr, dsoundptr[i]->handle, pan);
          }
         #endif
     }
}

void
updatevehiclesnds(int i, long sndx, long sndy)
{
     long      dist=0L,vol=0L,pan=0L;

     if( soundmode == SM_NOHARDWARE ) {
          return;
     }
     if( (i < 0) || (i > MAXSOUNDS) ) {
          return;
     }

     dsoundptr[i]->x=sndx;
     dsoundptr[i]->y=sndy;

     dist=labs(posx[screenpeek]-sndx)+labs(posy[screenpeek]-sndy);

     if( dist < 1000L ) {
          vol = 0x7fff;
     }
     else if( dist > 9000L ) {
          vol = 0x0000;
     }
     else {
          vol = 36000L-(dist<<2);
     }
     if( (vol < 0) || (vol > 0x7FFF) ) {
          vol=0x7fff;
     }

     if( dsoundptr[i]->handle != NULL_HANDLE )
          sosDIGISetSampleVolume(*fhdigidriverptr, dsoundptr[i]->handle, vol);

     pan=((getangle(posx[screenpeek]-dsoundptr[i]->x,posy[screenpeek]-dsoundptr[i]->y)+(2047-ang[screenpeek]))&2047) >> 6;
     if( (pan < 0) || (pan > 35) )
          pan=13;

     if( dsoundptr[i]->handle != NULL_HANDLE )
          sosDIGISetPanLocation(*fhdigidriverptr, dsoundptr[i]->handle, PanArray[pan]);
}

VOID _far cdecl
songcallback(WORD shandle)
{
}

VOID _far cdecl
triggercallback(WORD shandle, BYTE track, BYTE id)
{
}

stopsong(int sn)
{
     if( musicmode == MM_NOHARDWARE )
          return;

     if( songptr[sn]->playing == 0 ) {
          return;
     }
     if( songptr[sn]->pending != 0 ) {    // cant stop a pending song
          return;                         // since may be interrupted
     }                                    // by trigger function

     sosMIDIStopSong(songptr[sn]->handle);
     songptr[sn]->playing=0;
}

removesong(int sn)
{
     if( musicmode == MM_NOHARDWARE )
          return;

     if( songptr[sn]->handle != NULL_HANDLE ) {
          songptr[sn]->pending=0;
          sosMIDIStopSong(songptr[sn]->handle);
          sosMIDIUnInitSong(songptr[sn]->handle);
          songptr[sn]->handle=NULL_HANDLE;
          songptr[sn]->playing=0;
     }
}

int
playsong(int sn)
{
     int       rv;
     int       fpos;

     if( (musicmode == MM_NOHARDWARE) || (toggles[TOGGLE_MUSIC] == 0) ) {
          return(0);
     }
     if( (sn < 0) || (sn >= SONGSPERLEVEL) || (songptr[sn]->playing != 0) || (songptr[sn]->pending != 0) ) {
          return(0);
     }

     if( songptr[sn]->handle != NULL_HANDLE ) {
          removesong(sn);
     }
     if( songptr[sn]->length == 0 )
         return(0);

     songdataptr->lpSongData=( LPSTR)songptr[sn]->buffer;
     songdataptr->lpSongCallback=( VOID _far *)NULL; //songcallback;

     fpos=flushall();
     if( songptr[sn]->handle == NULL_HANDLE ) {
          lseek(fhsongs,0,SEEK_SET);
          fpos=filelength(fhsongs);
          lseek(fhsongs, songptr[sn]->offset, SEEK_SET);
          fpos=tell(fhsongs);
          rv=read(fhsongs, ( void *)songptr[sn]->buffer, songptr[sn]->length);
          if( rv != songptr[sn]->length ) {
              crash("playsong: bad read");
          }
          rv=sosMIDIInitSong(songdataptr, trackmapptr, ( WORD _far *)&(songptr[sn]->handle));
          if( rv != _ERR_NO_ERROR ) {
               songptr[sn]->handle=NULL_HANDLE;
               return(0);
          }
     }
     else {
          rv=sosMIDIResetSong(songptr[sn]->handle, songdataptr);
          if( rv != _ERR_NO_ERROR ) {
               songptr[sn]->handle=NULL_HANDLE;
              #ifdef MUSICDEBUG
               showmessage("CANT RESET SONG %2d", sn);
              #endif
          }
     }

     rv=sosMIDIStartSong(songptr[sn]->handle);
     if( rv != _ERR_NO_ERROR ) {
          songptr[sn]->handle=NULL_HANDLE;
          return(0);
     }

     if( (musicv<<3) > 0 ) {
          sosMIDIFadeSong(songptr[sn]->handle,_SOS_MIDI_FADE_IN,250,
                          0,(musicv<<3), 50);
     }

    #ifdef MUSICDEBUG
     showmessage("PLAYING SONG %2d", sn);
    #endif
     songptr[sn]->playing=1;
     songptr[sn]->pending=0;

     return(1);
}

void menusong(int insubway)
{
int i,index;

     if( musicmode == MM_NOHARDWARE )
     return;

    for( i=0; i<SONGSPERLEVEL; i++ ) {
         removesong(i);
    }

     if(insubway)
          index=(NUMLEVELS*(AVAILMODES*SONGSPERLEVEL)+3);

     else
          index=NUMLEVELS*(AVAILMODES*SONGSPERLEVEL);

     switch( musicmode ) {
     case MM_MIDIFM:
          break;
     case MM_MIDIAWE32:
           index++;
          break;
     case MM_MIDIGEN:
           index+=2;
          break;
     }

     for( i=0; i<SONGSPERLEVEL; i++ ) {
          songptr[0]->handle=NULL_HANDLE;
          songptr[0]->offset=songlist[index*3]*4096;
          songptr[0]->playing=0;
          songptr[0]->pending=0;
          songptr[0]->length=( WORD)songlist[(index*3)+1];
          if( songptr[0]->length >= MAXBASESONGLENGTH ) {
               crash("prepsongs: basesong exceeded max length");
          }
     }
     songptr[0]->buffer=&basesongdata;

     playsong(BASESONG);

}

startmusic(int level)
{
     int       i,index;

     if( musicmode == MM_NOHARDWARE ) {
          return;
     }

     if( level > 6 ) {
          return;
     }

     for( i=0; i<SONGSPERLEVEL; i++ ) {
          removesong(i);
     }

     index=totalsongsperlevel*(level);

     switch( musicmode ) {
     case MM_MIDIFM:
          break;
     case MM_MIDIAWE32:
           index+=SONGSPERLEVEL;
          break;
     case MM_MIDIGEN:
           index+=SONGSPERLEVEL*2;
          break;
     }

     for( i=0; i<SONGSPERLEVEL; i++ ) {
          songptr[i]->handle=NULL_HANDLE;
          songptr[i]->offset=songlist[(index*3)+(i*3)]*4096;
          songptr[i]->playing=0;
          songptr[i]->pending=0;
          songptr[i]->length=( WORD)songlist[((index*3)+(i*3))+1];
          if( songptr[i]->length >= MAXBASESONGLENGTH ) {
               crash("prepsongs: basesong exceeded max length");
          }
     }
     songptr[0]->buffer=&basesongdata;
     songptr[1]->buffer=&secondsongdata;
     songptr[2]->buffer=&thirdsongdata;

     playsong(BASESONG);
}

songmastervolume(int vol)
{
     if( musicmode == MM_NOHARDWARE )
          return;

     if( (vol < 0) || (vol > 127) )
          vol=127;
     sosMIDISetMasterVolume(vol);
}

soundmastervolume(int vol)
{
     if( soundmode == SM_NOHARDWARE )
          return;

     if( (vol < 0) || (vol > 0x7FFF) )
          vol=0x7fff;
     sosDIGISetMasterVolume(*fhdigidriverptr, vol);
}

musicfade(int  dir)
{
     int i;

     if( musicmode == MM_NOHARDWARE ) {
          return;
     }
     for( i=0; i<SONGSPERLEVEL; i++ ) {
          if( (songptr[i]->handle != NULL_HANDLE) ) {
               if( ((musicv<<3) > 0) && (sosMIDISongDone(songptr[i]->handle) == _FALSE) ) {
                    sosMIDIFadeSong(songptr[i]->handle,_SOS_MIDI_FADE_OUT_STOP, 700,
                                    (musicv<<3),0,50);
                    while( (sosMIDISongDone(songptr[i]->handle)==_FALSE) ) {
                    }
               }
               removesong(i);
          }
     }
}

musicoff(void)
{
     int  i;

     if( musicmode != MM_NOHARDWARE )  {
          for( i=0; i<SONGSPERLEVEL; i++ ) {
               if( songptr[i]->handle == NULL_HANDLE )
                    continue;
               sosMIDIStopSong(songptr[i]->handle);
               sosMIDIUnInitSong(songptr[i]->handle);
          }
    }
}

stopallsounds()
{
     int       i;

     if( soundmode == SM_NOHARDWARE )
          return;

     for( i=0; i< MAXSOUNDS; i++ ) {
          if( dsoundptr[i]->handle == NULL_HANDLE )
               continue;
          sosDIGIStopSample(*fhdigidriverptr, dsoundptr[i]->handle);
          sbufptr[dsoundptr[i]->sndnum]->users--;
          if( sbufptr[dsoundptr[i]->sndnum]->users < 0 )
              sbufptr[dsoundptr[i]->sndnum]->users=0;
          if( sbufptr[dsoundptr[i]->sndnum]->users == 0 ) {
               sbufptr[dsoundptr[i]->sndnum]->cache_lock=0x00;
          }
          dsoundptr[i]->handle=NULL_HANDLE;
          dsoundptr[i]->plevel=0;
          dsoundptr[i]->sndnum=-1;
     }

//clear variables that track looping sounds
     loopinsound=-1;
     baydoorloop=-1;
     ambsubloop=-1;

}

uninitsb(void)
{
     int       i;

     if( musicmode != MM_NOHARDWARE )  {
          for( i=0; i<SONGSPERLEVEL; i++ ) {
               if( songptr[i]->handle == NULL_HANDLE )
                    continue;
               sosMIDIStopSong(songptr[i]->handle);
               sosMIDIUnInitSong(songptr[i]->handle);
          }
          sosMIDIUnInitDriver(*fhmididriverptr, _TRUE );
         sosMIDIUnInitSystem();
    }

    if( digiloopflag != 0 ) {
          for( i=0; i<MAXLOOPS; i++ ) {
               if( lsoundptr[i]->handle == NULL_HANDLE )
                    continue;
               sosDIGIStopSample(*fhdigidriverptr, lsoundptr[i]->handle);
          }
    }

     if( soundmode != SM_NOHARDWARE ) {
          for( i=0; i<MAXSOUNDS; i++ ) {
               if( dsoundptr[i]->handle == NULL_HANDLE )
                    continue;
               sosDIGIStopSample(*fhdigidriverptr, dsoundptr[i]->handle);
          }
          if( soundmode != SM_NOHARDWARE ) {
               sosTIMERRemoveEvent(*fhdigifillptr);
               sosDIGIUnInitDriver(*fhdigidriverptr, _TRUE,_TRUE);
               sosDIGIUnInitSystem();
          }
     }

     if( fhsounds >= 0 )
          close(fhsounds);
     if( fhsongs >= 0 )
          close(fhsongs);

     if( hLoopFile != -1 )
           close( hLoopFile );
     if( LoopList != ( DWORD *)NULL )
           free( LoopList );

     if( melodicbankptr )
          free( ( void *)melodicbankptr);
     if( drumbankptr )
          free( ( void *)drumbankptr);
     if( digitalbankptr )
          free( ( void *)digitalbankptr);

     smkuninit(*fhdigidriverptr);
}

void
initlooptable(void)
{
     if(!digiloopflag)
          return;

     hLoopFile = open("LOOPS",O_RDONLY | O_BINARY);
     if( hLoopFile == -1 ) {
          crash("initlooptable: cant open loops");
     }
     LoopList = ( DWORD    *)malloc(0x1000);
    if( LoopList == ( DWORD *)NULL )
         crash("initlooptable: cant get mem for LoopList");
     lseek(hLoopFile,-4096L,SEEK_END);
     read(hLoopFile,(void *)FP_OFF(LoopList),4096);
}

void
tekprepdigiloops(void)
{
     if( !digiloopflag )
          return;

     loopbufptr[0]->cache_lock=1;
     allocache(&(loopbufptr[0]->cache_ptr), MAX_LOOP_LENGTH, &(loopbufptr[0]->cache_lock));
     if( loopbufptr[0]->cache_ptr == 0L ) {
          loopbufptr[0]->cache_lock=0x00;
          digiloopflag=0;
     }

     loopbufptr[1]->cache_lock=1;
     allocache(&(loopbufptr[1]->cache_ptr), MAX_LOOP_LENGTH, &(loopbufptr[1]->cache_lock));
     if( loopbufptr[1]->cache_ptr == 0L ) {
          loopbufptr[1]->cache_lock=0x00;
          digiloopflag=0;
     }
}

void
SND_LoadLoop(int load_start)
{
     int  nr=0;
     SeekIndex = ( LoopList[(LoopIndex * 3)+0] * 4096 );
     SampleSize= (WORD)LoopList[(LoopIndex * 3) + 1];
     lseek(hLoopFile, SeekIndex, SEEK_SET);

     if(!load_start) {
          nr=read(hLoopFile,( void *)(loopbufptr[looptoggle]->cache_ptr),SampleSize);
          if( nr != SampleSize ) {
               loopbufptr[looptoggle]->cache_ptr=0L;
               loopbufptr[looptoggle]->cache_lock=0x00;
               crash("read problem with loops");
          }
          loopsampleptr[looptoggle]->lpSamplePtr=( LPSTR)loopbufptr[looptoggle]->cache_ptr;
          loopsampleptr[looptoggle]->dwSampleSize= SampleSize;
          loopsampleptr[looptoggle]->dwSampleByteLength= SampleSize;
     }
     else {
          nr=read(hLoopFile,( void *)(loopbufptr[looptoggle]->cache_ptr),SampleSize);
          if( nr != SampleSize ) {
               loopbufptr[looptoggle]->cache_ptr=0L;
               loopbufptr[looptoggle]->cache_lock=0x00;
               crash("read problem with loops");
          }
         loopsampleptr[looptoggle]->lpSamplePtr=( LPSTR)loopbufptr[looptoggle]->cache_ptr;
          loopsampleptr[looptoggle]->dwSampleSize= SampleSize;
          loopsampleptr[looptoggle]->dwSampleByteLength= SampleSize;
          lsoundptr[looptoggle]->handle=sosDIGIStartSample(*fhdigidriverptr,loopsampleptr[looptoggle]);
          looptoggle^=1;
     }

     LoopIndex++;
     if(LoopIndex>MAX_SND_LOOPS-1)
          LoopIndex=0;
}

VOID
SND_SwapLoops(VOID)
{
     int temp,i;

     temp=looptoggle^1;

     if( !sosDIGISampleDone(*fhdigidriverptr,lsoundptr[temp]->handle) )
     {
          sosDIGIStopSample(*fhdigidriverptr,lsoundptr[temp]->handle);
          lsoundptr[looptoggle]->handle = sosDIGIStartSample(*fhdigidriverptr,loopsampleptr[looptoggle] );
     }

     looptoggle^=1;

}

#endif

#define NULL_HANDLE        -1

buildvfs_kfd fhsongs = buildvfs_kfd_invalid;

#define BASESONG            0
#define MAXBASESONGLENGTH   44136
#define AVAILMODES          3
#define SONGSPERLEVEL       3
#define	NUMLEVELS           7

int       totalsongsperlevel;
char      basesongdata[MAXBASESONGLENGTH];
char      secondsongdata[MAXBASESONGLENGTH]; // TODO: Unused ?
char      thirdsongdata[MAXBASESONGLENGTH];

struct songtype {
    int32_t   handle;
    int32_t   offset;
    int32_t   playing;
    int32_t   pending;
    char     *buffer;
    int32_t   length;
};

InitSong songdataptr;

songtype song[SONGSPERLEVEL];
songtype *songptr[SONGSPERLEVEL];
int       songlist[4096];

#define MM_NOHARDWARE       0
#define MM_MIDIFM           1
#define MM_MIDIDIGI         2
#define MM_MIDIGEN          3
#define MM_MIDIAWE32        4

char      musicmode = MM_NOHARDWARE;

TrackDevice songtrackmap = {
    0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff
};

int       digiloopflag=0;
extern int MV_MixRate;
extern int musicv;

#define MELODICBANKLENGTH   0x152C
#define DRUMBANKLENGTH      0x152C
char *melodicbankptr;
char *drumbankptr;

void
setupmidi()
{
    buildvfs_kfd fh;
    int       rv, i;

    if (musicmode == MM_NOHARDWARE)
        return;

    melodicbankptr = NULL;
    drumbankptr = NULL;

    if ((musicmode != MM_MIDIFM) && (musicmode != MM_MIDIDIGI))
        goto nobanks;

    melodicbankptr = (char*)malloc(MELODICBANKLENGTH);
    drumbankptr = (char*)malloc(DRUMBANKLENGTH);
    if ((melodicbankptr == NULL) || (drumbankptr == NULL)) {
        crash("setupmidi: failed malloc");
    }
    if ((fh = kopen4load("melodic.bnk", 0)) == buildvfs_kfd_invalid) {
        crash("setupmidi: cant open melodic.bnk");
    }
    kread(fh, (void*)melodicbankptr, MELODICBANKLENGTH);
    kclose(fh);
    rv = FMSetBank(melodicbankptr);
    if (rv != 0) {
        crash("setupmidi: bad SetInsData");
    }
    if ((fh = kopen4load("drum.bnk", O_RDONLY)) == buildvfs_kfd_invalid) {
        crash("setupmidi: cant open drum.bnk");
    }
    kread(fh, (void*)drumbankptr, DRUMBANKLENGTH);
    kclose(fh);
    rv = FMSetBank(drumbankptr);
    if (rv != 0) {
        crash("setupmidi: bad SetInsData");
    }

#if 0
    if ((musicmode == MM_MIDIDIGI) && (midihardwareptr->wPort == 0x388)) {
        if ((fh = open("test.dig", O_BINARY | O_RDWR)) == -1) {
            crash("setupmidi: cant open test.dig");
        }
        dl = lseek(fh, 0L, SEEK_END);
        lseek(fh, 0L, SEEK_SET);
        digitalbankptr = (LPSTR)malloc((size_t)dl);
        if (digitalbankptr == (LPSTR)NULL) {
            crash("setupmidi: failed malloc digbnkptr");
        }
        rv = read(fh, (void*)digitalbankptr, dl);
        if (rv != dl) {
            crash("setupmidi: bad .dig read");
        }
        close(fh);
        rv = sosMIDISetInsData(*fhmididigidriverptr, digitalbankptr, 1);
        if (rv != _ERR_NO_ERROR) {
            crash("setupmidi: bad SetInsData");
        }
    }
#endif

nobanks:

    if (musicmode != MM_NOHARDWARE) {
        if ((fhsongs = kopen4load("SONGS", 0)) == buildvfs_kfd_invalid) {
            crash("setupmidi: cant open songs");
        }
        klseek(fhsongs, 0, SEEK_SET);
        klseek(fhsongs, -4096, SEEK_END);
        kread(fhsongs, (void*)songlist, 4096);
    }

    //jsa venom
    for (i = 0; i < SONGSPERLEVEL; i++) {
        songptr[i] = &song[i];
        songptr[i]->handle = NULL_HANDLE;
        songptr[i]->offset = 0;
        songptr[i]->playing = 0;
        songptr[i]->pending = 0;
        songptr[i]->length = 0L;
    }
    songptr[0]->buffer = basesongdata;
    songptr[1]->buffer = secondsongdata;
    songptr[2]->buffer = thirdsongdata;

    totalsongsperlevel = SONGSPERLEVEL * AVAILMODES;
}

void
initsb(char UNUSED(option1),char UNUSED(option2),int UNUSED(digihz),
    char UNUSED(option7a),char UNUSED(option7b),int UNUSED(val),char UNUSED(option7c))
{
    #ifdef MIXERTYPEWIN
    void* initdata = (void*)win_gethwnd(); // used for DirectSound
    #else
    void* initdata = NULL;
    #endif

    if (FX_Init(NumVoices, NumChannels, MixRate, initdata) != FX_Ok)
    {
        initprintf("Error initializing sound card!\n");
        return;
    }

    musicmode = MM_MIDIFM;

    HMIInit(MV_MixRate);
    setupmidi();
}

void
uninitsb(void)
{
    int i;
    if (musicmode != MM_NOHARDWARE)
    {
        for (i = 0; i < SONGSPERLEVEL; i++)
        {
            if (songptr[i]->handle == NULL_HANDLE)
                continue;
            HMIStopSong(songptr[i]->handle);
            HMIUnInitSong(songptr[i]->handle);
        }
        HMIUnInit();
    }
    if (fhsongs != buildvfs_kfd_invalid)
        kclose(fhsongs);
    if (melodicbankptr)
        free(melodicbankptr);
    if (drumbankptr)
        free(drumbankptr);

    FX_Shutdown();
}

void
musicoff(void)
{
    int i;

    if (musicmode != MM_NOHARDWARE)
    {
        for (i = 0; i < SONGSPERLEVEL; i++) {
            if (songptr[i]->handle == NULL_HANDLE)
                continue;
            HMIStopSong(songptr[i]->handle);
            HMIUnInitSong(songptr[i]->handle);
        }
    }
}

int
playsound(int UNUSED(sn), int UNUSED(sndx), int UNUSED(sndy), int UNUSED(loop), short UNUSED(type))
{
    return -1;
}

void
updatevehiclesnds(int UNUSED(i), int UNUSED(sndx), int UNUSED(sndy))
{
}

void
stopsound(int UNUSED(i))
{
}

void
songmastervolume(int vol)
{
    if (musicmode == MM_NOHARDWARE)
        return;

    if ((vol < 0) || (vol > 127))
        vol = 127;
    HMISetMasterVolume(vol);
}

void
soundmastervolume(int UNUSED(vol))
{
}

void
updatesounds(int UNUSED(snum))
{
}

void
stopallsounds()
{
}

void
removesong(int sn)
{
     if( musicmode == MM_NOHARDWARE )
          return;

     if( songptr[sn]->handle != NULL_HANDLE )
     {
          songptr[sn]->pending=0;
          HMIStopSong(songptr[sn]->handle);
          HMIUnInitSong(songptr[sn]->handle);
          songptr[sn]->handle=NULL_HANDLE;
          songptr[sn]->playing=0;
     }
}

int
playsong(int sn)
{
    int       rv;
    int       fpos;

    if ((musicmode == MM_NOHARDWARE) || (toggles[TOGGLE_MUSIC] == 0))
        return(0);

    if ((sn < 0) || (sn >= SONGSPERLEVEL) || (songptr[sn]->playing != 0) || (songptr[sn]->pending != 0))
        return(0);

    if (songptr[sn]->handle != NULL_HANDLE)
        removesong(sn);

    if (songptr[sn]->length == 0)
        return(0);

    songdataptr.songptr = (uint8_t*)songptr[sn]->buffer;
    songdataptr.callback = NULL; //songcallback;

    if (songptr[sn]->handle == NULL_HANDLE) {
        klseek(fhsongs, 0, SEEK_SET);
        fpos = kfilelength(fhsongs);
        klseek(fhsongs, songptr[sn]->offset, SEEK_SET);
        fpos = ktell(fhsongs);
        rv = kread(fhsongs, (void*)songptr[sn]->buffer, songptr[sn]->length);
        if (rv != songptr[sn]->length) {
            crash("playsong: bad read");
        }
        rv = HMIInitSong(&songdataptr, &songtrackmap, (uint32_t*)&songptr[sn]->handle);
        if (rv != 0) {
            songptr[sn]->handle = NULL_HANDLE;
            return(0);
        }
    }
    else {
        rv = HMIResetSong(songptr[sn]->handle, &songdataptr);
        if (rv != 0) {
            songptr[sn]->handle = NULL_HANDLE;
#ifdef MUSICDEBUG
            showmessage("CANT RESET SONG %2d", sn);
#endif
        }
    }

    rv = HMIStartSong(songptr[sn]->handle);
    if (rv != 0) {
        songptr[sn]->handle = NULL_HANDLE;
        return(0);
    }

    if ((musicv << 3) > 0) {
        HMIFadeSong(songptr[sn]->handle, SONG_FADE_IN, 250,
            0, (musicv << 3), 50);
    }

#ifdef MUSICDEBUG
    showmessage("PLAYING SONG %2d", sn);
#endif
    songptr[sn]->playing = 1;
    songptr[sn]->pending = 0;

    return(1);
}

void
musicfade()
{
    int i;

    if (musicmode == MM_NOHARDWARE)
        return;

    for (i = 0; i < SONGSPERLEVEL; i++)
    {
        if ((songptr[i]->handle != NULL_HANDLE)) {
            if (((musicv << 3) > 0) && !HMISongDone(songptr[i]->handle)) {
                HMIFadeSong(songptr[i]->handle, SONG_FADE_OUT_STOP, 700,
                    (musicv << 3), 0, 50);
                while (!HMISongDone(songptr[i]->handle)) {
                }
            }
            removesong(i);
        }
    }
}

void
menusong(int insubway)
{
    int i, index;

    if (musicmode == MM_NOHARDWARE)
        return;

    for (i = 0; i < SONGSPERLEVEL; i++)
        removesong(i);

    if (insubway)
        index = (NUMLEVELS * (AVAILMODES * SONGSPERLEVEL) + 3);

    else
        index = NUMLEVELS * (AVAILMODES * SONGSPERLEVEL);

    switch (musicmode) {
    case MM_MIDIFM:
        break;
    case MM_MIDIAWE32:
        index++;
        break;
    case MM_MIDIGEN:
        index += 2;
        break;
    }

    for (i = 0; i < SONGSPERLEVEL; i++) {
        songptr[0]->handle = NULL_HANDLE;
        songptr[0]->offset = songlist[index * 3] * 4096;
        songptr[0]->playing = 0;
        songptr[0]->pending = 0;
        songptr[0]->length = (uint16_t)songlist[(index * 3) + 1];
        if (songptr[0]->length >= MAXBASESONGLENGTH) {
            crash("prepsongs: basesong exceeded max length");
        }
    }
    songptr[0]->buffer = basesongdata;

    playsong(BASESONG);
}

void
startmusic(int level)
{
    int i, index;

    if (musicmode == MM_NOHARDWARE)
        return;

    if (level > 6)
        return;

    for (i = 0; i < SONGSPERLEVEL; i++)
        removesong(i);

    index = totalsongsperlevel * (level);

    switch (musicmode) {
    case MM_MIDIFM:
        break;
    case MM_MIDIAWE32:
        index += SONGSPERLEVEL;
        break;
    case MM_MIDIGEN:
        index += SONGSPERLEVEL * 2;
        break;
    }

    for (i = 0; i < SONGSPERLEVEL; i++)
    {
        songptr[i]->handle = NULL_HANDLE;
        songptr[i]->offset = songlist[(index * 3) + (i * 3)] * 4096;
        songptr[i]->playing = 0;
        songptr[i]->pending = 0;
        songptr[i]->length = (uint16_t)songlist[((index * 3) + (i * 3)) + 1];
        if (songptr[i]->length >= MAXBASESONGLENGTH) {
            crash("prepsongs: basesong exceeded max length");
        }
    }
    songptr[0]->buffer = basesongdata;
    songptr[1]->buffer = secondsongdata;
    songptr[2]->buffer = thirdsongdata;

    playsong(BASESONG);
}