Beispiel #1
0
INT
PAL_MKFDecompressChunk(
   LPBYTE          lpBuffer,
   UINT            uiBufferSize,
   UINT            uiChunkNum,
   FILE           *fp
)
/*++
  Purpose:

    Decompress a compressed chunk from an MKF archive into lpBuffer.

  Parameters:

    [OUT] lpBuffer - pointer to the destination buffer.

    [IN]  uiBufferSize - size of the destination buffer.

    [IN]  uiChunkNum - the number of the chunk in the MKF archive to read.

    [IN]  fp - pointer to the fopen'ed MKF file.

  Return value:

    Integer value which indicates the size of the chunk.
    -1 if there are error in parameters, or buffer size is not enough.
    -3 if cannot allocate memory for decompression.

--*/
{
   LPBYTE          buf;
   int             len;

   len = PAL_MKFGetChunkSize(uiChunkNum, fp);

   if (len <= 0)
   {
      return len;
   }

   buf = (LPBYTE)malloc(len);
   if (buf == NULL)
   {
      return -3;
   }

   PAL_MKFReadChunk(buf, len, uiChunkNum, fp);

   len = Decompress(buf, lpBuffer, uiBufferSize);
   free(buf);

   return len;
}
Beispiel #2
0
INT
PAL_InitUI(
   VOID
)
/*++
  Purpose:

    Initialze the UI subsystem.

  Parameters:

    None.

  Return value:

    0 = success, -1 = fail.

--*/
{
   int        iSize;

   //
   // Load the UI sprite.
   //
   iSize = PAL_MKFGetChunkSize(CHUNKNUM_SPRITEUI, gpGlobals->f.fpDATA);
   if (iSize < 0)
   {
      return -1;
   }

   gpSpriteUI = (LPSPRITE)calloc(1, iSize);
   if (gpSpriteUI == NULL)
   {
      return -1;
   }

   PAL_MKFReadChunk(gpSpriteUI, iSize, CHUNKNUM_SPRITEUI, gpGlobals->f.fpDATA);

   return 0;
}
Beispiel #3
0
INT
PAL_InitText(
   VOID
)
/*++
  Purpose:

    Initialize the in-game texts.

  Parameters:

    None.

  Return value:

    0 = success.
    -1 = memory allocation error.

--*/
{
   FILE       *fpMsg, *fpWord;
   int         i;

   //
   // Open the message and word data files.
   //
   fpMsg = UTIL_OpenRequiredFile("m.msg");
   fpWord = UTIL_OpenRequiredFile("word.dat");

   //
   // See how many words we have
   //
   fseek(fpWord, 0, SEEK_END);
   i = ftell(fpWord);

   //
   // Each word has 10 bytes
   //
   g_TextLib.nWords = (i + (WORD_LENGTH - 1)) / WORD_LENGTH;

   //
   // Read the words
   //
   g_TextLib.lpWordBuf = (LPBYTE)malloc(i);
   if (g_TextLib.lpWordBuf == NULL)
   {
      fclose(fpWord);
      fclose(fpMsg);
      return -1;
   }
   fseek(fpWord, 0, SEEK_SET);
   fread(g_TextLib.lpWordBuf, i, 1, fpWord);

   //
   // Close the words file
   //
   fclose(fpWord);

   //
   // Read the message offsets. The message offsets are in SSS.MKF #3
   //
   i = PAL_MKFGetChunkSize(3, gpGlobals->f.fpSSS) / sizeof(DWORD);
   g_TextLib.nMsgs = i - 1;

   g_TextLib.lpMsgOffset = (LPDWORD)malloc(i * sizeof(DWORD));
   if (g_TextLib.lpMsgOffset == NULL)
   {
      free(g_TextLib.lpWordBuf);
      fclose(fpMsg);
      return -1;
   }

   PAL_MKFReadChunk((LPBYTE)(g_TextLib.lpMsgOffset), i * sizeof(DWORD), 3,
      gpGlobals->f.fpSSS);

   //
   // Read the messages.
   //
   fseek(fpMsg, 0, SEEK_END);
   i = ftell(fpMsg);

   g_TextLib.lpMsgBuf = (LPBYTE)malloc(i);
   if (g_TextLib.lpMsgBuf == NULL)
   {
      free(g_TextLib.lpMsgOffset);
      free(g_TextLib.lpWordBuf);
      fclose(fpMsg);
      return -1;
   }

   fseek(fpMsg, 0, SEEK_SET);
   fread(g_TextLib.lpMsgBuf, 1, i, fpMsg);

   fclose(fpMsg);

   g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT;
   g_TextLib.bIcon = 0;
   g_TextLib.posIcon = 0;
   g_TextLib.nCurrentDialogLine = 0;
   g_TextLib.iDelayTime = 3;
   g_TextLib.posDialogTitle = PAL_XY(12, 8);
   g_TextLib.posDialogText = PAL_XY(44, 26);
   g_TextLib.bDialogPosition = kDialogUpper;
   g_TextLib.fUserSkip = FALSE;

   PAL_MKFReadChunk(g_TextLib.bufDialogIcons, 282, 12, gpGlobals->f.fpDATA);

   return 0;
}
Beispiel #4
0
VOID
MIDI_Play(
   INT       iNumRIX,
   BOOL      fLoop
)
/*++
  Purpose:

    Start playing the specified music in MIDI format.

  Parameters:

    [IN]  iNumRIX - number of the music. 0 to stop playing current music.

    [IN]  fLoop - Whether the music should be looped or not.

  Return value:

    None.

--*/
{
   FILE            *fp;
   unsigned char   *buf;
   int              size;
   SDL_RWops       *rw;
#ifdef PAL_WIN95
   char             filename[1024];
#endif

   if (g_pMid != NULL && iNumRIX == iMidCurrent && native_midi_active())
   {
      return;
   }

   SOUND_PlayCDA(-1);
   native_midi_freesong(g_pMid);
   g_pMid = NULL;
   iMidCurrent = -1;

   if (g_fNoMusic || iNumRIX <= 0)
   {
      return;
   }

#ifdef PAL_WIN95
   sprintf(filename, "%s/musics/%.3d.mid", PAL_PREFIX, iNumRIX);

   g_pMid = native_midi_loadsong(filename);
   if (g_pMid != NULL)
   {
      native_midi_start(g_pMid);

      iMidCurrent = iNumRIX;
      fMidLoop = fLoop;
   }
#else
   fp = UTIL_OpenFile("midi.mkf");
   if (fp == NULL)
   {
      return;
   }

   if (iNumRIX > PAL_MKFGetChunkCount(fp))
   {
      fclose(fp);
      return;
   }

   size = PAL_MKFGetChunkSize(iNumRIX, fp);
   if (size <= 0)
   {
      fclose(fp);
      return;
   }

   buf = (unsigned char *)UTIL_malloc(size);

   PAL_MKFReadChunk((LPBYTE)buf, size, iNumRIX, fp);
   fclose(fp);

   rw = SDL_RWFromConstMem((const void *)buf, size);

   g_pMid = native_midi_loadsong_RW(rw);
   if (g_pMid != NULL)
   {
      native_midi_start(g_pMid);

      iMidCurrent = iNumRIX;
      fMidLoop = fLoop;
   }

   SDL_RWclose(rw);
   free(buf);
#endif
}
Beispiel #5
0
/*++
 Start a battle.
 
 Parameters:
 [IN]  wEnemyTeam - the number of the enemy team.
 [IN]  fIsBoss - TRUE for boss fight (not allowed to flee).
 
 Return value:
 The result of the battle.
 --*/
