static SongHeader extract_song_header(byte[] file_contents, int song_header_address) { SongHeader song_header = new SongHeader(); if (song_header_address < ROM_ADDRESS_MASK || (song_header_address - ROM_ADDRESS_MASK) > file_contents.Length) { return(song_header); } var working_address = song_header_address - ROM_ADDRESS_MASK; // Addresses are stored with 0x08 because of ROM memory domain song_header.number_of_tracks = read_8_bit_from_file_at_offset(file_contents, working_address); working_address += 1; song_header.unknown = read_8_bit_from_file_at_offset(file_contents, working_address); working_address += 1; song_header.song_priority = read_8_bit_from_file_at_offset(file_contents, working_address); working_address += 1; song_header.echo = read_8_bit_from_file_at_offset(file_contents, working_address); working_address += 1; song_header.instrument_def_ptr = read_32_bit_from_file_at_offset(file_contents, working_address); working_address += 4; song_header.track_data_ptrs = new List <int>(); for (int track_idx = 0; track_idx < song_header.number_of_tracks; ++track_idx) { song_header.track_data_ptrs.Add(read_32_bit_from_file_at_offset(file_contents, working_address)); working_address += 4; } return(song_header); }
static void write_song_header_to_bytearray(SongHeader song_header, List <byte> arr) { arr.Add(song_header.number_of_tracks); arr.Add(song_header.unknown); arr.Add(song_header.song_priority); arr.Add(song_header.echo); arr.AddRange(BitConverter.GetBytes(song_header.instrument_def_ptr)); foreach (var track_data_ptr in song_header.track_data_ptrs) { arr.AddRange(BitConverter.GetBytes(track_data_ptr)); } }
static List <byte> extract_track_data(byte[] file_contents, SongHeader song_header, int track_data_starting_address, int track_data_starting_address_relative, Dictionary <string, List <int> > out_track_starting_addresses) { List <byte> all_track_data = new List <byte>(); out_track_starting_addresses[KEY_POINTERS] = new List <int>(); out_track_starting_addresses[KEY_TRANSPOSE] = new List <int>(); out_track_starting_addresses[KEY_TEMPO] = new List <int>(); // Iterate over all track pointers in song_header for (int track_idx = 0; track_idx < song_header.track_data_ptrs.Count; ++track_idx) { var track_data_ptr = song_header.track_data_ptrs[track_idx]; var out_control_ptrs = new Dictionary <string, List <int> >(); var raw_track_data = extract_track(file_contents, track_data_ptr, track_data_starting_address, out_control_ptrs, track_data_starting_address_relative); if (out_control_ptrs.ContainsKey(KEY_TRANSPOSE)) { out_track_starting_addresses[KEY_TRANSPOSE].AddRange(out_control_ptrs[KEY_TRANSPOSE]); } if (out_control_ptrs.ContainsKey(KEY_TEMPO)) { out_track_starting_addresses[KEY_TEMPO].AddRange(out_control_ptrs[KEY_TEMPO]); } song_header.track_data_ptrs[track_idx] = track_data_starting_address + ROM_ADDRESS_MASK; out_track_starting_addresses[KEY_POINTERS].Add(track_data_starting_address_relative); track_data_starting_address += raw_track_data.Count; track_data_starting_address_relative += raw_track_data.Count; all_track_data.AddRange(raw_track_data); } return(all_track_data); }
static List <byte> extract_instrument_data(byte[] file_contents, SongHeader song_header, int instrument_start_offset, Dictionary <int, int> used_instruments) { // Iterate over number of tracks (that's the number of main instruments we have) // parse every instrument, stick it into a bytearray(). Offset is computed on the fly // This bytearray stores the whole combined instrument definition, including all instruments and samples List <byte> all_instrument_bytes = new List <byte>(); // This bytearray stores only the instrument defs List <byte> instrument_defs_bytes = new List <byte>(); // This bytearray stores the instrument data (samples, programmable waveforms, key split instruments, etc.) List <byte> instrument_data_bytes = new List <byte>(); var main_instrument_def_address = song_header.instrument_def_ptr - ROM_ADDRESS_MASK; var instrument_data_starting_address = instrument_start_offset + (used_instruments[USED_INSTRUMENTS_MAX] + 1) * 12; var total_instrument_data_address = instrument_data_starting_address; for (int track_idx = 0; track_idx < used_instruments[USED_INSTRUMENTS_MAX] + 1; ++track_idx) { // Parse instrument definition var main_track_instrument_def = extract_instrument_def(file_contents, main_instrument_def_address); System.Console.WriteLine("Main instrument address at " + main_instrument_def_address.ToString("X")); main_instrument_def_address += 12; // 12 bytes per track instrument def Console.WriteLine("Instrument " + track_idx + ", Type: " + main_track_instrument_def.instrument_type); // If we don't use the instrument, no need to extract samples or programmable synth stuff if (used_instruments.ContainsKey(track_idx) == false) { write_instrument_def_to_bytearray(main_track_instrument_def, instrument_defs_bytes); continue; } // Check for special instrument types that require additional handling if (main_track_instrument_def.instrument_type == INSTRUMENT_SAMPLE_1 || main_track_instrument_def.instrument_type == INSTRUMENT_SAMPLE_2) { // Sample Instrument if (main_track_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(main_track_instrument_def, instrument_defs_bytes); continue; } var sampled_instrument = extract_sampled_instrument(file_contents, main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap main_track_instrument.ptr1 // We start at the current "data" address main_track_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_sampled_instrument_to_bytearray(sampled_instrument, instrument_data_bytes); total_instrument_data_address += 16 + sampled_instrument.sample_size; // Pad to aligned 4 bytes if necessary var num_padding_bytes = 0x04 - (total_instrument_data_address) & 0x03; for (int i = 0; i < num_padding_bytes; ++i) { instrument_data_bytes.Add(0); total_instrument_data_address += 1; } if ((total_instrument_data_address & 0x03) != 0) { System.Console.WriteLine("Padding Info: " + (total_instrument_data_address & 0x03)); System.Diagnostics.Debug.Assert(false); } } else if (main_track_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_1 || main_track_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_2) { // Programmable Waveform if (main_track_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(main_track_instrument_def, instrument_defs_bytes); continue; } var programmable_waveform = extract_programmable_waveform(file_contents, main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap main_track_instrument.ptr1 // We start at the current "data" address main_track_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_programmable_waveform_to_bytearray(programmable_waveform, instrument_data_bytes); total_instrument_data_address += 16; } else if (main_track_instrument_def.instrument_type == INSTRUMENT_KEY_SPLIT) { // key_split instrument var key_split_instrument_def_address = main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK; var key_split_table_address = main_track_instrument_def.ptr_2 - ROM_ADDRESS_MASK; // We first store the key_split table main_track_instrument_def.ptr_2 = total_instrument_data_address + ROM_ADDRESS_MASK; total_instrument_data_address += 128; main_track_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; List <byte> key_split_data = new List <byte>(); List <byte> key_split_instruments = new List <byte>(); List <byte> key_split_table_data = new List <byte>(); // Iterate from 0 to max_key (more instruments do not need to be copied) int max_key = Int32.MinValue; // Read and allocate key_split table for (int idx = 0; idx < 128; ++idx) { var key = read_8_bit_from_file_at_offset(file_contents, key_split_table_address); key_split_table_address += 1; key_split_table_data.Add(key); if (key > max_key) { max_key = key; } } // Add the key_split table key_split_data.AddRange(key_split_table_data); total_instrument_data_address += ((max_key + 1) * 12); for (int instrument_index = 0; instrument_index < max_key + 1; ++instrument_index) { var key_split_instrument_def = extract_instrument_def(file_contents, key_split_instrument_def_address); key_split_instrument_def_address += 12; // We assume a key_split instrument can not split again. if (key_split_instrument_def.instrument_type == INSTRUMENT_SAMPLE_1 || key_split_instrument_def.instrument_type == INSTRUMENT_SAMPLE_2) { // Sample Instrument if (key_split_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(key_split_instrument_def, key_split_instruments); continue; } var sampled_instrument = extract_sampled_instrument(file_contents, key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap key_split_instrument_def.ptr1 // We start at the current "data" address key_split_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_sampled_instrument_to_bytearray(sampled_instrument, key_split_data); total_instrument_data_address += 16 + sampled_instrument.sample_size; // Pad to aligned 4 bytes if necessary var num_padding_bytes = 0x04 - (total_instrument_data_address) & 0x03; for (int i = 0; i < num_padding_bytes; ++i) { key_split_data.Add(0); total_instrument_data_address += 1; } if ((total_instrument_data_address & 0x03) != 0) { System.Console.WriteLine("Padding Info: " + (total_instrument_data_address & 0x03)); System.Diagnostics.Debug.Assert(false); } } else if (key_split_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_1 || key_split_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_2) { // Programmable Waveform if (key_split_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(key_split_instrument_def, key_split_instruments); continue; } var programmable_waveform = extract_programmable_waveform(file_contents, key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap key_split_instrument_def.ptr1 // We start at the current "data" address key_split_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_programmable_waveform_to_bytearray(programmable_waveform, key_split_data); total_instrument_data_address += 16; } else if (key_split_instrument_def.instrument_type == INSTRUMENT_KEY_SPLIT) { System.Console.WriteLine("Ignored key_split instrument while inside key_split instrument!"); } else if (key_split_instrument_def.instrument_type == INSTRUMENT_EVERY_KEY_SPLIT) { System.Console.WriteLine("Ignored every_key_split instrument while inside key_split instrument!"); } // Write key_split_instrument_def into bytearry write_instrument_def_to_bytearray(key_split_instrument_def, key_split_instruments); } // Write sub-instrument into instrument_data_bytes instrument_data_bytes.AddRange(key_split_instruments); instrument_data_bytes.AddRange(key_split_data); } else if (main_track_instrument_def.instrument_type == INSTRUMENT_EVERY_KEY_SPLIT) { // every_key_split instrument // For this, ptr_1 points to exactly 128 sub-instruments (e.g. for percussion) // Therefore, we need to extract 128 sub-instruments (with their respective samples) // Ideally, we would go through the track and check which sub-instruments are actually used, but it's fine as is for now var every_key_split_instrument_def_address = main_track_instrument_def.ptr_1 - ROM_ADDRESS_MASK; // Adjust the main_Track_instrument_def.ptr1 to the new region main_track_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Since all 128 instruments need to be after each other, we need another bytearray to store data. List <byte> every_key_split_data = new List <byte>(); List <byte> every_key_split_instruments = new List <byte>(); total_instrument_data_address += 128 * 12; for (int every_key_split_index = 0; every_key_split_index < 128; ++every_key_split_index) { var every_key_split_instrument_def = extract_instrument_def(file_contents, every_key_split_instrument_def_address); every_key_split_instrument_def_address += 12; // We assume an every_key_split instrument can not split again. if (every_key_split_instrument_def.instrument_type == INSTRUMENT_SAMPLE_1 || every_key_split_instrument_def.instrument_type == INSTRUMENT_SAMPLE_2) { // Sample Instrument if (every_key_split_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (every_key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(every_key_split_instrument_def, every_key_split_instruments); continue; } var sampled_instrument = extract_sampled_instrument(file_contents, every_key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap every_key_split_instrument_def.ptr1 // We start at the current "data" address every_key_split_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_sampled_instrument_to_bytearray(sampled_instrument, every_key_split_data); total_instrument_data_address += 16 + sampled_instrument.sample_size; // Pad to aligned 4 bytes if necessary var num_padding_bytes = 0x04 - (total_instrument_data_address) & 0x03; for (int i = 0; i < num_padding_bytes; ++i) { every_key_split_data.Add(0); total_instrument_data_address += 1; } if ((total_instrument_data_address & 0x03) != 0) { System.Console.WriteLine("Padding Info: " + (total_instrument_data_address & 0x03)); System.Diagnostics.Debug.Assert(false); } } else if (every_key_split_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_1 || every_key_split_instrument_def.instrument_type == INSTRUMENT_PROGRAMMABLE_WAVEFORM_2) { // Programmable Waveform if (every_key_split_instrument_def.ptr_1 <= ROM_ADDRESS_MASK || (every_key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK) > file_contents.Length) { write_instrument_def_to_bytearray(every_key_split_instrument_def, every_key_split_instruments); continue; } var programmable_waveform = extract_programmable_waveform(file_contents, every_key_split_instrument_def.ptr_1 - ROM_ADDRESS_MASK); // Remap every_key_split_instrument_def.ptr1 // We start at the current "data" address every_key_split_instrument_def.ptr_1 = total_instrument_data_address + ROM_ADDRESS_MASK; // Write sample data into bytearray write_programmable_waveform_to_bytearray(programmable_waveform, every_key_split_data); total_instrument_data_address += 16; } else if (every_key_split_instrument_def.instrument_type == INSTRUMENT_KEY_SPLIT) { System.Console.WriteLine("Ignored key_split instrument while inside key_split instrument!"); } else if (every_key_split_instrument_def.instrument_type == INSTRUMENT_EVERY_KEY_SPLIT) { System.Console.WriteLine("Ignored every_key_split instrument while inside key_split instrument!"); } // Write key_split_instrument_def into bytearry write_instrument_def_to_bytearray(every_key_split_instrument_def, every_key_split_instruments); } // Write sub-instrument into instrument_data_bytes instrument_data_bytes.AddRange(every_key_split_instruments); instrument_data_bytes.AddRange(every_key_split_data); } // Write main instrument into the instrument def buffer write_instrument_def_to_bytearray(main_track_instrument_def, instrument_defs_bytes); } // Combine instrument defs and instrument data all_instrument_bytes.AddRange(instrument_defs_bytes); all_instrument_bytes.AddRange(instrument_data_bytes); return(all_instrument_bytes); }