Exemple #1
0
        /// <summary>
        /// Attempts to parse the <code>byte</code> array as an it file, populating the instance of the class this function was called from with data.
        /// </summary>
        /// <param name="data"><code>byte</code> array that contains the data, which should be a complete .it file. </param>
        /// <throws>FormatException</throws>
        public void parse( byte[] data )
        {
            #region Parse the header

            magicword = read4( data, 0x00 );
            if ( magicword != 0x4D504D49 )
                throw new FormatException( "Magic word mismatch. File must begin with the letters \"IMPM\". Are you sure this is an it file?" );

            songname = System.Text.Encoding.ASCII.GetString( data, 0x04, 26 );

            pathighlight = read2( data, 0x1E );

            numorders = read2( data, 0x20 );

            insnum = read2( data, 0x22 );
            if ( insnum != 0 )
                throw new FormatException( "Instruments are detected, they are not supported. This program was made for it files that were made using OpenSPC." );

            numsamples = read2( data, 0x24 );

            numpatterns = read2( data, 0x26 );

            createdwithtracker = read2( data, 0x28 );

            compatablewithtracker = read2( data, 0x2A );

            flags = read2( data, 0x2C );

            special = read2( data, 0x2E );

            globalvol = data[ 0x30 ];

            mixvol = data[ 0x31 ];

            initialspeed = data[ 0x32 ];

            initialtempo = data[ 0x33 ];

            panseperation = data[ 0x34 ];

            pitchwheeldepth = data[ 0x35 ];

            messagelength = read2( data, 0x36 );

            messageoffset = read4( data, 0x38 );
            if ( messagelength > 8000 )
                throw new FormatException( "Message length is greater than 8000 bytes." );
            message = new byte[ messagelength ];
            for ( int i = 0; i < messagelength; i++ ) {
                message[ i ] = data[ messageoffset + i ];
            }

            reserved = read4( data, 0x3C );

            channelpan = new byte[ 64 ];
            channels = 0;
            for ( int i = 0; i < 64; i++ ) {
                channelpan[ i ] = data[ 0x40 + i ];
                if ( channelpan[ i ] < 128 )
                    channels++;
            }

            channelvol = new byte[ 64 ];
            for ( int i = 0; i < 64; i++ ) {
                channelvol[ i ] = data[ 0x80 + i ];
            }

            orders = new byte[ numorders ];
            for ( int i = 0; i < numorders; i++ ) {
                orders[ i ] = data[ 0xC0 + i ];
            }

            // ignoring instruments, since they're not supported there shouldn't be any to read

            sampleoffsets = new uint[ numsamples ];
            for ( int i = 0; i < numsamples; i++ ) {
                sampleoffsets[ i ] = read4( data, 0xC0 + numorders + i * 4 );
            }

            patternoffsets = new uint[ numpatterns ];
            for ( int i = 0; i < numpatterns; i++ ) {
                patternoffsets[ i ] = read4( data, 0xC0 + numorders + numsamples * 4 + i * 4 );
            }

            #endregion

            #region Parse the samples

            samples = new itsample[ numsamples ];
            try {
                for ( int i = 0; i < numsamples; i++ ) {

                    samples[ i ].magicword = read4( data, sampleoffsets[ i ] + 0x00 );
                    if ( samples[ i ].magicword != 0x53504D49 )
                        throw new FormatException( String.Format( "Magic word mismatch on sample #{0}, offset 0x{1:X}. Header must begin with the letters \"IMPS\". File may be corrupt.", i, sampleoffsets[ i ] ) );

                    samples[ i ].dosfilename = System.Text.Encoding.ASCII.GetString( data, (int) sampleoffsets[ i ] + 0x04, 12 );

                    samples[ i ].globalvol = data[ sampleoffsets[ i ] + 0x11 ];

                    samples[ i ].flags = data[ sampleoffsets[ i ] + 0x12 ];

                    samples[ i ].defaultvol = data[ sampleoffsets[ i ] + 0x13 ];

                    samples[ i ].samplename = System.Text.Encoding.ASCII.GetString( data, (int) sampleoffsets[ i ] + 0x14, 26 );

                    samples[ i ].convert = data[ sampleoffsets[ i ] + 0x2E ];

                    samples[ i ].defaultpan = data[ sampleoffsets[ i ] + 0x2F ];

                    samples[ i ].length = read4( data, sampleoffsets[ i ] + 0x30 );
                    if ( samples[ i ].length > ( 64 * 1024 * 1024 ) )
                        throw new FormatException( String.Format( "Sample size for sample #{0} is greater than 64MB (according to the header, it's {1}MB), aborting to avoid excessive memory usage.", i, samples[ i ].length / 1024 / 1024 ) );

                    samples[ i ].loopbegin = read4( data, sampleoffsets[ i ] + 0x34 );

                    samples[ i ].loopend = read4( data, sampleoffsets[ i ] + 0x38 );

                    samples[ i ].c5speed = read4( data, sampleoffsets[ i ] + 0x3C );

                    samples[ i ].susloopbegin = read4( data, sampleoffsets[ i ] + 0x40 );

                    samples[ i ].susloopend = read4( data, sampleoffsets[ i ] + 0x44 );

                    samples[ i ].samplepointer = read4( data, sampleoffsets[ i ] + 0x48 );

                    samples[ i ].vibratospeed = data[ sampleoffsets[ i ] + 0x4C ];

                    samples[ i ].vibratodepth = data[ sampleoffsets[ i ] + 0x4D ];

                    samples[ i ].vibratorate = data[ sampleoffsets[ i ] + 0x4E ];

                    samples[ i ].vwaveformtype = data[ sampleoffsets[ i ] + 0x4F ];

                    samples[ i ].sample = new byte[ samples[ i ].length * 2 ];
                    for ( int j = 0; j < samples[ i ].length * 2; j++ ) {
                        samples[ i ].sample[ j ] = data[ samples[ i ].samplepointer + j ];
                    }
                }
            }
            catch ( Exception e ) {
                throw e;
            }

            #endregion

            #region Parse the patterns

            patterns = new itpattern[ numpatterns ];
            try {
                for ( int i = 0; i < numpatterns; i++ ) {

                    patterns[ i ].length = read2( data, patternoffsets[ i ] + 0x00 );

                    patterns[ i ].rows = read2( data, patternoffsets[ i ] + 0x02 );

                    patterns[ i ].packeddata = new byte[ patterns[ i ].length ];
                    for ( int j = 0; j < patterns[ i ].length; j++ ) {
                        patterns[ i ].packeddata[ j ] = data[ patternoffsets[ i ] + 0x08 + j ];
                    }

                    patterns[ i ].row = new rowgroup[ patterns[ i ].rows ];

                    for ( int row = 0; row < patterns[ i ].rows; row++ ) {
                        patterns[ i ].row[ row ].channel = new channelgroup[ channels ];
                        for ( int channel = 0; channel < channels; channel++ ) {
                            patterns[ i ].row[ row ].channel[ channel ].note = 0;
                            patterns[ i ].row[ row ].channel[ channel ].instrument = 0;
                            patterns[ i ].row[ row ].channel[ channel ].volume = 0;
                            patterns[ i ].row[ row ].channel[ channel ].command = 0;
                            patterns[ i ].row[ row ].channel[ channel ].parameter = 0;
                        }
                    }

                    // now, unpack the data
                    channelgroup[] prev = new channelgroup[ 64 ];
                    byte[] channelmask = new byte[ 64 ];
                    byte[] maskvar = new byte[ 64 ];

                    ushort rowsize = (ushort) ( channels * 5 );
                    ushort offset = 0;
                    byte currentchannel = 0;
                    int currentrow = 0;

                    try {
                        while ( currentrow < patterns[ i ].rows ) {
                            if ( offset >= patterns[ i ].packeddata.Length ) { // sanity check
                                throw new FormatException( String.Format( "Error parsing pattern: offset > length, pattern #{0}, offset = {1:X} length = {2:X}. The file may be corrupt.", i, offset, patterns[ i ].length ) );
                                //break;
                            }
                            byte channelvar = patterns[ i ].packeddata[ offset++ ];

                            if ( channelvar == 0 ) {
                                currentrow++;
                                continue;
                            }

                            if ( currentrow > 255 ) {
                                throw new FormatException( String.Format( "Error parsing pattern: currentrow > 255, pattern #{0}. The file may be corrupt.", i ) );
                                //break;
                            }

                            currentchannel = (byte) ( ( channelvar - 1 ) & 63 );

                            if ( ( channelvar & 128 ) == 128 ) // will not be updated if the highest bit is not set, will instead use whatever's already there at that position
                                maskvar[ currentchannel ] = patterns[ i ].packeddata[ offset++ ];

                            if ( ( maskvar[ currentchannel ] & 1 ) == 1 ) { // note command
                                byte note = patterns[ i ].packeddata[ offset++ ];
                                if ( note < 0xFD )
                                    note++;
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 0 ] = note;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].note = note;
                                prev[ currentchannel ].note = note;
                            }
                            if ( ( maskvar[ currentchannel ] & 2 ) == 2 ) { // instrument command
                                byte instrument = patterns[ i ].packeddata[ offset++ ];
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 1 ] = instrument;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].instrument = instrument;
                                prev[ currentchannel ].instrument = instrument;
                            }
                            if ( ( maskvar[ currentchannel ] & 4 ) == 4 ) { // volume command
                                byte volume = (byte) ( patterns[ i ].packeddata[ offset++ ] + 1 );
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 2 ] = volume;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].volume = volume;
                                prev[ currentchannel ].volume = volume;
                            }
                            if ( ( maskvar[ currentchannel ] & 8 ) == 8 ) { // effect command
                                byte command = patterns[ i ].packeddata[ offset++ ];
                                byte parameter = patterns[ i ].packeddata[ offset++ ];
                                if ( command != 0 ) {
                                    //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 3 ] = command;
                                    patterns[ i ].row[ currentrow ].channel[ currentchannel ].command = command;
                                    //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 4 ] = parameter;
                                    patterns[ i ].row[ currentrow ].channel[ currentchannel ].parameter = parameter;
                                    prev[ currentchannel ].command = command;
                                    prev[ currentchannel ].parameter = parameter;
                                }
                            }
                            if ( ( maskvar[ currentchannel ] & 16 ) == 16 ) {  // previous note command
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 0 ] = prev[ currentchannel ].note;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].note = prev[ currentchannel ].note;
                            }
                            if ( ( maskvar[ currentchannel ] & 32 ) == 32 ) { // previous instrument command
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 1 ] = prev[ currentchannel ].instrument;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].instrument = prev[ currentchannel ].instrument;
                            }
                            if ( ( maskvar[ currentchannel ] & 64 ) == 64 ) { // previous volume command
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 2 ] = prev[ currentchannel ].volume;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].volume = prev[ currentchannel ].volume;
                            }
                            if ( ( maskvar[ currentchannel ] & 128 ) == 128 ) { // previous effect command
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 3 ] = prev[ currentchannel ].command;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].command = prev[ currentchannel ].command;
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 4 ] = prev[ currentchannel ].parameter;
                                patterns[ i ].row[ currentrow ].channel[ currentchannel ].parameter = prev[ currentchannel ].parameter;
                            }

                        }
                    }
                    catch ( Exception e ) {
                        throw e;
                    }

                }
            }
            catch ( Exception e ) {
                throw e;
            }

            #endregion
        }
