void SetModuleData(MdatResource resource, byte[] sampleData, bool autoDelete) { lock (_mutex) { StopSongImpl(true); FreeResourceDataImpl(); _resource = resource; _resourceSample = sampleData; _deleteResource = autoDelete; } }
MdatResource LoadMdatFile(Stream musicData) { var br = new BinaryReader(musicData); bool hasHeader = false; var mdatSize = musicData.Length; if (mdatSize >= 0x200) { // 0x0000: 10 Bytes Header "TFMX-SONG " var buf = System.Text.Encoding.UTF8.GetString(br.ReadBytes(10)); hasHeader = buf == "TFMX-SONG "; } if (!hasHeader) { Debug.WriteLine("Tfmx: File is not a Tfmx Module"); return null; } var resource = new MdatResource(); resource.mdatAlloc = null; resource.mdatOffset = 0; resource.mdatLen = 0; // 0x000A: int16 flags resource.headerFlags = br.ReadUInt16BigEndian(); // 0x000C: int32 ? // 0x0010: 6*40 Textfield musicData.Seek(4 + 6 * 40, SeekOrigin.Current); /* 0x0100: Songstart x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].songstart = br.ReadUInt16BigEndian(); /* 0x0140: Songend x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].songend = br.ReadUInt16BigEndian(); /* 0x0180: Tempo x 32*/ for (int i = 0; i < NumSubsongs; ++i) resource.subsong[i].tempo = br.ReadUInt16BigEndian(); /* 0x01c0: unused ? */ musicData.Seek(16, SeekOrigin.Current); /* 0x01d0: trackstep, pattern data p, macro data p */ var offTrackstep = br.ReadUInt32BigEndian(); uint offPatternP, offMacroP; // This is how MI`s TFMX-Player tests for unpacked Modules. if (offTrackstep == 0) { // unpacked File resource.trackstepOffset = 0x600 + 0x200; offPatternP = 0x200 + 0x200; offMacroP = 0x400 + 0x200; } else { // packed File resource.trackstepOffset = offTrackstep; offPatternP = br.ReadUInt32BigEndian(); offMacroP = br.ReadUInt32BigEndian(); } // TODO: if a File is packed it could have for Ex only 2 Patterns/Macros // the following loops could then read beyond EOF. // To correctly handle this it would be necessary to sort the pointers and // figure out the number of Macros/Patterns // We could also analyze pointers if they are correct offsets, // so that accesses can be unchecked later // Read in pattern starting offsets musicData.Seek(offPatternP, SeekOrigin.Begin); for (int i = 0; i < MaxPatternOffsets; ++i) resource.patternOffset[i] = br.ReadUInt32BigEndian(); // use last PatternOffset (stored at 0x5FC in mdat) if unpacked File // or fixed offset 0x200 if packed resource.sfxTableOffset = offTrackstep != 0 ? 0x200 : resource.patternOffset[127]; // Read in macro starting offsets musicData.Seek(offMacroP, SeekOrigin.Begin); for (int i = 0; i < MaxMacroOffsets; ++i) resource.macroOffset[i] = br.ReadUInt32BigEndian(); // Read in mdat-file // TODO: we can skip everything thats already stored in the resource-structure. var mdatOffset = offTrackstep != 0 ? 0x200 : 0x600; // 0x200 is very conservative var allocSize = mdatSize - mdatOffset; musicData.Seek(mdatOffset, SeekOrigin.Begin); var mdatAlloc = br.ReadBytes((int)allocSize); resource.mdatAlloc = mdatAlloc; resource.mdatOffset = mdatOffset; resource.mdatLen = (int)mdatSize; return resource; }