BATTLERESULT PAL_StartBattle(WORD wEnemyTeam, BOOL fIsBoss)
{
    int            i;
    WORD           w, wPrevWaveLevel;
    SHORT          sPrevWaveProgression;
    
    //
    // Set the screen waving effects
    //
    wPrevWaveLevel = gpGlobals->wScreenWave;
    sPrevWaveProgression = gpGlobals->sWaveProgression;
    
    gpGlobals->sWaveProgression = 0;
    gpGlobals->wScreenWave = gpGlobals->g.lprgBattleField[gpGlobals->wNumBattleField].wScreenWave;
    
    //
    // Make sure everyone in the party is alive, also clear all hidden
    // EXP count records
    //
    for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
    {
        w = gpGlobals->rgParty[i].wPlayerRole;
        
        if (gpGlobals->g.PlayerRoles.rgwHP[w] == 0)
        {
            gpGlobals->g.PlayerRoles.rgwHP[w] = 1;
            gpGlobals->rgPlayerStatus[w][kStatusPuppet] = 0;
        }
        
        gpGlobals->Exp.rgHealthExp[w].wCount = 0;
        gpGlobals->Exp.rgMagicExp[w].wCount = 0;
        gpGlobals->Exp.rgAttackExp[w].wCount = 0;
        gpGlobals->Exp.rgMagicPowerExp[w].wCount = 0;
        gpGlobals->Exp.rgDefenseExp[w].wCount = 0;
        gpGlobals->Exp.rgDexterityExp[w].wCount = 0;
        gpGlobals->Exp.rgFleeExp[w].wCount = 0;
    }
    
    //
    // Clear all item-using records
    //
    for (i = 0; i < MAX_INVENTORY; i++)
    {
        gpGlobals->rgInventory[i].nAmountInUse = 0;
    }
    
    //
    // Store all enemies
    //
    for (i = 0; i < MAX_ENEMIES_IN_TEAM; i++)
    {
        memset(&(g_Battle.rgEnemy[i]), 0, sizeof(BATTLEENEMY));
        w = gpGlobals->g.lprgEnemyTeam[wEnemyTeam].rgwEnemy[i];
        
        if (w == 0xFFFF)
        {
            break;
        }
        
        if (w != 0)
        {
            g_Battle.rgEnemy[i].e = gpGlobals->g.lprgEnemy[gpGlobals->g.rgObject[w].enemy.wEnemyID];
            g_Battle.rgEnemy[i].wObjectID = w;
            g_Battle.rgEnemy[i].state = kFighterWait;
            g_Battle.rgEnemy[i].wScriptOnTurnStart = gpGlobals->g.rgObject[w].enemy.wScriptOnTurnStart;
            g_Battle.rgEnemy[i].wScriptOnBattleEnd = gpGlobals->g.rgObject[w].enemy.wScriptOnBattleEnd;
            g_Battle.rgEnemy[i].wScriptOnReady = gpGlobals->g.rgObject[w].enemy.wScriptOnReady;
            g_Battle.rgEnemy[i].iColorShift = 0;
            g_Battle.rgEnemy[i].dwMaxHealth = g_Battle.rgEnemy[i].e.wHealth;
            
#ifndef PAL_CLASSIC
            g_Battle.rgEnemy[i].flTimeMeter = 50;
            
            //
            // HACK: Otherwise the black thief lady will be too hard to beat
            //
            if (g_Battle.rgEnemy[i].e.wDexterity == 164)
            {
                g_Battle.rgEnemy[i].e.wDexterity /= ((gpGlobals->wMaxPartyMemberIndex == 0) ? 6 : 3);
            }
            
            //
            // HACK: Heal up automatically for final boss
            //
            if (g_Battle.rgEnemy[i].e.wHealth == 32760)
            {
                for (w = 0; w < MAX_PLAYER_ROLES; w++)
                {
                    gpGlobals->g.PlayerRoles.rgwHP[w] = gpGlobals->g.PlayerRoles.rgwMaxHP[w];
                    gpGlobals->g.PlayerRoles.rgwMP[w] = gpGlobals->g.PlayerRoles.rgwMaxMP[w];
                }
            }
            
            //
            // Yet another HACKs
            //
            if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -32)
            {
                g_Battle.rgEnemy[i].e.wDexterity = 0; // for Grandma Knife
            }
            else if (g_Battle.rgEnemy[i].e.wDexterity == 20)
            {
                //
                // for Fox Demon
                //
                if (gpGlobals->g.PlayerRoles.rgwLevel[0] < 15)
                {
                    g_Battle.rgEnemy[i].e.wDexterity = 8;
                }
                else if (gpGlobals->g.PlayerRoles.rgwLevel[4] > 28 ||
                         gpGlobals->Exp.rgPrimaryExp[4].wExp > 0)
                {
                    g_Battle.rgEnemy[i].e.wDexterity = 60;
                }
            }
            else if (g_Battle.rgEnemy[i].e.wExp == 250 &&
                     g_Battle.rgEnemy[i].e.wCash == 1100)
            {
                g_Battle.rgEnemy[i].e.wDexterity += 12; // for Snake Demon
            }
            else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -60)
            {
                g_Battle.rgEnemy[i].e.wDexterity = 15; // for Spider
            }
            else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -30)
            {
                g_Battle.rgEnemy[i].e.wDexterity = (WORD)-10; // for Stone Head
            }
            else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -16)
            {
                g_Battle.rgEnemy[i].e.wDexterity = 0; // for Zombie
            }
            else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -20)
            {
                g_Battle.rgEnemy[i].e.wDexterity = -8; // for Flower Demon
            }
            else if (g_Battle.rgEnemy[i].e.wLevel < 20 &&
                     gpGlobals->wNumScene >= 0xD8 && gpGlobals->wNumScene <= 0xE2)
            {
                //
                // for low-level monsters in the Cave of Trial
                //
                g_Battle.rgEnemy[i].e.wLevel += 15;
                g_Battle.rgEnemy[i].e.wDexterity += 25;
            }
            else if (gpGlobals->wNumScene == 0x90)
            {
                g_Battle.rgEnemy[i].e.wDexterity += 25; // for Tower Dragons
            }
            else if (g_Battle.rgEnemy[i].e.wLevel == 2 &&
                     g_Battle.rgEnemy[i].e.wCash == 48)
            {
                g_Battle.rgEnemy[i].e.wDexterity += 8; // for Miao Fists
            }
            else if (g_Battle.rgEnemy[i].e.wLevel == 4 &&
                     g_Battle.rgEnemy[i].e.wCash == 240)
            {
                g_Battle.rgEnemy[i].e.wDexterity += 18; // for Fat Miao
            }
            else if (g_Battle.rgEnemy[i].e.wLevel == 16 &&
                     g_Battle.rgEnemy[i].e.wMagicRate == 4 &&
                     g_Battle.rgEnemy[i].e.wAttackEquivItemRate == 4)
            {
                g_Battle.rgEnemy[i].e.wDexterity += 50; // for Black Spider
            }
