public override void Deserialize(Stream stream)
 {
     _tape.Blocks.Clear();
     byte[] bsize = new byte[2];
     while (stream.Position < stream.Length)
     {
         stream.Read(bsize, 0, 2);
         int size = BitConverter.ToUInt16(bsize, 0);
         if (size == 0) break;
         byte[] block = new byte[size];
         stream.Read(block, 0, size);
         TapeBlock tb = new TapeBlock();
         tb.Description = getBlockDescription(block, 0, block.Length);
         tb.Periods = getBlockPeriods(block, 0, block.Length, 2168, 667, 735, 855, 1710, (block[0] < 4) ? 8064 : 3220, 1000, 8);
         tb.TapData = block;
         _tape.Blocks.Add(tb);
     }
     _tape.Reset();
 }
        public override void Deserialize(Stream stream)
        {
            _tape.Blocks.Clear();
            List<int> pulses = new List<int>();
            byte[] hdr = new byte[0x34];
            stream.Read(hdr, 0, 0x20);

            if (Encoding.ASCII.GetString(hdr, 0, 22) != "Compressed Square Wave")
            {
                DialogProvider.Show(
                    "Invalid CSW file, identifier not found! ",
                    "CSW loader",
                    DlgButtonSet.OK,
                    DlgIcon.Error);
                return;
            }
            if (hdr[0x17] > 2)
            {
                DialogProvider.Show(
                    string.Format("Format CSW V{0}.{1} not supported!", hdr[0x17], hdr[0x18]),
                    "CSW loader",
                    DlgButtonSet.OK,
                    DlgIcon.Error);
                return;
            }
            if (hdr[0x17] == 2)  // CSW V2
            {
                stream.Read(hdr, 0x20, 0x14);
                byte[] extHdr = new byte[hdr[0x23]];
                stream.Read(extHdr, 0, extHdr.Length);
            }
            int cswSampleRate = (hdr[0x17] == 2) ? BitConverter.ToInt32(hdr, 0x19) : BitConverter.ToUInt16(hdr, 0x19);
            int cswCompressionType = (hdr[0x17] == 2) ? hdr[0x21] : hdr[0x1B];

            byte[] buf;
            if (hdr[0x17] == 2)
                buf = new byte[BitConverter.ToInt32(hdr, 0x1D)];
            else
                buf = new byte[stream.Length - 0x20];

            if (cswCompressionType == 1)        // RLE
            {
                stream.Read(buf, 0, buf.Length);
            }
            else if (cswCompressionType == 2)   // Z-RLE
            {
                csw2_uncompress(stream, buf);
            }
            else
            {
                DialogProvider.Show(
                    "Unknown compression type!",
                    "CSW loader",
                    DlgButtonSet.OK,
                    DlgIcon.Error);
                return;
            }

            int rate = _tape.TactsPerSecond / cswSampleRate; // usually 3.5mhz / 44khz
            for (int ptr = 0; ptr < buf.Length; )
            {
                int len = buf[ptr++] * rate;
                if (len == 0)
                {
                    len = BitConverter.ToInt32(buf, ptr) / rate;
                    ptr += 4;
                }
                pulses.Add(len);
            }

            pulses.Add(_tape.TactsPerSecond / 10);
            TapeBlock tb = new TapeBlock();
            tb.Description = "CSW tape image";
            tb.Periods = pulses;
            _tape.Blocks.Add(tb);
            _tape.Reset();
        }
        private int ReadGeneralizedDataBlock(byte[] buf, int index, ref TapeBlock tb)
        {
            tb.Description = "CSW Recording";
            int size = getInt32(buf, index); index += 4;
            int pause = getUInt16(buf, index); index += 2;
            int totp = getInt32(buf, index); index += 4;
            int npp = buf[index]; index+=1;
            int asp = buf[index]; index += 1;
            int totd = getInt32(buf, index); index += 4;
            int npd = buf[index]; index += 1;
            int asd = buf[index]; index += 1;

            return size;
        }
        public override void Deserialize(Stream stream)
        {
            _tape.Blocks.Clear();
            byte[] snbuf = new byte[stream.Length];
            stream.Read(snbuf, 0, snbuf.Length);

            if (Encoding.ASCII.GetString(snbuf, 0, 7) != "ZXTape!" || snbuf[7] != 0x1A)
            {
                DialogProvider.Show(
                    "Invalid TZX file, identifier not found! ",
                    "TZX loader",
                    DlgButtonSet.OK,
                    DlgIcon.Warning);
                return;
            }
            //int verMajor = snbuf[0x08];
            //int verMinor = snbuf[0x09];

            int ptr = 0;
            int size, pause, i, n, t, t0, j;
            int pl, p, last;//, *end; char *p;
            //int loop_n = 0, loop_p = 0;
            //byte[] nm = new byte[512];

            //TapeBlock tb = null;
            //bool grouping = false;

            //int newOffset;

            List<TapeBlock> tzxBlocks = new List<TapeBlock>();
            Stack<KeyValuePair<int, int>> loopStack = new Stack<KeyValuePair<int, int>>();

            while (ptr < snbuf.Length)
            {
                int blockId = snbuf[ptr++];

                TapeBlock tb = new TapeBlock();
                tb.TzxId = blockId;

                switch (blockId)
                {
                    #region ID 10 - Standard Speed Data Block
                    case 0x10: // Standard Speed Data Block
                        size = getUInt16(snbuf, ptr + 2);
                        pause = getUInt16(snbuf, ptr);
                        ptr += 4;
                        tb.Description = TapSerializer.getBlockDescription(snbuf, ptr, size);
                        tb.Periods = TapSerializer.getBlockPeriods(snbuf, ptr, size, 2168, 667, 735, 855, 1710, (snbuf[ptr] < 4) ? 8064 : 3220, pause, 8);
                        tb.TapData = TapSerializer.getBlockTapData(snbuf, ptr, size);
                        ptr += size;
                        break;
                    #endregion

                    #region ID 11 - Turbo Speed Data Block
                    case 0x11: // Turbo Speed Data Block
                        size = 0xFFFFFF & getInt32(snbuf, ptr + 0x0F);
                        tb.Description = TapSerializer.getBlockDescription(snbuf, ptr + 0x12, size);
                        tb.Periods = TapSerializer.getBlockPeriods(snbuf, ptr + 0x12, size,
                           getUInt16(snbuf, ptr), getUInt16(snbuf, ptr + 2),
                           getUInt16(snbuf, ptr + 4), getUInt16(snbuf, ptr + 6),
                           getUInt16(snbuf, ptr + 8), getUInt16(snbuf, ptr + 10),
                           getUInt16(snbuf, ptr + 13), snbuf[ptr + 12]);
                        //tb.TapData = TapSerializer.getBlockTapData(snbuf, ptr+0x12, size);
                        // todo: test used bits - ptr+12
                        ptr += size + 0x12;
                        break;
                    #endregion

                    #region ID 12 - Pure Tone
                    case 0x12: // Pure Tone
                        tb.Description = "Pure Tone";
                        pl = getUInt16(snbuf, ptr);
                        n = getUInt16(snbuf, ptr + 2);
                        tb.Periods = new List<int>(n);
                        for (i = 0; i < n; i++)
                            tb.Periods.Add(pl);
                        ptr += 4;
                        break;
                    #endregion

                    #region ID 13 - Pulse sequence
                    case 0x13: // Pulse sequence
                        tb.Description = "Pulse sequence";
                        n = snbuf[ptr++];
                        tb.Periods = new List<int>(n);
                        for (i = 0; i < n; i++, ptr += 2)
                            tb.Periods.Add(getUInt16(snbuf, ptr));
                        break;
                    #endregion

                    #region ID 14 - Pure Data Block
                    case 0x14: // Pure Data Block
                        tb.Description = "Pure Data Block";
                        size = 0xFFFFFF & getInt32(snbuf, ptr + 7);
                        tb.Periods = TapSerializer.getBlockPeriods(snbuf, ptr + 0x0A, size,
                           0, 0, 0, getUInt16(snbuf, ptr),
                           getUInt16(snbuf, ptr + 2),
                           -1,
                           getUInt16(snbuf, ptr + 5), snbuf[ptr + 4]);
                        tb.TapData = TapSerializer.getBlockTapData(snbuf, ptr + 0x0A, size);
                        ptr += size + 0x0A;
                        break;
                    #endregion

                    #region ID 15 - Direct Recording
                    case 0x15: // Direct Recording
                        tb.Description = "Direct Recording";
                        size = 0xFFFFFF & getInt32(snbuf, ptr + 5);
                        t0 = getUInt16(snbuf, ptr);
                        pause = getUInt16(snbuf, ptr + 2);
                        last = snbuf[ptr + 4];
                        ptr += 8;
                        pl = 0; n = 0;
                        for (i = 0; i < size; i++) // count number of pulses
                            for (j = 0x80; j != 0; j >>= 1)
                                if (((snbuf[ptr + i] ^ pl) & j) != 0)
                                {
                                    n++;
                                    pl ^= -1;
                                }
                        t = 0; pl = 0;
                        tb.Periods = new List<int>(n + 2);
                        for (i = 1; i < size; i++, ptr++) // find pulses
                            for (j = 0x80; j != 0; j >>= 1)
                            {
                                t += t0;
                                if (((snbuf[ptr] ^ pl) & j) != 0)
                                {
                                    tb.Periods.Add(t);
                                    pl ^= -1;
                                    t = 0;
                                }
                            }
                        // find pulses - last byte
                        for (j = 0x80; j != (byte)(0x80 >> last); j >>= 1)
                        {
                            t += t0;
                            if (((snbuf[ptr] ^ pl) & j) != 0)
                            {
                                tb.Periods.Add(t);
                                pl ^= -1;
                                t = 0;
                            }
                        }
                        ptr++;
                        tb.Periods.Add(t); // last pulse ???
                        if (pause != 0)
                            tb.Periods.Add(pause * 3500);
                        break;
                    #endregion

                    //#region ID 18
                    //case 0x18:
                    //    tb.Description = "CSW Recording";
                    //    break;
                    //#endregion

                    #region ID 19 - Generalized Data Block
                    case 0x19:
                        ptr += ReadGeneralizedDataBlock(snbuf, ptr, ref tb);
                        break;
                    #endregion

                    #region ID 20 - Pause (silence) or 'Stop the Tape' command
                    case 0x20: // Pause (silence) or 'Stop the Tape' command
                        pause = getUInt16(snbuf, ptr);
                        tb.Description = (pause != 0 ? "[Pause " + pause + " ms]" : "[Stop the Tape]");
                        tb.Periods = new List<int>(2);
                        ptr += 2;
                        if (pause == 0) // at least 1ms pulse as specified in TZX 1.13
                        {
                            tb.Command = TapeCommand.STOP_THE_TAPE;
                            tb.Periods.Add(3500);
                            pause = -1;
                        }
                        else pause *= 3500;
                        tb.Periods.Add(pause);
                        break;
                    #endregion

                    #region ID 21 - Group start
                    case 0x21: // Group start
                        n = snbuf[ptr++];
                        tb.Description = "[GROUP: " + Encoding.ASCII.GetString(snbuf, ptr, n) + "]";
                        tb.Command = TapeCommand.BEGIN_GROUP;
                        ptr += n;
                        tb.Periods = new List<int>();
                        //grouping = true;
                        break;
                    #endregion

                    #region ID 22 - Group end
                    case 0x22: // Group end
                        tb.Description = "[END GROUP]";
                        tb.Command = TapeCommand.END_GROUP;
                        tb.Periods = new List<int>();
                        //grouping = false;
                        break;
                    #endregion

                    #region ID 23 - Jump to block
                    case 0x23: // Jump to block
                        tb.Description = "[JUMP TO BLOCK " + getUInt16(snbuf, ptr) + "]";  // 1=next block, 2=skip 1 block, -1=prev block
                        tb.Periods = new List<int>();
                        ptr += 2;
                        break;
                    #endregion

                    #region ID 24 - Loop start
                    case 0x24: // Loop start
                        {
                            int loop_p = tzxBlocks.Count + 1; // from next
                            int loop_n = getUInt16(snbuf, ptr);  // TODO: as Command
                            loopStack.Push(new KeyValuePair<int, int>(loop_p, loop_n));
                            ptr += 2;
                            tb.Description = string.Format("[LOOP {0}]", loop_n);
                            tb.Periods = new List<int>();
                            break;
                        }
                    #endregion

                    #region ID 25 - Loop end
                    case 0x25: // Loop end
                        {
                            tb.Description = string.Format("[END LOOP]");
                            tb.Periods = new List<int>();

                            KeyValuePair<int, int> loopInfo = loopStack.Pop();
                            int loop_p = loopInfo.Key;
                            int loop_n = loopInfo.Value;

                            if (loop_n == 0)
                                break;  // loop_n is repeat count, =1 for mem economy
                            size = tzxBlocks.Count - loop_p;
                            for (i = 0; i < loop_n; i++)
                            {
                                TapeBlock repeatBlock = new TapeBlock();
                                repeatBlock.Description = string.Format("[LOOP REPEAT {0}]", i+1);
                                repeatBlock.Periods = new List<int>();
                                _tape.Blocks.Add(repeatBlock);
                                for (int z = 0; z < size; z++)
                                    _tape.Blocks.Add(tzxBlocks[loop_p + z]);
                            }
                            break;
                        }
                    #endregion

                    #region ID 26 - Call sequence
                    case 0x26: // Call sequence
                        tb.Description = "[CALL SEQUENCE]";
                        tb.Periods = new List<int>();
                        ptr += 2 + 2 * getUInt16(snbuf, ptr);
                        break;
                    #endregion

                    #region ID 27 - Return from sequence
                    case 0x27: // Return from sequence
                        tb.Description = "[RETURN SEQUENCE]";
                        tb.Periods = new List<int>();
                        break;
                    #endregion

                    #region ID 28 - Select block
                    case 0x28: // Select block
                        tb.Description = "[SELECT BLOCK]";
                        tb.Periods = new List<int>();
                        ptr += 2 + getUInt16(snbuf, ptr);
                        break;
                    #endregion

                    #region ID 2A - Stop tape if in 48K mode
                    case 0x2A: // Stop tape if in 48K mode
                        tb.Description = "[Stop tape if in 48K mode]";
                        tb.Command = TapeCommand.STOP_THE_TAPE_48K;
                        tb.Periods = new List<int>();
                        ptr += 4 + getUInt16(snbuf, ptr);
                        break;
                    #endregion

                    #region ID 30 - Text description
                    case 0x30: // Text description
                        n = snbuf[ptr++];
                        tb.Description = "[" + Encoding.ASCII.GetString(snbuf, ptr, n) + "]";
                        tb.Periods = new List<int>();
                        ptr += n;
                        break;
                    #endregion

                    #region ID 31 - Message block
                    case 0x31: // Message block
                        ptr++;//n = snbuf[ptr++]; // msg display time
                        n = snbuf[ptr++];
                        tb.Description = "[Message: " + Encoding.ASCII.GetString(snbuf, ptr, n) + "]";
                        tb.Command = TapeCommand.SHOW_MESSAGE;
                        tb.Periods = new List<int>();
                        ptr += n;
                        break;
                    #endregion

                    #region ID 32 - Archive info+
                    case 0x32: // Archive info
                        {
                            tb.Description = "Archive info";
                            tb.Periods = new List<int>();
                            p = ptr + 3;
                            StringBuilder builderInfo = new StringBuilder();
                            for (i = 0; i < snbuf[ptr + 2]; i++)
                            {
                                string info;
                                int infoType = snbuf[p++];
                                switch (infoType)
                                {
                                    case 0: info = "Full title"; break;
                                    case 1: info = "Publisher"; break;
                                    case 2: info = "Author"; break;
                                    case 3: info = "Year"; break;
                                    case 4: info = "Language"; break;
                                    case 5: info = "Type"; break;
                                    case 6: info = "Price"; break;
                                    case 7: info = "Protection"; break;
                                    case 8: info = "Origin"; break;
                                    case 0xFF: info = "Comment"; break;
                                    default: info = "info"; break;
                                }
                                size = snbuf[p++];
                                string infoValue = Encoding.ASCII.GetString(snbuf, p, size);
                                p += size;

                                if (infoType == 0 || infoType == 7 || infoType == 0xFF)
                                {
                                    if (builderInfo.Length > 0 && infoValue.Length > 0) // insert separator
                                        builderInfo.Append("; ");
                                    builderInfo.Append(string.Format("{0}: {1}", info, infoValue));
                                }
                            }
                            tb.Description = builderInfo.ToString();
                            ptr += 2 + getUInt16(snbuf, ptr);
                            break;
                        }
                    #endregion

                    #region ID 33 - Hardware type
                    case 0x33: // Hardware type
                        n = snbuf[ptr++];    // hwinfo count
                        tb.Description = "[HARDWARE TYPE]";
                        tb.Periods = new List<int>();
                        ptr += 3 * n;
                        break;
                    #endregion

                    #region ID 34 - Emulation info
                    case 0x34: // Emulation info
                        tb.Description = "[EMULATION INFO]";
                        tb.Periods = new List<int>();
                        ptr += 8;
                        break;
                    #endregion

                    #region ID 35 - Custom info block+
                    case 0x35: // Custom info block
                        {
                            string infoValue = Encoding.ASCII.GetString(snbuf, ptr, 0x10);
                            tb.Description = string.Format("[CUSTOM INFO - {0}]", infoValue);
                            ptr += 0x10;
                            tb.Periods = new List<int>();
                            int customLength = BitConverter.ToInt32(snbuf, ptr);
                            ptr += 4 + customLength;
                            break;
                        }
                    #endregion

                    #region ID 40 - Snapshot block
                    case 0x40: // Snapshot block
                        tb.Description = "[SNAPSHOT - ";
                        if (snbuf[ptr] == 0)
                            tb.Description += ".Z80]";
                        else if (snbuf[ptr] == 1)
                            tb.Description += ".SNA]";
                        else
                            tb.Description += "???]";
                        ptr++;
                        size = snbuf[ptr] | snbuf[ptr + 1] << 8 | snbuf[ptr + 2] << 16;
                        ptr += 3;
                        tb.Periods = new List<int>();
                        ptr += size;
                        break;
                    #endregion

                    #region ID 5A - "Glue" block+
                    case 0x5A: // "Glue" block
                        tb.Description = string.Format("[GLUE]");
                        tb.Periods = new List<int>();
                        ptr += 9;
                        break;
                    #endregion

                    default:
                        tb.Description = string.Format(
                            "[NOT SUPPORTED BLOCK 0x{0:X2}]",
                            snbuf[ptr - 1]);
                        tb.Periods = new List<int>();
                        ptr += getInt32(snbuf, ptr) & 0xFFFFFF;
                        ptr += 4;
                        break;
                }
                _tape.Blocks.Add(tb);
                tzxBlocks.Add(tb);
            }
            _tape.Reset();
        }
        public override void Deserialize(Stream stream)
        {
            m_tape.Blocks.Clear();
            List<int> pulses = new List<int>();

            WavStreamReader reader = new WavStreamReader(stream);
            int rate = m_tape.TactsPerSecond / reader.Header.sampleRate; // usually 3.5mhz / 44khz
            int smpCounter = 0;
            int state = reader.ReadNext();
            for (int i = 0; i < reader.Count; i++)
            {
                int sample = reader.ReadNext();
                smpCounter++;
                if ((state < 0 && sample < 0) || (state >= 0 && sample >= 0))
                    continue;
                pulses.Add(smpCounter * rate);
                smpCounter = 0;
                state = sample;
            }
            pulses.Add(m_tape.TactsPerSecond / 10);

            TapeBlock tb = new TapeBlock();
            tb.Description = "WAV tape image";
            tb.Periods = pulses;
            m_tape.Blocks.Add(tb);
            m_tape.Reset();
        }