public void ReadBin() { using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) { filemagic = reader.ReadUInt16(); filecount = reader.ReadUInt16(); Console.WriteLine(filecount); for (int i = 0; i < filecount; i++) { sfxfile newsfxfile = new sfxfile(); sfxfiles.Add(newsfxfile); newsfxfile.parentbinfile = this; newsfxfile.offset = reader.ReadUInt32(); newsfxfile.sizedividedby4 = reader.ReadUInt32(); if (filemagic == 0x0103) //hr only { isHR = true; Console.WriteLine("hr"); reader.BaseStream.Position += 0x04; } newsfxfile.samplerate = reader.ReadUInt16(); newsfxfile.unk1 = reader.ReadUInt16(); } Console.WriteLine(sfxfiles.Count); for (int i = 0; i < sfxfiles.Count; i++) { reader.BaseStream.Position = sfxfiles[i].offset; if (sfxfiles[i].sizedividedby4 * 4 < filebytes.Length) { sfxfiles[i].filebytes = reader.ReadBytes((int)sfxfiles[i].sizedividedby4 * 4); } } } }
public void ReadBin() { using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) { filemagic = reader.ReadUInt16(); filecount = reader.ReadUInt16(); for (int i = 0; i < filecount; i++) { sfxfile newsfxfile = new sfxfile(); sfxfiles.Add(newsfxfile); newsfxfile.parentbinfile = this; newsfxfile.offset = reader.ReadUInt32(); newsfxfile.sizedividedby4 = reader.ReadUInt32(); if (filemagic == 0x0103) //hr only { isHR = true; reader.BaseStream.Position += 0x04; } newsfxfile.samplerate = reader.ReadUInt16(); newsfxfile.indexInBin = i; newsfxfile.isPCM = reader.ReadUInt16() != 2; //because 0x02 is ADPCM format } for (int i = 0; i < sfxfiles.Count; i++) { reader.BaseStream.Position = sfxfiles[i].offset; if (sfxfiles[i].sizedividedby4 * 4 < filebytes.Length) { sfxfiles[i].filebytes = reader.ReadBytes((int)sfxfiles[i].sizedividedby4 * 4); } } } }
public List <Byte> MakeXM() { List <Byte> output = new List <byte>(); foreach (char c in "Extended Module: ") { output.Add((byte)c); } for (int i = 0; i < Math.Min(name.Length - 3, 20); i++) { output.Add((byte)name[i]); } while (output.Count < 0x25) { output.Add(0x20); } output.Add(0x1A); foreach (char c in "EPFExplorer") { output.Add((byte)c); } while (output.Count < 0x3A) { output.Add(0x00); } output.Add(0x04); output.Add(0x01); output.Add(0x14); output.Add(0x01); output.Add(0x00); output.Add(0x00); output.Add((byte)number_of_patterns_in_one_loop); output.Add((byte)(number_of_patterns_in_one_loop >> 8)); output.Add((byte)restartPosition); output.Add((byte)(restartPosition >> 8)); output.Add((byte)numchannels); output.Add((byte)(numchannels >> 8)); output.Add((byte)numpatterns); output.Add((byte)(numpatterns >> 8)); output.Add((byte)parentbinfile.samplecount); output.Add((byte)(parentbinfile.samplecount >> 8)); output.Add(0x01); output.Add(0x00); output.Add((byte)tempo); output.Add((byte)(tempo >> 8)); output.Add((byte)bpm); output.Add((byte)(bpm >> 8)); for (int i = 0; i < patternsInPlayingOrder.Count; i++) { output.Add((byte)patternsInPlayingOrder[i].index); } while (output.Count < 0x150) { output.Add(0x00); } foreach (Pattern p in patterns) { output.Add(0x09); output.Add(0x00); output.Add(0x00); output.Add(0x00); output.Add(0x00); output.Add((byte)p.number_of_rows); output.Add((byte)(p.number_of_rows >> 8)); output.Add((byte)(p.patternSize)); output.Add((byte)((p.patternSize) >> 8)); foreach (byte[] row in p.rows) { foreach (byte b in row) { output.Add(b); } } } //write instrument section string instrument_name = "instrument_"; string sample_name = "sample_"; for (int i = 0; i < samples.Count; i++) { sfxfile sample = (i >= 0 && i < samples.Count && samples[i] != null) ? samples[i] : null; output.Add(252); output.Add(0); output.Add(0); output.Add(0); int namelength = 0; foreach (char c in (instrument_name + i.ToString())) { output.Add((byte)c); namelength++; } while (namelength < 0x16) { output.Add(0x00); namelength++; } output.Add(0); output.Add(1); output.Add(0); output.Add(40); output.Add(0); output.Add(0); output.Add(0); for (int j = 0; j < 96; j++) { output.Add(0); } if (sample != null) { for (int j = 0; j < 24; j++) { output.Add(sample != null ? (byte)(sample.volenv.nodes[j] & 0xFF) : (byte)0); output.Add(sample != null ? (byte)(sample.volenv.nodes[j] >> 8) : (byte)0); } for (int j = 0; j < 24; j++) { output.Add(sample != null ? (byte)(sample.panenv.nodes[j] & 0xFF) : (byte)0); output.Add(sample != null ? (byte)(sample.panenv.nodes[j] >> 8) : (byte)0); } output.Add((byte)sample.volenv.count); output.Add((byte)sample.panenv.count); output.Add(sample.volenv.sustainPoint != 0xFF ? sample.volenv.sustainPoint : (byte)0); output.Add(sample.volenv.loopStart != 0xFF ? sample.volenv.loopStart : (byte)0); output.Add(sample.volenv.loopEnd != 0xFF ? sample.volenv.loopEnd : (byte)0); output.Add(sample.panenv.sustainPoint != 0xFF ? sample.panenv.sustainPoint : (byte)0); output.Add(sample.panenv.loopStart != 0xFF ? sample.panenv.loopStart : (byte)0); output.Add(sample.panenv.loopEnd != 0xFF ? sample.panenv.loopEnd : (byte)0); output.Add((byte)( (sample.volenv.count > 0 ? 1 : 0) | (sample.volenv.sustainPoint < sample.volenv.count ? 2 : 0) | (sample.volenv.loopStart < sample.volenv.count && sample.volenv.loopEnd < sample.volenv.count ? 4 : 0) )); output.Add((byte)( (sample.panenv.count > 0 ? 1 : 0) | (sample.panenv.sustainPoint < sample.panenv.count ? 2 : 0) | (sample.panenv.loopStart < sample.panenv.count && sample.panenv.loopEnd < sample.panenv.count ? 4 : 0) )); output.Add(0); output.Add(0); output.Add(0); output.Add(0); output.Add(0); output.Add(0); } else { for (int j = 0; j < 96 + 16; j++) { output.Add(0); } } for (int j = 0; j < 11; j++) { output.Add(0); } short[] pcm = sample != null?sample.ConvertToPCM() : new short[0] { }; output.Add((byte)((pcm.Length * 2) & 0xFF)); output.Add((byte)(((pcm.Length * 2) >> 8) & 0xFF)); output.Add((byte)(((pcm.Length * 2) >> 16) & 0xFF)); output.Add((byte)(((pcm.Length * 2) >> 24) & 0xFF)); if (sample != null) { output.Add((byte)((sample.loopstart * 4) & 0xFF)); output.Add((byte)(((sample.loopstart * 4) >> 8) & 0xFF)); output.Add((byte)(((sample.loopstart * 4) >> 16) & 0xFF)); output.Add((byte)(((sample.loopstart * 4) >> 24) & 0xFF)); output.Add((byte)(((sample.loopend * 4) - 0x10) & 0xFF)); output.Add((byte)(((sample.loopend * 4) >> 8) & 0xFF)); output.Add((byte)(((sample.loopend * 4) >> 16) & 0xFF)); output.Add((byte)(((sample.loopend * 4) >> 24) & 0xFF)); } else { for (int j = 0; j < 8; j++) { output.Add(0); } } output.Add(sample != null ? sample.defaultvol : (byte)0); output.Add(sample != null ? (byte)sample.finetune : (byte)0); output.Add((byte)(0x10 | (sample != null && sample.loopstart != 0xFFFFFFFF && sample.loopend != 0xFFFFFFFF ? 1 : 0))); output.Add(sample != null ? sample.defaultpan : (byte)0x80); output.Add(sample != null ? (byte)sample.transpose : (byte)0); output.Add(0); namelength = 0; foreach (char c in (sample_name + i.ToString())) { output.Add((byte)c); namelength++; } while (namelength < 0x16) { output.Add(0x00); namelength++; } short old = 0; for (int j = 0; j < pcm.Length; j++) { short n = (short)(pcm[j] - old); output.Add((byte)(n & 0xFF)); output.Add((byte)((n >> 8) & 0xFF)); old = pcm[j]; } } return(output); }
public void ReadMusicBin() { using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) { filemagic = reader.ReadUInt16(); if (filemagic == 0x0103) { isHR = true; } samplecount = reader.ReadByte(); filecount = reader.ReadByte(); //music file count reader.BaseStream.Position = 0x08; offsetOfMusicInstructionData = reader.ReadUInt32(); reader.BaseStream.Position = 0x10; Console.WriteLine(filecount); for (int i = 0; i < filecount; i++) { xmfile newxmfile = new xmfile(); xmfiles.Add(newxmfile); newxmfile.parentbinfile = this; newxmfile.offset = reader.ReadUInt32(); } offset_of_end_of_index_table = (int)reader.BaseStream.Position; Console.WriteLine(xmfiles.Count); for (int i = 0; i < xmfiles.Count; i++) { if (i < xmfiles.Count - 1) { xmfiles[i].size = xmfiles[i + 1].offset - xmfiles[i].offset; } else { xmfiles[i].size = (uint)(filebytes.Length - xmfiles[i].offset); //it might be bigger than its intended size, but it won't matter once it's exported to XM } reader.BaseStream.Position = xmfiles[i].offset; if (xmfiles[i].size < filebytes.Length) { xmfiles[i].filebytes = reader.ReadBytes((int)xmfiles[i].size); xmfiles[i].ReadSongInfo(); } } if (xmfiles[2].offset == 2054556) { ApplyEPFMusicFileNames(); } else if (xmfiles[2].offset == 2339468) { ApplyHRMusicFileNames(); } else { foreach (xmfile xm in xmfiles) { xm.name = Path.GetFileName(filename) + xm.offset; } } samples = new sfxfile[samplecount]; int pos = offset_of_end_of_index_table; for (int i = 0; i < samplecount; i++) { sfxfile newSample = new sfxfile(); /* * So it turns out that the chunk of data at the top of each sample, * which was previously ignored, is actually chock full of information * about the sample, including some instrument parameters including * volume and panning envelopes. The previous behavior of splitting by * 0x02004000 was flawed as well - PCM samples would have 0x00004000 in * that place instead, causing all sorts of wacky issues when trying to * decode an ADPCM sample that is followed by one or more PCM samples * (this happened quite a bit during debugging). * * To keep things sane for the future, I'm including a speculative * specification for the data contained in this chunk. Take this with a * grain of salt, but I believe it should be correct (as long as my ears * are working properly). Some of the descriptions may require * understanding of the format of XM instruments. * * Offset | Size | Description * --------+----------+------------------------------------------------- * 0x00 | 4 | The size of the sample data divided by 4. * 0x04 | 4 | The position of the start of the loop divided by 4, or 0xFFFFFFFF for no loop. (?) * 0x08 | 4 | The position of the end of the loop divided by 4, or size+1 for no loop. (?) * 0x0C | 1 | The type of sample. 0 = s16 PCM, 2 = MS IMA ADPCM * 0x0D | 1 | Unknown (always 0). Could be upper byte of above. * 0x0E | 1 | Default volume for the sample. * 0x0F | 1 | Unknown (always 0). Could be upper byte of above. * 0x10 | 1 | Finetune value. Stored as a signed byte. * 0x11 | 1 | Transpose value. Stored as a signed byte. (For some reason, if the sample is PCM you should decrease this value by 12.) * 0x12 | 0/4 | This field is present in HR but not EPF. Not sure what's in it yet. If on HR, add 4 to each subsequent offset. * 0x12 | 1 | Default pan position. Stored as an unsigned byte, with 0x80 = center. * 0x13 | 1 | Unknown (always 0). * 0x14 | 1 | Number of nodes in the volume envelope. * 0x15 | 1 | Position of volume envelope sustain node, or 0xFF for no sustain. * 0x16 | 1 | Position of volume envelope loop start, or 0xFF for no loop. * 0x17 | 1 | Position of volume envelope loop end, or 0xFF for no loop. * 0x18 | 2*24 | Nodes in the volume envelope, alternating (x, y). * 0x48 | 1 | Number of nodes in the panning envelope. * 0x49 | 1 | Position of panning envelope sustain node, or 0xFF for no sustain. * 0x4A | 1 | Position of panning envelope loop start, or 0xFF for no loop. * 0x4B | 1 | Position of panning envelope loop end, or 0xFF for no loop. * 0x4C | 2*24 | Nodes in the panning envelope, alternating (x, y). * 0x7C | <size> | The actual sample data. */ uint offset = (uint)pos; int length = (filebytes[pos] | (filebytes[pos + 1] << 8) | (filebytes[pos + 2] << 16) | (filebytes[pos + 3] << 24)) * 4; newSample.loopstart = (uint)(filebytes[pos + 4] | (filebytes[pos + 5] << 8) | (filebytes[pos + 6] << 16) | (filebytes[pos + 7] << 24)) * 4; newSample.loopend = (uint)(filebytes[pos + 8] | (filebytes[pos + 9] << 8) | (filebytes[pos + 10] << 16) | (filebytes[pos + 11] << 24)) * 4; newSample.isPCM = filebytes[pos + 12] != 2; newSample.finetune = (sbyte)filebytes[pos + 16]; newSample.transpose = (sbyte)filebytes[pos + 17]; if (newSample.isPCM) { newSample.transpose -= 12; } newSample.defaultvol = filebytes[pos + 14]; if (isHR) { pos += 22; } else { pos += 18; } if (!isHR && i >= 68 && i <= 73) { newSample.defaultpan = 0x80; // override for odd right-panned samples } else { newSample.defaultpan = filebytes[pos]; } // Hot-patch for Snake Game samples, which have the wrong transpose // (also changing finetune since that seems to make it sound better) if (isHR) { if (i == 54 || i == 58 || i == 59) { newSample.transpose--; } if (i == 54 || i == 57 || i == 58 || i == 59) { newSample.defaultpan = 0x80; } if (i == 54) { newSample.finetune = -16; } else if (i == 57) { newSample.finetune = -30; } else if (i == 59) { newSample.finetune = -26; } } newSample.volenv.count = filebytes[pos + 2]; newSample.volenv.sustainPoint = filebytes[pos + 3]; newSample.volenv.loopStart = filebytes[pos + 4]; newSample.volenv.loopEnd = filebytes[pos + 5]; for (int j = 0; j < 24; j++) { newSample.volenv.nodes[j] = (short)(filebytes[pos + j * 2 + 6] | (filebytes[pos + j * 2 + 7] << 8)); } newSample.panenv.count = filebytes[pos + 54]; newSample.panenv.sustainPoint = filebytes[pos + 55]; newSample.panenv.loopStart = filebytes[pos + 56]; newSample.panenv.loopEnd = filebytes[pos + 57]; for (int j = 0; j < 24; j++) { newSample.panenv.nodes[j] = (short)(filebytes[pos + j * 2 + 58] | (filebytes[pos + j * 2 + 59] << 8)); } pos += 106; newSample.parentbinfile = this; newSample.samplerate = 44100; newSample.filebytes = new byte[length]; Array.Copy(filebytes, pos, newSample.filebytes, 0, length); newSample.offset = offset; pos += length; samples[i] = newSample; } foreach (xmfile xm in xmfiles) { xm.samples = new List <sfxfile>(samples); } } }
public void ReadMusicBin() { using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open))) { filemagic = reader.ReadUInt16(); if (filemagic == 0x0103) { isHR = true; } samplecount = reader.ReadByte(); filecount = reader.ReadByte(); //music file count reader.BaseStream.Position = 0x08; offsetOfMusicInstructionData = reader.ReadUInt32(); reader.BaseStream.Position = 0x10; Console.WriteLine(filecount); for (int i = 0; i < filecount; i++) { xmfile newxmfile = new xmfile(); xmfiles.Add(newxmfile); newxmfile.parentbinfile = this; newxmfile.offset = reader.ReadUInt32(); } offset_of_end_of_index_table = (int)reader.BaseStream.Position; Console.WriteLine(xmfiles.Count); for (int i = 0; i < xmfiles.Count; i++) { if (i < xmfiles.Count - 1) { xmfiles[i].size = xmfiles[i + 1].offset - xmfiles[i].offset; } else { xmfiles[i].size = (uint)(filebytes.Length - xmfiles[i].offset); //it might be bigger than its intended size, but it won't matter once it's exported to XM } reader.BaseStream.Position = xmfiles[i].offset; if (xmfiles[i].size < filebytes.Length) { xmfiles[i].filebytes = reader.ReadBytes((int)xmfiles[i].size); xmfiles[i].ReadSongInfo(); } } if (xmfiles[2].offset == 2054556) { ApplyEPFMusicFileNames(); } else if (xmfiles[2].offset == 2339468) { ApplyHRMusicFileNames(); } else { foreach (xmfile xm in xmfiles) { xm.name = Path.GetFileName(filename) + xm.offset; } } samples = new sfxfile[samplecount]; int pos = (int)(offset_of_end_of_index_table + 76); if (isHR) { pos += 4; } bool keepgoing = true; for (int i = 0; i < samplecount; i++) { List <byte> newSampleBytes = new List <byte>(); int i_within_loop = i; uint offset = (uint)pos; while (keepgoing) { if (pos + 3 >= filebytes.Length) { keepgoing = false; i = samplecount - 1; } else if (filebytes[pos] == 0x02 && filebytes[pos + 1] == 0x00 && filebytes[pos + 2] == 0x40 && filebytes[pos + 3] == 0x00) { keepgoing = false; pos += 64; if (isHR) { pos += 4; } } else { newSampleBytes.Add(filebytes[pos]); pos++; } } newSampleBytes.RemoveRange(newSampleBytes.Count - 0x13, 0x13); sfxfile newSample = new sfxfile(); newSample.parentbinfile = this; newSample.samplerate = 44100; newSample.filebytes = newSampleBytes.ToArray(); newSample.offset = offset; samples[i_within_loop] = newSample; keepgoing = true; } } }