#endif
        }
    }
    
    g_Battle.wMaxEnemyIndex = i - 1;
    
    //
    // Store all players
    //
    for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
    {
        g_Battle.rgPlayer[i].flTimeMeter = 15.0f;
#ifndef PAL_CLASSIC
        g_Battle.rgPlayer[i].flTimeSpeedModifier = 2.0f;
        g_Battle.rgPlayer[i].sTurnOrder = -1;
#endif
        g_Battle.rgPlayer[i].wHidingTime = 0;
        g_Battle.rgPlayer[i].state = kFighterWait;
        g_Battle.rgPlayer[i].action.sTarget = -1;
        g_Battle.rgPlayer[i].fDefending = FALSE;
        g_Battle.rgPlayer[i].wCurrentFrame = 0;
        g_Battle.rgPlayer[i].iColorShift = FALSE;
    }
    
    //
    // Load sprites and background
    //
    PAL_LoadBattleSprites();
    PAL_LoadBattleBackground();
    
    //
    // Create the surface for scene buffer
    //
    g_Battle.lpSceneBuf =
    SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
                         gpScreen->format->Rmask, gpScreen->format->Gmask,
                         gpScreen->format->Bmask, gpScreen->format->Amask);
    
    if (g_Battle.lpSceneBuf == NULL)
    {
        TerminateOnError("PAL_StartBattle(): creating surface for scene buffer failed!");
    }
    
