package turgen.plugin.csturbo;

import turgen.plugin.*;
import turgen.generator.SignalGenerator;
import turgen.config.Configuration;
import turgen.*;
import java.io.*;
import java.util.Iterator;
import javax.swing.*;
import turgen.config.BooleanConfigEntry;
import turgen.config.ConfigClass;
import turgen.config.IntegerConfigEntry;
import turgen.dtb.*;
import turgen.generator.PulseCrate;
import turgen.generator.PulseCreator;
import turgen.playlist.AbstractPlayListItem;
import turgen.tapeimage.DataChunk;
import turgen.tapeimage.TapeImageChunk;
import turgen.tapeimage.TurboChunk;

public class SuperTurboPlugin implements Plugin {

    private CsTurboPluginsPanel panel;
    private final int[] casChunkName = {116, 114, 115, 116};

    private ConfigClass cfcSuperTurbo;
    private IntegerConfigEntry cfgPilotLenA;
    private IntegerConfigEntry cfgPilotLenB;
    private BooleanConfigEntry cfgProlongatePilotTone;
    private IntegerConfigEntry cfgSilenceAfterHeader;
    private IntegerConfigEntry cfgPilotLenBlockLoading;
    private BooleanConfigEntry cfgProlongateBlockLoadingPilotTone;
    private BooleanConfigEntry cfgPrependLoader;

    /** Nova instance SuperTurboPlugin */
    public SuperTurboPlugin() {
        
    }

    /** Jake rychlosti jsou podporovany*/
    

    /** Identifikacni jmeno modulu*/
    public String getName() {
        return "Super Turbo";

    }

    /*************************************************************/
    /*Tvorba instrukci pro generator*/
    public int[] convertItem(AbstractPlayListItem _pl) throws Exception {

        /*Get CsTurboPlaylistItem*/
        CsTurboPlaylistItem pl = (CsTurboPlaylistItem) _pl;

        
        RandomAccessFile raf = null;
        

        try {


            /*Pripraven vystupni proud pro instrukce*/
            InstructionStream is = new InstructionStream();
            
            /*If loader is to be prepended, then we prepend it*/
            if (cfgPrependLoader.getAsBoolean()==true) {
                is.add(LoaderBinaryData.universalTurboLoader);
            }

            /*Nastaveni, pro vsechny mozne konverze je nastaveni stejne,
             *bloky nebo soubory jsou vzdy v Super Turbu o dane rychlosti
             */
            Utils.addSetupInstruction(is, pl.pulses, casChunkName,SignalGenerator.FLAG_POLARITY_10,SignalGenerator.FLAG_ORDER_HL);

            /*Nyni se vybere konverze podle typu v polozce playlistu*/
            switch (pl.conversionType) {

                /*Zde se prevadi cely soubor*/
                case CsTurboPlaylistItem.CV_BINTURBO:
                case CsTurboPlaylistItem.CV_BASIC:
                case CsTurboPlaylistItem.CV_DATA: {
                    convertFullFile(is, pl);
                    break;
                }
                /*Monoliticky binarni soubor*/
                case CsTurboPlaylistItem.CV_MONOLITHIC: {
                    convertMonolithicBinary(is, pl);
                    break;
                }
                /*Specialni retezove zavadeni*/
                case CsTurboPlaylistItem.CV_SEGMENTED_CHAIN: {
                    convertChainLoading(is, pl);
                    break;
                }
                /*Specialni blokove zavadeni*/
                case CsTurboPlaylistItem.CV_SEGMENTED_BLOCK: {
                    convertBlockLoading(is, pl);
                    break;
                }
            }

            is.add(SignalGenerator.INSTR_END);

            
            /*Instrukce pro generatory*/
            return is.getInstructions();

        } catch (Exception e) {
            /*Uklidit*/
            try {
                if (raf!=null) raf.close();
            } catch (IOException xe) {
                /*No reaction*/
            }
            throw e;

        }
    }

    public AbstractPlayListItem getItemFromPanel() {
        CsTurboPlaylistItem dpr = panel.getRecord();
        dpr.pluginClass = Turgen.pluginManager.getPluginClassName(this);
        return dpr;
    }