Exemple #2
0
        /// <summary>
        /// Packs the data contained within the instance of the class this was called from into a .it file, ready to be written to the disk.
        /// </summary>
        /// <returns> A <code>byte</code> array that contains a complete it file, ready to be written to the disk. </returns>
        public byte[] pack()
        {
            #region Pack the patterns

            ushort rowsize = (ushort)(channels * 5);

            for (int i = 0; i < numpatterns; i++)
            {
                channelgroup[] previousvalues = new channelgroup[64];
                byte[]         channelmask    = new byte[64];
                byte[]         prevmaskvar    = new byte[64];

                ushort length = 0;
                patterns[i].packeddata = new byte[256 * 64 * 7];
                for (int row = 0; row < patterns[i].rows; row++)
                {
                    for (int channel = 0; channel < channels; channel++)
                    {
                        byte maskvar = 0;
                        if (patterns[i].row[row].channel[channel].note != 0)
                        {
                            maskvar |= 1;
                        }
                        if (patterns[i].row[row].channel[channel].instrument != 0)
                        {
                            maskvar |= 2;
                        }
                        if (patterns[i].row[row].channel[channel].volume != 0)
                        {
                            maskvar |= 4;
                        }
                        if (patterns[i].row[row].channel[channel].command != 0)
                        {
                            maskvar |= 8;
                        }

                        if (maskvar != 0)
                        {
                            if ((maskvar & 1) == 1)
                            {
                                if (patterns[i].row[row].channel[channel].note == previousvalues[channel].note)
                                {
                                    unchecked {
                                        maskvar &= (byte)~0x01;
                                        maskvar |= 0x10;
                                    }
                                }
                                else
                                {
                                    previousvalues[channel].note = patterns[i].row[row].channel[channel].note;
                                }
                            }
                            if ((maskvar & 2) == 2)
                            {
                                if (patterns[i].row[row].channel[channel].instrument == previousvalues[channel].instrument)
                                {
                                    unchecked {
                                        maskvar &= (byte)~0x02;
                                        maskvar |= 0x20;
                                    }
                                }
                                else
                                {
                                    previousvalues[channel].instrument = patterns[i].row[row].channel[channel].instrument;
                                }
                            }
                            if ((maskvar & 4) == 4)
                            {
                                if (patterns[i].row[row].channel[channel].volume == previousvalues[channel].volume)
                                {
                                    unchecked {
                                        maskvar &= (byte)~0x04;
                                        maskvar |= 0x40;
                                    }
                                }
                                else
                                {
                                    previousvalues[channel].volume = patterns[i].row[row].channel[channel].volume;
                                }
                            }
                            if ((maskvar & 8) == 8)
                            {
                                if ((patterns[i].row[row].channel[channel].command == previousvalues[channel].command) && (patterns[i].row[row].channel[channel].parameter == previousvalues[channel].parameter))
                                {
                                    unchecked {
                                        maskvar &= (byte)~0x08;
                                        maskvar |= 0x80;
                                    }
                                }
                                else
                                {
                                    previousvalues[channel].command   = patterns[i].row[row].channel[channel].command;
                                    previousvalues[channel].parameter = patterns[i].row[row].channel[channel].parameter;
                                }
                            }
                            if (maskvar != channelmask[channel])
                            {
                                channelmask[channel]             = maskvar;
                                patterns[i].packeddata[length++] = (byte)((channel + 1) | 0x80);            // write channelvar
                                patterns[i].packeddata[length++] = maskvar;                                 // write maskvar
                            }
                            else
                            {
                                patterns[i].packeddata[length++] = (byte)(channel + 1);
                            }
                            if ((maskvar & 1) == 1)
                            {
                                patterns[i].packeddata[length++] = (patterns[i].row[row].channel[channel].note < 0xFE) ? (byte)(patterns[i].row[row].channel[channel].note - 1) : patterns[i].row[row].channel[channel].note;
                            }
                            if ((maskvar & 2) == 2)
                            {
                                patterns[i].packeddata[length++] = patterns[i].row[row].channel[channel].instrument;
                            }
                            if ((maskvar & 4) == 4)
                            {
                                patterns[i].packeddata[length++] = (byte)(patterns[i].row[row].channel[channel].volume - 1);
                            }
                            if ((maskvar & 8) == 8)
                            {
                                patterns[i].packeddata[length++] = patterns[i].row[row].channel[channel].command;
                                patterns[i].packeddata[length++] = patterns[i].row[row].channel[channel].parameter;
                            }
                        }
                    }
                    patterns[i].packeddata[length++] = 0;
                }
                patterns[i].length = length;
                Array.Resize(ref patterns[i].packeddata, length);
            }

            #endregion

            #region Calculate file size

            long filesize = 0;
            filesize     += 0xC0 + numorders + numsamples * 4 + numpatterns * 4 + 1;
            messageoffset = (uint)(filesize + 1);
            filesize     += messagelength + 1;
            for (int i = 0; i < numsamples; i++)
            {
                sampleoffsets[i] = (uint)filesize;
                filesize        += 0x51;
            }
            for (int i = 0; i < numpatterns; i++)
            {
                patternoffsets[i] = (uint)filesize;
                filesize         += patterns[i].length + 9;
            }
            for (int i = 0; i < numsamples; i++)
            {
                samples[i].samplepointer = (uint)filesize;
                filesize += samples[i].length * 2 + 1;
            }

            #endregion

            byte[] data = new byte[filesize];

            #region Write the header

            write4(magicword, ref data, 0x00);

            byte[] songnamearr = System.Text.Encoding.ASCII.GetBytes(songname);
            for (int i = 0; i < 26; i++)
            {
                data[i + 0x04] = songnamearr[i];
            }

            write2(pathighlight, ref data, 0x1E);

            write2(numorders, ref data, 0x20);

            write2(insnum, ref data, 0x22);

            write2(numsamples, ref data, 0x24);

            write2(numpatterns, ref data, 0x26);

            write2(createdwithtracker, ref data, 0x28);

            write2(compatablewithtracker, ref data, 0x2A);

            write2(flags, ref data, 0x2C);

            write2(special, ref data, 0x2E);

            data[0x30] = globalvol;

            data[0x31] = mixvol;

            data[0x32] = initialspeed;

            data[0x33] = initialtempo;

            data[0x34] = panseperation;

            data[0x35] = pitchwheeldepth;

            write2(messagelength, ref data, 0x36);

            write4(messageoffset, ref data, 0x38);
            for (int i = 0; i < messagelength; i++)
            {
                data[messageoffset + i] = message[i];
            }

            write4(reserved, ref data, 0x3C);

            for (int i = 0; i < 64; i++)
            {
                data[0x40 + i] = channelpan[i];
            }

            for (int i = 0; i < 64; i++)
            {
                data[0x80 + i] = channelvol[i];
            }

            for (int i = 0; i < numorders; i++)
            {
                data[0xC0 + i] = orders[i];
            }

            // ignoring instruments, since they're not supported there shouldn't be any to write

            for (int i = 0; i < numsamples; i++)
            {
                write4(sampleoffsets[i], ref data, 0xC0 + numorders + i * 4);
            }

            for (int i = 0; i < numpatterns; i++)
            {
                write4(patternoffsets[i], ref data, 0xC0 + numorders + numsamples * 4 + i * 4);
            }

            #endregion

            #region Write the sample headers

            for (int i = 0; i < numsamples; i++)
            {
                write4(samples[i].magicword, ref data, sampleoffsets[i] + 0x00);

                byte[] dosfilename = System.Text.Encoding.ASCII.GetBytes(samples[i].dosfilename);
                for (int j = 0; j < 12; j++)
                {
                    data[sampleoffsets[i] + 0x04 + j] = dosfilename[j];
                }

                data[sampleoffsets[i] + 0x11] = samples[i].globalvol;

                data[sampleoffsets[i] + 0x12] = samples[i].flags;

                data[sampleoffsets[i] + 0x13] = samples[i].defaultvol;

                byte[] samplename = System.Text.Encoding.ASCII.GetBytes(samples[i].samplename);
                for (int j = 0; j < 26; j++)
                {
                    data[sampleoffsets[i] + 0x14 + j] = samplename[j];
                }

                data[sampleoffsets[i] + 0x2E] = samples[i].convert;

                data[sampleoffsets[i] + 0x2F] = samples[i].defaultpan;

                write4(samples[i].length, ref data, sampleoffsets[i] + 0x30);

                write4(samples[i].loopbegin, ref data, sampleoffsets[i] + 0x34);

                write4(samples[i].loopend, ref data, sampleoffsets[i] + 0x38);

                write4(samples[i].c5speed, ref data, sampleoffsets[i] + 0x3C);

                write4(samples[i].susloopbegin, ref data, sampleoffsets[i] + 0x40);

                write4(samples[i].susloopend, ref data, sampleoffsets[i] + 0x44);

                write4(samples[i].samplepointer, ref data, sampleoffsets[i] + 0x48);

                data[sampleoffsets[i] + 0x4C] = samples[i].vibratospeed;

                data[sampleoffsets[i] + 0x4D] = samples[i].vibratodepth;

                data[sampleoffsets[i] + 0x4E] = samples[i].vibratorate;

                data[sampleoffsets[i] + 0x4F] = samples[i].vwaveformtype;

                for (int j = 0; j < samples[i].length * 2; j++)
                {
                    data[samples[i].samplepointer + j] = samples[i].sample[j];
                }
            }

            #endregion

            #region Write the pattern headers

            for (int i = 0; i < numpatterns; i++)
            {
                write2(patterns[i].length, ref data, patternoffsets[i] + 0x00);

                write2(patterns[i].rows, ref data, patternoffsets[i] + 0x02);

                for (int j = 0; j < patterns[i].length; j++)
                {
                    data[patternoffsets[i] + 0x08 + j] = patterns[i].packeddata[j];
                }
            }

            #endregion

            return(data);
        }