#if SDL_VERSION_ATLEAST(2, 0, 0)
    SDL_SetSurfacePalette(g_Battle.lpSceneBuf, gpScreen->format->palette);
#else
    SDL_SetPalette(g_Battle.lpSceneBuf, SDL_PHYSPAL | SDL_LOGPAL, VIDEO_GetPalette(), 0, 256);
#endif
    
    PAL_UpdateEquipments();
    
    g_Battle.iExpGained = 0;
    g_Battle.iCashGained = 0;
    
    g_Battle.fIsBoss = fIsBoss;
    g_Battle.fEnemyCleared = FALSE;
    g_Battle.fEnemyMoving = FALSE;
    g_Battle.iHidingTime = 0;
    g_Battle.wMovingPlayerIndex = 0;
    
    g_Battle.UI.szMsg[0] = '\0';
    g_Battle.UI.szNextMsg[0] = '\0';
    g_Battle.UI.dwMsgShowTime = 0;
    g_Battle.UI.state = kBattleUIWait;
    g_Battle.UI.fAutoAttack = FALSE;
    g_Battle.UI.wSelectedIndex = 0;
    g_Battle.UI.wPrevEnemyTarget = 0;
    
    memset(g_Battle.UI.rgShowNum, 0, sizeof(g_Battle.UI.rgShowNum));
    
    g_Battle.lpSummonSprite = NULL;
    g_Battle.sBackgroundColorShift = 0;
    
    gpGlobals->fInBattle = TRUE;
    g_Battle.BattleResult = kBattleResultPreBattle;
    
    PAL_BattleUpdateFighters();
    
    //
    // Load the battle effect sprite.
    //
    i = PAL_MKFGetChunkSize(10, gpGlobals->f.fpDATA);
    g_Battle.lpEffectSprite = UTIL_malloc(i);
    
    PAL_MKFReadChunk(g_Battle.lpEffectSprite, i, 10, gpGlobals->f.fpDATA);
    
