diff --git a/NScumm.Audio.Player/NScumm.Audio.ALPlayer.csproj b/NScumm.Audio.Player/NScumm.Audio.ALPlayer.csproj
index d661e22..aad5757 100644
--- a/NScumm.Audio.Player/NScumm.Audio.ALPlayer.csproj
+++ b/NScumm.Audio.Player/NScumm.Audio.ALPlayer.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp3.1
+ net5.0
diff --git a/NScumm.Audio.Players.Tests/NScumm.Audio.Players.Tests.csproj b/NScumm.Audio.Players.Tests/NScumm.Audio.Players.Tests.csproj
index 642034b..a98e0b2 100644
--- a/NScumm.Audio.Players.Tests/NScumm.Audio.Players.Tests.csproj
+++ b/NScumm.Audio.Players.Tests/NScumm.Audio.Players.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp3.0
+ net5.0
false
diff --git a/NScumm.Audio.Players/AdlPlayer.cs b/NScumm.Audio.Players/AdlPlayer.cs
index 9ccb921..cc0056e 100644
--- a/NScumm.Audio.Players/AdlPlayer.cs
+++ b/NScumm.Audio.Players/AdlPlayer.cs
@@ -60,119 +60,124 @@ public bool Load(string path)
{
return false;
}
- Init();
using (var fs = File.OpenRead(path))
{
- if (fs.Length < 720) return false;
+ return Load(fs);
+ }
+ }
- unk2();
- unk1();
+ public bool Load(Stream stream)
+ {
+ Init();
+ if (stream.Length < 720) return false;
+
+ unk2();
+ unk1();
- // detect format version
- var br = new BinaryReader(fs);
- _version = 3; // assuming we have v3
- for (int i = 0; i < 120; i += 2)
+ // detect format version
+ var br = new BinaryReader(stream);
+ _version = 3; // assuming we have v3
+ for (int i = 0; i < 120; i += 2)
+ {
+ ushort w = br.ReadUInt16();
+ // all entries should be in range 0..500-1 or 0xFFFF
+ if (w >= 500 && w < 0xffff)
{
- ushort w = br.ReadUInt16();
- // all entries should be in range 0..500-1 or 0xFFFF
- if (w >= 500 && w < 0xffff)
- {
- _version = 1; // actually 1 or 2
- break;
- }
+ _version = 1; // actually 1 or 2
+ break;
}
- if (_version == 1)
- { // detect whether v1 or v2
- fs.Seek(120, SeekOrigin.Begin);
- _version = 2; // assuming we have v2
- for (int i = 0; i < 150; i += 2)
- {
- ushort w = br.ReadUInt16();
- if (w > 0 && w < 600)
- { // minimum track offset for v1 is 600
- return false;
- }
- // minimum track offset for v2 is 1000
- if (w > 0 && w < 1000)
- _version = 1;
+ }
+ if (_version == 1)
+ { // detect whether v1 or v2
+ stream.Seek(120, SeekOrigin.Begin);
+ _version = 2; // assuming we have v2
+ for (int i = 0; i < 150; i += 2)
+ {
+ ushort w = br.ReadUInt16();
+ if (w > 0 && w < 600)
+ { // minimum track offset for v1 is 600
+ return false;
}
+ // minimum track offset for v2 is 1000
+ if (w > 0 && w < 1000)
+ _version = 1;
}
- if (_version == 2 && fs.Length < 1120)
- { // minimum file size of v2
- return false;
- }
- if (_version == 3 && fs.Length < 2500)
- { // minimum file size of v3
- return false;
- }
+ }
+ if (_version == 2 && stream.Length < 1120)
+ { // minimum file size of v2
+ return false;
+ }
+ if (_version == 3 && stream.Length < 2500)
+ { // minimum file size of v3
+ return false;
+ }
- fs.Seek(0, SeekOrigin.Begin);
- var file_size = (int)fs.Length;
+ stream.Seek(0, SeekOrigin.Begin);
+ var file_size = (int)stream.Length;
- _driver.snd_unkOpcode3(-1);
- _soundDataPtr = null;
+ _driver.snd_unkOpcode3(-1);
+ _soundDataPtr = null;
- ushort _EntriesSize;
- if (_version < 3)
- {
- _EntriesSize = 120;
- _trackEntries = br.ReadBytes(_EntriesSize);
- }
- else
+ ushort _EntriesSize;
+ if (_version < 3)
+ {
+ _EntriesSize = 120;
+ _trackEntries = br.ReadBytes(_EntriesSize);
+ }
+ else
+ {
+ _EntriesSize = 250 * 2;
+ for (int i = 0; i < 250; i++)
{
- _EntriesSize = 250 * 2;
- for (int i = 0; i < 250; i++)
- {
- _trackEntries16[i] = br.ReadUInt16();
- }
+ _trackEntries16[i] = br.ReadUInt16();
}
+ }
- int soundDataSize = file_size - _EntriesSize;
+ int soundDataSize = file_size - _EntriesSize;
- _soundDataPtr = br.ReadBytes(soundDataSize);
+ _soundDataPtr = br.ReadBytes(soundDataSize);
- file_size = 0;
+ file_size = 0;
- _driver.snd_setSoundData(_soundDataPtr);
+ _driver.snd_setSoundData(_soundDataPtr);
- // _soundFileLoaded = file;
+ // _soundFileLoaded = file;
- // find last subsong
- ushort maxEntry = 0xffff;
- switch (_version)
- {
- case 1:
- maxEntry = 150 - 1;
- break;
- case 2:
- maxEntry = 250 - 1;
+ // find last subsong
+ ushort maxEntry = 0xffff;
+ switch (_version)
+ {
+ case 1:
+ maxEntry = 150 - 1;
+ break;
+ case 2:
+ maxEntry = 250 - 1;
+ break;
+ case 3:
+ maxEntry = 500 - 1;
+ break;
+ }
+ if (_version < 3)
+ {
+ for (int i = 120 - 1; i >= 0; i--)
+ if (_trackEntries[i] <= maxEntry)
+ {
+ numsubsongs = i + 1;
break;
- case 3:
- maxEntry = 500 - 1;
+ }
+ }
+ else
+ {
+ for (int i = 250 - 1; i >= 0; i--)
+ if (_trackEntries16[i] <= maxEntry)
+ {
+ numsubsongs = i + 1;
break;
- }
- if (_version < 3)
- {
- for (int i = 120 - 1; i >= 0; i--)
- if (_trackEntries[i] <= maxEntry)
- {
- numsubsongs = i + 1;
- break;
- }
- }
- else
- {
- for (int i = 250 - 1; i >= 0; i--)
- if (_trackEntries16[i] <= maxEntry)
- {
- numsubsongs = i + 1;
- break;
- }
- }
-
- cursubsong = -1;
- return true;
+ }
}
+
+ cursubsong = -1;
+ return true;
}
public bool Update()
diff --git a/NScumm.Audio.Players/BamPlayer.cs b/NScumm.Audio.Players/BamPlayer.cs
index dfd8665..d5ad1b7 100644
--- a/NScumm.Audio.Players/BamPlayer.cs
+++ b/NScumm.Audio.Players/BamPlayer.cs
@@ -67,9 +67,16 @@ public BamPlayer(IOpl opl)
public bool Load(string path)
{
using (var fs = File.OpenRead(path))
- using (var br = new BinaryReader(fs))
{
- size = fs.Length - 4;
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ using (var br = new BinaryReader(stream))
+ {
+ size = stream.Length - 4;
var id = new string(br.ReadChars(4));
if (!string.Equals(id, "CBMF", StringComparison.OrdinalIgnoreCase)) return false;
diff --git a/NScumm.Audio.Players/CmfPlayer.cs b/NScumm.Audio.Players/CmfPlayer.cs
index cd10b56..1ab1374 100644
--- a/NScumm.Audio.Players/CmfPlayer.cs
+++ b/NScumm.Audio.Players/CmfPlayer.cs
@@ -163,117 +163,122 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- var cSig = new string(br.ReadChars(4));
- if (cSig != "CTMF")
- {
- // Not a CMF file
- return false;
- }
+ return Load(fs);
+ }
+ }
- ushort iVer = br.ReadUInt16();
- if ((iVer != 0x0101) && (iVer != 0x0100))
- {
- Console.Error.WriteLine($"CMF file is not v1.0 or v1.1 (reports {iVer >> 8}.{iVer & 0xFF})");
- return false;
- }
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ var cSig = new string(br.ReadChars(4));
+ if (cSig != "CTMF")
+ {
+ // Not a CMF file
+ return false;
+ }
- cmfHeader.iInstrumentBlockOffset = br.ReadUInt16();
- cmfHeader.iMusicOffset = br.ReadUInt16();
- cmfHeader.iTicksPerQuarterNote = br.ReadUInt16();
- cmfHeader.iTicksPerSecond = br.ReadUInt16();
- cmfHeader.iTagOffsetTitle = br.ReadUInt16();
- cmfHeader.iTagOffsetComposer = br.ReadUInt16();
- cmfHeader.iTagOffsetRemarks = br.ReadUInt16();
-
- // This checks will fix crash for a lot of broken files
- // Title, Composer and Remarks blocks usually located before Instrument block
- // But if not this will indicate invalid offset value (sometimes even bigger than filesize)
- if (cmfHeader.iTagOffsetTitle >= cmfHeader.iInstrumentBlockOffset)
- cmfHeader.iTagOffsetTitle = 0;
- if (cmfHeader.iTagOffsetComposer >= cmfHeader.iInstrumentBlockOffset)
- cmfHeader.iTagOffsetComposer = 0;
- if (cmfHeader.iTagOffsetRemarks >= cmfHeader.iInstrumentBlockOffset)
- cmfHeader.iTagOffsetRemarks = 0;
-
- cmfHeader.iChannelsInUse = br.ReadBytes(16);
- if (iVer == 0x0100)
- {
- cmfHeader.iNumInstruments = br.ReadByte();
- cmfHeader.iTempo = 0;
- }
- else
- { // 0x0101
- cmfHeader.iNumInstruments = br.ReadUInt16();
- cmfHeader.iTempo = br.ReadUInt16();
- }
+ ushort iVer = br.ReadUInt16();
+ if ((iVer != 0x0101) && (iVer != 0x0100))
+ {
+ Console.Error.WriteLine($"CMF file is not v1.0 or v1.1 (reports {iVer >> 8}.{iVer & 0xFF})");
+ return false;
+ }
- // Load the instruments
+ cmfHeader.iInstrumentBlockOffset = br.ReadUInt16();
+ cmfHeader.iMusicOffset = br.ReadUInt16();
+ cmfHeader.iTicksPerQuarterNote = br.ReadUInt16();
+ cmfHeader.iTicksPerSecond = br.ReadUInt16();
+ cmfHeader.iTagOffsetTitle = br.ReadUInt16();
+ cmfHeader.iTagOffsetComposer = br.ReadUInt16();
+ cmfHeader.iTagOffsetRemarks = br.ReadUInt16();
+
+ // This checks will fix crash for a lot of broken files
+ // Title, Composer and Remarks blocks usually located before Instrument block
+ // But if not this will indicate invalid offset value (sometimes even bigger than filesize)
+ if (cmfHeader.iTagOffsetTitle >= cmfHeader.iInstrumentBlockOffset)
+ cmfHeader.iTagOffsetTitle = 0;
+ if (cmfHeader.iTagOffsetComposer >= cmfHeader.iInstrumentBlockOffset)
+ cmfHeader.iTagOffsetComposer = 0;
+ if (cmfHeader.iTagOffsetRemarks >= cmfHeader.iInstrumentBlockOffset)
+ cmfHeader.iTagOffsetRemarks = 0;
+
+ cmfHeader.iChannelsInUse = br.ReadBytes(16);
+ if (iVer == 0x0100)
+ {
+ cmfHeader.iNumInstruments = br.ReadByte();
+ cmfHeader.iTempo = 0;
+ }
+ else
+ { // 0x0101
+ cmfHeader.iNumInstruments = br.ReadUInt16();
+ cmfHeader.iTempo = br.ReadUInt16();
+ }
- fs.Seek(cmfHeader.iInstrumentBlockOffset, SeekOrigin.Begin);
- pInstruments = new SBI[
- (cmfHeader.iNumInstruments < 128) ? 128 : cmfHeader.iNumInstruments
- ]; // Always at least 128 available for use
+ // Load the instruments
- for (int i = 0; i < cmfHeader.iNumInstruments; i++)
- {
- pInstruments[i].op = new OPERATOR[2];
- pInstruments[i].op[0].iCharMult = br.ReadByte();
- pInstruments[i].op[1].iCharMult = br.ReadByte();
- pInstruments[i].op[0].iScalingOutput = br.ReadByte();
- pInstruments[i].op[1].iScalingOutput = br.ReadByte();
- pInstruments[i].op[0].iAttackDecay = br.ReadByte();
- pInstruments[i].op[1].iAttackDecay = br.ReadByte();
- pInstruments[i].op[0].iSustainRelease = br.ReadByte();
- pInstruments[i].op[1].iSustainRelease = br.ReadByte();
- pInstruments[i].op[0].iWaveSel = br.ReadByte();
- pInstruments[i].op[1].iWaveSel = br.ReadByte();
- pInstruments[i].iConnection = br.ReadByte();
- fs.Seek(5, SeekOrigin.Current); // skip over the padding bytes
- }
+ stream.Seek(cmfHeader.iInstrumentBlockOffset, SeekOrigin.Begin);
+ pInstruments = new SBI[
+ (cmfHeader.iNumInstruments < 128) ? 128 : cmfHeader.iNumInstruments
+ ]; // Always at least 128 available for use
- // Set the rest of the instruments to the CMF defaults
- for (int i = cmfHeader.iNumInstruments; i < 128; i++)
- {
- pInstruments[i].op = new OPERATOR[2];
- pInstruments[i].op[0].iCharMult = cDefaultPatches[(i % 16) * 11 + 0];
- pInstruments[i].op[1].iCharMult = cDefaultPatches[(i % 16) * 11 + 1];
- pInstruments[i].op[0].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 2];
- pInstruments[i].op[1].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 3];
- pInstruments[i].op[0].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 4];
- pInstruments[i].op[1].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 5];
- pInstruments[i].op[0].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 6];
- pInstruments[i].op[1].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 7];
- pInstruments[i].op[0].iWaveSel = cDefaultPatches[(i % 16) * 11 + 8];
- pInstruments[i].op[1].iWaveSel = cDefaultPatches[(i % 16) * 11 + 9];
- pInstruments[i].iConnection = cDefaultPatches[(i % 16) * 11 + 10];
- }
+ for (int i = 0; i < cmfHeader.iNumInstruments; i++)
+ {
+ pInstruments[i].op = new OPERATOR[2];
+ pInstruments[i].op[0].iCharMult = br.ReadByte();
+ pInstruments[i].op[1].iCharMult = br.ReadByte();
+ pInstruments[i].op[0].iScalingOutput = br.ReadByte();
+ pInstruments[i].op[1].iScalingOutput = br.ReadByte();
+ pInstruments[i].op[0].iAttackDecay = br.ReadByte();
+ pInstruments[i].op[1].iAttackDecay = br.ReadByte();
+ pInstruments[i].op[0].iSustainRelease = br.ReadByte();
+ pInstruments[i].op[1].iSustainRelease = br.ReadByte();
+ pInstruments[i].op[0].iWaveSel = br.ReadByte();
+ pInstruments[i].op[1].iWaveSel = br.ReadByte();
+ pInstruments[i].iConnection = br.ReadByte();
+ stream.Seek(5, SeekOrigin.Current); // skip over the padding bytes
+ }
- if (cmfHeader.iTagOffsetTitle != 0)
- {
- fs.Seek(cmfHeader.iTagOffsetTitle, SeekOrigin.Begin);
- strTitle = ReadString(br);
- }
- if (cmfHeader.iTagOffsetComposer != 0)
- {
- fs.Seek(cmfHeader.iTagOffsetComposer, SeekOrigin.Begin);
- strComposer = ReadString(br);
- }
- if (cmfHeader.iTagOffsetRemarks != 0)
- {
- fs.Seek(cmfHeader.iTagOffsetRemarks, SeekOrigin.Begin);
- strRemarks = ReadString(br);
- }
+ // Set the rest of the instruments to the CMF defaults
+ for (int i = cmfHeader.iNumInstruments; i < 128; i++)
+ {
+ pInstruments[i].op = new OPERATOR[2];
+ pInstruments[i].op[0].iCharMult = cDefaultPatches[(i % 16) * 11 + 0];
+ pInstruments[i].op[1].iCharMult = cDefaultPatches[(i % 16) * 11 + 1];
+ pInstruments[i].op[0].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 2];
+ pInstruments[i].op[1].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 3];
+ pInstruments[i].op[0].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 4];
+ pInstruments[i].op[1].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 5];
+ pInstruments[i].op[0].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 6];
+ pInstruments[i].op[1].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 7];
+ pInstruments[i].op[0].iWaveSel = cDefaultPatches[(i % 16) * 11 + 8];
+ pInstruments[i].op[1].iWaveSel = cDefaultPatches[(i % 16) * 11 + 9];
+ pInstruments[i].iConnection = cDefaultPatches[(i % 16) * 11 + 10];
+ }
+
+ if (cmfHeader.iTagOffsetTitle != 0)
+ {
+ stream.Seek(cmfHeader.iTagOffsetTitle, SeekOrigin.Begin);
+ strTitle = ReadString(br);
+ }
+ if (cmfHeader.iTagOffsetComposer != 0)
+ {
+ stream.Seek(cmfHeader.iTagOffsetComposer, SeekOrigin.Begin);
+ strComposer = ReadString(br);
+ }
+ if (cmfHeader.iTagOffsetRemarks != 0)
+ {
+ stream.Seek(cmfHeader.iTagOffsetRemarks, SeekOrigin.Begin);
+ strRemarks = ReadString(br);
+ }
- // Load the MIDI data into memory
- fs.Seek(cmfHeader.iMusicOffset, SeekOrigin.Begin);
- iSongLen = (int)(fs.Length - cmfHeader.iMusicOffset);
- data = br.ReadBytes(iSongLen);
+ // Load the MIDI data into memory
+ stream.Seek(cmfHeader.iMusicOffset, SeekOrigin.Begin);
+ iSongLen = (int)(stream.Length - cmfHeader.iMusicOffset);
+ data = br.ReadBytes(iSongLen);
- Rewind(0);
+ Rewind(0);
- return true;
- }
+ return true;
}
public bool Update()
diff --git a/NScumm.Audio.Players/Dro2Player.cs b/NScumm.Audio.Players/Dro2Player.cs
index eba001e..c8a61ec 100644
--- a/NScumm.Audio.Players/Dro2Player.cs
+++ b/NScumm.Audio.Players/Dro2Player.cs
@@ -26,189 +26,194 @@
namespace NScumm.Audio.Players
{
- public class Dro2Player: IMusicPlayer
+ public class Dro2Player : IMusicPlayer
{
- private byte iCmdDelayS, iCmdDelayL;
- private int iConvTableLen;
- private byte[] piConvTable;
-
- private byte[] data;
- private int iLength;
- private int iPos;
- private int iDelay;
- private string author;
- private string desc;
- private string title;
-
- public IOpl Opl { get; }
-
- public float RefreshRate
- {
- get
- {
- if (iDelay > 0) return 1000.0f / iDelay;
- else return 1000.0f;
- }
- }
-
- public Dro2Player(IOpl opl)
- {
- if (opl == null) throw new ArgumentNullException(nameof(opl));
- Opl = opl;
- }
-
- public bool Load(string path)
- {
- using (var fs = File.OpenRead(path))
- {
- var br = new BinaryReader(fs);
- var id = new string(br.ReadChars(8));
- if (id != "DBRAWOPL") return false;
-
- var version = br.ReadInt32();
- if (version != 0x2) return false;
-
- iLength = br.ReadInt32(); // should better use an unsigned type
- if (iLength <= 0 || iLength >= 1 << 30 ||
- iLength > br.BaseStream.Length - br.BaseStream.Position)
- {
- return false;
- }
- iLength *= 2; // stored in file as number of byte p
- br.BaseStream.Seek(4, SeekOrigin.Current); // Length in milliseconds
- br.BaseStream.Seek(1, SeekOrigin.Current); /// OPL type (0 == OPL2, 1 == Dual OPL2, 2 == OPL3)
- int iFormat = br.ReadByte();
- if (iFormat != 0)
- {
- return false;
- }
- int iCompression = br.ReadByte();
- if (iCompression != 0)
- {
- return false;
- }
- iCmdDelayS = br.ReadByte();
- iCmdDelayL = br.ReadByte();
- iConvTableLen = br.ReadByte();
-
- piConvTable = br.ReadBytes(iConvTableLen);
-
- // Read the OPL data.
- data = br.ReadBytes(iLength);
-
- int tagsize = (int)(br.BaseStream.Length - br.BaseStream.Position);
- if (tagsize >= 3)
- {
- // The arbitrary Tag Data section begins here.
- if (br.ReadByte() != 0xFF ||
- br.ReadByte() != 0xFF ||
- br.ReadByte() != 0x1A)
- {
- // Tag data does not present or truncated.
- goto end_section;
- }
-
- // "title" is maximum 40 characters long.
- title = ReadString(br, 40);
-
- // Skip "author" if Tag marker byte is missing.
- if (br.ReadByte() != 0x1B)
- {
- br.BaseStream.Seek(-1, SeekOrigin.Current);
- goto desc_section;
- }
-
- // "author" is maximum 40 characters long.
- author = ReadString(br, 40);
-
- desc_section:
- // Skip "desc" if Tag marker byte is missing.
- if (br.ReadByte() != 0x1C)
- {
- goto end_section;
- }
-
- // "desc" is now maximum 1023 characters long (it was 140).
- desc = ReadString(br, 1023);
- }
-
- end_section:
- Rewind(0);
-
- return true;
- }
- }
-
- public bool Update()
+ private byte iCmdDelayS, iCmdDelayL;
+ private int iConvTableLen;
+ private byte[] piConvTable;
+
+ private byte[] data;
+ private int iLength;
+ private int iPos;
+ private int iDelay;
+ private string author;
+ private string desc;
+ private string title;
+
+ public IOpl Opl { get; }
+
+ public float RefreshRate
+ {
+ get
+ {
+ if (iDelay > 0) return 1000.0f / iDelay;
+ else return 1000.0f;
+ }
+ }
+
+ public Dro2Player(IOpl opl)
+ {
+ if (opl == null) throw new ArgumentNullException(nameof(opl));
+ Opl = opl;
+ }
+
+ public bool Load(string path)
+ {
+ using (var fs = File.OpenRead(path))
+ {
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ var id = new string(br.ReadChars(8));
+ if (id != "DBRAWOPL") return false;
+
+ var version = br.ReadInt32();
+ if (version != 0x2) return false;
+
+ iLength = br.ReadInt32(); // should better use an unsigned type
+ if (iLength <= 0 || iLength >= 1 << 30 ||
+ iLength > br.BaseStream.Length - br.BaseStream.Position)
+ {
+ return false;
+ }
+ iLength *= 2; // stored in file as number of byte p
+ br.BaseStream.Seek(4, SeekOrigin.Current); // Length in milliseconds
+ br.BaseStream.Seek(1, SeekOrigin.Current); /// OPL type (0 == OPL2, 1 == Dual OPL2, 2 == OPL3)
+ int iFormat = br.ReadByte();
+ if (iFormat != 0)
+ {
+ return false;
+ }
+ int iCompression = br.ReadByte();
+ if (iCompression != 0)
+ {
+ return false;
+ }
+ iCmdDelayS = br.ReadByte();
+ iCmdDelayL = br.ReadByte();
+ iConvTableLen = br.ReadByte();
+
+ piConvTable = br.ReadBytes(iConvTableLen);
+
+ // Read the OPL data.
+ data = br.ReadBytes(iLength);
+
+ int tagsize = (int)(br.BaseStream.Length - br.BaseStream.Position);
+ if (tagsize >= 3)
+ {
+ // The arbitrary Tag Data section begins here.
+ if (br.ReadByte() != 0xFF ||
+ br.ReadByte() != 0xFF ||
+ br.ReadByte() != 0x1A)
+ {
+ // Tag data does not present or truncated.
+ goto end_section;
+ }
+
+ // "title" is maximum 40 characters long.
+ title = ReadString(br, 40);
+
+ // Skip "author" if Tag marker byte is missing.
+ if (br.ReadByte() != 0x1B)
+ {
+ br.BaseStream.Seek(-1, SeekOrigin.Current);
+ goto desc_section;
+ }
+
+ // "author" is maximum 40 characters long.
+ author = ReadString(br, 40);
+
+ desc_section:
+ // Skip "desc" if Tag marker byte is missing.
+ if (br.ReadByte() != 0x1C)
+ {
+ goto end_section;
+ }
+
+ // "desc" is now maximum 1023 characters long (it was 140).
+ desc = ReadString(br, 1023);
+ }
+
+ end_section:
+ Rewind(0);
+
+ return true;
+ }
+
+ public bool Update()
{
- while (iPos < iLength)
- {
- int iIndex = data[iPos++];
- int iValue = data[iPos++];
-
- // Short delay
- if (iIndex == iCmdDelayS)
- {
- iDelay = iValue + 1;
- return true;
-
- // Long delay
- }
- else if (iIndex == iCmdDelayL)
- {
- iDelay = (iValue + 1) << 8;
- return true;
-
- // Normal write
- }
- else
- {
- if ((iIndex & 0x80)!=0)
- {
- // High bit means use second chip in dual-OPL2 config
- // TODO:?
- //Opl.setchip(1);
- iIndex &= 0x7F;
- }
- else
- {
- // TODO:?
- //Opl.Setchip(0);
- }
- if (iIndex >= iConvTableLen)
- {
- Console.WriteLine("DRO2: Error - index beyond end of codemap table! Corrupted .dro?\n");
- return false; // EOF
- }
- int iReg = piConvTable[iIndex];
- Opl.WriteReg(iReg, iValue);
- }
-
- }
-
- // This won't result in endless-play using Adplay, but IMHO that code belongs
- // in Adplay itself, not here.
- return iPos < iLength;
- }
-
- public void Rewind(int subsong)
+ while (iPos < iLength)
+ {
+ int iIndex = data[iPos++];
+ int iValue = data[iPos++];
+
+ // Short delay
+ if (iIndex == iCmdDelayS)
+ {
+ iDelay = iValue + 1;
+ return true;
+
+ // Long delay
+ }
+ else if (iIndex == iCmdDelayL)
+ {
+ iDelay = (iValue + 1) << 8;
+ return true;
+
+ // Normal write
+ }
+ else
+ {
+ if ((iIndex & 0x80) != 0)
+ {
+ // High bit means use second chip in dual-OPL2 config
+ // TODO:?
+ //Opl.setchip(1);
+ iIndex &= 0x7F;
+ }
+ else
+ {
+ // TODO:?
+ //Opl.Setchip(0);
+ }
+ if (iIndex >= iConvTableLen)
+ {
+ Console.WriteLine("DRO2: Error - index beyond end of codemap table! Corrupted .dro?\n");
+ return false; // EOF
+ }
+ int iReg = piConvTable[iIndex];
+ Opl.WriteReg(iReg, iValue);
+ }
+
+ }
+
+ // This won't result in endless-play using Adplay, but IMHO that code belongs
+ // in Adplay itself, not here.
+ return iPos < iLength;
+ }
+
+ public void Rewind(int subsong)
{
- iDelay = 0;
- iPos = 0;
- }
+ iDelay = 0;
+ iPos = 0;
+ }
- private static string ReadString(BinaryReader br, int maxLength)
+ private static string ReadString(BinaryReader br, int maxLength)
{
- char c;
- int i = 0;
- var text = new StringBuilder();
- while ((c = br.ReadChar()) != 0 && i
- internal sealed class DroPlayer: IMusicPlayer
+ internal sealed class DroPlayer : IMusicPlayer
{
private const byte iCmdDelayS = 0x00;
private const byte iCmdDelayL = 0x01;
@@ -61,82 +61,87 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- var id = new string(br.ReadChars(8));
- if (id != "DBRAWOPL") return false;
-
- var version = br.ReadInt32();
- if (version != 0x10000) return false;
-
- var lengthInMs = br.ReadInt32();
- var length = br.ReadInt32();
- _data = new byte[length];
-
- // Some early .DRO files only used one byte for the hardware type, then
- // later changed to four bytes with no version number change.
- // OPL type (0 == OPL2, 1 == OPL3, 2 == Dual OPL2)
- br.ReadChar(); // Type of opl data this can contain - ignored
- int i;
- for (i = 0; i < 3; i++)
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ var id = new string(br.ReadChars(8));
+ if (id != "DBRAWOPL") return false;
+
+ var version = br.ReadInt32();
+ if (version != 0x10000) return false;
+
+ var lengthInMs = br.ReadInt32();
+ var length = br.ReadInt32();
+ _data = new byte[length];
+
+ // Some early .DRO files only used one byte for the hardware type, then
+ // later changed to four bytes with no version number change.
+ // OPL type (0 == OPL2, 1 == OPL3, 2 == Dual OPL2)
+ br.ReadChar(); // Type of opl data this can contain - ignored
+ int i;
+ for (i = 0; i < 3; i++)
+ {
+ _data[i] = br.ReadByte();
+ }
+
+ if (_data[0] == 0 || _data[1] == 0 || _data[2] == 0)
+ {
+ // If we're here then this is a later (more popular) file with
+ // the full four bytes for the hardware-type.
+ i = 0; // so ignore the three bytes we just read and start again
+ }
+
+ // Read the OPL data.
+ br.BaseStream.Read(_data, i, length - i);
+
+ var tagsize = stream.Length - stream.Position;
+ if (tagsize >= 3)
+ {
+ // The arbitrary Tag Data section begins here.
+ if (br.ReadByte() != 0xFF ||
+ br.ReadByte() != 0xFF ||
+ br.ReadByte() != 0x1A)
{
- _data[i] = br.ReadByte();
+ // Tag data does not present or truncated.
+ return true;
}
- if (_data[0] == 0 || _data[1] == 0 || _data[2] == 0)
+ // "title" is maximum 40 characters long.
+ var title = new StringBuilder();
+ char c;
+ while ((c = br.ReadChar()) != 0)
{
- // If we're here then this is a later (more popular) file with
- // the full four bytes for the hardware-type.
- i = 0; // so ignore the three bytes we just read and start again
+ title.Append(c);
}
- // Read the OPL data.
- br.BaseStream.Read(_data, i, length - i);
-
- var tagsize = fs.Length - fs.Position;
- if (tagsize >= 3)
+ // "author" Tag marker byte is present ?
+ if (br.ReadByte() == 0x1B)
{
- // The arbitrary Tag Data section begins here.
- if (br.ReadByte() != 0xFF ||
- br.ReadByte() != 0xFF ||
- br.ReadByte() != 0x1A)
- {
- // Tag data does not present or truncated.
- return true;
- }
-
- // "title" is maximum 40 characters long.
- var title = new StringBuilder();
- char c;
+ // "author" is maximum 40 characters long.
+ var author = new StringBuilder();
while ((c = br.ReadChar()) != 0)
{
- title.Append(c);
- }
-
- // "author" Tag marker byte is present ?
- if (br.ReadByte() == 0x1B)
- {
- // "author" is maximum 40 characters long.
- var author = new StringBuilder();
- while ((c = br.ReadChar()) != 0)
- {
- author.Append(c);
- }
+ author.Append(c);
}
+ }
- // "desc" Tag marker byte is present..
- if (br.ReadByte() == 0x1C)
+ // "desc" Tag marker byte is present..
+ if (br.ReadByte() == 0x1C)
+ {
+ // "desc" is now maximum 1023 characters long (it was 140).
+ var desc = new StringBuilder();
+ while ((c = br.ReadChar()) != 0)
{
- // "desc" is now maximum 1023 characters long (it was 140).
- var desc = new StringBuilder();
- while ((c = br.ReadChar()) != 0)
- {
- desc.Append(c);
- }
+ desc.Append(c);
}
}
- Rewind(0);
- return true;
}
+ Rewind(0);
+ return true;
}
public bool Update()
@@ -193,12 +198,14 @@ private void Rewind(int subsong)
// Registers not initialized to 0 will be corrected
// in the data stream.
// opl->setchip(0);
- for(var i = 0; i < 256; i++) {
+ for (var i = 0; i < 256; i++)
+ {
Opl.WriteReg(i, 0);
}
-
+
// opl->setchip(1);
- for(var i = 0; i < 256; i++) {
+ for (var i = 0; i < 256; i++)
+ {
Opl.WriteReg(i, 0);
}
diff --git a/NScumm.Audio.Players/HscPlayer.cs b/NScumm.Audio.Players/HscPlayer.cs
index 903f42e..4beadea 100644
--- a/NScumm.Audio.Players/HscPlayer.cs
+++ b/NScumm.Audio.Players/HscPlayer.cs
@@ -71,47 +71,52 @@ public bool Load(string path)
using (var fs = File.OpenRead(path))
{
- if (fs.Length > (59187 + 1)) // +1 is for some files that have a trailing 0x00 on the end
- return false;
- if (fs.Length < (1587 + 1152)) // no 0x00 byte here as this is the smallest possible size
- return false;
-
- var br = new BinaryReader(fs);
- int total_patterns_in_hsc = (int)((fs.Length - 1587) / 1152);
-
- // load section
- instr = new byte[128][];
- for (var i = 0; i < 128; i++) // load instruments
- instr[i] = br.ReadBytes(12);
- for (var i = 0; i < 128; i++)
- { // correct instruments
- instr[i][2] = (byte)(instr[i][2] ^ (instr[i][2] & 0x40) << 1);
- instr[i][3] = (byte)(instr[i][3] ^ (instr[i][3] & 0x40) << 1);
- instr[i][11] >>= 4; // slide
- }
- for (var i = 0; i < 51; i++)
- { // load tracklist
- song[i] = br.ReadByte();
- // if out of range, song ends here
- if (
- ((song[i] & 0x7F) > 0x31)
- || ((song[i] & 0x7F) >= total_patterns_in_hsc)
- ) song[i] = 0xFF;
- }
- var len = (fs.Length - fs.Position)/2/64/9;
- patterns = new hscnote[len, 64 * 9];
- for (var i = 0; i < len; i++)
- { // load patterns
- for (var j = 0; j < 64 * 9; j++)
- {
- patterns[i, j].note = br.ReadByte();
- patterns[i, j].effect = br.ReadByte();
- }
- }
+ return Load(fs);
+ }
+ }
- Rewind(0); // rewind module
- return true;
+ public bool Load(Stream stream)
+ {
+ if (stream.Length > (59187 + 1)) // +1 is for some files that have a trailing 0x00 on the end
+ return false;
+ if (stream.Length < (1587 + 1152)) // no 0x00 byte here as this is the smallest possible size
+ return false;
+
+ var br = new BinaryReader(stream);
+ int total_patterns_in_hsc = (int)((stream.Length - 1587) / 1152);
+
+ // load section
+ instr = new byte[128][];
+ for (var i = 0; i < 128; i++) // load instruments
+ instr[i] = br.ReadBytes(12);
+ for (var i = 0; i < 128; i++)
+ { // correct instruments
+ instr[i][2] = (byte)(instr[i][2] ^ (instr[i][2] & 0x40) << 1);
+ instr[i][3] = (byte)(instr[i][3] ^ (instr[i][3] & 0x40) << 1);
+ instr[i][11] >>= 4; // slide
}
+ for (var i = 0; i < 51; i++)
+ { // load tracklist
+ song[i] = br.ReadByte();
+ // if out of range, song ends here
+ if (
+ ((song[i] & 0x7F) > 0x31)
+ || ((song[i] & 0x7F) >= total_patterns_in_hsc)
+ ) song[i] = 0xFF;
+ }
+ var len = (stream.Length - stream.Position) / 2 / 64 / 9;
+ patterns = new hscnote[len, 64 * 9];
+ for (var i = 0; i < len; i++)
+ { // load patterns
+ for (var j = 0; j < 64 * 9; j++)
+ {
+ patterns[i, j].note = br.ReadByte();
+ patterns[i, j].effect = br.ReadByte();
+ }
+ }
+
+ Rewind(0); // rewind module
+ return true;
}
public bool Update()
@@ -166,14 +171,14 @@ public bool Update()
switch (effect & 0xf0)
{ // effect handling
case 0: // global effect
- /* The following fx are unimplemented on purpose:
- * 02 - Slide Mainvolume up
- * 03 - Slide Mainvolume down (here: fade in)
- * 04 - Set Mainvolume to 0
- *
- * This is because i've never seen any HSC modules using the fx this way.
- * All modules use the fx the way, i've implemented it.
- */
+ /* The following fx are unimplemented on purpose:
+ * 02 - Slide Mainvolume up
+ * 03 - Slide Mainvolume down (here: fade in)
+ * 04 - Set Mainvolume to 0
+ *
+ * This is because i've never seen any HSC modules using the fx this way.
+ * All modules use the fx the way, i've implemented it.
+ */
switch (eff_op)
{
case 1: pattbreak++; break; // jump to next pattern
diff --git a/NScumm.Audio.Players/IMusicPlayer.cs b/NScumm.Audio.Players/IMusicPlayer.cs
index 4031bcd..47e1669 100644
--- a/NScumm.Audio.Players/IMusicPlayer.cs
+++ b/NScumm.Audio.Players/IMusicPlayer.cs
@@ -19,6 +19,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+using System.IO;
using NScumm.Core.Audio.OPL;
namespace NScumm.Audio.Players
@@ -29,6 +30,7 @@ public interface IMusicPlayer
float RefreshRate { get; }
bool Load(string path);
+ bool Load(Stream stream);
bool Update();
}
}
diff --git a/NScumm.Audio.Players/ImfPlayer.cs b/NScumm.Audio.Players/ImfPlayer.cs
index d15271f..34aedb1 100644
--- a/NScumm.Audio.Players/ImfPlayer.cs
+++ b/NScumm.Audio.Players/ImfPlayer.cs
@@ -30,20 +30,20 @@ namespace NScumm.Audio.Players
/// IMF Player by Simon Peter dn.tlp@gmx.net
/// This code has been adapted from adplug https://github.com/adplug/adplug
///
- internal sealed class ImfPlayer: IMusicPlayer
+ public class ImfPlayer : IMusicPlayer
{
private string track_name, game_name, author_name, remarks;
private long _size;
private Sdata[] _data;
private string _footer;
- private float _rate;
+ private float _rate = 700.0f;
private int _pos;
private bool _songend;
private ushort _del;
public IOpl Opl { get; }
- public float RefreshRate { get; private set; }
+ public float RefreshRate { get; set; }
struct Sdata
{
@@ -59,87 +59,93 @@ public ImfPlayer(IOpl opl)
public bool Load(string path)
{
+ if (!string.Equals(Path.GetExtension(path), ".imf", System.StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(Path.GetExtension(path), ".wlf", System.StringComparison.OrdinalIgnoreCase))
+ {
+ // It's no IMF file at all
+ return false;
+ }
+
+ _rate = GetRate(path);
+
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- long fsize, flsize, mfsize = 0;
- uint i;
+ return Load(fs);
+ }
+ }
- // file validation section
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ long fsize, flsize, mfsize = 0;
+ uint i;
+
+ // file validation section
+ {
+ var header = new string(br.ReadChars(5));
+ var version = br.ReadByte();
+
+ if (header != "ADLIB" || version != 1)
{
- var header = new string(br.ReadChars(5));
- var version = br.ReadByte();
-
- if (header != "ADLIB" || version != 1)
- {
- if (!string.Equals(Path.GetExtension(path), ".imf", System.StringComparison.OrdinalIgnoreCase)
- && !string.Equals(Path.GetExtension(path), ".wlf", System.StringComparison.OrdinalIgnoreCase))
- {
- // It's no IMF file at all
- return false;
- }
- fs.Seek(0, SeekOrigin.Begin); // It's a normal IMF file
- }
- else
- {
- // It's a IMF file with header
- track_name = ReadString(br);
- game_name = ReadString(br);
- br.ReadByte();
- mfsize = fs.Position + 2;
- }
+ stream.Seek(0, SeekOrigin.Begin); // It's a normal IMF file
}
-
- // load section
- if (mfsize > 0)
- fsize = br.ReadInt32();
else
- fsize = br.ReadInt16();
- flsize = fs.Length;
- if (fsize == 0)
- { // footerless file (raw music data)
- if (mfsize != 0)
- fs.Seek(-4, SeekOrigin.Current);
- else
- fs.Seek(-2, SeekOrigin.Current);
- _size = (flsize - mfsize) / 4;
- }
- else // file has got a footer
- _size = fsize / 4;
-
- _data = new Sdata[_size];
- for (i = 0; i < _size; i++)
{
- _data[i].reg = br.ReadByte(); _data[i].val = br.ReadByte();
- _data[i].time = br.ReadUInt16();
+ // It's a IMF file with header
+ track_name = ReadString(br);
+ game_name = ReadString(br);
+ br.ReadByte();
+ mfsize = stream.Position + 2;
}
+ }
+
+ // load section
+ if (mfsize > 0)
+ fsize = br.ReadInt32();
+ else
+ fsize = br.ReadInt16();
+ flsize = stream.Length;
+ if (fsize == 0)
+ { // footerless file (raw music data)
+ if (mfsize != 0)
+ stream.Seek(-4, SeekOrigin.Current);
+ else
+ stream.Seek(-2, SeekOrigin.Current);
+ _size = (flsize - mfsize) / 4;
+ }
+ else // file has got a footer
+ _size = fsize / 4;
+
+ _data = new Sdata[_size];
+ for (i = 0; i < _size; i++)
+ {
+ _data[i].reg = br.ReadByte(); _data[i].val = br.ReadByte();
+ _data[i].time = br.ReadUInt16();
+ }
- // read footer, if any
- if (fsize != 0 && (fsize < flsize - 2 - mfsize))
+ // read footer, if any
+ if (fsize != 0 && (fsize < flsize - 2 - mfsize))
+ {
+ if (br.ReadByte() == 0x1a)
{
- if (br.ReadByte() == 0x1a)
- {
- // Adam Nielsen's footer format
- track_name = ReadString(br);
- author_name = ReadString(br);
- remarks = ReadString(br);
- }
- else
- {
- // Generic footer
- long footerlen = flsize - fsize - 2 - mfsize;
-
- _footer = ReadString(br, footerlen);
- }
+ // Adam Nielsen's footer format
+ track_name = ReadString(br);
+ author_name = ReadString(br);
+ remarks = ReadString(br);
}
+ else
+ {
+ // Generic footer
+ long footerlen = flsize - fsize - 2 - mfsize;
- _rate = GetRate(path);
+ _footer = ReadString(br, footerlen);
+ }
+ }
- _pos = 0; _del = 0; RefreshRate = _rate; _songend = false;
- Opl.WriteReg(1, 32); // go to OPL2 mode
+ _pos = 0; _del = 0; RefreshRate = _rate; _songend = false;
+ Opl.WriteReg(1, 32); // go to OPL2 mode
- return true;
- }
+ return true;
}
public bool Update()
@@ -156,7 +162,7 @@ public bool Update()
_pos = 0;
_songend = true;
}
- else RefreshRate = _rate / (float)_del;
+ else RefreshRate = _rate / _del;
return !_songend;
}
diff --git a/NScumm.Audio.Players/KsmPlayer.cs b/NScumm.Audio.Players/KsmPlayer.cs
index b2574b8..67e8c10 100644
--- a/NScumm.Audio.Players/KsmPlayer.cs
+++ b/NScumm.Audio.Players/KsmPlayer.cs
@@ -72,44 +72,55 @@ public bool Load(string path)
}
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- AdPlug_LogWrite($"*** CksmPlayer::load(,\"{path}\") ***\n");
+ return Load(fs);
+ }
+ }
- // Load instruments from 'insts.dat'
- var fn = Path.Combine(Path.GetDirectoryName(path), "insts.dat");
- if (!File.Exists(fn))
- {
- AdPlug_LogWrite("Couldn't open instruments file! Aborting!\n");
- AdPlug_LogWrite("--- CksmPlayer::load ---\n");
- return false;
- }
- AdPlug_LogWrite("Instruments file: \"{fn}\"\n");
- loadinsts(fn);
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ AdPlug_LogWrite($"*** CksmPlayer::load ***\n");
- for (var i = 0; i < 16; i++) trinst[i] = br.ReadByte();
- for (var i = 0; i < 16; i++) trquant[i] = br.ReadByte();
- for (var i = 0; i < 16; i++) trchan[i] = br.ReadByte();
- fs.Seek(16, SeekOrigin.Current);
- for (var i = 0; i < 16; i++) trvol[i] = br.ReadByte();
- numnotes = br.ReadUInt16();
- note = new int[numnotes];
- for (var i = 0; i < numnotes; i++) note[i] = br.ReadInt32();
+ // Load instruments from 'insts.dat'
+ var fs = stream as FileStream;
+ if (fs == null)
+ {
+ AdPlug_LogWrite("Couldn't open instruments for Ksm from a pure stream! Aborting!\n");
+ return false;
+ }
+ var fn = Path.Combine(Path.GetDirectoryName(fs.Name), "insts.dat");
+ if (!File.Exists(fn))
+ {
+ AdPlug_LogWrite("Couldn't open instruments file! Aborting!\n");
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return false;
+ }
+ AdPlug_LogWrite($"Instruments file: \"{fn}\"\n");
+ loadinsts(fn);
- if (trchan[11] == 0)
- {
- drumstat = 0;
- numchans = 9;
- }
- else
- {
- drumstat = 32;
- numchans = 6;
- }
+ for (var i = 0; i < 16; i++) trinst[i] = br.ReadByte();
+ for (var i = 0; i < 16; i++) trquant[i] = br.ReadByte();
+ for (var i = 0; i < 16; i++) trchan[i] = br.ReadByte();
+ stream.Seek(16, SeekOrigin.Current);
+ for (var i = 0; i < 16; i++) trvol[i] = br.ReadByte();
+ numnotes = br.ReadUInt16();
+ note = new int[numnotes];
+ for (var i = 0; i < numnotes; i++) note[i] = br.ReadInt32();
- Rewind(0);
- AdPlug_LogWrite("--- CksmPlayer::load ---\n");
- return true;
+ if (trchan[11] == 0)
+ {
+ drumstat = 0;
+ numchans = 9;
}
+ else
+ {
+ drumstat = 32;
+ numchans = 6;
+ }
+
+ Rewind(0);
+ AdPlug_LogWrite("--- CksmPlayer::load ---\n");
+ return true;
}
public bool Update()
@@ -339,14 +350,15 @@ private void loadinsts(string path)
for (var i = 0; i < 256; i++)
{
instname[i] = new string(br.ReadChars(20));
- for (var j = 0; j < 11; j++) inst[i,j] = br.ReadByte();
+ for (var j = 0; j < 11; j++) inst[i, j] = br.ReadByte();
fs.Seek(2, SeekOrigin.Current);
}
}
}
- private void AdPlug_LogWrite(string fmt, params object[] parameters)
+ private void AdPlug_LogWrite(string message)
{
+ Console.Error.WriteLine(message);
}
}
}
\ No newline at end of file
diff --git a/NScumm.Audio.Players/MidPlayer.cs b/NScumm.Audio.Players/MidPlayer.cs
index 3e274f7..f84b4ec 100644
--- a/NScumm.Audio.Players/MidPlayer.cs
+++ b/NScumm.Audio.Players/MidPlayer.cs
@@ -179,47 +179,60 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- var s = br.ReadBytes(6);
- int good = 0;
- subsongs = 0;
- switch (s[0])
- {
- case (byte)'A':
- if (s[1] == 'D' && s[2] == 'L') good = FILE_LUCAS;
- break;
- case (byte)'M':
- if (s[1] == 'T' && s[2] == 'h' && s[3] == 'd') good = FILE_MIDI;
- break;
- case (byte)'C':
- if (s[1] == 'T' && s[2] == 'M' && s[3] == 'F') good = FILE_CMF;
- break;
- case 0x84:
- if (s[1] == 0x00 && load_sierra_ins(path))
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ var s = br.ReadBytes(6);
+ int good = 0;
+ subsongs = 0;
+ switch (s[0])
+ {
+ case (byte)'A':
+ if (s[1] == 'D' && s[2] == 'L') good = FILE_LUCAS;
+ break;
+ case (byte)'M':
+ if (s[1] == 'T' && s[2] == 'h' && s[3] == 'd') good = FILE_MIDI;
+ break;
+ case (byte)'C':
+ if (s[1] == 'T' && s[2] == 'M' && s[3] == 'F') good = FILE_CMF;
+ break;
+ case 0x84:
+ if (s[1] == 0x00)
+ {
+ var fs = stream as FileStream;
+ if (fs == null)
{
- if (s[2] == 0xf0)
- good = FILE_ADVSIERRA;
- else
- good = FILE_SIERRA;
+ AdPlug_LogWrite("Couldn't open instruments for Mid from a pure stream! Aborting!\n");
+ return false;
}
- break;
- default:
- fs.Seek(0, SeekOrigin.Begin);
- var size = br.ReadUInt32(); // size of FILE_OLDLUCAS
- if (size == fs.Length && s[4] == 'A' && s[5] == 'D') good = FILE_OLDLUCAS;
- break;
- }
- if (good == 0) return false;
- subsongs = 1;
+ load_sierra_ins(fs.Name);
- type = good;
- fs.Seek(0, SeekOrigin.Begin);
- flen = fs.Length;
- data = br.ReadBytes((int)flen);
-
- Rewind(0);
- return true;
+ if (s[2] == 0xf0)
+ good = FILE_ADVSIERRA;
+ else
+ good = FILE_SIERRA;
+ }
+ break;
+ default:
+ stream.Seek(0, SeekOrigin.Begin);
+ var size = br.ReadUInt32(); // size of FILE_OLDLUCAS
+ if (size == stream.Length && s[4] == 'A' && s[5] == 'D') good = FILE_OLDLUCAS;
+ break;
}
+ if (good == 0) return false;
+ subsongs = 1;
+
+ type = good;
+ stream.Seek(0, SeekOrigin.Begin);
+ flen = stream.Length;
+ data = br.ReadBytes((int)flen);
+
+ Rewind(0);
+ return true;
}
public bool Update()
@@ -1150,5 +1163,10 @@ private byte datalook(long pos)
private void midiprintf(string format, params object[] parameters)
{
}
+
+ private void AdPlug_LogWrite(string message)
+ {
+ Console.Error.WriteLine(message);
+ }
}
}
diff --git a/NScumm.Audio.Players/MkjPlayer.cs b/NScumm.Audio.Players/MkjPlayer.cs
index 92cfe45..0196eba 100644
--- a/NScumm.Audio.Players/MkjPlayer.cs
+++ b/NScumm.Audio.Players/MkjPlayer.cs
@@ -54,7 +54,14 @@ public MkjPlayer(IOpl opl)
public bool Load(string path)
{
using (var fs = File.OpenRead(path))
- using (var br = new BinaryReader(fs))
+ {
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ using (var br = new BinaryReader(stream))
{
// file validation
var id = new string(br.ReadChars(6));
diff --git a/NScumm.Audio.Players/S3mPlayer.cs b/NScumm.Audio.Players/S3mPlayer.cs
index 8c12133..4fc6746 100644
--- a/NScumm.Audio.Players/S3mPlayer.cs
+++ b/NScumm.Audio.Players/S3mPlayer.cs
@@ -112,101 +112,106 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
- var insptr = new ushort[99];
- var pattptr = new ushort[99];
- int row;
- byte bufval, bufval2;
- ushort ppatlen;
- var adlibins = false;
-
- // file validation section
- var checkhead = LoadHeader(br);
- if (checkhead.kennung != 0x1a || checkhead.typ != 16
- || checkhead.insnum > 99)
- {
- return false;
- }
- if (checkhead.scrm != "SCRM")
- {
- return false;
- }
- fs.Seek(checkhead.ordnum, SeekOrigin.Current);
- for (var i = 0; i < checkhead.insnum; i++)
- insptr[i] = br.ReadUInt16();
- for (var i = 0; i < checkhead.insnum; i++)
+ return Load(fs);
+ }
+ }
+
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+ var insptr = new ushort[99];
+ var pattptr = new ushort[99];
+ int row;
+ byte bufval, bufval2;
+ ushort ppatlen;
+ var adlibins = false;
+
+ // file validation section
+ var checkhead = LoadHeader(br);
+ if (checkhead.kennung != 0x1a || checkhead.typ != 16
+ || checkhead.insnum > 99)
+ {
+ return false;
+ }
+ if (checkhead.scrm != "SCRM")
+ {
+ return false;
+ }
+ stream.Seek(checkhead.ordnum, SeekOrigin.Current);
+ for (var i = 0; i < checkhead.insnum; i++)
+ insptr[i] = br.ReadUInt16();
+ for (var i = 0; i < checkhead.insnum; i++)
+ {
+ stream.Seek(insptr[i] * 16, SeekOrigin.Begin);
+ if (br.ReadByte() >= 2)
{
- fs.Seek(insptr[i] * 16, SeekOrigin.Begin);
- if (br.ReadByte() >= 2)
- {
- adlibins = true;
- break;
- }
+ adlibins = true;
+ break;
}
- if (!adlibins) return false;
+ }
+ if (!adlibins) return false;
- // load section
- fs.Seek(0, SeekOrigin.Begin); // rewind for load
- _header = LoadHeader(br); // read header
+ // load section
+ stream.Seek(0, SeekOrigin.Begin); // rewind for load
+ _header = LoadHeader(br); // read header
- // security check
- if (_header.ordnum > 256 || _header.insnum > 99 || _header.patnum > 99)
- {
- return false;
- }
+ // security check
+ if (_header.ordnum > 256 || _header.insnum > 99 || _header.patnum > 99)
+ {
+ return false;
+ }
- for (var i = 0; i < _header.ordnum; i++) _orders[i] = br.ReadByte(); // read orders
- for (var i = 0; i < _header.insnum; i++) insptr[i] = br.ReadUInt16(); // instrument parapointers
- for (var i = 0; i < _header.patnum; i++) pattptr[i] = br.ReadUInt16(); // pattern parapointers
-
- for (var i = 0; i < _header.insnum; i++)
- { // load instruments
- fs.Seek(insptr[i] * 16, SeekOrigin.Begin);
- _inst[i].type = br.ReadByte();
- _inst[i].filename = new string(br.ReadChars(15));
- _inst[i].d00 = br.ReadByte(); _inst[i].d01 = br.ReadByte();
- _inst[i].d02 = br.ReadByte(); _inst[i].d03 = br.ReadByte();
- _inst[i].d04 = br.ReadByte(); _inst[i].d05 = br.ReadByte();
- _inst[i].d06 = br.ReadByte(); _inst[i].d07 = br.ReadByte();
- _inst[i].d08 = br.ReadByte(); _inst[i].d09 = br.ReadByte();
- _inst[i].d0a = br.ReadByte(); _inst[i].d0b = br.ReadByte();
- _inst[i].volume = br.ReadByte(); _inst[i].dsk = br.ReadByte();
- fs.Seek(2, SeekOrigin.Current);
- _inst[i].c2spd = br.ReadUInt32();
- fs.Seek(12, SeekOrigin.Current);
- _inst[i].name = new string(br.ReadChars(28));
- _inst[i].scri = new string(br.ReadChars(4));
- }
+ for (var i = 0; i < _header.ordnum; i++) _orders[i] = br.ReadByte(); // read orders
+ for (var i = 0; i < _header.insnum; i++) insptr[i] = br.ReadUInt16(); // instrument parapointers
+ for (var i = 0; i < _header.patnum; i++) pattptr[i] = br.ReadUInt16(); // pattern parapointers
+
+ for (var i = 0; i < _header.insnum; i++)
+ { // load instruments
+ stream.Seek(insptr[i] * 16, SeekOrigin.Begin);
+ _inst[i].type = br.ReadByte();
+ _inst[i].filename = new string(br.ReadChars(15));
+ _inst[i].d00 = br.ReadByte(); _inst[i].d01 = br.ReadByte();
+ _inst[i].d02 = br.ReadByte(); _inst[i].d03 = br.ReadByte();
+ _inst[i].d04 = br.ReadByte(); _inst[i].d05 = br.ReadByte();
+ _inst[i].d06 = br.ReadByte(); _inst[i].d07 = br.ReadByte();
+ _inst[i].d08 = br.ReadByte(); _inst[i].d09 = br.ReadByte();
+ _inst[i].d0a = br.ReadByte(); _inst[i].d0b = br.ReadByte();
+ _inst[i].volume = br.ReadByte(); _inst[i].dsk = br.ReadByte();
+ stream.Seek(2, SeekOrigin.Current);
+ _inst[i].c2spd = br.ReadUInt32();
+ stream.Seek(12, SeekOrigin.Current);
+ _inst[i].name = new string(br.ReadChars(28));
+ _inst[i].scri = new string(br.ReadChars(4));
+ }
- for (var i = 0; i < _header.patnum; i++)
- { // depack patterns
- fs.Seek(pattptr[i] * 16, SeekOrigin.Begin);
- ppatlen = br.ReadUInt16();
- long pattpos = fs.Position;
- for (row = 0; (row < 64) && (pattpos - pattptr[i] * 16 <= ppatlen); row++)
- do
+ for (var i = 0; i < _header.patnum; i++)
+ { // depack patterns
+ stream.Seek(pattptr[i] * 16, SeekOrigin.Begin);
+ ppatlen = br.ReadUInt16();
+ long pattpos = stream.Position;
+ for (row = 0; (row < 64) && (pattpos - pattptr[i] * 16 <= ppatlen); row++)
+ do
+ {
+ bufval = br.ReadByte();
+ if ((bufval & 32) != 0)
{
- bufval = br.ReadByte();
- if ((bufval & 32) != 0)
- {
- bufval2 = br.ReadByte();
- _pattern[i, row, bufval & 31].note = (byte)(bufval2 & 15);
- _pattern[i, row, bufval & 31].oct = (byte)((bufval2 & 240) >> 4);
- _pattern[i, row, bufval & 31].instrument = br.ReadByte();
- }
- if ((bufval & 64) != 0)
- _pattern[i, row, bufval & 31].volume = br.ReadByte();
- if ((bufval & 128) != 0)
- {
- _pattern[i, row, bufval & 31].command = br.ReadByte();
- _pattern[i, row, bufval & 31].info = br.ReadByte();
- }
- } while (bufval != 0);
- }
-
- Rewind(0);
- return true; // done
+ bufval2 = br.ReadByte();
+ _pattern[i, row, bufval & 31].note = (byte)(bufval2 & 15);
+ _pattern[i, row, bufval & 31].oct = (byte)((bufval2 & 240) >> 4);
+ _pattern[i, row, bufval & 31].instrument = br.ReadByte();
+ }
+ if ((bufval & 64) != 0)
+ _pattern[i, row, bufval & 31].volume = br.ReadByte();
+ if ((bufval & 128) != 0)
+ {
+ _pattern[i, row, bufval & 31].command = br.ReadByte();
+ _pattern[i, row, bufval & 31].info = br.ReadByte();
+ }
+ } while (bufval != 0);
}
+
+ Rewind(0);
+ return true; // done
}
public bool Update()
diff --git a/NScumm.Audio.Players/SngPlayer.cs b/NScumm.Audio.Players/SngPlayer.cs
index 2472e82..7134e7a 100644
--- a/NScumm.Audio.Players/SngPlayer.cs
+++ b/NScumm.Audio.Players/SngPlayer.cs
@@ -65,29 +65,34 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
+ return Load(fs);
+ }
+ }
- // load header
- header.id = new string(br.ReadChars(4));
- header.length = br.ReadUInt16(); header.start = br.ReadUInt16();
- header.loop = br.ReadUInt16(); header.delay = br.ReadByte();
- header.compressed = br.ReadByte() != 0;
+ public bool Load(Stream fs)
+ {
+ var br = new BinaryReader(fs);
- // file validation section
- if (!string.Equals(header.id, "ObsM", System.StringComparison.OrdinalIgnoreCase)) return false;
+ // load header
+ header.id = new string(br.ReadChars(4));
+ header.length = br.ReadUInt16(); header.start = br.ReadUInt16();
+ header.loop = br.ReadUInt16(); header.delay = br.ReadByte();
+ header.compressed = br.ReadByte() != 0;
- // load section
- header.length /= 2; header.start /= 2; header.loop /= 2;
- data = new Sdata[header.length];
- for (var i = 0; i < header.length && fs.Position < fs.Length - 2; i++)
- {
- data[i].val = br.ReadByte();
- data[i].reg = br.ReadByte();
- }
+ // file validation section
+ if (!string.Equals(header.id, "ObsM", System.StringComparison.OrdinalIgnoreCase)) return false;
- Rewind(0);
- return true;
+ // load section
+ header.length /= 2; header.start /= 2; header.loop /= 2;
+ data = new Sdata[header.length];
+ for (var i = 0; i < header.length && fs.Position < fs.Length - 2; i++)
+ {
+ data[i].val = br.ReadByte();
+ data[i].reg = br.ReadByte();
}
+
+ Rewind(0);
+ return true;
}
public bool Update()
diff --git a/NScumm.Audio.Players/XsmPlayer.cs b/NScumm.Audio.Players/XsmPlayer.cs
index c276a14..2fc75cf 100644
--- a/NScumm.Audio.Players/XsmPlayer.cs
+++ b/NScumm.Audio.Players/XsmPlayer.cs
@@ -57,32 +57,37 @@ public bool Load(string path)
{
using (var fs = File.OpenRead(path))
{
- var br = new BinaryReader(fs);
-
- // check if header matches
- var id = new string(br.ReadChars(6)); songlen = br.ReadUInt16();
- if (!string.Equals(id, "ofTAZ!", StringComparison.Ordinal) || songlen > 3200) return false;
-
- // read and set instruments
- for (var i = 0; i < 9; i++)
- {
- inst[i] = new Instrument();
- inst[i].value = br.ReadBytes(11);
- fs.Seek(5, SeekOrigin.Current);
- }
-
- // read song data
- music = new byte[songlen * 9];
- for (var i = 0; i < 9; i++)
- for (var j = 0; j < songlen; j++)
- music[j * 9 + i] = br.ReadByte();
-
- // success
- Rewind(0);
- return true;
+ return Load(fs);
}
}
+ public bool Load(Stream stream)
+ {
+ var br = new BinaryReader(stream);
+
+ // check if header matches
+ var id = new string(br.ReadChars(6)); songlen = br.ReadUInt16();
+ if (!string.Equals(id, "ofTAZ!", StringComparison.Ordinal) || songlen > 3200) return false;
+
+ // read and set instruments
+ for (var i = 0; i < 9; i++)
+ {
+ inst[i] = new Instrument();
+ inst[i].value = br.ReadBytes(11);
+ stream.Seek(5, SeekOrigin.Current);
+ }
+
+ // read song data
+ music = new byte[songlen * 9];
+ for (var i = 0; i < 9; i++)
+ for (var j = 0; j < songlen; j++)
+ music[j * 9 + i] = br.ReadByte();
+
+ // success
+ Rewind(0);
+ return true;
+ }
+
private void Rewind(int subsong)
{
notenum = last = 0;
@@ -118,7 +123,7 @@ public bool Update()
for (var c = 0; c < 9; c++)
{
- if (music[notenum * 9 + c]!=0)
+ if (music[notenum * 9 + c] != 0)
PlayNote(c, music[notenum * 9 + c] % 12, music[notenum * 9 + c] / 12);
else
PlayNote(c, 0, 0);