Exemple #1
0
        protected override uint DecodePacket(out byte[] packet)
        {
            if (nextBlock >= codecInfo.Count)
            {
                // No more data
                packet = null;
                return(0);
            }

            packet = outputBuffer;

            MCMPBlockCodecInfo info = codecInfo[nextBlock];

            nextBlock++;

            BinReader reader = currentChunk.GetReader(); // .IMX file

            reader.Position = info.Offset;

            reader.Read(inputBuffer, 0, info.CompressedSize);
            uint result;

            if (info.Codec >= codecNames.Count)
            {
                throw new DecodingException("Invalid codec index: {0}", info.Codec);
            }

            switch (codecNames[info.Codec])
            {
            case "NULL":     // Uncompressed
                result = DecodeUncompressed(inputBuffer, packet, info);
                break;

            case "VIMA":     //
                result = DecodeVIMA(inputBuffer, packet, info);
                break;

            default:
                throw new DecodingException("Unsupported IMC codec: {0}", codecNames[info.Codec]);
            }
            return(result);
        }
Exemple #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="compChunk">MCMP chunk</param>
        /// <returns>Maximum decompressed size</returns>
        protected uint ReadCompressionTable(Chunk compChunk)
        {
            uint currentOffset = compChunk.Size; // Start of compressed (even if NULL-compressed) data

            codecInfo = new List <MCMPBlockCodecInfo>();
            BinReader reader = compChunk.GetReader();

            reader.Position = 4;
            int  count = reader.ReadU16BE();
            uint maxUncompressedSize = 0;

            for (int i = 0; i < count; i++)
            {
                MCMPBlockCodecInfo info = new MCMPBlockCodecInfo();
                info.Codec            = reader.ReadU8();
                info.UncompressedSize = reader.ReadU32BE();
                maxUncompressedSize   = Math.Max(maxUncompressedSize, info.UncompressedSize);
                info.CompressedSize   = reader.ReadU32BE();
                info.Offset           = currentOffset;
                currentOffset        += info.CompressedSize;
                codecInfo.Add(info);
            }

            int nameTableSize = reader.ReadU16BE();
            int readCount     = 0;

            codecNames = new List <string>();
            while (readCount < nameTableSize)
            {
                string codecName = reader.ReadStringZ();
                codecNames.Add(codecName);
                readCount += codecName.Length + 1; // Plus 0 terminator
            }

            return(maxUncompressedSize);
        }
Exemple #3
0
        protected uint DecodeVIMA(byte[] input, byte[] output, MCMPBlockCodecInfo info)
        {
            // A VIMA block in this (GF) version of the format is similar to the previous (CMI) format
            // with a few key differences:
            // - No "uncompressed data length" (the NULL compression block takes care of that)
            // - The unused table value initial state is removed
            // - The output initial state is now stored in a signed word (rather than unsigned dword)
            // - Stereo is indicated by a negated first channel table position initial state (i.e. 0x00 => 0xFF)
            // - The bit stream may include "keyframe" data, i.e. single uncompressed words, indicated by setting all LSB bits on, followed by the uncompressed word
            // - The bit count table and navigation tables have changed.

            int[] startTablePos = new int[2];
            int[] startOutput   = new int[2];

            using (MemoryStream stream = new MemoryStream(input))
            {
                using (BinReader reader = new BinReader(stream))
                {
                    reader.Position = 0;

                    startTablePos[0] = reader.ReadU8();
                    startOutput[0]   = reader.ReadS16BE();

                    int channels = 1;

                    if ((startTablePos[0] & 0x80) != 0)
                    {
                        // Stereo
                        channels         = 2;
                        startTablePos[0] = (byte)(~startTablePos[0]);
                        startTablePos[1] = reader.ReadU8();
                        startOutput[1]   = reader.ReadS16BE();
                    }

                    int sampleCount = (int)info.UncompressedSize / (2 * channels); // 16 bit
                    int bits        = reader.ReadU16BE();
                    int bitptr      = 0;

                    // Decode each channel:
                    for (int channel = 0; channel < channels; channel++)
                    {
                        int tablePos   = startTablePos[channel];
                        int outputWord = startOutput[channel];

                        int destPos = 2 * channel;

                        for (int i = 0; i < sampleCount; i++)
                        {
                            // Read bits based on current bit table value:
                            int bitCount = IMC_BIT_COUNTS[tablePos];
                            bitptr += bitCount;

                            byte signBitMask = (byte)(1 << (bitCount - 1)); // mask for determining sign
                            byte dataBitMask = (byte)(signBitMask - 1);     // mask for determining data

                            byte compressedSample = (byte)((bits >> (16 - bitptr)) & (signBitMask | dataBitMask));

                            // TODO: Put bitcrap into class
                            if (bitptr > 7) // Read more
                            {
                                bits    = ((bits & 0xff) << 8) | reader.ReadU8();
                                bitptr -= 8;
                            }

                            if ((compressedSample & signBitMask) != 0)
                            {
                                compressedSample ^= signBitMask;
                            }
                            else
                            {
                                signBitMask = 0;
                            }

                            // If all bits are set, this is a "keyframe" - read one word of uncompressed data:
                            if (compressedSample == dataBitMask)
                            {
                                outputWord  = (int)((short)(bits << bitptr) & 0xffffff00);
                                bits        = ((bits & 0xff) << 8) | reader.ReadU8();
                                outputWord |= ((bits >> (8 - bitptr)) & 0xff);
                                bits        = ((bits & 0xff) << 8) | reader.ReadU8();
                            }
                            else
                            {
                                // Read delta from table:
                                int tableIndex = (compressedSample << (7 - bitCount)) | (tablePos << 6);
                                int delta      = IMC_VALUE_TABLE[tableIndex];
                                if (compressedSample != 0)
                                {
                                    delta += (IMC_DELTA_TABLE[tablePos] >> (bitCount - 1));
                                }

                                // Apply sign:
                                if (signBitMask != 0)
                                {
                                    delta = -delta;
                                }

                                // Apply delta:
                                outputWord += delta;

                                // Clip to signed 16 bit sample:
                                if (outputWord > 0x7fff)
                                {
                                    outputWord = 0x7fff;
                                }
                                if (outputWord < -0x8000)
                                {
                                    outputWord = -0x8000;
                                }
                            }

                            output[destPos]     = (byte)(outputWord & 0xff);
                            output[destPos + 1] = (byte)(outputWord >> 8);

                            destPos += channels * 2;

                            tablePos += IMC_NAV_TABLE[bitCount - 2][compressedSample];

                            // Clip table position to table:
                            if (tablePos < 0)
                            {
                                tablePos = 0;
                            }
                            else if (tablePos >= IMC_DELTA_TABLE.Length)
                            {
                                tablePos = IMC_DELTA_TABLE.Length - 1;
                            }
                        }
                    }
                }
            }
            return(info.UncompressedSize);
        }
Exemple #4
0
 protected uint DecodeUncompressed(byte[] input, byte[] output, MCMPBlockCodecInfo info)
 {
     Array.Copy(input, 0, output, 0, info.UncompressedSize);
     return(info.UncompressedSize);
 }