// Подключаем файл с дефайнами
#include "modload.inc"
#include "io.hmm"



// Объявление процедур
static word fastcall SwapAndDouble(AX);



// Меняет местами старшую и младшую части и удваивает результат
:static word fastcall SwapAndDouble(AX)
{
 AL><AH;
 AX<<=1;
}



// Подгрузка мод-файла
:void static fastcall MODMODULE::LoadModule(EDX)
{
 $push ES,EBP

  // Открываем файл с модулем
  BX=OPEN(EDX,0);

   // Считываем заголовок мод-файла
   FREAD(BX,#ModModule,1084);
   // Узнаем количество семплов в модуле
   IF (ModModule.Signature[0]<32){
      // 15 семплов
      ModModule.NumSamples=15;
      // 4 канала
      ModModule.NumVoices=4;
      // Сдвигаем считанный заголовок
      ES=DS; EDI=#ModModule.OrderLength;
      ESI=#ModModule.ModSample[15].Name;
      ECX=130>>1;
      $rep $movsw
      // Устанавливаем положение в файле на первый паттерн
      LLSEEK(BX,600,0);
   }else{
      // 31 семпл
      ModModule.NumSamples=31;
      // Распознаем количество каналов по сигнатуре
      // "xCHN" или "FLTx" - x каналов
      // "CHxx" - xx каналов
      // "xxCH" - xx каналов
      // "M.K." или "M!K!" - 4 канала
      // "OKTA" - 8 каналов
      EAX=DSDWORD[#ModModule.Signature];
      IF (EAX=='.K.M')||(EAX=='!K!M') ModModule.NumVoices=4;
      else IF (EAX=='ATKO') ModModule.NumVoices=8;
      ELSE IF (AX=='HC'){
         // Узнаем количество каналов (AAD <=> AX=10*AH+AL)
         EAX>>=16; AL><AH; AX-=3030h; $aad
         ModModule.NumVoices=AL;
      }ELSE{
         // Сохраняем хендлер файла с модулем
         $push EBX
          EBX=EAX>>16;
          IF (BX=='HC'){
             // Узнаем количество каналов (AAD <=> AX=10*AH+AL)
             AL><AH; AX-=3030h; $aad
             ModModule.NumVoices=AL;
          }ELSE{
             EBX=EAX>>8;
             IF (EBX=='NHC') ModModule.NumVoices=AL-'0';
             ELSE{
                EBX=EAX&0FFFFFFh; // 4TLF
                IF (EBX=='TLF') ModModule.NumVoices=dword EAX>>24-'0';
             }
          }
         // Восстанавливаем хендлер файла с модулем
         $pop EBX
      }
   }

   // Узнаем количество паттернов
   FOR (ESI=0,AL=0; ESI<128; ESI++){
       AH=ModModule.Order[ESI];
       IF (AL<AH) AL=AH;
   }
   ModModule.NumPatterns=AL+1;

   // Считываем паттерны
   FOR (EBP=0; EBP<ModModule.NumPatterns; EBP++){
       $push EBX
        // Смещение к дескриптору буфера паттерна
        ESI=EBP*sizeof(BUFFERDESCRIPTOR)+#ModModule.Pattern;
        // Выделяем память под паттерн
        ModModule.AllocateBuffer(sizeof(PATTERNELEMENT)*64*ModModule.NumVoices,ESI);
       $pop EBX
       // Считываем паттерн
        $push DS
         // Устанавливаем DS на селектор паттерна
         DS=ESI.BUFFERDESCRIPTOR.Sel;
         // Считываем поэлементно весь паттерн
         FOR (EDI=sizeof(PATTERNELEMENT)*64*CSBYTE[#ModModule.NumVoices],EDX=0; EDX<EDI; EDX+=sizeof(PATTERNELEMENT)){
             // Считываем элемент паттерна
             FREAD(BX,EDX,4);
             // Преобразуем в собственный формат
             ESI=EDX;
             // Параметр эффекта
             ESI.PATTERNELEMENT.EffectParametr=DSBYTE[ESI+3];
             // Номер эффекта
             AL=DSBYTE[ESI+2]&0Fh;
             ESI.PATTERNELEMENT.EffectNumber=AL;
             // Период
             CH=DSWORD[ESI]&0Fh; CL=DSWORD[ESI+1];
             // Номер семпла
             AH=DSBYTE[ESI]>>4; AL=DSBYTE[ESI+2]; AX>>=4;
             // Период
             ESI.PATTERNELEMENT.NotePeriod=CX;
             // Номер семпла
             ESI.PATTERNELEMENT.SampleNumber=AL;
         }
        $pop DS
   }

   // Считываем семплы
   for (EDI=0,EBP=#ModModule.ModSample; EDI<ModModule.NumSamples; EDI++,EBP+=sizeof(MODSAMPLE)){

       // Преобразуем
       EBP.MODSAMPLE.Length=SwapAndDouble(EBP.MODSAMPLE.Length);
       EBP.MODSAMPLE.LoopOffs=SwapAndDouble(EBP.MODSAMPLE.LoopOffs);
       EBP.MODSAMPLE.LoopLength=SwapAndDouble(EBP.MODSAMPLE.LoopLength);

       // Смещение зацикливания > длины семпла ?
       IF (EBP.MODSAMPLE.LoopOffs>EBP.MODSAMPLE.Length){
          // Сбрасываем информацию о зацикливании семпла
          EBP.MODSAMPLE.LoopOffs=0;
          EBP.MODSAMPLE.LoopLength=0;
       }

       // Длина зацикливание+смещение зацикливания > длины семпла ?
       IF (EBP.MODSAMPLE.LoopOffs+EBP.MODSAMPLE.LoopLength>EBP.MODSAMPLE.Length){
          // Перерассчитываем длину зацикливания
          EBP.MODSAMPLE.LoopLength=EBP.MODSAMPLE.Length-EBP.MODSAMPLE.LoopOffs;
       }

       IF (EBP.MODSAMPLE.Length==2){
          // Если размер семпла == 2, то семпла нету
          EBP.MODSAMPLE.Length=0;
          CONTINUE;
       }

       $push EBX
        // Выделяем память под семпл
        // Смещение к дескриптору буфера семпла
        ESI=EDI*sizeof(BUFFERDESCRIPTOR)+#ModModule.Sample;
        $push EDI
         // Выделяем память под семпл
         ModModule.AllocateBuffer(EBP.MODSAMPLE.Length,ESI);
        $pop EDI
       $pop EBX
       // Сохраняем сегмент данных
       $push DS
        // Устанавливаем DS на выделенный сегмент под семпл
        DS=ESI.BUFFERDESCRIPTOR.Sel;
        // Считываем семпл в формате AMIGA (-128...+127)
        @FREAD(BX,0,CS:EBP.MODSAMPLE.Length);
       // Восстанавливаем сегмент данных
       $pop DS
   }

   // Позиция перезапуска песни больше длины списка паттернов ?
   IF (ModModule.ReStart>=ModModule.OrderLength) ModModule.ReStart=0;

  // Закрываем файл с модулем
  CLOSE(BX);

  // Устанавливаем флаг загруженного модуля
  ModModule.ModuleLoaded=TRUE;

 $pop EBP,ES
}



// Выделение буфера ////////////////////////////////////////////////////////
// EAX - размер выделяемой памяти
// ESI - адрес дескриптора выделенного буфера (dword Sel,Handler)
// На выходе: BX - селектор
:void static CARRYFLAG fastcall MODMODULE::AllocateBuffer(EAX,ESI)
{
 $push ESI
  // Запрашиваем новый селектор
  AllocateMemoryBlock(EAX);
 $pop ESI
 // Если все ОК
 IF (!CARRYFLAG){
    // Сохраняем Handle выделенного блока памяти
    DSDWORD[ESI+4]=EDX;
    // Выделяем селектор
    $push EAX
     DSWORD[ESI]=BX=AllocateSelector(1);
    $pop EAX
    // Устанавливаем селектор на адрес выделенной памяти
    SetSelectorBaseAddress(BX,EAX);
    // Устанавливаем границу селектора в 4Gb
    SetSelectorLimit(BX,-1);
 }
}



// Распределение буфера ////////////////////////////////////////////////////
//
:void static CARRYFLAG fastcall MODMODULE::FreeBuffer(EDX)
{
 // Распределяем память буфера
 FreeMemoryBlock(EDX.BUFFERDESCRIPTOR.Handle);
 // Распределяем селектор буфера
 FreeSelector(EDX.BUFFERDESCRIPTOR.Sel);
}



// Выгрузка загруженного модуля
:void static fastcall MODMODULE::UnLoadModule()
{
 // Останавливаем воспроизведение
 ModModule.Play=FALSE;

 // Распределяем паттерны
 FOR (ECX=0; ECX<ModModule.NumPatterns; ECX++){
     // Распределяем буфер паттерна
     ModModule.FreeBuffer(#ModModule.Pattern[ECX]);
 }

 // Распределяем семплы
 FOR (ECX=0; ECX<ModModule.NumSamples; ECX++){
     // Распределяем буфер семпла, если семпл подгружен
     IF (ModModule.ModSample[ECX].Length) ModModule.FreeBuffer(#ModModule.Sample[ECX]);
 }
}