    public void resetPanel() {
        panel.clear();
    }

    public String validateItemOnPanel() {
        return panel.verify();
    }

    public JPanel getItemPanel() {
        return panel;
    }

    public void init() {
        panel = new CsTurboPluginsPanel(true);

        /*Initialize configuration class*/
        cfcSuperTurbo = new ConfigClass("SuperTurbo","Super Turbo");

        /*Initialize configuration entries*/
        cfgPilotLenA = new IntegerConfigEntry("pilotLenA","Header block pilot tone length [pulse]", 3072, 256, 8192);
        cfgPilotLenB = new IntegerConfigEntry("pilotLenB","Data block pilot tone length [pulse]", 3072, 256, 8192);
        cfgProlongatePilotTone = new BooleanConfigEntry("prolongatePilotTone","Prolongate pilot tone", true);
        cfgSilenceAfterHeader = new IntegerConfigEntry("silenceAfterHeader","Silence after header [s]",0,0,30);
        cfgPilotLenBlockLoading = new IntegerConfigEntry("pilotLenBlockLoading","Pilot tone length for BlockLoading [pulse]", 2048, 256, 8192);
        cfgProlongateBlockLoadingPilotTone = new BooleanConfigEntry("prolongateBlockLoadingPilotTone","Prolongate pilot tone for BlockLoading", true);
        cfgPrependLoader = new BooleanConfigEntry("prependLoader","Prepend Universal Turbo loader",false);

        /*Add entries to the class*/
        cfcSuperTurbo.addEntry(cfgPilotLenA);
        cfcSuperTurbo.addEntry(cfgPilotLenB);
        cfcSuperTurbo.addEntry(cfgPilotLenBlockLoading);
        cfcSuperTurbo.addEntry(cfgProlongatePilotTone);
        cfcSuperTurbo.addEntry(cfgSilenceAfterHeader);
        cfcSuperTurbo.addEntry(cfgProlongateBlockLoadingPilotTone);
        cfcSuperTurbo.addEntry(cfgPrependLoader);

        /*Register configuration class*/
        Configuration.registerClass(cfcSuperTurbo);
    }

    public void fillPanel(AbstractPlayListItem plr) {
        CsTurboPlaylistItem dpr = (CsTurboPlaylistItem) plr;
        panel.fill(dpr);
    }

    public void setInOutProvider(InOutProvider prov) {
        panel.ioProvider = prov;
    }

    private void convertFullFile(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int plenA = cfgPilotLenA.getAsInt();
        int plenB = cfgPilotLenA.getAsInt();
        
        if (cfgProlongatePilotTone.getAsBoolean()==true) {
            plenA = PulseCreator.prolongatePilotTone(plenA, 32,cpi.pulses.pilotTonePulseWidth);
            plenB = PulseCreator.prolongatePilotTone(plenB, 32,cpi.pulses.pilotTonePulseWidth);
        }

        int hdrLoad = Integer.parseInt(cpi.load);
        int hdrRun = Integer.parseInt(cpi.run);
        int hdrLength = Integer.parseInt(cpi.length);
        int hdrType = Integer.parseInt(cpi.type);

        /*Header*/
        int[] hdr = Utils.createSTHeader(cpi.fname, hdrType, hdrLoad, hdrLength, hdrRun);
        addBlock(is,hdr,-1,true,plenA);
        addSilenceAfterHeader(is);
        
        /*Data*/
        addBlock(is,readFileData(cpi.inFile, 0, 512*1024),237,false,plenB);
    }
    