#ifdef PAL_CLASSIC
    g_Battle.Phase = kBattlePhaseSelectAction;
    g_Battle.fRepeat = FALSE;
    g_Battle.fForce = FALSE;
    g_Battle.fFlee = FALSE;
#endif
    
#ifdef PAL_ALLOW_KEYREPEAT
    SDL_EnableKeyRepeat(120, 75);
#endif
    
    //
    // Run the main battle routine.
    //
    i = PAL_BattleMain();
    
#ifdef PAL_ALLOW_KEYREPEAT
    SDL_EnableKeyRepeat(0, 0);
    PAL_ClearKeyState();
    g_InputState.prevdir = kDirUnknown;
#endif
    
    if (i == kBattleResultWon)
    {
        //
        // Player won the battle. Add the Experience points.
        //
        PAL_BattleWon();
    }
    
    //
    // Clear all item-using records
    //
    for (w = 0; w < MAX_INVENTORY; w++)
    {
        gpGlobals->rgInventory[w].nAmountInUse = 0;
    }
    
    //
    // Clear all player status, poisons and temporary effects
    //
    PAL_ClearAllPlayerStatus();
    for (w = 0; w < MAX_PLAYER_ROLES; w++)
    {
        PAL_CurePoisonByLevel(w, 3);
        PAL_RemoveEquipmentEffect(w, kBodyPartExtra);
    }
    
    //
    // Free all the battle sprites
    //
    PAL_FreeBattleSprites();
    free(g_Battle.lpEffectSprite);
    
    //
    // Free the surfaces for the background picture and scene buffer
    //
    SDL_FreeSurface(g_Battle.lpBackground);
    SDL_FreeSurface(g_Battle.lpSceneBuf);
    
    g_Battle.lpBackground = NULL;
    g_Battle.lpSceneBuf = NULL;
    
    gpGlobals->fInBattle = FALSE;
    
    PAL_PlayMUS(gpGlobals->wNumMusic, TRUE, 1);
    
    //
    // Restore the screen waving effects
    //
    gpGlobals->sWaveProgression = sPrevWaveProgression;
    gpGlobals->wScreenWave = wPrevWaveLevel;
    
    return i;
}
Beispiel #6
0
VOID
PAL_SaveGame(
   LPCSTR         szFileName,
   WORD           wSavedTimes
)
/*++
  Purpose:

    Save the current game state to file.

  Parameters:

    [IN]  szFileName - file name of saved game.

  Return value:

    None.

--*/
{
   FILE                     *fp;
   static   SAVEDGAME       s;
   UINT32                    i;

   //
   // Put all the data to the saved game struct.
   //
   s.wViewportX = PAL_X(gpGlobals->viewport);
   s.wViewportY = PAL_Y(gpGlobals->viewport);
   s.nPartyMember = gpGlobals->wMaxPartyMemberIndex;
   s.wNumScene = gpGlobals->wNumScene;
   s.wPaletteOffset = (gpGlobals->fNightPalette ? 0x180 : 0);
   s.wPartyDirection = gpGlobals->wPartyDirection;
   s.wNumMusic = gpGlobals->wNumMusic;
   s.wNumBattleMusic = gpGlobals->wNumBattleMusic;
   s.wNumBattleField = gpGlobals->wNumBattleField;
   s.wScreenWave = gpGlobals->wScreenWave;
   s.wCollectValue = gpGlobals->wCollectValue;
   s.wLayer = gpGlobals->wLayer;
   s.wChaseRange = gpGlobals->wChaseRange;
   s.wChasespeedChangeCycles = gpGlobals->wChasespeedChangeCycles;
   s.nFollower = gpGlobals->nFollower;
   s.dwCash = gpGlobals->dwCash;
#ifndef PAL_CLASSIC
   s.wBattleSpeed = gpGlobals->bBattleSpeed;
#else
   s.wBattleSpeed = 2;
#endif

   memcpy(s.rgParty, gpGlobals->rgParty, sizeof(gpGlobals->rgParty));
   memcpy(s.rgTrail, gpGlobals->rgTrail, sizeof(gpGlobals->rgTrail));
   s.Exp = gpGlobals->Exp;
   s.PlayerRoles = gpGlobals->g.PlayerRoles;
   memcpy(s.rgPoisonStatus, gpGlobals->rgPoisonStatus, sizeof(gpGlobals->rgPoisonStatus));
   memcpy(s.rgInventory, gpGlobals->rgInventory, sizeof(gpGlobals->rgInventory));
   memcpy(s.rgScene, gpGlobals->g.rgScene, sizeof(gpGlobals->g.rgScene));
   memcpy(s.rgObject, gpGlobals->g.rgObject, sizeof(gpGlobals->g.rgObject));
   memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject,
      sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);

   s.wSavedTimes = wSavedTimes;

   //
   // Adjust endianness
   //
   DO_BYTESWAP(&s, sizeof(SAVEDGAME));

   //
   // Cash amount is in DWORD, so do a wordswap in Big-Endian.
   //
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
   s.dwCash = ((s.dwCash >> 16) | (s.dwCash << 16));
