|
From: <sag...@us...> - 2009-08-22 10:21:34
|
Revision: 337
http://modplug.svn.sourceforge.net/modplug/?rev=337&view=rev
Author: saga-games
Date: 2009-08-22 10:21:22 +0000 (Sat, 22 Aug 2009)
Log Message:
-----------
[Fix] A brand new PSM loader! Ditched the old and buggy loader as the new loader works way better, it can even handle modules from Extreme Pinball. Some finetuning is still mandatory, though.
[Fix] S3M Loading: Disable loop for files with very short loop at the beginning of the sample
Modified Paths:
--------------
trunk/OpenMPT/soundlib/Load_psm.cpp
trunk/OpenMPT/soundlib/Load_s3m.cpp
Modified: trunk/OpenMPT/soundlib/Load_psm.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_psm.cpp 2009-08-20 20:43:52 UTC (rev 336)
+++ trunk/OpenMPT/soundlib/Load_psm.cpp 2009-08-22 10:21:22 UTC (rev 337)
@@ -1,841 +1,558 @@
/*
- * This source code is public domain.
+ * Purpose: Load new PSM (ProTracker Studio) modules
+ * Authors: Johannes Schultz
*
- * Authors: Olivier Lapicque <oli...@jp...>
-*/
+ * This is partly based on http://www.shikadi.net/moddingwiki/ProTracker_Studio_Module
+ * and partly reverse-engineered.
+ *
+ * What's playing?
+ * - Epic Pinball - Seems to be working perfectly, apart from some portas? (esp. Deep Sea, Shareware Info - Linear freq slides needed!?)
+ * - Extreme Pinball - Default tempo / speed / restart position of subtunes is missing.
+ * I'm using the last default values, restart position is still completely missing
+ * - Jazz Jackrabbit - Hmm, I don't like some of the portas, but apart from that it's great. (same problem as Epic Pinball)
+ * - One Must Fall! - Perfect! (I modelled the volume slide and portamento conversion after this, as I got the original MTM files)
+ * - Silverball - Currently not supported (old PSM16 format)
+ */
-
-///////////////////////////////////////////////////
-//
-// PSM module loader
-//
-///////////////////////////////////////////////////
#include "stdafx.h"
#include "sndfile.h"
-//#define PSM_LOG
-
-#define PSM_ID_NEW 0x204d5350 // "PSM "
-#define PSM_ID_OLD 0xfe4d5350 // "PSM\xFE"
-#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
+struct PSMHEADER
{
- DWORD id;
- DWORD len;
- DWORD listid;
-} PSMCHUNK;
+ DWORD formatID; // "PSM " (new format)
+ DWORD fileSize; // Filesize - 12
+ DWORD fileInfoID; // "FILE" Start of file info
+};
-typedef struct _PSMSONGHDR
+struct PSMSONGHEADER
{
- CHAR songname[8]; // "MAINSONG"
- BYTE reserved1;
- BYTE reserved2;
- BYTE channels;
-} PSMSONGHDR;
+ CHAR songType[9]; // Mostly "MAINSONG " (But not in Extreme Pinball!)
+ BYTE compression; // 1 - uncompressed
+ BYTE numChannels; // Number of channels, usually 4
-typedef struct _PSMPATTERN
+};
+
+struct PSMSAMPLEHEADERTEST // apparently, this is wrong.
{
- DWORD size;
- DWORD name;
- WORD rows;
- WORD reserved1;
- BYTE data[4];
-} PSMPATTERN;
+ CHAR sampleFormat[9];
+ DWORD sampleID; // INS0...INS9 (only last digit of sample ID, i.e. sample 1 and sample 11 are equal)
+ CHAR sampleName[33];
+ CHAR unknown1[6]; // 00 00 00 00 00 FF
+ WORD sampleNumber;
+ DWORD loopStart;
+ CHAR loop; // 1 = loop
+ CHAR unknown2[3]; // 00 00 00 00 00 FF
+ DWORD loopEnd; // FF FF FF FF = end of sample
+ WORD unknown3;
+ CHAR defaultVolume;
+ DWORD unknown4;
+ WORD C5Freq;
+ CHAR padding[21]; // 00 ... 00
+};
-typedef struct _PSMSAMPLE
+struct PSMSAMPLEHEADER // Use this instead
{
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];
-} PSMSAMPLE;
+ CHAR sampleFormat[8];
+ DWORD sampleID; // INS0...INS9 (only last digit of sample ID, i.e. sample 1 and sample 11 are equal)
+ CHAR sampleName[33];
+ CHAR unknown1[6]; // 00 00 00 00 00 FF
+ WORD sampleNumber;
+ DWORD sampleLength;
+ DWORD loopStart;
+ DWORD loopEnd; // FF FF FF FF = end of sample
+ WORD unknown3;
+ CHAR defaultVolume;
+ DWORD unknown4;
+ WORD C5Freq;
+ CHAR unknown5[21]; // 00 ... 00
+};
#pragma pack()
+BYTE convert_psm_param(BYTE param)
+{
+ // special conversion of some PSM parameters. is this done correctly?
+ return param >> 1;
+ //return (param + 1) >> 1;
+}
-bool CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength)
-//-----------------------------------------------------------
+bool CSoundFile::ReadPSM(const LPCBYTE lpStream, const DWORD dwMemLength)
+//-----------------------------------------------------------------------
{
- PSMCHUNK *pfh = (PSMCHUNK *)lpStream;
- DWORD dwMemPos, dwSongPos;
- DWORD smpnames[MAX_SAMPLES];
- DWORD patptrs[MAX_PATTERNS];
- BYTE samplemap[MAX_SAMPLES];
- UINT nPatterns;
+ #define ASSERT_CAN_READ(x) \
+ if( dwMemPos > dwMemLength || x > dwMemLength - dwMemPos ) return false;
- // 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;
+ DWORD dwMemPos = 0;
+
+ ASSERT_CAN_READ(12);
+ PSMHEADER shdr;
+ memcpy(&shdr, lpStream, sizeof(PSMHEADER));
+ if(LittleEndian(shdr.formatID) != 0x204d5350 // "PSM "
+ || LittleEndian(shdr.fileSize) != dwMemLength - 12
+ || LittleEndian(shdr.fileInfoID) != 0x454C4946 // "FILE"
+ ) return false;
+
+ // Yep, this seems to be a valid file.
m_nType = MOD_TYPE_PSM;
- m_nChannels = 16;
- m_nSamples = 0;
- nPatterns = 0;
- dwMemPos = 12;
- dwSongPos = 0;
- for (UINT iChPan=0; iChPan<16; iChPan++)
+ //m_dwSongFlags |= SONG_LINEARSLIDES; // TODO
+ m_nChannels = 0;
+
+ dwMemPos += 12;
+
+ memset(m_szNames, 0, sizeof(m_szNames));
+
+ m_nVSTiVolume = m_nSamplePreAmp = 48; // not supported in this format, so use a good default value
+
+ // pattern offset and identifier
+ PATTERNINDEX numPatterns = 0;
+ vector<DWORD> patternOffsets;
+ vector<DWORD> patternIDs;
+ patternOffsets.clear();
+ patternIDs.clear();
+ Order.clear();
+
+ std::string sComment; // we will store some information about the tune here
+
+ while(dwMemPos + 8 < dwMemLength)
{
- 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;
+ // Skip through the chunks
+ ASSERT_CAN_READ(8);
+ DWORD chunkID = LittleEndian(*(DWORD *)(lpStream + dwMemPos));
+ DWORD chunkSize = LittleEndian(*(DWORD *)(lpStream + dwMemPos + 4));
dwMemPos += 8;
- PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos);
- ULONG len = pchunk->len;
- if (len) switch(pchunk->id)
+
+ ASSERT_CAN_READ(chunkSize);
+
+ switch(chunkID)
{
- // "TITL": Song title
- case IFFID_TITL:
- if (!pdata[0]) { pdata++; len--; }
- memcpy(m_szNames[0], pdata, (len>31) ? 31 : len);
+ case 0x4C544954: // "TITL" - Song Title
+ memcpy(m_szNames[0], lpStream + dwMemPos, (chunkSize < 31) ? chunkSize : 31);
m_szNames[0][31] = 0;
break;
- // "PBOD": Pattern
- case IFFID_PBOD:
- if ((len >= 12) && (nPatterns < MAX_PATTERNS))
- {
- patptrs[nPatterns++] = dwMemPos-8;
- }
+
+ case 0x54464453: // "SDFT" - Format info (song data starts here)
+ if(chunkSize != 8 || memcmp(lpStream + dwMemPos, "MAINSONG", 8)) return false;
break;
- // "SONG": Song description
- case IFFID_SONG:
- if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos))
- {
- dwSongPos = dwMemPos - 8;
- }
+
+ case 0x444F4250: // "PBOD" - Pattern data of a single pattern
+ if(chunkSize < 4 || chunkSize != LittleEndian(*(DWORD *)(lpStream + dwMemPos))) return false; // same value twice
+
+ // Pattern ID (something like "P0 " or "P13 ") follows
+ if(memcmp(lpStream + dwMemPos + 4, "P", 1)) return false;
+ patternOffsets.push_back(dwMemPos + 8);
+ patternIDs.push_back(*(DWORD *)(lpStream + dwMemPos + 4));
+ numPatterns++;
+
+ // Convert later as we have to know how many channels there are.
break;
- // "DSMP": Sample Data
- case IFFID_DSMP:
- if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES))
+
+ case 0x474E4F53: // "SONG" - Information about this file (channel count etc)
+ // We will not check for the "song type", because it is not ALWAYS "MAINSONG " (Extreme Pinball seems to use other song types for the sub songs)
+ // However, the last char always seems to be a space char.
{
- m_nSamples++;
- MODSAMPLE *pSmp = &Samples[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
- pSmp->nGlobalVol = 0x40;
- pSmp->nC5Speed = psmp->samplerate;
- pSmp->nLength = psmp->length;
- pSmp->nLoopStart = psmp->loopstart;
- pSmp->nLoopEnd = psmp->loopend;
- pSmp->nPan = 128;
- pSmp->nVolume = (psmp->defvol+1) * 2;
- pSmp->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0;
- if (pSmp->nLoopStart > 0) pSmp->nLoopStart--;
- // Point to sample data
- pdata += 0x60;
- len -= 0x60;
- // Load sample data
- if ((pSmp->nLength > 3) && (len > 3))
+ if(chunkSize < sizeof(PSMSONGHEADER)) return false;
+ PSMSONGHEADER *pSong = (PSMSONGHEADER *)(lpStream + dwMemPos);
+ if(pSong->compression != 0x01) return false;
+ m_nChannels = max(m_nChannels, pSong->numChannels); // subsongs *might* have different channel count
+
+ CHAR cSongName[10];
+ memcpy(cSongName, &pSong->songType, 9);
+ cSongName[9] = 0;
+ sComment += "\r\nSubsong: ";
+ sComment += cSongName;
+
+ DWORD dwChunkPos = dwMemPos + sizeof(PSMSONGHEADER);
+
+ // Sub chunks
+ while(dwChunkPos + 8 < dwMemPos + chunkSize)
{
- ReadSample(pSmp, RS_PCM8D, (LPCSTR)pdata, len);
- } else
- {
- pSmp->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)
+ DWORD subChunkID = LittleEndian(*(DWORD *)(lpStream + dwChunkPos));
+ DWORD subChunkSize = LittleEndian(*(DWORD *)(lpStream + dwChunkPos + 4));
+ dwChunkPos += 8;
+
+ switch(subChunkID)
{
- BOOL bFound = FALSE;
- pos -= 5;
- DWORD dwName = *(DWORD *)(pdata+pos);
- for (UINT i=0; i<nPatterns; i++)
+ case 0x45544144: // "DATE" - Song date (YYMMDD)
+ if(subChunkSize < 6) break;
+ CHAR cDate[7];
+ memcpy(cDate, lpStream + dwChunkPos, 6);
+ cDate[6] = 0;
+ sComment += "\r\nDate: ";
+ sComment += cDate;
+ // We don't care about the date.
+ break;
+
+ case 0x484C504F: // "OPLH" - Order list, channel + module settings
{
- DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
- if (dwName == dwPatName)
+ if(subChunkSize < 9) return false;
+ // First byte = "Memory alloc, roughly ordlen + 10, if too small, song freezes"
+ if(LittleEndian(*(DWORD *)(lpStream + dwChunkPos + 1)) != 0xFF000C00 // "Something"
+ || LittleEndian(*(DWORD *)(lpStream + dwChunkPos + 5)) != 0x00010000) // "Something"
+ return false;
+
+ // Now, the interesting part begins!
+ DWORD dwSettingsOffset = dwChunkPos + 9;
+ while(dwSettingsOffset - dwChunkPos + 1 < subChunkSize)
{
- bFound = TRUE;
- break;
+ switch(lpStream[dwSettingsOffset])
+ {
+ case 0x01: // Order list item
+ if(dwSettingsOffset - dwChunkPos + 5 > subChunkSize) return false;
+ // Pattern name follows - find pattern (this is the orderlist)
+ for(PATTERNINDEX i = 0; i < patternIDs.size(); i++)
+ {
+ if(patternIDs[i] == *(DWORD *)(lpStream + dwSettingsOffset + 1))
+ {
+ Order.push_back(i);
+ break;
+ }
+ }
+ dwSettingsOffset += 5;
+ break;
+
+ case 0x04: // "end?" "04 03 00 00" in most cases (not Extreme Pinball - maybe restart positions are dumped here?)
+ if(dwSettingsOffset - dwChunkPos + 4 > subChunkSize) return false;
+ dwSettingsOffset += 4;
+ break;
+
+ case 0x07: // Default Speed
+ if(dwSettingsOffset - dwChunkPos + 2 > subChunkSize) break;
+ // NOTE: This should not be global! (Extreme Pinball!!!)
+ m_nDefaultSpeed = lpStream[dwSettingsOffset + 1];
+ dwSettingsOffset += 2;
+ break;
+
+ case 0x08: // Default Tempo
+ if(dwSettingsOffset - dwChunkPos + 2 > subChunkSize) break;
+ // Same note as above!
+ m_nDefaultTempo = lpStream[dwSettingsOffset + 1];
+ dwSettingsOffset += 2;
+ break;
+
+ case 0x0D: // This is a channel header
+ if(dwSettingsOffset - dwChunkPos + 4 > subChunkSize) break;
+ ChnSettings[min(lpStream[dwSettingsOffset + 1], pSong->numChannels)].nPan = lpStream[dwSettingsOffset + 2];
+ // Last byte: "either 0, 2 or 4 (Some sort of priority?)"
+ dwSettingsOffset += 4;
+ break;
+
+ case 0x0E: // Unknown - "0E <ChnID> FF"
+ if(dwSettingsOffset - dwChunkPos + 3 > subChunkSize) break;
+ dwSettingsOffset += 3;
+ break;
+
+ case 0x00: // "end?"
+ dwSettingsOffset += 1;
+ break;
+
+ default: // How the hell should this happen? I've listened through all existing PSM files. :)
+ CString s;
+ s.Format("Please report to the OpenMPT team: Unknown chunk %d found at position %d (in the OPLH chunk of this PSM file)", lpStream[dwSettingsOffset], dwSettingsOffset);
+ MessageBox(NULL, s, TEXT("OpenMPT PSM import"), MB_ICONERROR);
+ return false;
+ break;
+
+ }
}
+ Order.push_back(Order.GetInvalidPatIndex());
}
- 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;
- }
+ break;
+
+ case 0x54544150: // PATT - Pattern list
+ // We don't really need this.
+ break;
+
+ case 0x4D415344: // DSAM - Sample list
+ // We don't need this either.
+ break;
+
+ default:
+ break;
+
}
- UINT iOrd = 0;
- Order.resize(MAX_ORDERS, Order.GetInvalidPatIndex());
- while ((pos+5<len) && (iOrd < MAX_ORDERS))
- {
- DWORD dwName = *(DWORD *)(pdata+pos);
- for (UINT i=0; i<nPatterns; i++)
- {
- DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name;
- if (dwName == dwPatName)
- {
- Order[iOrd++] = i;
- break;
- }
- }
- pos += 5;
- }
+
+ dwChunkPos += subChunkSize;
}
- break;
}
- dwMemPos += pchunk->len;
+
+ break;
+
+ case 0x504D5344: // DSMP - Samples
+
+ {
+ if(chunkSize < sizeof(PSMSAMPLEHEADER)) return false;
+ PSMSAMPLEHEADER *pSample = (PSMSAMPLEHEADER *)(lpStream + dwMemPos);
+ SAMPLEINDEX smp = (SAMPLEINDEX)(LittleEndianW(pSample->sampleNumber) + 1);
+ m_nSamples = max(m_nSamples, smp);
+ memcpy(m_szNames[smp], pSample->sampleName, 31);
+ m_szNames[0][31] = 0;
+
+ Samples[smp].nGlobalVol = 0x40;
+ Samples[smp].nC5Speed = LittleEndianW(pSample->C5Freq);
+ Samples[smp].nLength = LittleEndian(pSample->sampleLength);
+ Samples[smp].nLoopStart = LittleEndian(pSample->loopStart);
+ Samples[smp].nLoopEnd = LittleEndian(pSample->loopEnd);
+ Samples[smp].nPan = 128;
+ Samples[smp].nVolume = (pSample->defaultVolume + 1) << 1;
+ Samples[smp].uFlags = (pSample->flags & 0x80) ? CHN_LOOP : 0;
+ if(Samples[smp].nLoopStart > 0) Samples[smp].nLoopStart--;
+ if(Samples[smp].nLoopEnd == 0xFFFFFF) Samples[smp].nLoopEnd = Samples[smp].nLength;
+
+ // Delta-encoded samples
+ ReadSample(&Samples[smp], RS_PCM8D, (LPCSTR)(lpStream + dwMemPos + sizeof(PSMSAMPLEHEADER)), Samples[smp].nLength);
+ }
+
+ break;
+
+ default:
+ break;
+
}
+
+ dwMemPos += chunkSize;
}
- // Step #2: convert patterns
- for (UINT nPat=0; nPat<nPatterns; nPat++)
+ if(m_nChannels == 0)
+ return false;
+
+ // Now that we know the number of channels, we can go through all the patterns.
+ for(PATTERNINDEX i = 0; i < numPatterns; i++)
{
- PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8);
- ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12;
- UINT nRows = pPsmPat->rows;
- if (len > pPsmPat->size) len = pPsmPat->size;
- if ((nRows < 64) || (nRows > 256)) nRows = 64;
- if(Patterns.Insert(nPat, nRows))
+ DWORD dwPatternOffset = patternOffsets[i];
+ if(dwPatternOffset + 2 > dwMemLength) return false;
+ WORD patternSize = LittleEndianW(*(WORD *)(lpStream + dwPatternOffset));
+ dwPatternOffset += 2;
+
+ if(Patterns.Insert(i, patternSize))
+ {
+ CString s;
+ s.Format(TEXT("Allocating patterns failed starting from pattern %u"), i);
+ MessageBox(NULL, s, TEXT("OpenMPT PSM import"), MB_ICONERROR);
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))
+ // Read pattern.
+ MODCOMMAND *row_data;
+ row_data = Patterns[i];
+
+ for(int nRow = 0; nRow < patternSize; nRow++)
{
- UINT flags = p[pos++];
- UINT ch = p[pos++];
+ if(dwPatternOffset + 2 > dwMemLength) return false;
+ WORD rowSize = LittleEndianW(*(WORD *)(lpStream + dwPatternOffset));
- #ifdef PSM_LOG
- //Log("flags+ch: %02X.%02X\n", flags, ch);
- #endif
- if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/)
+ DWORD dwRowOffset = dwPatternOffset + 2;
+
+ while(dwRowOffset < dwPatternOffset + rowSize)
{
- if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels))
+ if(dwRowOffset + 1 > dwMemLength) return false;
+ BYTE mask = lpStream[dwRowOffset];
+ // Point to the correct channel
+ MODCOMMAND *m = row_data + min(m_nChannels, lpStream[dwRowOffset + 1]);
+ dwRowOffset += 2;
+
+ if(mask & 0x80)
{
- #ifdef PSM_LOG
- //if (!nPat) Log("Continuing on new row\n");
- #endif
- row++;
- m += m_nChannels;
- oldch = ch;
- continue;
+ if(dwRowOffset + 1 > dwMemLength) return false;
+ // Note present
+ BYTE bNote = lpStream[dwRowOffset];
+ bNote = (bNote & 0x0F) + 12 * (bNote >> 4) + 13;
+ m->note = bNote;
+ dwRowOffset++;
}
- }
- if ((pos >= 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)
+
+ if(mask & 0x40)
{
- // 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;
+ if(dwRowOffset + 1 > dwMemLength) return false;
+ // Instrument present
+ m->instr = lpStream[dwRowOffset] + 1;
+ dwRowOffset++;
}
- 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;
-}
+ if(mask & 0x20)
+ {
+ if(dwRowOffset + 1 > dwMemLength) return false;
+ // Volume present
+ m->volcmd = VOLCMD_VOLUME;
+ m->vol = (min(lpStream[dwRowOffset], 127)) >> 1;
+ dwRowOffset++;
+ }
+ if(mask & 0x10)
+ {
+ // Effect present - convert
+ if(dwRowOffset + 2 > dwMemLength) return false;
+ BYTE command = lpStream[dwRowOffset], param = lpStream[dwRowOffset + 1];
-//////////////////////////////////////////////////////////////
-//
-// PSM Old Format
-//
+ switch(command)
+ {
+ // Volslides
+ case 0x01: // fine volslide up
+ command = CMD_VOLUMESLIDE;
+ param = (param << 3) | 0x0F;
+ break;
+ case 0x02: // volslide up
+ command = CMD_VOLUMESLIDE;
+ param = (param << 3);
+ break;
+ case 0x03: // fine volslide down
+ command = CMD_VOLUMESLIDE;
+ param = 0xF0 | (param >> 1);
+ break;
+ case 0x04: // volslide down
+ command = CMD_VOLUMESLIDE;
+ param >>= 1;
+ break;
-/*
+ // Portamento
+ case 0x0B: // fine portamento up
+ command = CMD_PORTAMENTOUP;
+ param = 0xF0 | convert_psm_param(param);
+ break;
+ case 0x0C: // portamento up
+ command = CMD_PORTAMENTOUP;
+ param = convert_psm_param(param);
+ break;
+ case 0x0D: // fine portamento down
+ command = CMD_PORTAMENTODOWN;
+ param = 0xF0 | convert_psm_param(param);
+ break;
+ case 0x0E: // portamento down
+ command = CMD_PORTAMENTODOWN;
+ param = convert_psm_param(param);
+ break;
+ case 0x0F: // tone portamento
+ command = CMD_TONEPORTAMENTO;
+ param = convert_psm_param(param);
+ break;
+ case 0x10: // glissando control
+ command = CMD_S3MCMDEX;
+ param = 0x10 | (param & 0x01);
+ break;
+ case 0x11: // tone portamento + volslide up
+ command = CMD_TONEPORTAVOL;
+ param = param & 0xF0;
+ break;
+ case 0x12: // tone portamento + volslide down
+ command = CMD_TONEPORTAVOL;
+ param = (param >> 4) & 0x0F;
+ break;
-CONST
- c_PSM_MaxOrder = $FF;
- c_PSM_MaxSample = $FF;
- c_PSM_MaxChannel = $0F;
+ // Vibrato
+ case 0x15: // vibrato
+ command = CMD_VIBRATO;
+ break;
+ case 0x16: // vibrato waveform
+ command = CMD_S3MCMDEX;
+ param = 0x30 | (param & 0x0F);
+ break;
+ case 0x17: // vibrato + volslide up
+ command = CMD_VIBRATOVOL;
+ param = 0xF0 | param;
+ break;
+ case 0x18: // vibrato + volslide down
+ command = CMD_VIBRATOVOL;
+ break;
- 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;
+ // Tremolo
+ case 0x1F: // tremolo
+ command = CMD_TREMOLO;
+ break;
+ case 0x20: // tremolo waveform
+ command = CMD_S3MCMDEX;
+ param = 0x40 | (param & 0x0F);
+ break;
- 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;
+ // Sample commands
+ case 0x29: // 3-byte offset - we only support the middle byte.
+ if(dwRowOffset + 4 > dwMemLength) return false;
+ command = CMD_OFFSET;
+ param = lpStream[dwRowOffset + 2];
+ dwRowOffset += 2;
+ break;
+ case 0x2A: // set finetune
+ command = CMD_S3MCMDEX;
+ param = 0x40 | (param & 0x0F);
+ break;
+ case 0x2B: // note cut
+ command = CMD_S3MCMDEX;
+ param = 0xC0 | (param & 0x0F);
+ break;
+ case 0x2C: // note delay
+ command = CMD_S3MCMDEX;
+ param = 0xD0 | (param & 0x0F);
+ break;
- PPSM_SampleList = ^TPSM_SampleList;
- TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample;
+ // Position change
+ case 0x33: // position jump
+ command = CMD_POSITIONJUMP;
+ break;
+ case 0x34: // pattern break
+ command = CMD_PATTERNBREAK;
+ break;
+ case 0x35: // loop pattern
+ command = CMD_S3MCMDEX;
+ param = 0xB0 | (param & 0x0F);
+ break;
+ case 0x36: // pattern delay
+ command = CMD_S3MCMDEX;
+ param = 0xE0 | (param & 0x0F);
+ break;
- PPSM_Order = ^TPSM_Order;
- TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE;
+ // speed change
+ case 0x3D: // set speed
+ command = CMD_SPEED;
+ break;
+ case 0x3E: // set tempo
+ command = CMD_TEMPO;
+ break;
- PPSM_ChannelSettings = ^TPSM_ChannelSettings;
- TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE;
+ // misc commands
+ case 0x47: // arpeggio
+ command = CMD_ARPEGGIO;
+ break;
+ case 0x48: // set finetune
+ command = CMD_MODCMDEX;
+ param = 0x50 | (param & 0x0F);
+ break;
+ case 0x49: // set balance
+ command = CMD_S3MCMDEX;
+ param = 0x80 | (param & 0x0F);
+ break;
- CONST
- PSM_NotesInPattern : BYTE = $00;
- PSM_ChannelInPattern : BYTE = $00;
+ default:
+ //ASSERT(false);
+ break;
- CONST
- c_PSM_SetSpeed = 60;
+ }
- FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT;
- BEGIN
- END;
+ m->command = command;
+ m->param = param;
- 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;
+ dwRowOffset += 2;
+ }
- 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;
+ row_data += m_nChannels;
+ dwPatternOffset += rowSize;
+ }
-*/
+ }
+ if(!sComment.empty())
+ {
+ m_lpszSongComments = new char[sComment.length() + 1];
+ if (m_lpszSongComments)
+ {
+ memset(m_lpszSongComments, 0, sComment.length() + 1);
+ memcpy(m_lpszSongComments, sComment.c_str(), sComment.length());
+ }
+ }
+
+ return true;
+
+ #undef ASSERT_CAN_READ
+}
Modified: trunk/OpenMPT/soundlib/Load_s3m.cpp
===================================================================
--- trunk/OpenMPT/soundlib/Load_s3m.cpp 2009-08-20 20:43:52 UTC (rev 336)
+++ trunk/OpenMPT/soundlib/Load_s3m.cpp 2009-08-22 10:21:22 UTC (rev 337)
@@ -376,6 +376,10 @@
insfile[iSmp] = ((DWORD)LittleEndianW(*((LPWORD)(s+0x0E)))) << 4;
insfile[iSmp] += ((DWORD)(BYTE)s[0x0D]) << 20;
if (insfile[iSmp] > dwMemLength) insfile[iSmp] &= 0xFFFF;
+
+ if(Samples[iSmp].nLoopEnd <= 4)
+ Samples[iSmp].nLoopStart = Samples[iSmp].nLoopEnd = 0;
+
if ((Samples[iSmp].nLoopStart >= Samples[iSmp].nLoopEnd) || (Samples[iSmp].nLoopEnd - Samples[iSmp].nLoopStart < 1))
Samples[iSmp].nLoopStart = Samples[iSmp].nLoopEnd = 0;
Samples[iSmp].nPan = 0x80;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|