    public void convertMonolithicBinaryUsingData(InstructionStream is,int[] data,String title,boolean addSetupInstruction,PulseCrate pulses) throws Exception {
        
        /* Add setup instruction - for callers that may require it*/
        if (addSetupInstruction==true) {
            if (pulses==null) {
                pulses = Turgen.pulseCache.getPulsesForPlugin(this)[0];
            }
            Utils.addSetupInstruction(is, pulses , casChunkName, SignalGenerator.FLAG_POLARITY_10, SignalGenerator.FLAG_ORDER_HL);
        }
        
        /* Get DOS 2 binary file*/
        DOS2Binary dtb = new DOS2Binary("");
        dtb.analyzeFromData(data);
        
        /* Check if the file is monolithic*/
        if (dtb.isMonolithic()==false) {
            throw new DOS2BinaryProcessingException("Input binary file is not a monolithic binary file");
        }
        
        convertMonolithicBinaryImpl(
                is,
                dtb,
                title,
                dtb.getFirstDataSegment().start,
                dtb.getCanonicalRunAddress(),
                dtb.getFirstDataSegment().data.length,
                3,
                Turgen.pulseCache.getPulsesForPlugin(this)[0]);
    }
    
    private void convertMonolithicBinary(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {
        
        DOS2Binary dtb = new DOS2Binary(cpi.inFile);
        dtb.analyzeFromFile();
        
        /* Check if the file is monolithic*/
        if (dtb.isMonolithic()==false) {
            throw new DOS2BinaryProcessingException("Input binary file is not a monolithic binary file");
        }
        
        convertMonolithicBinaryImpl(
                is,
                dtb,
                cpi.fname,
                Integer.parseInt(cpi.load),
                Integer.parseInt(cpi.run),
                Integer.parseInt(cpi.length),
                Integer.parseInt(cpi.type),
                cpi.pulses
                );
    }

    private void convertMonolithicBinaryImpl(InstructionStream is,DOS2Binary dtb, String hdrFileName,int hdrLoad,int hdrRun,int hdrLength, int hdrType,PulseCrate pulses) throws Exception {

        /* Check if the file is monolithic*/
        if (dtb.isMonolithic()==false) {
            throw new DOS2BinaryProcessingException("Input file is not a monolithic binary file");
        }
        
        int plenA = cfgPilotLenA.getAsInt();
        int plenB = cfgPilotLenA.getAsInt();
        if (cfgProlongatePilotTone.getAsBoolean()==true) {
            plenA = PulseCreator.prolongatePilotTone(plenA, 32,pulses.pilotTonePulseWidth);
            plenB = PulseCreator.prolongatePilotTone(plenB, 32,pulses.pilotTonePulseWidth);
        }

        /*Header*/
        int[] hdr = Utils.createSTHeader(hdrFileName, hdrType, hdrLoad, hdrLength, hdrRun);
        addBlock(is,hdr,-1,true,plenA);
        addSilenceAfterHeader(is);
        
        /*Data*/
        int[] fileData = dtb.getFirstDataSegment().data;
        addBlock(is,fileData,237,false,plenB);
        
    }

    private void convertChainLoading(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int currentPause = 0;
        int numPauses = cpi.silenceList.length;

        /*Create DOS 2 Binary and analyze it*/
        DOS2Binary dtb = new DOS2Binary(cpi.inFile);
        dtb.analyzeFromFile();
        
        /*Pulses Used*/
        int currentPilotLenA = cfgPilotLenA.getAsInt();
        int currentPilotLenB = cfgPilotLenB.getAsInt();
        
        if (cfgProlongatePilotTone.getAsBoolean()==true) {
            currentPilotLenA = PulseCreator.prolongatePilotTone(currentPilotLenA, 32,cpi.pulses.pilotTonePulseWidth);
            currentPilotLenB = PulseCreator.prolongatePilotTone(currentPilotLenB, 32,cpi.pulses.pilotTonePulseWidth);
        }
        
        /*Convert loader*/
        convertMonolithicBinaryUsingData(is, CHAINLOADER_DATA,cpi.fname,false,cpi.pulses);
        
        /*Certain silence after loader*/
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(2);
        
        /*Convert segments of the binary file*/
        Segment seg;
        Iterator<Segment> it = dtb.allSegments.iterator();
        
        int[] headerData = new int[4];
        
        while(it.hasNext()) {
            seg = it.next();
            
            /*Convert segment header*/
            headerData[0]=seg.start % 256;
            headerData[1]=seg.start / 256;
            headerData[2]=seg.stop % 256;
            headerData[3]=seg.stop / 256;
            addBlock(is,headerData,183,false,currentPilotLenA);
            
            /*Convert segment data*/
            addBlock(is,seg.data,237,false,currentPilotLenB);
            
            /*Handle silence*/
            if (currentPause<numPauses) {
                if (seg.type==Segment.INIT_SEGMENT || seg.type==Segment.RUNINIT_SEGMENT) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(cpi.silenceList[currentPause]);
                    currentPause++;
                }
            }
        }
        
        /*Convert termination header*/
        for(int i=0;i<headerData.length;i++) {
            headerData[i]=255;
        }
        addBlock(is,headerData,183,false,currentPilotLenA);

        
    }
    
