public void InitializeLists(xmfile xm) { sourceInstrumentsBox.Items.Clear(); for (int i = 0; i < xm.samples.Count; i++) { sourceInstrumentsBox.Items.Add(xm.samples[i].name); } IngameInstrumentsBox.Items.Clear(); for (int i = 0; i < xm.parentbinfile.samplecount; i++) { IngameInstrumentsBox.Items.Add("Instrument_" + i); } }
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; } } }