#endif

   //
   // Try writing to file
   //
   fp = fopen(szFileName, "wb");
   if (fp == NULL)
   {
      return;
   }

   i = PAL_MKFGetChunkSize(0, gpGlobals->f.fpSSS);
   i += sizeof(SAVEDGAME) - sizeof(EVENTOBJECT) * MAX_EVENT_OBJECTS;

   fwrite(&s, i, 1, fp);
   fclose(fp);
}
Beispiel #7
0
VOID
MIDI_Play(
   INT       iNumRIX,
   BOOL      fLoop
)
/*++
  Purpose:

    Start playing the specified music in MIDI format.

  Parameters:

    [IN]  iNumRIX - number of the music. 0 to stop playing current music.

    [IN]  fLoop - Whether the music should be looped or not.

  Return value:

    None.

--*/
{
   FILE            *fp;
   unsigned char   *buf;
   int              size;
   SDL_RWops       *rw;

#ifdef TIMIDITY
   if (g_pMid != NULL && iNumRIX == iMidCurrent && Timidity_Active())
#else
   if (g_pMid != NULL && iNumRIX == iMidCurrent && native_midi_active())
#endif
   {
      return;
   }

   SOUND_PlayCDA(-1);
#ifdef TIMIDITY
   Timidity_FreeSong(g_pMid);
#else
   native_midi_freesong(g_pMid);
#endif
   g_pMid = NULL;
   iMidCurrent = -1;

   if (g_fNoMusic || iNumRIX <= 0)
   {
      return;
   }

   fp = fopen(PAL_PREFIX "midi.mkf", "rb");
   if (fp == NULL)
   {
      return;
   }

   if (iNumRIX > PAL_MKFGetChunkCount(fp))
   {
      fclose(fp);
      return;
   }

   size = PAL_MKFGetChunkSize(iNumRIX, fp);
   if (size <= 0)
   {
      fclose(fp);
      return;
   }

   buf = (unsigned char *)UTIL_malloc(size);

   PAL_MKFReadChunk((LPBYTE)buf, size, iNumRIX, fp);
   fclose(fp);

   rw = SDL_RWFromConstMem((const void *)buf, size);

#ifdef TIMIDITY
   g_pMid = Timidity_LoadSong_RW(rw);
#else
   g_pMid = native_midi_loadsong_RW(rw);
#endif
   if (g_pMid != NULL)
   {
#ifdef TIMIDITY
      Timidity_Start(g_pMid);
#else
      native_midi_start(g_pMid);
#endif

      iMidCurrent = iNumRIX;
      fMidLoop = fLoop;
   }

   SDL_RWclose(rw);
   free(buf);
}