    /** Convert binary file - blockloading*/
    private void convertBlockLoading(InstructionStream is, CsTurboPlaylistItem cpi) throws Exception {

        int currentPause = 0;
        int numPauses = cpi.silenceList.length;


        /*Get DOS 2 Binary and analyze it*/
        DOS2Binary dtb = new DOS2Binary(cpi.inFile);
        dtb.analyzeFromFile();
        dtb.prepareForDumbLoader();

        /*Create copy of the loader skeleton*/
        int[] loaderData = new int[BLOCKLOADER_DATA.length];
        System.arraycopy(BLOCKLOADER_DATA, 0, loaderData, 0, BLOCKLOADER_DATA.length);
        
        /*Update the loader skeleton with segment information*/
        int sl = dtb.allSegments.size();
        if (sl > 62) {
            throw new DOS2BinaryProcessingException("Blockloader cannot process binary file with more than 62 segments");
        }

        Segment s;
        int index = BLOCKLOADER_TABLE_OFFSET;
        loaderData[BLOCKLOADER_COUNTER_OFFSET] = sl * 4;

        for (int i = 0; i < sl; i++) {
            s = (Segment) dtb.allSegments.get(i);
            loaderData[index] = (s.start) % 256;
            loaderData[index + 1] = (s.start) / 256;
            loaderData[index + 2] = (s.stop + 1) % 256;
            loaderData[index + 3] = (s.stop + 1) / 256;
            index += 4;
        }
        
        convertMonolithicBinaryUsingData(is, loaderData, cpi.fname,false,cpi.pulses);
        
        /*Certain silence after loader*/
        is.add(SignalGenerator.INSTR_LONGSILENCE);
        is.add(2);
        
        /*Convert segments to super turbo blocks*/
        int pilotTonePulses=cfgPilotLenBlockLoading.getAsInt();
        if (cfgProlongatePilotTone.getAsBoolean()==true) {
            pilotTonePulses = PulseCreator.prolongatePilotTone(pilotTonePulses, 32,cpi.pulses.pilotTonePulseWidth);
        }
        
        Iterator<Segment> it = dtb.allSegments.iterator();
        while (it.hasNext()) {

            /*Convert segment*/
            Segment sct = it.next();
            addBlock(is,sct.data,183,false,pilotTonePulses);

            /*Silence after INIT segments*/
            if (sct.type == Segment.INIT_SEGMENT || sct.type == Segment.RUNINIT_SEGMENT) {
                if (currentPause < numPauses) {
                    is.add(SignalGenerator.INSTR_LONGSILENCE);
                    is.add(cpi.silenceList[currentPause]);
                    currentPause++;
                }
            }
            
        } 


    }


    private int[] readFileData(String file, int skipBytes, int maxLen) throws Exception {

        byte[] b;
        RandomAccessFile raf = new RandomAccessFile(file, "r");

        int flen = (int) raf.length();
        if (flen > maxLen) {
            raf.close();
            throw new FileTooLargeException("File exceeds " + Integer.toString(maxLen) + " bytes");
        }

        b = new byte[flen - skipBytes];
        raf.skipBytes(skipBytes);
        raf.readFully(b);
        raf.close();


        /*Konverze na int*/
        int[] fileData = new int[b.length];
        for (int i = 0; i < b.length; i++) {
            fileData[i] = b[i] < 0 ? b[i] + 256 : b[i];
        }

        return fileData;
    }

    public String[] getSupportedTapeImageChunks() {
        return new String[] {"trst"};
    }
    