Exemple #3
0
        /// <summary>
        /// Attempts to parse the <code>byte</code> array as an it file, populating the instance of the class this function was called from with data.
        /// </summary>
        /// <param name="data"><code>byte</code> array that contains the data, which should be a complete .it file. </param>
        /// <throws>FormatException</throws>
        public void parse(byte[] data)
        {
            #region Parse the header

            magicword = read4(data, 0x00);
            if (magicword != 0x4D504D49)
            {
                throw new FormatException("Magic word mismatch. File must begin with the letters \"IMPM\". Are you sure this is an it file?");
            }

            songname = System.Text.Encoding.ASCII.GetString(data, 0x04, 26);

            pathighlight = read2(data, 0x1E);

            numorders = read2(data, 0x20);

            insnum = read2(data, 0x22);
            if (insnum != 0)
            {
                throw new FormatException("Instruments are detected, they are not supported. This program was made for it files that were made using OpenSPC.");
            }

            numsamples = read2(data, 0x24);

            numpatterns = read2(data, 0x26);

            createdwithtracker = read2(data, 0x28);

            compatablewithtracker = read2(data, 0x2A);

            flags = read2(data, 0x2C);

            special = read2(data, 0x2E);

            globalvol = data[0x30];

            mixvol = data[0x31];

            initialspeed = data[0x32];

            initialtempo = data[0x33];

            panseperation = data[0x34];

            pitchwheeldepth = data[0x35];

            messagelength = read2(data, 0x36);

            messageoffset = read4(data, 0x38);
            if (messagelength > 8000)
            {
                throw new FormatException("Message length is greater than 8000 bytes.");
            }
            message = new byte[messagelength];
            for (int i = 0; i < messagelength; i++)
            {
                message[i] = data[messageoffset + i];
            }


            reserved = read4(data, 0x3C);

            channelpan = new byte[64];
            channels   = 0;
            for (int i = 0; i < 64; i++)
            {
                channelpan[i] = data[0x40 + i];
                if (channelpan[i] < 128)
                {
                    channels++;
                }
            }

            channelvol = new byte[64];
            for (int i = 0; i < 64; i++)
            {
                channelvol[i] = data[0x80 + i];
            }

            orders = new byte[numorders];
            for (int i = 0; i < numorders; i++)
            {
                orders[i] = data[0xC0 + i];
            }

            // ignoring instruments, since they're not supported there shouldn't be any to read

            sampleoffsets = new uint[numsamples];
            for (int i = 0; i < numsamples; i++)
            {
                sampleoffsets[i] = read4(data, 0xC0 + numorders + i * 4);
            }

            patternoffsets = new uint[numpatterns];
            for (int i = 0; i < numpatterns; i++)
            {
                patternoffsets[i] = read4(data, 0xC0 + numorders + numsamples * 4 + i * 4);
            }

            #endregion

            #region Parse the samples

            samples = new itsample[numsamples];
            try {
                for (int i = 0; i < numsamples; i++)
                {
                    samples[i].magicword = read4(data, sampleoffsets[i] + 0x00);
                    if (samples[i].magicword != 0x53504D49)
                    {
                        throw new FormatException(String.Format("Magic word mismatch on sample #{0}, offset 0x{1:X}. Header must begin with the letters \"IMPS\". File may be corrupt.", i, sampleoffsets[i]));
                    }

                    samples[i].dosfilename = System.Text.Encoding.ASCII.GetString(data, (int)sampleoffsets[i] + 0x04, 12);

                    samples[i].globalvol = data[sampleoffsets[i] + 0x11];

                    samples[i].flags = data[sampleoffsets[i] + 0x12];

                    samples[i].defaultvol = data[sampleoffsets[i] + 0x13];

                    samples[i].samplename = System.Text.Encoding.ASCII.GetString(data, (int)sampleoffsets[i] + 0x14, 26);

                    samples[i].convert = data[sampleoffsets[i] + 0x2E];

                    samples[i].defaultpan = data[sampleoffsets[i] + 0x2F];

                    samples[i].length = read4(data, sampleoffsets[i] + 0x30);
                    if (samples[i].length > (64 * 1024 * 1024))
                    {
                        throw new FormatException(String.Format("Sample size for sample #{0} is greater than 64MB (according to the header, it's {1}MB), aborting to avoid excessive memory usage.", i, samples[i].length / 1024 / 1024));
                    }

                    samples[i].loopbegin = read4(data, sampleoffsets[i] + 0x34);

                    samples[i].loopend = read4(data, sampleoffsets[i] + 0x38);

                    samples[i].c5speed = read4(data, sampleoffsets[i] + 0x3C);

                    samples[i].susloopbegin = read4(data, sampleoffsets[i] + 0x40);

                    samples[i].susloopend = read4(data, sampleoffsets[i] + 0x44);

                    samples[i].samplepointer = read4(data, sampleoffsets[i] + 0x48);

                    samples[i].vibratospeed = data[sampleoffsets[i] + 0x4C];

                    samples[i].vibratodepth = data[sampleoffsets[i] + 0x4D];

                    samples[i].vibratorate = data[sampleoffsets[i] + 0x4E];

                    samples[i].vwaveformtype = data[sampleoffsets[i] + 0x4F];

                    samples[i].sample = new byte[samples[i].length * 2];
                    for (int j = 0; j < samples[i].length * 2; j++)
                    {
                        samples[i].sample[j] = data[samples[i].samplepointer + j];
                    }
                }
            }
            catch (Exception e) {
                throw e;
            }

            #endregion

            #region Parse the patterns

            patterns = new itpattern[numpatterns];
            try {
                for (int i = 0; i < numpatterns; i++)
                {
                    patterns[i].length = read2(data, patternoffsets[i] + 0x00);

                    patterns[i].rows = read2(data, patternoffsets[i] + 0x02);

                    patterns[i].packeddata = new byte[patterns[i].length];
                    for (int j = 0; j < patterns[i].length; j++)
                    {
                        patterns[i].packeddata[j] = data[patternoffsets[i] + 0x08 + j];
                    }

                    patterns[i].row = new rowgroup[patterns[i].rows];

                    for (int row = 0; row < patterns[i].rows; row++)
                    {
                        patterns[i].row[row].channel = new channelgroup[channels];
                        for (int channel = 0; channel < channels; channel++)
                        {
                            patterns[i].row[row].channel[channel].note       = 0;
                            patterns[i].row[row].channel[channel].instrument = 0;
                            patterns[i].row[row].channel[channel].volume     = 0;
                            patterns[i].row[row].channel[channel].command    = 0;
                            patterns[i].row[row].channel[channel].parameter  = 0;
                        }
                    }

                    // now, unpack the data
                    channelgroup[] prev        = new channelgroup[64];
                    byte[]         channelmask = new byte[64];
                    byte[]         maskvar     = new byte[64];

                    ushort rowsize        = (ushort)(channels * 5);
                    ushort offset         = 0;
                    byte   currentchannel = 0;
                    int    currentrow     = 0;

                    try {
                        while (currentrow < patterns[i].rows)
                        {
                            if (offset >= patterns[i].packeddata.Length)       // sanity check
                            {
                                throw new FormatException(String.Format("Error parsing pattern: offset > length, pattern #{0}, offset = {1:X} length = {2:X}. The file may be corrupt.", i, offset, patterns[i].length));
                                //break;
                            }
                            byte channelvar = patterns[i].packeddata[offset++];

                            if (channelvar == 0)
                            {
                                currentrow++;
                                continue;
                            }

                            if (currentrow > 255)
                            {
                                throw new FormatException(String.Format("Error parsing pattern: currentrow > 255, pattern #{0}. The file may be corrupt.", i));
                                //break;
                            }

                            currentchannel = (byte)((channelvar - 1) & 63);

                            if ((channelvar & 128) == 128)     // will not be updated if the highest bit is not set, will instead use whatever's already there at that position
                            {
                                maskvar[currentchannel] = patterns[i].packeddata[offset++];
                            }

                            if ((maskvar[currentchannel] & 1) == 1)         // note command
                            {
                                byte note = patterns[i].packeddata[offset++];
                                if (note < 0xFD)
                                {
                                    note++;
                                }
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 0 ] = note;
                                patterns[i].row[currentrow].channel[currentchannel].note = note;
                                prev[currentchannel].note = note;
                            }
                            if ((maskvar[currentchannel] & 2) == 2)         // instrument command
                            {
                                byte instrument = patterns[i].packeddata[offset++];
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 1 ] = instrument;
                                patterns[i].row[currentrow].channel[currentchannel].instrument = instrument;
                                prev[currentchannel].instrument = instrument;
                            }
                            if ((maskvar[currentchannel] & 4) == 4)         // volume command
                            {
                                byte volume = (byte)(patterns[i].packeddata[offset++] + 1);
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 2 ] = volume;
                                patterns[i].row[currentrow].channel[currentchannel].volume = volume;
                                prev[currentchannel].volume = volume;
                            }
                            if ((maskvar[currentchannel] & 8) == 8)         // effect command
                            {
                                byte command   = patterns[i].packeddata[offset++];
                                byte parameter = patterns[i].packeddata[offset++];
                                if (command != 0)
                                {
                                    //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 3 ] = command;
                                    patterns[i].row[currentrow].channel[currentchannel].command = command;
                                    //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 4 ] = parameter;
                                    patterns[i].row[currentrow].channel[currentchannel].parameter = parameter;
                                    prev[currentchannel].command   = command;
                                    prev[currentchannel].parameter = parameter;
                                }
                            }
                            if ((maskvar[currentchannel] & 16) == 16)          // previous note command
                            //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 0 ] = prev[ currentchannel ].note;
                            {
                                patterns[i].row[currentrow].channel[currentchannel].note = prev[currentchannel].note;
                            }
                            if ((maskvar[currentchannel] & 32) == 32)         // previous instrument command
                            //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 1 ] = prev[ currentchannel ].instrument;
                            {
                                patterns[i].row[currentrow].channel[currentchannel].instrument = prev[currentchannel].instrument;
                            }
                            if ((maskvar[currentchannel] & 64) == 64)         // previous volume command
                            //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 2 ] = prev[ currentchannel ].volume;
                            {
                                patterns[i].row[currentrow].channel[currentchannel].volume = prev[currentchannel].volume;
                            }
                            if ((maskvar[currentchannel] & 128) == 128)         // previous effect command
                            //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 3 ] = prev[ currentchannel ].command;
                            {
                                patterns[i].row[currentrow].channel[currentchannel].command = prev[currentchannel].command;
                                //patterns[ i ].unpackeddata[ currentrow * rowsize + currentchannel * 5 + 4 ] = prev[ currentchannel ].parameter;
                                patterns[i].row[currentrow].channel[currentchannel].parameter = prev[currentchannel].parameter;
                            }
                        }
                    }
                    catch (Exception e) {
                        throw e;
                    }
                }
            }
            catch (Exception e) {
                throw e;
            }

            #endregion
        }
