Exemple #1
0
        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);
        }
Exemple #2
0
        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));
            }
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
        }