    private void addBlock(InstructionStream is,int[] data,int idByte,boolean isFull,int pilotTonePulses) {
        
        /*Pilot tone and sync pulse*/
        is.add(SignalGenerator.INSTR_PILOTTONE);
        is.add(pilotTonePulses);
        is.add(SignalGenerator.INSTR_SYNC);
        
        /*A full block is just a full block*/
        if (isFull==true) {
            is.add(SignalGenerator.INSTR_DATA);
            is.add(data.length);
            is.add(data);
        }
        /*From raw data, we create noble super turbo blocks*/
        else {
            int chsum = idByte;
            is.add(SignalGenerator.INSTR_DATA);
            is.add(data.length+2);
            is.add(idByte);
            is.add(data);
            for(int i=0;i<data.length;i++) {
                chsum = chsum ^ data[i];
            }
            is.add(chsum);
            
        }
        
        /*Safety pulse + safety pause*/
        is.add(SignalGenerator.INSTR_NARROW);
        is.add(SignalGenerator.INSTR_BLOCKSEP);
    }
    
    private void addSilenceAfterHeader(InstructionStream is) {
        int silenceAfterHeader = cfgSilenceAfterHeader.getAsInt();
        if (silenceAfterHeader!=0) {
            is.add(SignalGenerator.INSTR_LONGSILENCE);
            is.add(silenceAfterHeader);
        }
    }

    
    private static final int[] CHAINLOADER_DATA = {
        0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0xdc, 0x21, 0x00, 0x20, 0x10, 0x22, 0x20, 0xc9, 0x08, 0xa9, 0x01, 0x8d, 0x99, 0x08,
        0x20, 0x66, 0x07, 0xa9, 0xb7, 0x20, 0x95, 0x07, 0xb0, 0x06, 0x20, 0xb7, 0x08, 0x4c, 0x0b, 0x07, 0xa2, 0x04, 0xbd, 0x99,
        0x08, 0xc9, 0xff, 0xd0, 0x06, 0xca, 0xd0, 0xf6, 0x6c, 0xe0, 0x02, 0xa9, 0x94, 0x8d, 0xe2, 0x02, 0xa9, 0x07, 0x8d, 0xe3,
        0x02, 0xad, 0x99, 0x08, 0xf0, 0x0f, 0xad, 0x9a, 0x08, 0x8d, 0xe0, 0x02, 0xad, 0x9b, 0x08, 0x8d, 0xe1, 0x02, 0xce, 0x99,
        0x08, 0x20, 0x77, 0x07, 0xa9, 0xed, 0x20, 0x95, 0x07, 0xb0, 0x06, 0x20, 0xb7, 0x08, 0x4c, 0x0b, 0x07, 0x20, 0x63, 0x07,
        0x4c, 0x10, 0x07, 0x6c, 0xe2, 0x02, 0xa9, 0x9a, 0x85, 0x32, 0xa9, 0x08, 0x85, 0x33, 0xa9, 0x9e, 0x85, 0x34, 0xa9, 0x08,
        0x85, 0x35, 0x60, 0xad, 0x9a, 0x08, 0x85, 0x32, 0xad, 0x9b, 0x08, 0x85, 0x33, 0x18, 0xee, 0x9c, 0x08, 0x90, 0x03, 0xee,
        0x9d, 0x08, 0xad, 0x9c, 0x08, 0x85, 0x34, 0xad, 0x9d, 0x08, 0x85, 0x35, 0x60, 0x85, 0x36, 0x20, 0x6d, 0x08, 0x08, 0x20,
        0xfc, 0x07, 0x20, 0x49, 0x08, 0xa4, 0x39, 0x20, 0x4e, 0x08, 0x90, 0xf6, 0xc4, 0x3a, 0xb0, 0xf5, 0x20, 0x4e, 0x08, 0x90,
        0x46, 0xa4, 0x38, 0x4c, 0xd2, 0x07, 0x28, 0xd0, 0x08, 0xa5, 0x36, 0x45, 0x3c, 0xd0, 0x39, 0xf0, 0x0c, 0xa0, 0x00, 0xa5,
        0x3c, 0x91, 0x32, 0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xa4, 0x38, 0xc8, 0xc8, 0x08, 0xa9, 0x01, 0x85, 0x3c, 0x20, 0x49,
        0x08, 0x90, 0x1c, 0xc4, 0x39, 0x26, 0x3c, 0xa4, 0x38, 0x90, 0xf3, 0xa5, 0x31, 0x45, 0x3c, 0x85, 0x31, 0xa5, 0x32, 0xc5,
        0x34, 0xa5, 0x33, 0xe5, 0x35, 0x90, 0xc3, 0xa9, 0x00, 0xc5, 0x31, 0x68, 0x20, 0x86, 0x08, 0x60, 0x20, 0x49, 0x08, 0x90,
        0xfb, 0xa9, 0x03, 0x85, 0x37, 0xa0, 0x00, 0x84, 0x40, 0xa2, 0x03, 0x20, 0x49, 0x08, 0x90, 0xec, 0xca, 0xd0, 0xf8, 0x98,
        0x4a, 0x48, 0x49, 0xff, 0x85, 0x38, 0x68, 0x4a, 0x48, 0x49, 0xff, 0x85, 0x39, 0x68, 0x4a, 0x49, 0xff, 0x85, 0x3a, 0xa0,
        0x00, 0x20, 0x4e, 0x08, 0x90, 0xce, 0xa4, 0x38, 0x20, 0x49, 0x08, 0x90, 0xc7, 0xc4, 0x39, 0x90, 0xc3, 0xe6, 0x40, 0xd0,
        0xf1, 0xc6, 0x37, 0x10, 0xc4, 0xa0, 0x00, 0x20, 0x4e, 0x08, 0x90, 0xb4, 0x60, 0x20, 0x4e, 0x08, 0x90, 0x1d, 0xad, 0x0a,
        0xd2, 0x29, 0xe4, 0x05, 0x30, 0x4a, 0x25, 0x37, 0x8d, 0x1a, 0xd0, 0xc8, 0xf0, 0x0d, 0xad, 0x0f, 0xd2, 0x29, 0x10, 0xc5,
        0x30, 0xf0, 0xf4, 0x85, 0x30, 0x38, 0x60, 0x18, 0x60, 0xa0, 0x34, 0x8c, 0x02, 0xd3, 0x8c, 0x03, 0xd3, 0xa0, 0x00, 0x84,
        0x30, 0x84, 0x31, 0x8c, 0x0e, 0xd4, 0x8c, 0x00, 0xd4, 0x8c, 0x0e, 0xd2, 0x18, 0x60, 0xa0, 0xc0, 0x8c, 0x0e, 0xd4, 0x84,
        0x10, 0x8c, 0x0e, 0xd2, 0xa0, 0x3c, 0x8c, 0x02, 0xd3, 0x8c, 0x03, 0xd3, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x54, 0x53,
        0x20, 0x53, 0x55, 0x50, 0x45, 0x52, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x9b, 0x4c, 0x4f, 0x41, 0x44, 0x20, 0x45, 0x52, 0x52,
        0x4f, 0x52, 0x9b, 0xa2, 0xac, 0xa0, 0x08, 0x20, 0x42, 0xc6, 0xa9, 0xff, 0x8d, 0xfc, 0x02, 0xa9, 0x01, 0x20, 0xfc, 0xfd,
        0x60, 0xa2, 0x9e, 0xa0, 0x08, 0x20, 0x42, 0xc6, 0xa9, 0x01, 0x8d, 0xfc, 0x02, 0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x44,
        0x02, 0xa9, 0x01, 0x85, 0x09, 0x60, 0xda, 0x01, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x08, 0x85, 0x34,
        0xa9, 0x07, 0x85, 0x35, 0xa0, 0x00, 0xae, 0xdb, 0x21, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33,
        0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0xda, 0x21, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x08,
        0x07
    };


    
    private static final int BLOCKLOADER_TABLE_OFFSET = 0x17C+0x0C;
    private static final int BLOCKLOADER_COUNTER_OFFSET = 0x17B+0x0C;
    private static final int[] BLOCKLOADER_DATA = {
       
    0xff, 0xff, 0xe0, 0x02, 0xe1, 0x02, 0x9d, 0x22, 0x00, 0x20, 0xd1, 0x22, 0x20, 0x8a, 0x0a, 0xa2, 0x0c, 0x8e, 0xfc, 0x02,
    0xa9, 0x01, 0x20, 0xfc, 0xfd, 0xa9, 0x00, 0x8d, 0x78, 0x09, 0xad, 0x78, 0x09, 0xcd, 0x7b, 0x09, 0xd0, 0x03, 0x6c, 0xe0,
    0x02, 0xa9, 0x79, 0x8d, 0xe2, 0x02, 0xa9, 0x09, 0x8d, 0xe3, 0x02, 0xac, 0x78, 0x09, 0xb9, 0x7c, 0x09, 0x85, 0x32, 0xc8,
    0xb9, 0x7c, 0x09, 0x85, 0x33, 0xc8, 0xb9, 0x7c, 0x09, 0x85, 0x34, 0xc8, 0xb9, 0x7c, 0x09, 0x85, 0x35, 0xa9, 0x34, 0x8d,
    0x02, 0xd3, 0x8d, 0x03, 0xd3, 0x20, 0x78, 0x08, 0xa9, 0x3c, 0x8d, 0x02, 0xd3, 0x8d, 0x03, 0xd3, 0xb0, 0x0d, 0xa9, 0xff,
    0x8d, 0xfc, 0x02, 0xa9, 0x04, 0x20, 0xfc, 0xfd, 0x4c, 0x0d, 0x08, 0x20, 0x75, 0x08, 0xee, 0x78, 0x09, 0xee, 0x78, 0x09,
    0xee, 0x78, 0x09, 0xee, 0x78, 0x09, 0x4c, 0x12, 0x08, 0x6c, 0xe2, 0x02, 0x20, 0xf6, 0x08, 0x20, 0x07, 0x09, 0x20, 0xed,
    0x08, 0xa5, 0x38, 0xc9, 0xb4, 0x90, 0xf1, 0xa9, 0xb7, 0x20, 0x8d, 0x08, 0x60, 0x85, 0x36, 0x20, 0xf6, 0x08, 0x08, 0x20,
    0x54, 0x09, 0xa4, 0x39, 0x20, 0x59, 0x09, 0x90, 0xf6, 0xc4, 0x3a, 0xb0, 0xf5, 0x20, 0x59, 0x09, 0x90, 0x46, 0xa4, 0x38,
    0x4c, 0xc7, 0x08, 0x28, 0xd0, 0x08, 0xa5, 0x36, 0x45, 0x3c, 0xd0, 0x39, 0xf0, 0x0c, 0xa0, 0x00, 0xa5, 0x3c, 0x91, 0x32,
    0xe6, 0x32, 0xd0, 0x02, 0xe6, 0x33, 0xa4, 0x38, 0xc8, 0xc8, 0x08, 0xa9, 0x01, 0x85, 0x3c, 0x20, 0x54, 0x09, 0x90, 0x1c,
    0xc4, 0x39, 0x26, 0x3c, 0xa4, 0x38, 0x90, 0xf3, 0xa5, 0x31, 0x45, 0x3c, 0x85, 0x31, 0xa5, 0x32, 0xc5, 0x34, 0xa5, 0x33,
    0xe5, 0x35, 0x90, 0xc3, 0xa9, 0x00, 0xc5, 0x31, 0x68, 0xa9, 0x40, 0x8d, 0x0e, 0xd4, 0x8d, 0x0e, 0xd2, 0x60, 0xa0, 0x00,
    0x84, 0x30, 0x84, 0x31, 0x8c, 0x0e, 0xd4, 0x8c, 0x00, 0xd4, 0x8c, 0x0e, 0xd2, 0x18, 0x60, 0x20, 0x54, 0x09, 0x90, 0xfb,
    0xa9, 0x03, 0x85, 0x37, 0xa0, 0x00, 0x84, 0x40, 0xa2, 0x03, 0x20, 0x54, 0x09, 0x90, 0xec, 0xca, 0xd0, 0xf8, 0x98, 0x4a,
    0x48, 0x49, 0xff, 0x85, 0x38, 0x68, 0x4a, 0x48, 0x49, 0xff, 0x85, 0x39, 0x68, 0x4a, 0x49, 0xff, 0x85, 0x3a, 0xa0, 0x00,
    0x20, 0x59, 0x09, 0x90, 0xce, 0xa4, 0x38, 0x20, 0x54, 0x09, 0x90, 0xc7, 0xc4, 0x39, 0x90, 0xc3, 0xe6, 0x40, 0xd0, 0xf1,
    0xc6, 0x37, 0x10, 0xc4, 0xa0, 0x00, 0x20, 0x59, 0x09, 0x90, 0xb4, 0x60, 0x20, 0x59, 0x09, 0x90, 0x1d, 0xad, 0x0a, 0xd2,
    0x29, 0xe4, 0x05, 0x30, 0x4a, 0x25, 0x37, 0x8d, 0x1a, 0xd0, 0xc8, 0xf0, 0x0d, 0xad, 0x0f, 0xd2, 0x29, 0x10, 0xc5, 0x30,
    0xf0, 0xf4, 0x85, 0x30, 0x38, 0x60, 0x18, 0x60, 0x00, 0x60, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x53, 0x20, 0x53, 0x55, 0x50, 0x45, 0x52, 0x42, 0x4c, 0x4f, 0x43,
    0x4b, 0x9b, 0xa2, 0x7c, 0xa0, 0x0a, 0x20, 0x42, 0xc6, 0xa9, 0x00, 0x8d, 0x44, 0x02, 0xa9, 0x01, 0x85, 0x09, 0x60, 0x9b,
    0x02, 0xa9, 0x00, 0x85, 0x32, 0xa9, 0x20, 0x85, 0x33, 0xa9, 0x00, 0x85, 0x34, 0xa9, 0x08, 0x85, 0x35, 0xa0, 0x00, 0xae,
    0x9c, 0x22, 0xf0, 0x0e, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xd0, 0xf9, 0xe6, 0x33, 0xe6, 0x35, 0xca, 0xd0, 0xf2, 0xae, 0x9b,
    0x22, 0xf0, 0x08, 0xb1, 0x32, 0x91, 0x34, 0xc8, 0xca, 0xd0, 0xf8, 0x4c, 0x00, 0x08

    };