Exemple #4
0
        /// <summary>
        /// Packs the data contained within the instance of the class this was called from into a .it file, ready to be written to the disk.
        /// </summary>
        /// <returns> A <code>byte</code> array that contains a complete it file, ready to be written to the disk. </returns>
        public byte[] pack()
        {
            #region Pack the patterns

            ushort rowsize = (ushort) ( channels * 5 );

            for ( int i = 0; i < numpatterns; i++ ) {
                channelgroup[] previousvalues = new channelgroup[ 64 ];
                byte[] channelmask = new byte[ 64 ];
                byte[] prevmaskvar = new byte[ 64 ];

                ushort length = 0;
                patterns[ i ].packeddata = new byte[ 256 * 64 * 7 ];
                for ( int row = 0; row < patterns[ i ].rows; row++ ) {
                    for ( int channel = 0; channel < channels; channel++ ) {
                        byte maskvar = 0;
                        if ( patterns[ i ].row[ row ].channel[ channel ].note != 0 )
                            maskvar |= 1;
                        if ( patterns[ i ].row[ row ].channel[ channel ].instrument != 0 )
                            maskvar |= 2;
                        if ( patterns[ i ].row[ row ].channel[ channel ].volume != 0 )
                            maskvar |= 4;
                        if ( patterns[ i ].row[ row ].channel[ channel ].command != 0 )
                            maskvar |= 8;

                        if ( maskvar != 0 ) {
                            if ( ( maskvar & 1 ) == 1 ) {
                                if ( patterns[ i ].row[ row ].channel[ channel ].note == previousvalues[ channel ].note ) {
                                    unchecked {
                                        maskvar &= (byte) ~0x01;
                                        maskvar |= 0x10;
                                    }
                                }
                                else {
                                    previousvalues[ channel ].note = patterns[ i ].row[ row ].channel[ channel ].note;
                                }
                            }
                            if ( ( maskvar & 2 ) == 2 ) {
                                if ( patterns[ i ].row[ row ].channel[ channel ].instrument == previousvalues[ channel ].instrument ) {
                                    unchecked {
                                        maskvar &= (byte) ~0x02;
                                        maskvar |= 0x20;
                                    }
                                }
                                else {
                                    previousvalues[ channel ].instrument = patterns[ i ].row[ row ].channel[ channel ].instrument;
                                }
                            }
                            if ( ( maskvar & 4 ) == 4 ) {
                                if ( patterns[ i ].row[ row ].channel[ channel ].volume == previousvalues[ channel ].volume ) {
                                    unchecked {
                                        maskvar &= (byte) ~0x04;
                                        maskvar |= 0x40;
                                    }
                                }
                                else {
                                    previousvalues[ channel ].volume = patterns[ i ].row[ row ].channel[ channel ].volume;
                                }
                            }
                            if ( ( maskvar & 8 ) == 8 ) {
                                if ( ( patterns[ i ].row[ row ].channel[ channel ].command == previousvalues[ channel ].command ) && ( patterns[ i ].row[ row ].channel[ channel ].parameter == previousvalues[ channel ].parameter ) ) {
                                    unchecked {
                                        maskvar &= (byte) ~0x08;
                                        maskvar |= 0x80;
                                    }
                                }
                                else {
                                    previousvalues[ channel ].command = patterns[ i ].row[ row ].channel[ channel ].command;
                                    previousvalues[ channel ].parameter = patterns[ i ].row[ row ].channel[ channel ].parameter;
                                }
                            }
                            if ( maskvar != channelmask[ channel ] ) {
                                channelmask[ channel ] = maskvar;
                                patterns[ i ].packeddata[ length++ ] = (byte) ( ( channel + 1 ) | 0x80 );   // write channelvar
                                patterns[ i ].packeddata[ length++ ] = maskvar;                             // write maskvar
                            }
                            else {
                                patterns[ i ].packeddata[ length++ ] = (byte) ( channel + 1 );
                            }
                            if ( ( maskvar & 1 ) == 1 )
                                patterns[ i ].packeddata[ length++ ] = ( patterns[ i ].row[ row ].channel[ channel ].note < 0xFE ) ? (byte) ( patterns[ i ].row[ row ].channel[ channel ].note - 1 ) : patterns[ i ].row[ row ].channel[ channel ].note;
                            if ( ( maskvar & 2 ) == 2 )
                                patterns[ i ].packeddata[ length++ ] = patterns[ i ].row[ row ].channel[ channel ].instrument;
                            if ( ( maskvar & 4 ) == 4 )
                                patterns[ i ].packeddata[ length++ ] = (byte) ( patterns[ i ].row[ row ].channel[ channel ].volume - 1 );
                            if ( ( maskvar & 8 ) == 8 ) {
                                patterns[ i ].packeddata[ length++ ] = patterns[ i ].row[ row ].channel[ channel ].command;
                                patterns[ i ].packeddata[ length++ ] = patterns[ i ].row[ row ].channel[ channel ].parameter;
                            }
                        }

                    }
                    patterns[ i ].packeddata[ length++ ] = 0;
                }
                patterns[ i ].length = length;
                Array.Resize( ref patterns[ i ].packeddata, length );
            }

            #endregion

            #region Calculate file size

            long filesize = 0;
            filesize += 0xC0 + numorders + numsamples * 4 + numpatterns * 4 + 1;
            messageoffset = (uint) ( filesize + 1 );
            filesize += messagelength + 1;
            for ( int i = 0; i < numsamples; i++ ) {
                sampleoffsets[ i ] = (uint) filesize;
                filesize += 0x51;
            }
            for ( int i =0; i < numpatterns; i++ ) {
                patternoffsets[ i ] = (uint) filesize;
                filesize += patterns[ i ].length + 9;
            }
            for ( int i = 0; i < numsamples; i++ ) {
                samples[ i ].samplepointer = (uint) filesize;
                filesize += samples[ i ].length * 2 + 1;
            }

            #endregion

            byte[] data = new byte[ filesize ];

            #region Write the header

            write4( magicword, ref data, 0x00 );

            byte[] songnamearr = System.Text.Encoding.ASCII.GetBytes( songname );
            for ( int i = 0; i < 26; i++ )
                data[ i + 0x04 ] = songnamearr[ i ];

            write2( pathighlight, ref data, 0x1E );

            write2( numorders, ref data, 0x20 );

            write2( insnum, ref data, 0x22 );

            write2( numsamples, ref data, 0x24 );

            write2( numpatterns, ref data, 0x26 );

            write2( createdwithtracker, ref data, 0x28 );

            write2( compatablewithtracker, ref data, 0x2A );

            write2( flags, ref data, 0x2C );

            write2( special, ref data, 0x2E );

            data[ 0x30 ] = globalvol;

            data[ 0x31 ] = mixvol;

            data[ 0x32 ] = initialspeed;

            data[ 0x33 ] = initialtempo;

            data[ 0x34 ] = panseperation;

            data[ 0x35 ] = pitchwheeldepth;

            write2( messagelength, ref data, 0x36 );

            write4( messageoffset, ref data, 0x38 );
            for ( int i = 0; i < messagelength; i++ ) {
                data[ messageoffset + i ] = message[ i ];
            }

            write4( reserved, ref data, 0x3C );

            for ( int i = 0; i < 64; i++ ) {
                data[ 0x40 + i ] = channelpan[ i ];
            }

            for ( int i = 0; i < 64; i++ ) {
                data[ 0x80 + i ] = channelvol[ i ];
            }

            for ( int i = 0; i < numorders; i++ ) {
                data[ 0xC0 + i ] = orders[ i ];
            }

            // ignoring instruments, since they're not supported there shouldn't be any to write

            for ( int i = 0; i < numsamples; i++ ) {
                write4( sampleoffsets[ i ], ref data, 0xC0 + numorders + i * 4 );
            }

            for ( int i = 0; i < numpatterns; i++ ) {
                write4( patternoffsets[ i ], ref data, 0xC0 + numorders + numsamples * 4 + i * 4 );
            }

            #endregion

            #region Write the sample headers

            for ( int i = 0; i < numsamples; i++ ) {
                write4( samples[ i ].magicword, ref data, sampleoffsets[ i ] + 0x00 );

                byte[] dosfilename = System.Text.Encoding.ASCII.GetBytes( samples[ i ].dosfilename );
                for ( int j = 0; j < 12; j++ ) {
                    data[ sampleoffsets[ i ] + 0x04 + j ] = dosfilename[ j ];
                }

                data[ sampleoffsets[ i ] + 0x11 ] = samples[ i ].globalvol;

                data[ sampleoffsets[ i ] + 0x12 ] = samples[ i ].flags;

                data[ sampleoffsets[ i ] + 0x13 ] = samples[ i ].defaultvol;

                byte[] samplename = System.Text.Encoding.ASCII.GetBytes( samples[ i ].samplename );
                for ( int j = 0; j < 26; j++ ) {
                    data[ sampleoffsets[ i ] + 0x14 + j ] = samplename[ j ];
                }

                data[ sampleoffsets[ i ] + 0x2E ] = samples[ i ].convert;

                data[ sampleoffsets[ i ] + 0x2F ] = samples[ i ].defaultpan;

                write4( samples[ i ].length, ref data, sampleoffsets[ i ] + 0x30 );

                write4( samples[ i ].loopbegin, ref data, sampleoffsets[ i ] + 0x34 );

                write4( samples[ i ].loopend, ref data, sampleoffsets[ i ] + 0x38 );

                write4( samples[ i ].c5speed, ref data, sampleoffsets[ i ] + 0x3C );

                write4( samples[ i ].susloopbegin, ref  data, sampleoffsets[ i ] + 0x40 );

                write4( samples[ i ].susloopend, ref data, sampleoffsets[ i ] + 0x44 );

                write4( samples[ i ].samplepointer, ref data, sampleoffsets[ i ] + 0x48 );

                data[ sampleoffsets[ i ] + 0x4C ] = samples[ i ].vibratospeed;

                data[ sampleoffsets[ i ] + 0x4D ] = samples[ i ].vibratodepth;

                data[ sampleoffsets[ i ] + 0x4E ] = samples[ i ].vibratorate;

                data[ sampleoffsets[ i ] + 0x4F ] = samples[ i ].vwaveformtype;

                for ( int j = 0; j < samples[ i ].length * 2; j++ ) {
                    data[ samples[ i ].samplepointer + j ] = samples[ i ].sample[ j ];
                }

            }

            #endregion

            #region Write the pattern headers

            for ( int i = 0; i < numpatterns; i++ ) {

                write2( patterns[ i ].length, ref data, patternoffsets[ i ] + 0x00 );

                write2( patterns[ i ].rows, ref data, patternoffsets[ i ] + 0x02 );

                for ( int j = 0; j < patterns[ i ].length; j++ ) {
                    data[ patternoffsets[ i ] + 0x08 + j ] = patterns[ i ].packeddata[ j ];
                }
            }

            #endregion

            return data;
        }