/* * This program is free software; you can redistribute it and modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the license or (at your * option) any later version. * * Authors: Olivier Lapicque */ /////////////////////////////////////////////////// // // PSM module loader // /////////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" //#define PSM_LOG #define PSM_ID_NEW 0x204d5350 #define PSM_ID_OLD 0xfe4d5350 #define IFFID_FILE 0x454c4946 #define IFFID_TITL 0x4c544954 #define IFFID_SDFT 0x54464453 #define IFFID_PBOD 0x444f4250 #define IFFID_SONG 0x474e4f53 #define IFFID_PATT 0x54544150 #define IFFID_DSMP 0x504d5344 #define IFFID_OPLH 0x484c504f #pragma pack(1) typedef struct _PSMCHUNK { DWORD id; DWORD len; DWORD listid; } Q_PACKED PSMCHUNK; typedef struct _PSMSONGHDR { CHAR songname[8]; // "MAINSONG" BYTE reserved1; BYTE reserved2; BYTE channels; } Q_PACKED PSMSONGHDR; typedef struct _PSMPATTERN { DWORD size; DWORD name; WORD rows; WORD reserved1; BYTE data[4]; } Q_PACKED PSMPATTERN; typedef struct _PSMSAMPLE { BYTE flags; CHAR songname[8]; DWORD smpid; CHAR samplename[34]; DWORD reserved1; BYTE reserved2; BYTE insno; BYTE reserved3; DWORD length; DWORD loopstart; DWORD loopend; WORD reserved4; BYTE defvol; DWORD reserved5; DWORD samplerate; BYTE reserved6[19]; } Q_PACKED PSMSAMPLE; #pragma pack() BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) //----------------------------------------------------------- { PSMCHUNK *pfh = (PSMCHUNK *)lpStream; DWORD dwMemPos, dwSongPos; DWORD smpnames[MAX_SAMPLES]; DWORD patptrs[MAX_PATTERNS]; BYTE samplemap[MAX_SAMPLES]; UINT nPatterns; // Chunk0: "PSM ",filesize,"FILE" if (dwMemLength < 256) return FALSE; if (pfh->id == PSM_ID_OLD) { #ifdef PSM_LOG Log("Old PSM format not supported\n"); #endif return FALSE; } if ((pfh->id != PSM_ID_NEW) || (pfh->len+12 > dwMemLength) || (pfh->listid != IFFID_FILE)) return FALSE; m_nType = MOD_TYPE_PSM; m_nChannels = 16; m_nSamples = 0; nPatterns = 0; dwMemPos = 12; dwSongPos = 0; for (UINT iChPan=0; iChPan<16; iChPan++) { UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; ChnSettings[iChPan].nPan = pan; } while (dwMemPos+8 < dwMemLength) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; dwMemPos += 8; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; if (len) switch(pchunk->id) { // "TITL": Song title case IFFID_TITL: if (!pdata[0]) { pdata++; len--; } memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); m_szNames[0][31] = 0; break; // "PBOD": Pattern case IFFID_PBOD: if ((len >= 12) && (nPatterns < MAX_PATTERNS)) { patptrs[nPatterns++] = dwMemPos-8; } break; // "SONG": Song description case IFFID_SONG: if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) { dwSongPos = dwMemPos - 8; } break; // "DSMP": Sample Data case IFFID_DSMP: if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) { m_nSamples++; MODINSTRUMENT *pins = &Ins[m_nSamples]; PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; smpnames[m_nSamples] = psmp->smpid; memcpy(m_szNames[m_nSamples], psmp->samplename, 31); m_szNames[m_nSamples][31] = 0; samplemap[m_nSamples-1] = (BYTE)m_nSamples; // Init sample pins->nGlobalVol = 0x40; pins->nC4Speed = psmp->samplerate; pins->nLength = psmp->length; pins->nLoopStart = psmp->loopstart; pins->nLoopEnd = psmp->loopend; pins->nPan = 128; pins->nVolume = (psmp->defvol+1) * 2; pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; if (pins->nLoopStart > 0) pins->nLoopStart--; // Point to sample data pdata += 0x60; len -= 0x60; // Load sample data if ((pins->nLength > 3) && (len > 3)) { ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); } else { pins->nLength = 0; } } break; #if 0 default: { CHAR s[8], s2[64]; *(DWORD *)s = pchunk->id; s[4] = 0; wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); OutputDebugString(s2); } #endif } dwMemPos += pchunk->len; } // Step #1: convert song structure PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; m_nChannels = pSong->channels; // Valid song header -> convert attached chunks { DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4); dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) while (dwMemPos + 8 < dwSongEnd) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); dwMemPos += 8; if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; switch(pchunk->id) { case IFFID_OPLH: if (len >= 0x20) { UINT pos = len - 3; while (pos > 5) { BOOL bFound = FALSE; pos -= 5; DWORD dwName = *(DWORD *)(pdata+pos); for (UINT i=0; iname; if (dwName == dwPatName) { bFound = TRUE; break; } } if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10) && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) { m_nDefaultSpeed = pdata[pos+1]; m_nDefaultTempo = pdata[pos+3]; break; } } UINT iOrd = 0; while ((pos+5name; if (dwName == dwPatName) { Order[iOrd++] = i; break; } } pos += 5; } } break; } dwMemPos += pchunk->len; } } // Step #2: convert patterns for (UINT nPat=0; nPatrows; if (len > pPsmPat->size) len = pPsmPat->size; if ((nRows < 64) || (nRows > 256)) nRows = 64; PatternSize[nPat] = nRows; if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; MODCOMMAND *m = Patterns[nPat]; BYTE *p = pPsmPat->data; UINT pos = 0; UINT row = 0; UINT oldch = 0; BOOL bNewRow = FALSE; #ifdef PSM_LOG Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); #endif while ((row < nRows) && (pos+1 < len)) { UINT flags = p[pos++]; UINT ch = p[pos++]; #ifdef PSM_LOG //Log("flags+ch: %02X.%02X\n", flags, ch); #endif if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/) { if ((pos+1= len) || (row >= nRows)) break; if (!(flags & 0xf0)) { #ifdef PSM_LOG //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]); #endif row++; m += m_nChannels; bNewRow = TRUE; oldch = ch; continue; } bNewRow = FALSE; if (ch >= m_nChannels) { #ifdef PSM_LOG if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch); #endif ch = 0; } // Note + Instr if ((flags & 0x40) && (pos+1 < len)) { UINT note = p[pos++]; UINT nins = p[pos++]; #ifdef PSM_LOG //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); #endif if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1; m[ch].instr = samplemap[nins]; m[ch].note = note; } // Volume if ((flags & 0x20) && (pos < len)) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = p[pos++] / 2; } // Effect if ((flags & 0x10) && (pos+1 < len)) { UINT command = p[pos++]; UINT param = p[pos++]; // Convert effects switch(command) { // 01: fine volslide up case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break; // 04: fine volslide down case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; // 0C: portamento up case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; // 0E: portamento down case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; // 33: Position Jump case 0x33: command = CMD_POSITIONJUMP; break; // 34: Pattern break case 0x34: command = CMD_PATTERNBREAK; break; // 3D: speed case 0x3D: command = CMD_SPEED; break; // 3E: tempo case 0x3E: command = CMD_TEMPO; break; // Unknown default: #ifdef PSM_LOG Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); #endif command = param = 0; } m[ch].command = (BYTE)command; m[ch].param = (BYTE)param; } oldch = ch; } #ifdef PSM_LOG if (pos < len) { Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); } #endif } // Done (finally!) return TRUE; } ////////////////////////////////////////////////////////////// // // PSM Old Format // /* CONST c_PSM_MaxOrder = $FF; c_PSM_MaxSample = $FF; c_PSM_MaxChannel = $0F; TYPE PPSM_Header = ^TPSM_Header; TPSM_Header = RECORD PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } PSM_SongName : ARRAY[01..58] OF CHAR; PSM_Byte00 : BYTE; PSM_Byte1A : BYTE; PSM_Unknown00 : BYTE; PSM_Unknown01 : BYTE; PSM_Unknown02 : BYTE; PSM_Speed : BYTE; PSM_Tempo : BYTE; PSM_Unknown03 : BYTE; PSM_Unknown04 : WORD; PSM_OrderLength : WORD; PSM_PatternNumber : WORD; PSM_SampleNumber : WORD; PSM_ChannelNumber : WORD; PSM_ChannelUsed : WORD; PSM_OrderPosition : LONGINT; PSM_ChannelSettingPosition : LONGINT; PSM_PatternPosition : LONGINT; PSM_SamplePosition : LONGINT; { *** perhaps there are some more infos in a larger header, but i have not decoded it and so it apears here NOT } END; PPSM_Sample = ^TPSM_Sample; TPSM_Sample = RECORD PSM_SampleFileName : ARRAY[01..12] OF CHAR; PSM_SampleByte00 : BYTE; PSM_SampleName : ARRAY[01..22] OF CHAR; PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; PSM_SamplePosition : LONGINT; PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; PSM_SampleNumber : BYTE; PSM_SampleFlags : WORD; PSM_SampleLength : LONGINT; PSM_SampleLoopBegin : LONGINT; PSM_SampleLoopEnd : LONGINT; PSM_Unknown03 : BYTE; PSM_SampleVolume : BYTE; PSM_SampleC5Speed : WORD; END; PPSM_SampleList = ^TPSM_SampleList; TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; PPSM_Order = ^TPSM_Order; TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; PPSM_ChannelSettings = ^TPSM_ChannelSettings; TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; CONST PSM_NotesInPattern : BYTE = $00; PSM_ChannelInPattern : BYTE = $00; CONST c_PSM_SetSpeed = 60; FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; BEGIN END; PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); VAR Witz : ARRAY[00..04] OF WORD; I1,I2 : WORD; I3,I4 : WORD; TopicalByte : ^BYTE; Pattern : PUnpackedPattern; ChannelP : BYTE; NoteP : BYTE; InfoByte : BYTE; CodeByte : BYTE; InfoWord : WORD; Effect : BYTE; Opperand : BYTE; Panning : BYTE; Volume : BYTE; PrevInfo : BYTE; InfoIndex : BYTE; BEGIN Pattern := @Destination; TopicalByte := @Source; { *** Initialize patttern } FOR I2 := 0 TO c_Maximum_NoteIndex DO FOR I3 := 0 TO c_Maximum_ChannelIndex DO BEGIN Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; END; { *** Byte-pointer on first pattern-entry } ChannelP := $00; NoteP := $00; InfoByte := $00; PrevInfo := $00; InfoIndex := $02; { *** read notes in pattern } PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); { *** unpack pattern } WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO BEGIN { *** Read info-byte } InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); IF InfoByte <> $00 THEN BEGIN ChannelP := InfoByte AND $0F; IF InfoByte AND 128 = 128 THEN { note and sample } BEGIN { *** read note } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); DEC(CodeByte); CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; { *** read sample } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; END; IF InfoByte AND 64 = 64 THEN { Volume } BEGIN CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; END; IF InfoByte AND 32 = 32 THEN { effect AND opperand } BEGIN Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); CASE Effect OF c_PSM_SetSpeed: BEGIN Effect := c_I_Set_Speed; END; ELSE BEGIN Effect := c_I_NoEffect; Opperand := $00; END; END; Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; END; END ELSE INC(NoteP); END; END; PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); { *** caution : Module has to be inited before!!!! } VAR Header : PPSM_Header; Sample : PPSM_SampleList; Order : PPSM_Order; ChannelSettings : PPSM_ChannelSettings; MultiPurposeBuffer : PByteArray; PatternBuffer : PUnpackedPattern; TopicalParaPointer : WORD; InFile : FILE; I1,I2 : WORD; I3,I4 : WORD; TempW : WORD; TempB : BYTE; TempP : PByteArray; TempI : INTEGER; { *** copy-vars for loop-extension } CopySource : LONGINT; CopyDestination : LONGINT; CopyLength : LONGINT; BEGIN { *** try to open file } ASSIGN(InFile,FileName); {$I-} RESET(InFile,1); {$I+} IF IORESULT <> $00 THEN BEGIN EXIT; END; {$I-} { *** seek start of module } IF FILESIZE(InFile) < FilePosition THEN BEGIN EXIT; END; SEEK(InFile,FilePosition); { *** look for enough memory for temporary variables } IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) THEN BEGIN EXIT; END; { *** init dynamic variables } NEW(Header); NEW(Sample); NEW(Order); NEW(ChannelSettings); NEW(MultiPurposeBuffer); NEW(PatternBuffer); { *** read header } BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); { *** test if this is a DSM-file } IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN BEGIN ErrorCode := c_NoValidFileFormat; CLOSE(InFile); EXIT; END; { *** read order } SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); { *** read channelsettings } SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); { *** read samplelist } SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); { *** copy header to intern NTMIK-structure } Module^.Module_Sign := 'MF'; Module^.Module_FileFormatVersion := $0100; Module^.Module_SampleNumber := Header^.PSM_SampleNumber; Module^.Module_PatternNumber := Header^.PSM_PatternNumber; Module^.Module_OrderLength := Header^.PSM_OrderLength; Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; Module^.Module_Initial_GlobalVolume := 64; Module^.Module_Initial_MasterVolume := $C0; Module^.Module_Initial_Speed := Header^.PSM_Speed; Module^.Module_Initial_Tempo := Header^.PSM_Tempo; { *** paragraph 01 start } Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + c_Module_Flags_Stereo * BYTE(1) + c_Module_Flags_ForceAmigaLimits * BYTE(0) + c_Module_Flags_Panning * BYTE(1) + c_Module_Flags_Surround * BYTE(1) + c_Module_Flags_QualityMixing * BYTE(1) + c_Module_Flags_FastVolumeSlides * BYTE(0) + c_Module_Flags_SpecialCustomData * BYTE(0) + c_Module_Flags_SongName * BYTE(1); I1 := $01; WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO BEGIN Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; INC(I1); END; Module^.Module_Name[c_Module_SongNameLength] := #00; { *** Init channelsettings } FOR I1 := 0 TO c_Maximum_ChannelIndex DO BEGIN IF I1 < Header^.PSM_ChannelUsed THEN BEGIN { *** channel enabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + c_ChannelSettings_Code_ChannelDigital * BYTE(1); Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + c_ChannelSettings_Controls_SurroundMode * BYTE(0); END ELSE BEGIN { *** channel disabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; END; END; { *** init and copy order } FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); { *** read pattern } SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO BEGIN NTMIK_LoadPatternProcedure; { *** read length } BLOCKREAD(InFile,TempW,2); { *** read pattern } BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); { *** unpack pattern and set notes per channel to 64 } PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; GETMEM(Module^.Module_PatternPointer^[I1],TempW); MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); { *** next pattern } END; { *** read samples } NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; FOR I1 := 1 TO Header^.PSM_SampleNumber DO BEGIN NTMIK_LoadSampleProcedure; { *** get index for sample } I3 := Sample^[I1].PSM_SampleNumber; { *** clip PSM-sample } IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; { *** init intern sample } NEW(Module^.Module_SamplePointer^[I3]); FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); { *** copy informations to intern sample } I2 := $01; WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO BEGIN Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; INC(I2); END; Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; { *** now it's time for the flags } Module^.Module_SamplePointer^[I3]^.Sample_Flags := c_Sample_Flags_DigitalSample * BYTE(1) + c_Sample_Flags_8BitSample * BYTE(1) + c_Sample_Flags_UnsignedSampleData * BYTE(1) + c_Sample_Flags_Packed * BYTE(0) + c_Sample_Flags_LoopCounter * BYTE(0) + c_Sample_Flags_SampleName * BYTE(1) + c_Sample_Flags_LoopActive * BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); { *** alloc memory for sample-data } E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, Module^.Module_SamplePointer^[I3]^.Sample_Position, Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); { *** read out data } EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; EPT(TempP).p_Offset := $0000; SEEK(InFile,Sample^[I1].PSM_SamplePosition); E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); { *** 'coz the samples are signed in a DSM-file -> PC-fy them } IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN BEGIN CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; { *** decode sample } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI XOR AH,AH { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB ADD AL,AH MOV AH,AL DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** make samples unsigned } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB SUB AL,080h DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** Create Loop-Extension } IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN BEGIN CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; CopyLength := CopyDestination - CopySource; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** calculate number of full sample-loops to copy } XOR DX,DX MOV AX,c_LoopExtensionSize MOV BX,WORD PTR CopyLength DIV BX OR AX,AX JE @@NoFullLoop { *** copy some full-loops (size=bx) } MOV CX,AX @@InnerLoop: PUSH CX DB 066h; MOV SI,WORD PTR CopySource MOV CX,BX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } POP CX LOOP @@InnerLoop @@NoFullLoop: { *** calculate number of rest-bytes to copy } DB 066h; MOV SI,WORD PTR CopySource MOV CX,DX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } END; END ELSE BEGIN CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** clear extension } MOV CX,c_LoopExtensionSize MOV AL,080h DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } END; END; END; { *** next sample } END; { *** init period-ranges } NTMIK_MaximumPeriod := $0000D600 SHR 1; NTMIK_MinimumPeriod := $0000D600 SHR 8; { *** close file } CLOSE(InFile); { *** dispose all dynamic variables } DISPOSE(Header); DISPOSE(Sample); DISPOSE(Order); DISPOSE(ChannelSettings); DISPOSE(MultiPurposeBuffer); DISPOSE(PatternBuffer); { *** set errorcode to noerror } ErrorCode := c_NoError; END; */