    public void addInstructionsForChunk(InstructionStream is, TapeImageChunk chunk) throws Exception {

        /*trXX chunk*/
        if (chunk instanceof TurboChunk) {
            int bdr = ((TurboChunk)(chunk)).getBaudRate();

            /*Upkeep baud rate range*/
            if (bdr<2270) bdr=2270;
            if (bdr>9600) bdr=9600;

            /*Choose closest known pulses*/
            PulseCrate speed = Utils.getNearestBaudRate(bdr, Turgen.pulseCache.getPulsesForPlugin(this));

            /*Generate setup instruction*/
            Utils.addSetupInstruction(is, speed, chunk.getType(),SignalGenerator.FLAG_POLARITY_10,SignalGenerator.FLAG_ORDER_HL);

            return;
        }

        /*Only data chunk accepted*/
        if (!(chunk instanceof DataChunk)) return;
        
        DataChunk dataChunk = (DataChunk)chunk;
        addBlock(is, dataChunk.getData(), -1, true, dataChunk.getAux());
       
        
    }

     public BinaryConvertor[] getBinaryConvertors() {
        BinaryConvertor[] bc = new BinaryConvertor[3];
        MonolithicBinaryConvertor mbc = new MonolithicBinaryConvertor(true,this);
        ChainloadingBinaryConvertor cbc = new ChainloadingBinaryConvertor(true, 1803, 2205,this);
        BlockloadingBinaryConvertor bbc = new BlockloadingBinaryConvertor(true, 2048, 2682,this);
        bc[0]=mbc;
        bc[1]=cbc;
        bc[2]=bbc;
        return bc;
    }

     public String getCountry() {
        return "CS";
    }

    public boolean isUsingAlternateFileChooser() {
        return false;
    }
    
    
}
