// https://github.com/horsicq/Detect-It-Easy signature file
// Authors:
// Kaens TG@kaens (for all the https://en.wikipedia.org/wiki/Music_tracker stuff)
// hypn0 <hypn0@mail.ru>

init("audio","");

includeScript("read");

function detect(bShowType,bShowVersion,bShowOptions) {

    if(Binary.compare("'[1tracker module]'0D0A")) {
        bDetected = 1;
        sName = "Shiru's 1tracker module (.1TM)";
        l = File.findString(1,0x40,"Engine="); if(l>=0) {
            r = File.findString(l+7,0x40,".");
            sVersion = "for "+File.read_ansiString(l+7,r-l-7)
        }
        if(Binary.isVerbose()) {
            l=File.findString(r,0x80,"Title="); if(l>=0) {
                r = File.findSignature(l+6,0x40,"0D0A");
                sOption(File.read_ansiString(l+6,r-l-6)) }
            l=File.findString(r,0x80,"Author="); if(l>=0) {
                r = File.findSignature(l+7,0x40,"0D0A");
                sOption(File.read_ansiString(l+7,r-l-7),"by: ") }
            l=File.findString(r,0x80,"Speed="); if(l>=0) {
                r = File.findSignature(l+7,0x40,"0D0A");
                sOption(File.read_ansiString(l+7,r-l-7),"spd:") }
        }
    }
    else if( (Binary.compare("'if'") || Binary.compare("'JN'")) && (Binary.getSize() > 0x1F1+0x600)
      && (File.read_uint8(0x6E) <= 0x40) && (File.read_uint8(0x6F) <= 0x80) ) {
        bDetected = 1; smp = File.read_uint8(0x6E);
        sName = "Composer 669 / UNIS 669 module (.669)";
        c = 0; for(i = 0; i < smp; i++) c += File.read_uint32(0x01F1+i*0x19+13,_LE);
        c += 0x1F1+(smp*0x19)+smp*0x600;
        if (Binary.isVerbose()) {
            if(File.read_ansiString(0,2)=="JN") sVersion = "extended";
            //if(Binary.getSize() != c) sVersion = sVersion.append("malformed");
            sOptionT(File.read_codePageString(0x02,36,"CP850")); //the seemingly most-used encoding for'em
            sOptionT(File.read_codePageString(0x26,36,"CP850"));
            sOptionT(File.read_codePageString(0x4A,36,"CP850"));
            sOption("ptn:"+File.read_uint8(0x6F)+" smp:"+smp)
        }
    }
    else if(Binary.compare("'_A2module'")) {
        bDetected = 1;
        sName = "AdLib Tracker II module (.A2M)";
        sVersion = "v"+File.read_uint8(0x0E);
        sOption("/┤DLiB TR/┤CK3R ][");
    }
    else if(Binary.compare("'AERO'")) {
        bDetected = 1;
        sName = "Aero Tracker module (.AERO)";
        sVersion = "v"+File.read_uint8(0x0E);
    }
    else if(Binary.compare("'THX'01")) {
        bDetected = 1;
        sName = "Abyss' Highest eXperience module (.AHX)";
        var ptitle = File.read_uint16(4,_BE);
        if (Binary.isVerbose())
            sOption(File.read_ansiString(ptitle,0x20));
    }
    else if(Binary.compare("'AM01'")) {
        bDetected = 1;
        sName = "Ace Tracker module (.AM)";
    }
    else if(Binary.compare("'AMC V'......' REPLAY!'")) {
        bDetected = 1;
        sName = "A.M. Composer module (.AMC)";
        sVersion = "v"+getString(5,3);
    }
    else if(Binary.compare("'AMX ")) {
        bDetected = 1;
        sName = "AND Xynth module (.AMX)";
    }
    else if(Binary.compare("'ASYLUM Music Format'")) {
        bDetected = 1;
        sName = "ASYLUM Music Format (.AMF)";
        sVersion = "v"+getString(0x15,3);
    }
    else if(Binary.compare("'AMF'0.")) {
        //some say this format is identical to ASYLUM. (X) --Kae
        bDetected = 1;
        sName = "Digital Sound and Music Interface Advanced Music Format module (.AMF)";
        sVersion = "v0"+Hex(File.read_uint8(3));
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(4,32));
    }
    else if(Binary.compare("'AMShdr'1A") && (File.read_uint8(7)<=30)) {
        //ref http://modland.com/pub/documents/format_documentation/Velvet%20Studio%20v2.2,%20Advanced%20Module%20System%20(.ams).txt
        bDetected = 1; t_ = File.read_uint8(7);
        sName = "Velvet Studio Advanced Module System tune (.AMS)";
        sVersion = "v"+File.read_uint8(9+t_)+"."+File.read_uint8(8+t_);
        malformed = false;
        ptn = File.read_uint16(8+t_+3); if(!ptn || ptn>1024) malformed=true;
        ord = File.read_uint16(8+t_+5); if(!ord) malformed=true;
        ch0 = File.read_uint8(8+t_+10); //if(!ch0 || ch0>32) malformed=true;
        cmd = File.read_uint8(8+t_+11); //if(!cmd || cmd>7) malformed=true;
        if(malformed) sVersion+=" malformed";
        if (Binary.isVerbose()) {
            ins = File.read_uint8(8+t_+2);
            bpm0 = File.read_uint8(8+t_+8)+"."+File.read_uint8(8+t_+7);
            spd0 = File.read_uint8(8+t_+9);
            rows = File.read_uint8(8+t_+12);
            flg =  File.read_uint16(8+t_+13);
            stereo = (flg>>6)&1?"stereo":"mono";
            linfreqtbl = (flg>>7)&1?" linfreqtbl":"";
            midiused = (flg>>8)&1?" midi used":"";
            sOptionT(File.read_codePageString(8,t_,"CP850"));
            sOption(stereo+" ins:"+ins+" ptn:"+ptn+" ord:"+ord+
                " bpm0:"+bpm0+" spd0:"+spd0+" ch0:"+ch0+" cmd0:"+cmd+" rows0:"+rows+
                linfreqtbl+midiused)
        }
    }
    else if(Binary.compare("'Extreme0'01")) {
        //ref http://modland.com/pub/documents/format_documentation/Extreme's%20Tracker%20v1.3%20(.ams).txt
        bDetected = 1;
        sName = "Extreme Tracker module (.AMS)";
        //song tech info
        sVersion = "v"+File.read_uint8(7)+"."+File.read_uint8(8);
        if (Binary.isVerbose()) {
            cccsssss = File.read_uint8(9);  cmd=cccsssss>>5; sch=cccsssss&0x1F+1;
            smp = File.read_uint8(10); ptn = File.read_uint16(11,_LE); pos = File.read_uint16(13,_LE);
            vmch = File.read_uint8(15); xtra = File.read_uint16(16,_LE); 
            //title
            pts = smp*17 + xtra + 18; ts = File.read_uint8(pts);
            sOptionT(File.read_ansiString(pts+1,ts));
            sOption("cmd:"+cmd+" strk:"+sch+" smp:"+smp
              +" ptn:"+ptn+" pos:"+pos+" mtrk:"+(vmch+1))
        }
    }
    else if(Binary.compare("'ACTIONAMICS SOUND TOOL'",0x3E)) {
        bDetected = 1;
        sName = "Actionamics Sound Tool module (.AST)";
        sVersion = "v"+File.read_ansiString(0x56,3);
    }
    else if(Binary.compare("08'AST '")) {
        bDetected = 1;
        sName = "All Sound Tracker module (.AST)";
        sVersion = "v" + File.read_ansiString(5,4);
        if (Binary.isVerbose()) {
            L = File.read_uint16(0x0A,_BE);
            info = File.read_codePageString(12,L+1,"CP850").trim();
            var s = ""; var q=0;
            for (var i = 0; q<2; i+=0x26) {
                a = info.slice(i,i+0x26);
                if (a.trim() != "") {q++; s += " "+a.trim();} //skipping the empty lines
            }
            sOption(s);
        }
    }
    else if(Binary.compare("'ADRVPACK'")) {
        bDetected = 1;
        sName = "AProSys module (.APS)";
    }
    else if(Binary.compare("'AON4'") || Binary.compare("'AON8'")) {
        bDetected = 1;
        sName = "ArtOfNoise/Chorus module (.AON)";
        sVersion = File.read_ansiString(3,1)+"ch";
        if (Binary.isVerbose()) {
            id = File.read_ansiString(4,0x2A);
            p = 0x2E; //header skipped
            t=''; a=''; d=''; c=''; ord=0;
            while (p<File.getSize()) {
                hkhd = File.read_ansiString(p,4);
                hksz = File.read_uint32(p+4,_BE);
                p+=0x08; //chunk header = 4xfourcc + 4xsize
                switch (hkhd) {
                    case "NAME": t = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "AUTH": a = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "DATE": d = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "RMRK": c = File.read_codePageString(p,hksz,"CP1252"); break; //apparently intended to be 78ch per line, maybe TODO auto-wrap (without left trimming!)
                    case "PLST": ord = hksz; break;
                }
                p += hksz;
            }
            sOptionT(t); sOptionT(a,"by: "); sOptionT(d,"on: ");
            sOption(c); sOption(id,"with: "); sOption(ord,"ord:");
        }
    }
    else if(Binary.compare("'ARP.'")) {
        bDetected = 1;
        sName = "Arpeggiator module (.ARP)";
    }
    else if(Binary.findSignature(0,0x80,"'ASM COMPILATION OF '")>0) {
        bDetected = 1;
        sName = "ASC Sound Master module (.ASC)";
        if(Binary.isVerbose()) {
            pt=Binary.findSignature(0,0x80,"'ASM COMPILATION OF '")+0x13;
            pa=Binary.findSignature(pt+0x13,0x20,"' BY '");
            t = File.read_ansiString(pt,pa-pt); a=File.read_ansiString(pa+4,0x12);
            sOptionT(t); sOptionT(a,"by: ")
        }
    }
    else if(Binary.compare("'##synth'0D0A")) {
        bDetected = 1;
        sName = "Athtune module (.ATHTUNE)";
    }
    else if(Binary.compare("'ZXAYAMAD'")) {
        //the format seems similar to .EMUL
        bDetected = 1;
        sName = "AY Amadeus module (.AMAD)";
        if (Binary.isVerbose()) {
            ptitle = 0x14 + File.read_uint16(0x14,_LE);
            sOptionT(File.read_ansiString(ptitle))
        }
    }
    else if(Binary.compare("'AudioSculpture'")) {
        bDetected = 1;
        sName = "Audio Sculpture module (.AS)";
    }
    else if(Binary.compare("'.snd'00")
      && (isHeuristicScan() || extIs("au"))) {
        bDetected = 1;
        sName = "NeXT/Sun audio (.AU)";
    }
    else if(Binary.compare("'FORM'.... ....'AXSFUSER'")) {
        bDetected = 1;
        sName = "AXS module (.AXS)";
    }
    else if(Binary.compare("'BBSONG'00")) {
        bDetected = 1;
        sName = "Beepola module (.BBSONG)";
        sVersion = "v"+File.read_ansiString(7,5)+":"+File.read_ansiString(Binary.findString(10,1000,"Engine=")+7);
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x18,100); 
            sOptionT(title);
            auth = File.read_ansiString(0x18+title.length+8,100); 
            sOptionT(auth,"by: ");
        }
    }
    else if(Binary.isHeuristicScan() && Binary.compare("60...... 60...... ....60")) {
        bDetected = 1;
        sName = "Ben Daglish's module (.BD)";
    }
    else if(Binary.compare("'NuDAGLISH!'",0x22)) {
        bDetected = 1;
        sName = "Ben Daglish's SID (.BDS)";
        sVersion = "v"+File.read_ansiString(0x2D,3);
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x5C,0x100);
            auth = File.read_ansiString(0x5C+title.length+1,0x100);
            misc = File.read_ansiString(0x5C+title.length+auth.length+2,0x100);
            sOptionT(title);
            sOptionT(auth,"by: ");
            sOptionT(misc)
        }
    }
    else if(Binary.compare("'Buzz'")) {
        bDetected = 1;
        sName = "Jeskola Buzz module (.BMX)";
        // Do NOT waste time trying to extract anything else...
        // You'll find the format dox lie and formats differ
        // For example, the no. of machines overlaps with some beta's version string
        // For example, that beta has no float x/y coordinates
        // And there is no signature difference. 
    }
    else if(Binary.compare("'buz2'02")) {
        bDetected = 1;
        sName = "Buzzic module (.BUZ2)";
        sVersion = "v2";
    }
    else if(Binary.compare("'BRTF'")) {
        bDetected = 1;
        sName = "BeRoTracker module (.BRT)";
        if (Binary.isVerbose()) //TODO actually read chunks
            if (File.read_ansiString(8,4) == "NAME") {
                tsz = File.read_uint8(0x10);
                sOptionT(File.read_ansiString(0x11,tsz));
                if (File.read_ansiString(0x11+tsz,4)=="MESS") {
                    msz = File.read_uint32(0x11+tsz+8,_LE);
                    sOptionT(Binary .getString(0x11+tsz+12,msz))
                }
            }
    }
    else if(Binary.compare("FFFFFFFF",0x34) && Binary.compare("1027",0x15C)
       && Binary.compare("1027",0x164)) {
        bDetected = 1;
        sName = "BoyScout module (.BSF)";
    }
    else if((Binary.isHeuristicScan() || extIs("bp")) && Binary.compare("'V.2'",0x1A)) {
        bDetected = 1;
        sName = "BP SoundMon module (.BP)";
        sVersion = "v2";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0,0x19));
    }
    else if((Binary.isHeuristicScan() || extIs("bp3")) && Binary.compare("'V.3'",0x1A)) {
        bDetected = 1;
        sName = "BP SoundMon module (.BP3)";
        sVersion = "v3";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0,0x19));
    }
    else if(Binary.compare("'NuBEATHOVEN'",0x22)) {
        bDetected = 1;
        sName = "Beathoven Synthesizer (.BSS)";
        sVersion = "v"+File.read_ansiString(0x2D,3);
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x6C,0x100);
            auth = File.read_ansiString(0x6C+title.length+1,0x100);
            misc = File.read_ansiString(0x6C+title.length+auth.length+2,0x100);
            sOptionT(title);
            sOptionT(auth,"by: ");
            sOptionT(misc);
        }
    }
    else if(Binary.compare("'CBMF'")) {
        bDetected = 1;
        sName = "Bob's AdLib Music (.BAM)";
    }
    else if(Binary.compare("'CBA'F9")) {
        bDetected = 1;
        sName = "CBA module (.CBA)";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(4,0x20))
    }
    else if(Binary.compare("'<CUD-FM-File>'")) {
        bDetected = 1;
        sName = "Boom Tracker module (.CFF)";
    }
    else if(Binary.compare("'CTMF'")) {
        bDetected = 1;
        sName = "Creative Music File (.CMF)";
    }
    else if(Binary.compare("'NuS.PHIPPS'",0x22)) {
        bDetected = 1;
        sName = "Code Design module (.CORE)";
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x68,0x100);
            auth = File.read_ansiString(0x68+title.length+1,0x100);
            misc = File.read_ansiString(0x68+title.length+auth.length+2,0x100);
            sOptionT(title);
            sOptionT(auth,"by: ");
            sOptionT(misc);
        }
    }
    else if(Binary.compare("'CAT '................'FORM'")) {
        bDetected = 1;
        sName = "Cybertracker module (.CT)";
        sVersion = "v1";
    }
    else if(Binary.compare("0004'NNTRKMZX'")) {
        bDetected = 1;
        sName = "Cybertracker module (.CT)";
        sVersion = "v2";
    }
    else if(Binary.compare("000003F3") && (Binary.findString(0,0x100,"NuDELIRIUM")>0)) {
        bDetected = 1;
        sName = "DeliTracker DELIX Custom module (.CUST)";
        sVersion = "CustomPlay";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(Binary.findString(0,0x100,"NuDELIRIUM")+20,0x100))
    }

    else if(Binary.compare("'JCH'26026601")) {
        bDetected = 1; sName = "Edlib Tracker module samples (.S01)";
    } // the order is important
    else if(Binary.compare("'JCH'260266")) {
        //ref http://ftp.modland.com/pub/documents/format_documentation/Edlib%20Tracker%20(.edl,%20.d00,%20.d01).txt
        bDetected = 1; malformed = false;
        sName = "Edlib Tracker module (.D00,.D01)";
        sVersion = "v"+File.read_uint8(7);
        if(File.read_uint8(6)==4) malformed = true;
        if(File.read_uint8(10)>0) malformed = true;
        if(malformed) sVersion += "/malformed";
        if(Binary.isVerbose()) {
            sOptionT(File.read_codePageString(0x0B,0x20,"CP850"));
            sOptionT(File.read_codePageString(0x2B,0x20,"CP850"),"by: ");
            p = File.read_uint16(0x71,_LE);
            sOptionT(File.read_codePageString(p,File.getSize()-p-2,"CP850"))
        }
    }

    else if(Binary.compare("'DBM0'")) {
        bDetected = 1;
        sName = "Digi Booster Pro module (.DBM)";
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x10,42).trim();
            if(title == "") title = File.read_ansiString(0x56,42).trim();
            if(title == "") title = File.read_ansiString(0xD8,28).trim();
            sOptionT(title)
        }
    }
    else if(Binary.compare("'DFM'1A")) {
        bDetected = 1;
        sName = "Digital FM module (.DFM)";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(7,32))
    }
    else if(Binary.compare("'DSNGSEQU'")) {
        bDetected = 1;
        sName = "David Hanney's module (.DH)";
    }
    else if(Binary.compare("'DIGI Booster module'")) {
        bDetected = 1;
        sName = "DIGI Booster module (.DIGI)";
        sVersion = "v"+File.read_ansiString(0x15,3);
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x262,0x20));
            sOptionT(File.read_ansiString(0x282,0x20),"by: ");
        }
    }
    else if(Binary.compare("'NuUNCLEART'",0x22)) {
        bDetected = 1;
        sName = "Dave Lowe/Uncle Art module (.DL)";
        if (Binary.isVerbose()) {
            title = File.read_ansiString(0x70,0x100);
            auth = File.read_ansiString(0x70+title.length+1,0x100);
            cmt = File.read_ansiString(0x70+title.length+auth.length+2,0x100);
            sOptionT(title);
            sOptionT(auth,"by: ");
            sOptionT(cmt)
        }
    }
    else if(Binary.compare("'ALL '00")) {
        bDetected = 1;
        sName = "Delta Music module (.DM)";
    }
    else if(Binary.compare("670003B40C000001670001EC0C",6)
      && (Binary.findString(0x146,181,"DELTA MUSIC") > 0)) {
        bDetected = 1;
        sName = "Delta Music 2 module (.DM2)";
    }
    else if(Binary.compare("'DMF'0E")) {
        bDetected = 1;
        sName = "Delusion Module Format track (.DMF)";
    }
    else if(Binary.compare("'DDMF'") && Binary.compare("'CMSG'", 0x42)) {
        bDetected = 1;
        sName = "X-Tracker module (.DMF)";
        sVersion = "v"+File.read_uint8(4);
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0xD,24));
            sOptionT(File.read_ansiString(5,8),"id: ")
    }
    else if(Binary.isHeuristicScan() && extIs("dmf")) {
        //TODO isDeepScan unpack and look at the signature
        bDetected = 1;
        sName = "DeFleMask module (.DMF)";
    }
    else if(Binary.compare("' MUGICIAN/SOFTEYES 1990 '")) {
        bDetected = 1;
        sName = "Digital Mugician module (.DMU)";
        sVersion = "v1";
        if (Binary.isVerbose())
            for (var i = 0x50; i<0xC0; i+=0x10)
                sOptionT(File.read_ansiString(i,12))
    }
    else if(Binary.compare("' MUGICIAN2/SOFTEYES 1990'")) {
        bDetected = 1;
        sName = "Digital Mugician 2 module (.MUG)";
        sVersion = "v2";
        var c = 0;
        for (var i = 0x50; i<0xC0; i+=0x10) {
            t = File.read_ansiString(i,12).trim();
            if(t!="") {
                c++; sOption(t);
            }
        }
    }
    else if(Binary.compare("'DBRAWOPL'")) {
        bDetected = 1;
        sName = "DOSBox Raw OPL stream (.DRO)";
    }
    else if(Binary.compare("'DSM'10")) {
        bDetected = 1;
        sName = "Digital Audio Sound Interface Kit module (.DSM)";
        sOptionT(File.read_ansiString(4,32));
    }
    else if(Binary.compare("'RIFF'........'DSMFSONG'")) {
        bDetected = 1;
        sName = "Digital Sound Interface Kit module (.DSM)";
        sVersion = "RIFF";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0x14,32));
    }
    else if(Binary.compare("'DSm'1A20")) {
        bDetected = 1;
        sName = "Dynamic Studio Pro module (.DSM)";
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(5,20));
            sOptionT(File.read_ansiString(25,20),"by: ");
        }
    }
    else if(Binary.compare("02647373")) {
        bDetected = 1;
        sName = "Digital Speech Standard audio (.DSS)";
        sVersion = "v2";
    }
    else if(Binary.compare("03647373")) {
        bDetected = 1;
        sName = "Digital Speech Standard audio (.DSS)";
        sVersion = "v3";
    }
    else if(Binary.compare("'DSFmt1'0D0A")) {
        bDetected = 1;
        sName = "DreamStation module (.DSS)";
        sVersion = "v1.0";
        //TODO find out how the text at file end is addressed
    }
    else if(Binary.compare("'DS2F0'....'Default'")) {
        bDetected = 1;
        sName = "DreamStation II module (.DS2)";
        sVersion = "v2";
        if (Binary.isVerbose()) {
            ts=File.read_uint8(0x62); t=File.read_ansiString(0x63,ts);
            pa=0x62+ts+1; as=File.read_uint8(pa); a=File.read_ansiString(pa+1,as);
            pc=pa+as+1;  cs=File.read_uint8(pc); c=File.read_ansiString(pc+1,cs);
            sOption(t);
            sOption(a,"by: ");
            sOption(c);
        }
    }
    else if(Binary.compare("'MMU2'")) {
        bDetected = 1;
        sName = "Digital Sound Studio module (.DSS)";
        if (Binary.isVerbose())
        sOptionT(File.read_ansiString(10,32));
    }
    else if(Binary.compare("'D.T.'00")) {
        sv = ["S.Q.","VERS"].indexOf(File.read_ansiString(0x2A,4));
        if(sv >= 0) {
            bDetected = 1;
            sName = "Digital Tracker module (.DTM)";
            if (Binary.isVerbose()) {
                if(sv==0) sOptionT(File.read_ansiString(0x16,20))
                //let's play along with Bummtschak here
                else sOptionT(File.read_ansiString(0x16,24))
            }
        }
    }
    else if(Binary.compare("'DeFy DTM'")) {
        bDetected = 1;
        sName = "DeFy AdLib Tracker module (.DTM)";
        sVersion = "v"+getString(9,3);
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0xD,20));
            sOptionT(File.read_ansiString(0x21,20));
        }
    }
    else if(Binary.compare("'SONG'........'NAME'")) {
        bDetected = 1;
        sName = "DigiTrekker module (.DTM)";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0x10,20));
    }
    else if( Binary.isHeuristicScan() && Binary.compare("0002",0x0E) && Binary.compare("0002",0x26)
        && Binary.compare("0002",0x32) && Binary.compare("0040",0x46)) {
        bDetected = 1;
        sName = "GT Game Systems module (.DUX)";
    }
    else if(Binary.compare("' PWD'03")) {
        bDetected = 1;
        sName = "DarkWave Studio module (.DWP)";
    }
    else if(Binary.compare("'48E7 00F0 41FA'")) {
        bDetected = 1;
        sName = "Darius Zendeh's music (.DZ)";
    }
    else if(Binary.compare("'EAS0'")) {
        bDetected = 1;
        sName = "EarAche module (.EAS)";
    }
    else if(Binary.compare("'FORM'.... ....'EMODEMIC'")) {
        bDetected = 1;
        sName = "Quadra Composer module (.EMOD)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x16,0x28));
            sOptionT(File.read_ansiString(0x44,0x18),"by: ");
        }
    }
    else if(Binary.compare("'E.M.S. V6.'")) {
        bDetected = 1;
        sName = "Electronic Music System module (.EMS)";
        sVersion = "v6."+File.read_ansiString(10,2)
    }
    else if(Binary.compare("'ZXAYEMUL'")) {
        // ref https://vgmrips.net/wiki/AY_File_Format
        bDetected = 1;
        sName = "AY Emul module (.EMUL)";
        sVersion = "Rel "+File.read_uint8(8)+" Plr "+File.read_uint8(9);
        if (Binary.isVerbose()) {
            psongst = 0x12 + File.read_uint16(0x12,_BE);
            title0 = File.read_ansiString(psongst + File.read_uint16(psongst,_BE));
            auth = File.read_ansiString(0x0C + File.read_uint16(0x0C,_BE));
            misc = File.read_ansiString(0x0E + File.read_uint16(0x0E,_BE));
            more = File.read_uint8(0x10);
            sOptionT(title0);
            if (more > 0) sOption(more+1,"x ");
            sOptionT(auth);
            sOptionT(misc);
        }
    }
    else if(Binary.compare("13FC") && Binary.compare("4E710439",8)) {
        bDetected = 1;
        sName = "Fashion Tracker module (.EX)";
    }
    else if(Binary.compare("'FAR'FE") && Binary.compare("0D0A1A",0x2C)) {
        bDetected = 1; nV = File.read_uint8(0x31);
        sName = "Farandole Composer module (.FAR)";
        sVersion = "v"+ (nV>>4) +"."+ (nV&0x0F);
        if (Binary.isVerbose()) {
            sOptionT(File.read_codePageString(4,0x28,"CP850"));
            sOption(File.read_uint8(0x4B),"spd:");
            msg_ = File.read_uint16(0x60,_LE);
            sOption(File.read_codePageString(0x62,msg_,"CP850").trim().slice(0,0x100).trim()+"...")
        }
    }
    else if(Binary.compare("'FC14'")) {
        bDetected = 1;
        sName="Future Composer module (.FC)";
        sVersion="v1.4";
    }
    else if(Binary.compare("'FUCO'C1")) {
        bDetected = 1;
        sName="Future Composer module (.BSI)";
        sVersion="ver.BSI";
    }
    else if(Binary.compare("'FMTracker'....'The FM Tracker!'")) {
        bDetected = 1;
        sName = "FM Tracker module (.FMT)";
        sVersion = "v"+File.read_uint8(9)+"."+File.read_uint8(10);
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0x1F,0x20));
    }
    else if(Binary.compare("'Funk'") && Binary.compare("FFFFFFFF",0x4D)) {
        bDetected = 1;
        sName = "Funk Tracker module (.FNK)";
        if(File.read_uint8(0x0C)==0x46)
            sVersion = "v"+File.read_ansiString(0x0D,1)+"."+File.read_ansiString(0x0E,2);
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0x191,0x13));
    }
    else if(Binary.compare("'NuF.PLAYER'",0x22)) {
        bDetected = 1;
        sName = "Future Player module (.FP)";
    }
    else if(Binary.compare("'Module: '") && Binary.compare("';Fast Tracker v1.00'", 0x32)) {
        bDetected = 1;
        sName = "Fast Tracker module (.FTС)";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(8,0x2A));
    }
    else if(Binary.compare("'FTMN'03")) {
        bDetected = 1;
        sName = "Face the Music module (.FTM)";
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x10,0x20));
            sOptionT(File.read_ansiString(0x30,0x20));
        }
    }
    else if(Binary.compare("'FamiTracker Module'")) {
        bDetected = 1;
        sName = "FamiTracker module (.FTM)";
        if (Binary.isVerbose()) {
            itag = Binary.findString(0x14,0x100,"INFO\x00");
            sOptionT(File.read_ansiString(itag+0x18,0x20));
            sOptionT(File.read_ansiString(itag+0x38,0x20),"by: ");
            sOptionT(File.read_ansiString(itag+0x58,0x20))
        }
    }
    else if(Binary.isHeuristicScan() && extIs("fts")) {
        bDetected = 1;
        sName = "Flash Tracker module (.FTS)"
    }
    else if(Binary.compare("'-Furnace '")) { //TODO also check in zlib files
        // from https://github.com/tildearrow/furnace/blob/master/papers/format.md
        switch(File.read_ansiString(9,7)) {
            case 'module-':
                cV = 'm'; bDetected = 1; sName = "Furnace Tracker module (.FUR)"; break;
            case 'instr.-':
                cV = 'i'; bDetected = 1; sName = "Furnace Tracker instrument (.FUI)"; break;
            case 'waveta-':            
                cV = 'w'; bDetected = 1; sName = "Furnace Tracker wavetable (.FUW)"; break
            default:
                cv = "?"; bDetected = 1; sName = "unknown Furnace Tracker file"
        }
        nV = File.read_uint16(0x10,_LE);
        //ref https://github.com/tildearrow/furnace/blob/master/papers/format.md
        if (nV<12) sVersion = "["+nV+"]"; else
        if (nV<=14) sVersion = "v0.2.X"; else
        if (nV<=16) sVersion = "v0.3.X"; else
        if (nV<=27) sVersion = "v0.4.X"; else
        if (nV<35) sVersion = "["+nV+"]"; else
        if (nV<=54) sVersion = "v0.5.X"; else
        if (nV<57) sVersion = "["+nV+"]"; else
        if (nV==75) sVersion = "v.dev75/April Fools' 0.6pre0"; else
        if (nV<=99) sVersion = "v.dev"+nV; else
        if (nV==100) sVersion = "v0.6pre1"; else
        if (nV==101) sVersion = "v0.6pre1 (dev101)"; else
        if (nV==102) sVersion = "v0.6pre1 (dev102)"; else
        if (nV<=115) sVersion = "v.dev"+nV; else
        if (nV==116) sVersion = "v.0.6pre1.5"; else
        if (nV<=131) sVersion = "v.dev"+nV; else
        if (nV==132) sVersion = "v.0.6pre2"; else
        if (nV==133) sVersion = "v.0.6pre3";
        else sVersion = "["+nV+"]";
        if(cV==='i') {
            p = File.read_uint32(0x14,_LE);
            if (p>0 && Binary.compare("'INST'",p)) {
                ity = File.read_uint16(0x10,_LE);
                switch(ity) {
                    case 0: sVersion+="/std"; break;
                    case 1: sVersion+="/FM (OPM/OPN)"; break;
                    case 2: sVersion+="/Game Boy"; break;
                    case 3: sVersion+="/C64"; break;
                    case 4: sVersion+="/Amiga|smp"; break;
                    case 5: sVersion+="/PC Engine"; break;
                    case 6: sVersion+="/AY-3-8910"; break;
                    case 7: sVersion+="/AY8930"; break;
                    case 8: sVersion+="/TIA"; break;
                    case 9: sVersion+="/SAA1099"; break;
                    case 10: sVersion+="/VIC"; break;
                    case 11: sVersion+="/PET"; break;
                    case 12: sVersion+="/VRC6"; break;
                    case 13: sVersion+="/OPLL"; break;
                    case 14: sVersion+="/OPL"; break;
                    case 15: sVersion+="/FDS"; break;
                    case 16: sVersion+="/Vritual Boy"; break;
                    case 17: sVersion+="/Namco 163"; break;
                    case 18: sVersion+="/SCC"; break;
                    case 19: sVersion+="/OPZ"; break;
                    case 20: sVersion+="/POKEY"; break;
                    case 21: sVersion+="/PC Speaker"; break;
                    case 22: sVersion+="/WonderSwan"; break;
                    case 23: sVersion+="/Lynx"; break;
                    case 24: sVersion+="/VERA"; break;
                    case 25: sVersion+="/X1-010"; break;
                    case 26: sVersion+="/VRC6(saw)"; break;
                    case 27: sVersion+="/ESS5506"; break;
                    case 28: sVersion+="/MultiPCM"; break;
                    case 29: sVersion+="/SNES"; break;
                    case 30: sVersion+="/Sound Unit"; break;
                    case 31: sVersion+="/Namco WSG"; break;
                    default: sVersion+= "/unk"
                }
                if(Binary.isVerbose()) {
                    wvt = File.read_uint16(p+0x18,_LE);
                    smp = File.read_uint16(p+0x1A,_LE);
                    sOptionT(File.read_utf8String(p+0x0C,0x200));
                    sOption(" wvt:"+wvt+" smp:"+smp);
                }
            } //verbose inst
        } //inst
        if(cV==='m' && Binary.isVerbose()) {
            p = File.read_uint32(0x14,_LE);
            if(p>0 && Binary.compare("'INFO'",p)) {
                t = File.read_utf8String(p+0x100,0x200);
                p1 = File.findSignature(p+0x100,0x200,"00")+1;
                a = File.read_utf8String(p1,0x200);
                p1 = File.findSignature(p1,0x200,"00")+1;
                ins = File.read_uint16(p+0x16,_LE);
                wvt = File.read_uint16(p+0x18,_LE);
                smp = File.read_uint16(p+0x1A,_LE);
                ptng = File.read_uint32(p+0x1C,_LE);
                freq = File.read_float(p+0x0C,_LE);
                A4freq = File.read_float(p1,_LE);
                s1spd = File.read_uint8(p+9)+"/"+File.read_uint8(p+0x0A);
                s1ptn = File.read_uint16(p+0x10);
                s1ord = File.read_uint16(p+0x12);
                sOptionT(t);
                sOptionT(a,"by: ");
                chips=[]; el=false; i=0;
                while(!el && (i<32)) {
                    switch(File.read_uint8(p+0x20+i)) {
                    case 0: el=true; break;
                    case 0x01: chips[i] = "YMU759 (17ch)"; break;
                    case 0x02: chips[i] = "Genesis (10ch comp.)"; break;
                    case 0x03: chips[i] = "SMS (SN76489) (4ch)"; break;
                    case 0x04: chips[i] = "Game Boy (4ch)"; break;
                    case 0x05: chips[i] = "PC Engine (6ch)"; break;
                    case 0x06: chips[i] = "NES (5ch)"; break;
                    case 0x07: chips[i] = "C64 (8580) (3ch)"; break;
                    case 0x08: chips[i] = "Arcade (YM2151+SegaPCM) (13ch)"; break;
                    case 0x09: chips[i] = "Neo Geo CD (YM2610) (13ch)"; break;
                    case 0x42: chips[i] = "Genesis extended (13ch)"; break;
                    case 0x43: chips[i] = "SMS (SN76489) + OPLL (YM2413) (13ch comp.)"; break;
                    case 0x46: chips[i] = "NES+VRC7 (11ch)"; break;
                    case 0x47: chips[i] = "C64 (6581) (3ch)"; break;
                    case 0x49: chips[i] = "Neo Geo CD extended (16ch)"; break;
                    case 0x80: chips[i] = "AY-3-8910 (3ch)"; break;
                    case 0x81: chips[i] = "Amiga (4ch)"; break;
                    case 0x82: chips[i] = "YM2151 (8ch)"; break;
                    case 0x83: chips[i] = "YM2612 (6ch)"; break;
                    case 0x84: chips[i] = "TIA (2ch)"; break;
                    case 0x85: chips[i] = "VIC-20 (4ch)"; break;
                    case 0x86: chips[i] = "PET (1ch)"; break;
                    case 0x87: chips[i] = "SNES (8ch)"; break;
                    case 0x88: chips[i] = "VRC6 (3ch)"; break;
                    case 0x89: chips[i] = "OPLL (YM2413) (9ch)"; break;
                    case 0x8A: chips[i] = "FDS (1ch)"; break;
                    case 0x8B: chips[i] = "MMC5 (3ch)"; break;
                    case 0x8C: chips[i] = "Namco 163 (8ch)"; break;
                    case 0x8D: chips[i] = "YM2203 (6ch)"; break;
                    case 0x8E: chips[i] = "YM2608 (16ch)"; break;
                    case 0x8F: chips[i] = "OPL (YM3526) (9ch)"; break;
                    case 0x90: chips[i] = "OPL2 (YM3812) (9ch)"; break;
                    case 0x91: chips[i] = "OPL3 (YMF262) (18ch)"; break;
                    case 0x92: chips[i] = "MultiPCM (28ch)"; break;
                    case 0x93: chips[i] = "Intel 8253 (beeper) (1ch)"; break;
                    case 0x94: chips[i] = "POKEY (4ch)"; break;
                    case 0x95: chips[i] = "RF5C68 (8ch)"; break;
                    case 0x96: chips[i] = "WonderSwan (4ch)"; break;
                    case 0x97: chips[i] = "Philips SAA1099 (6ch)"; break;
                    case 0x98: chips[i] = "OPZ (YM2414) (8ch)"; break;
                    case 0x99: chips[i] = "Pokémon Mini (1ch)"; break;
                    case 0x9A: chips[i] = "AY8930 (3ch)"; break;
                    case 0x9B: chips[i] = "SegaPCM (16ch)"; break;
                    case 0x9C: chips[i] = "Virtual Boy (6ch)"; break;
                    case 0x9D: chips[i] = "VRC7 (6ch)"; break;
                    case 0x9E: chips[i] = "YM2610B (16ch)"; break;
                    case 0x9F: chips[i] = "ZX Spectrum (beeper) (6ch)"; break;
                    case 0xA0: chips[i] = "YM2612 extended (9ch)"; break;
                    case 0xA1: chips[i] = "Konami SCC (5ch)"; break;
                    case 0xA2: chips[i] = "OPL drums (YM3526) (11ch)"; break;
                    case 0xA3: chips[i] = "OPL2 drums (YM3812) (11ch)"; break;
                    case 0xA4: chips[i] = "OPL3 drums (YMF262) (20ch)"; break;
                    case 0xA5: chips[i] = "Neo Geo (YM2610) (14ch)"; break;
                    case 0xA6: chips[i] = "Neo Geo extended (YM2610) (17ch)"; break;
                    case 0xA7: chips[i] = "OPLL drums (YM2413) (11ch)"; break;
                    case 0xA8: chips[i] = "Atari Lynx (4ch)"; break;
                    case 0xA9: chips[i] = "SegaPCM (DefleMask compat.) (5ch)"; break;
                    case 0xAA: chips[i] = "MSM6295 (4ch)"; break;
                    case 0xAB: chips[i] = "MSM6258 (1ch)"; break;
                    case 0xAC: chips[i] = "Commander X16 (VERA) (17ch)"; break;
                    case 0xAD: chips[i] = "Bubble System WSG (2ch)"; break;
                    case 0xAE: chips[i] = "OPL4 (YMF278B) (42ch)"; break;
                    case 0xAF: chips[i] = "OPL4 drums (YMF278B) (44ch)"; break;
                    case 0xB0: chips[i] = "Seta/Allumer X1-010 (16ch)"; break;
                    case 0xB1: chips[i] = "Ensoniq ES5506 (32ch)"; break;
                    case 0xB2: chips[i] = "Yamaha Y8950 (10ch)"; break;
                    case 0xB3: chips[i] = "Yamaha Y8950 drums (12ch)"; break;
                    case 0xB4: chips[i] = "Konami SCC+ (5ch)"; break;
                    case 0xB5: chips[i] = "tildearrow Sound Unit (8ch)"; break;
                    case 0xB6: chips[i] = "YM2203 extended (9ch)"; break;
                    case 0xB7: chips[i] = "YM2608 extended (19ch)"; break;
                    case 0xB8: chips[i] = "YMZ280B (8ch)"; break;
                    case 0xB9: chips[i] = "Namco WSG (3ch)"; break;
                    case 0xBA: chips[i] = "Namco 15xx (8ch)"; break;
                    case 0xBB: chips[i] = "Namco CUS30 (8ch)"; break;
                    case 0xBC: chips[i] = "MSM5232 (8ch)"; break;
                    case 0xBD: chips[i] = "YM2612 extra features extended (11ch)"; break;
                    case 0xBE: chips[i] = "YM2612 extra features (7ch)"; break;
                    case 0xBF: chips[i] = "T6W28 (4ch)"; break;
                    case 0xC0: chips[i] = "PCM DAC (1ch)"; break;
                    case 0xC1: chips[i] = "YM2612 CSM (10ch)"; break;
                    case 0xC2: chips[i] = "Neo Geo CSM (YM2610) (18ch)"; break;
                    case 0xC3: chips[i] = "YM2203 CSM (10ch)"; break;
                    case 0xC4: chips[i] = "YM2608 CSM (20ch)"; break;
                    case 0xC5: chips[i] = "YM2610B CSM (20ch)"; break;
                    case 0xC6: chips[i] = "K007232 (2ch)"; break;
                    case 0xC7: chips[i] = "GA20 (4ch)"; break;
                    case 0xDE: chips[i] = "YM2610B extended (19ch)"; break;
                    case 0xE0: chips[i] = "QSound (19ch)"; break;
                    case 0xFC: chips[i] = "Pong (1ch)"; break;
                    case 0xFD: chips[i] = "Dummy System (8ch)"; break;
                    case 0xFE: case 0xFF: chips[i] = "reserved for development"; break;
                    default: chips[i]="unk.";
                    }
                    if(!el) i++;
                }
                sOption("chips: "+chips.join("+"));
                sOption("ticks="+freq+"Hz, A4="+A4freq+"Hz");
                sOption("ins:"+ins+" wvt:"+wvt+" smp:"+smp+" glob.ptn:"+ptng);
                sOption("1st song's spd:"+s1spd+" ptn:"+s1ptn+" ord:"+s1ord);
            } //verbose mod
        } //mod
    }
    else if(Binary.compare("'FWMP'00")) {
        bDetected = 1;
        sName = "Forgotten Worlds BGM module (.FW)";
    }
    else if(Binary.compare("'FXSM'")) {
        bDetected = 1;
        sName = "Fuxoft AY Language module (.FXM)";
    }
    else if(Binary.compare("'GBRF'")) {
        bDetected = 1;
        sName = "Gameboy Ripped Format module (.GBR)";
        t = File.read_ansiString(0x154,0x13);
        if(t != "") if(/^([a-zA-Z0-9_ -]{4,})/.test(t)) sOptions = sOptions.append(t);
    }
    else if(Binary.compare("'GBS'01")) {
        bDetected = 1;
        sName = "Gameboy Sound System module (.GBS)";
        if (Binary.isVerbose())
            sOptionT(File.read_ansiString(0x10,0x20));
        tc = File.read_uint8(4); if (tc>0) sOption("x "+tc);
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x30,0x20),"by: ");
            sOptionT(File.read_ansiString(0x50,0x20));
        }
    }
    else if((Binary.isHeuristicScan() || extIs("gdm"))
      && Binary.compare("4EFA....4EFA....4EFA....4EFA")) {
        bDetected = 1;
        sName = "FredMon module (.GDM)";
        if (Binary.isVerbose()) sOptionT(File.read_ansiString(4,0x20));
    }
    else if(Binary.compare("'GDM'FE") && Binary.compare("0D0A1A'GMFS'",0x44)) {
        //ref http://modland.com/pub/documents/format_documentation/General%20Digital%20Music%20(.gdm).txt
        bDetected = 1;
        sName = "General Digital Music module (.GDM)";
        trkr = "";
        if (File.read_uint16(0x4D,_BE)==0) trkr = "2gdm";
        sVersion = "v"+ File.read_uint8(0x4B) +"."+ File.read_uint8(0x4C);
        if (trkr!="") sVersion+="/"+trkr+" v."+File.read_uint8(0x4F)+"."+File.read_uint8(0x50);
        if (Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x04,0x20));
            sOptionT(File.read_ansiString(0x24,0x20),"by: ");
            tof = File.read_uint16(0x74,_BE);
            switch(tof) {
                case 1: sOptionT("orig: MOD"); break;
                case 2: sOptionT("orig: MTM"); break;
                case 3: sOptionT("orig: S3M"); break;
                case 4: sOptionT("orig: 669"); break;
                case 5: sOptionT("orig: FAR"); break;
                case 6: sOptionT("orig: ULT"); break;
                case 7: sOptionT("orig: STM"); break;
                case 8: sOptionT("orig: MED")
            }
            sOptionT(File.read_ansiString(File.read_uint32(0x8A,_BE),File.read_uint32(0x8D,_BE)))
        }
    }
    else if(Binary.compare("'GLUE'B8B3AABA")) {
        bDetected = 1;
        sName = "GlueMon module (.GLUE)";
        if (Binary.isVerbose())
            sOption(File.read_ansiString(8,8));
    }
    else if(Binary.compare("'NuFREDGRAY'",0x22)) {
        bDetected = 1;
        sName = "Fred Gray's module (.GRAY)";
        if (Binary.isVerbose()) {
            t = File.read_ansiString(0x50,0x100); pn = 0x50+t.length+1;
            a = File.read_ansiString(pn,0x100); pn += a.length+1;
            c = File.read_ansiString(pn,0x100);
            sOptionT(t); sOptionT(a,"by: "); sOptionT(c)
        }
    }
    else if(Binary.compare("'+SNT'")) {
        bDetected = 1;
        sName = "Beaver Sweeper module (.GTK)";
    }
    else if(/GT[2K]/.test(File.read_ansiString(0,3))) {
        //GTK from http://ldesoras.free.fr/src/gt2/dev_gtk.zip / formats.txt
        //GT2 from mod_gt2.cpp
        //+ input from the author Laurent "Dumbo" de Soras (http://ldesoras.free.fr)
        bDetected = 1;
        sigv = 0;
        switch(File.read_uint8(2)){
            case 0x4B: sName = "Graoumf Tracker module (.GTK)"; sVersion="Amiga "; sigv=1; break;
            case 0x32: sName = "Graoumf Tracker 2 module (.GT2)"; sVersion="PC " ;sigv=2;
        }
        v = File.read_uint8(3); sVersion += "v"+v;
        if (v<6) switch(v){
            case 1: sVersion+="/GT v0.7"; break;
            case 2: sVersion+="/GT v0.726"; break;
            case 3: sVersion+="/GT v0.731";break;
            //lost in time
        }
        else {
             if(v==9) sVersion+="/GT r27";
        }

        if(Binary.isVerbose() && Binary.isDeepScan()) {
            hksz = File.read_uint32(4,_BE); pn=hksz;
        }
        if (Binary.isVerbose()) {
            if (sigv==1) { //v1
                t = File.read_ansiString(4,0x20).trim(); d = xc = "";
                if(Binary.isDeepScan()){
                    c = File.read_ansiString(0x24,0xA0).trim(); //the author says he never implemented this
                    ins = File.read_uint16(0xC4,_BE); lpp = File.read_uint16(0xC6,_BE);
                    songlen = File.read_uint16(0xC8,_BE);
                    statln = "ins:"+File.read_uint16(0xC4,_BE)+" lpp:"+File.read_uint16(0xC6,_BE)+
                      " len:"+Hex(File.read_uint16(0xCA,_BE))+" trk:"+File.read_uint16(0xC8,_BE);
                }
            }
            else { //v2
                t = File.read_ansiString(8,0x20).trim();
                d = File.read_uint16(0xCA,_BE)+"-"+File.read_uint8(0xC9)+"-"+File.read_uint8(0xC8);
                sVersion += "/"+File.read_ansiString(0xCC,0x18).trim();
                if(Binary.isDeepScan()) {
                    c = File.read_ansiString(0x28,0xA0).trim();
                    if(v<6) statln = "spd:"+File.read_uint16(0xE4,_BE)+" bpm:"+File.read_uint16(0xE6,_BE);
                    else statln = "";
                    xc = [];
                    for(i=0;i<10;i++) {
                        //traverse some hunks... for no good reason, the author never implemented XCOM
                        //but forks exist
                        if (pn>Binary.getSize()) break;
                        hkhd = File.read_ansiString(pn,4); hksz = File.read_uint32(pn+4,_BE);
                        //statln+=" "+hkhd;
                        switch (hkhd) {
                          case "XCOM": xcc = File.read_uint16(pn+8,_BE);
                            xc = xc.push(File.read_ansiString(pn+10,xcc).trim());
                            break;
                        }
                        pn+=hksz;
                    }
                }
            }
            sOption(t);
            sOption(d);
            if(Binary.isDeepScan()) {
                sOption(c);
                sOption(statln); 
                if(xc.length>0) sOptions = sOptions.append(xc,join("\n"));
            }
        }
    }
    else if(Binary.compare("0.'GTR'")) {
        sName = "Global Tracker (.GTR)";
        bDetected = 1; sVersion = "v"+File.read_uint8(0);
        if(Binary.isVerbose()) sOptionT(File.read_ansiString(7,0x20))
    }
    else if(Binary.compare("'GYMX'")) {
        bDetected = 1;
        sName = "Sega Genesis/Mega Drive YM stream (.GYM)";
        e = File.read_ansiString(0x64,0x20).trim();
        if(e != "") sVersion = e;
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x4,0x20));
            sOptionT(File.read_ansiString(0x24,0x20),"for: ");
            sOptionT(File.read_ansiString(0x44,0x20),"at: ");
            sOptionT(File.read_ansiString(0x84,0x20),"by: ");
            sOptionT(File.read_ansiString(0xA4,0x100));
        }
    }
    else if(Binary.compare("'NuH.DAVIES'",0x22)) {
        bDetected = 1;
        sName = "Howie Davies's module (.HD)";
        if(Binary.isVerbose()) {
            t = File.read_ansiString(0x64,0x100); pn = 0x64+t.length+1;
            a = File.read_ansiString(pn,0x100); pn += a.length+1;
            c = File.read_ansiString(pn,0x100);
            sOptionT(t);
            sOptionT(a,"by: ");
            sOptionT(c);
        }
    }
    else if(Binary.compare("'HESM'") && Binary.compare("'DATA'",0x10)) {
        bDetected = 1;
        sName = "HES module (.hes)";
        sVersion = "v"+File.read_uint8(4);
        /* TODO get what's going on! Most files are good but some just have codeseg up there...
        if(Binary.isVerbose()) {
            t = ""; a = ""; c = ""; pn=0x40; dataat = File.read_uint16(6,_BE);
            if (pn<dataat && 31<File.read_uint8(pn)<128) t = File.read_ansiString(pn,0x100);
            if (t.length+1>0x1F) pn=Math.round((pn+t.length)/0x10)*0x10;
            else pn += 0x20;
            if (pn<dataat && 31<File.read_uint8(pn)<128) a = File.read_ansiString(pn,0x100);
            if (a.length+1>0x1F) pn=Math.round((pn+a.length)/0x10)*0x10;
            else pn += 0x20;
            if (pn<dataat && 31<File.read_uint8(pn)<128) c = File.read_ansiString(pn,0x100);

            sOptionT(t); sOptionT(a,"by: "); OptionT(c); 
        }
        */
    }
    else if( (extIs("hip") || Binary.isHeuristicScan())
      && (Binary.compare("60..60") || Binary.compare("60..00")) ) {
        bDetected = 1;
        sName = "Jochen Hippel's module (.HIP)";
    }
    else if(Binary.compare("'**** Player by Jochen Hippel 1990 ****'",5)
      || ( (extIs("hip7") || Binary.isHeuristicScan())
          && (Binary.compare("'TFMX'") || Binary.compare("600000")) ) ) {
        bDetected = 1;
        sName = "Jochen Hippel's 7V module (.HIP7)";
    }
    else if(Binary.compare("'COSO'") && (Binary.compare("'MMME'",0x20) ||Binary.compare("'TFMX'",0x20))) {
        bDetected = 1;
        sName = "Jochen Hippel's COSO module (.HIPC,.SOC)";
    }
    else if((Binary.isHeuristicScan()||extIs("sog")) && (Binary.compare("'MMME'") ||Binary.compare("'TFMX'"))) {
        bDetected = 1;
        sName = "Jochen Hippel's ST module (.SOG)";
    }
    else if(Binary.compare("'mpl1'")||Binary.compare("'mpl2'")||Binary.compare("'mpl3'")) {
        bDetected = 1;
        sName = "Anders Oland's Music & Player module (.HOT)";
        sVersion = "v"+File.read_ansiString(3,1);
    }
    else if(Binary.compare("'HVL'00") || Binary.compare("'HVL'01")) {
        bDetected = 1;
        sName = "Hively Tracker module (.HVL)";
        sVersion = "v"+File.read_uint8(3);
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(File.read_uint16(4,_BE),0x20))
    }
    else if(Binary.compare("'IM10'",0x3C)) {
        bDetected = 1;
        sName = "Imago Orpheus module (.IMF)";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(title,0x20))
    }
    else if(Binary.compare("'GTI5'")) {
        bDetected = 1;
        sName="GoatTracker 2 Instrument (.INS)";
    }
    else if(Binary.compare("'IMPM'")) {
        bDetected = 1;
        sName = "Impulse Tracker module (.IT)";
        sVersion = "";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(4,25))
    }
    else if(Binary.compare("'ISM!V1.2'")) {
        bDetected = 1;
        sName = "Sound Invasion Music System/In Stereo! module (.IS)";
        sVersion = "v1.2";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(4,25))
    }
    else if(Binary.compare("'IS20DF10STBL'")) {
        bDetected = 1;
        sName = "Sound Invasion Music System/In Stereo! module (.IS20)";
        sVersion = "v2.0";
    }
    else if(Binary.compare("'IXS!'")) {
        bDetected = 1;
        sName = "Ixalance module (.IXS)";
        sVersion = "v1.2";
        if(Binary.isVerbose())
          if(File.read_uint8(0x17) == 0x40)
            sOptionT(File.read_ansiString(0x18,0x20))
    }
    else if(Binary.compare("'MUSE'DEADBEAF") || Binary.compare("'MUSE'DEADBABE")) {
        sName = "Jazz Jackrabbit 2 container (.J2B)/Galaxy Sound System module";
        bDetected = 1;
        //TODO: confirm if zlib extraction available; if so, derive the module name too
    }
    else if(Binary.compare("'RIFF'........'AM  '")) {
        bDetected = 1;
        sName = "Galaxy Sound System module (.J2B)";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x14,64))
    }
    else if(Binary.compare("'BeEp'")) {
        bDetected = 1;
        sName = "JamCracker module (.JAM,.JC)";
        sOptionT(File.read_ansiString(Binary.getSize()-19,19));
    }
    else if(Binary.compare("'NuJ.FLOGEL'",0x22)) {
        bDetected = 1;
        sName = "Janko Mrsic-Flogel's module (.JMF)";
        if(Binary.isVerbose()) {
            t = File.read_ansiString(0x54,0x100); pn = 0x54+t.length+1;
            a = File.read_ansiString(pn,0x100); pn += a.length+1;
            c = File.read_ansiString(pn,0x100);
            sOptionT(t); sOptionT(a,"by: "); sOptionT(c)
        }
    }
    else if( Binary.compare("2B7C") && Binary.compare("2B7C",8)
      && Binary.compare("2B7C",0x10) && Binary.compare("2B7C",0x18)
      && Binary.compare("303C",0x20) ) {
        sName = "Jason Page's old module (.JPO)";
        bDetected = 1;
    }
    else if(Binary.compare("0239000100......6602'Nux'001839") && Binary.compare("660A13F9",0x22) ) {
        sName = "Jeroel Tel's module (.JT)";
        bDetected = 1;
    }
    else if(Binary.compare("0040....0040....0040....0040....0040....0040",0x19D1) ) {
        //unreliable for potential new modules (but there's not gonna be even one)
        bDetected = 1;
        sName = "JayTrax module (.JXS)";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0xA6,0x40));
    }
    else if(Binary.compare("'cyd!song'") ) {
        //ref https://github.com/kometbomb/klystron/tree/master/src/snd/music.c , .h
        bDetected = 1;
        sName = "Klystrack module (.KT)";
        v = File.read_uint8(8); sVersion = "v"+v;
        if(Binary.isVerbose()) {
            pn=9;
            if (v>=6) trk = File.read_uint8(pn++); else if (v>3) ch = 4; else ch = 3;
            timesig = File.read_uint8(pn++)+"/"+File.read_uint8(pn++);
            if (v>=17) pn+=2; //seqstep;
            instc = File.read_uint8(pn++);  patc = File.read_uint16(pn,_LE); pn+=2;
            pn+=ch*2; //seq cnt per channel, ignore
            songlen = File.read_uint16(pn,_LE); pn+=2; /*loopat = File.read_uint16(pn,_LE);*/ pn+=2;
            if (v>=12) pn++; //master_volume
            spd = File.read_uint8(pn++); spd2 = File.read_uint8(pn++);
            rate = File.read_uint8(pn++); //bpm?...
            if (v>2) pn+=4; if (v>=9) pn++; if (v>=16) pn++; //flags, mulpd, ptcin

            tlen = 17; if (v>=11) tlen = File.read_uint8(pn++);
            if (v>=5) tlen = Math.min(tlen,65);
            sOptionT(File.read_ansiString(pn,tlen));
            sOption("trk:"+trk+" tsig:"+timesig+" bpm:"+rate+" ins:"+instc+
                " len:"+Hex(songlen)+" ptn:"+Hex(patc)+" spd:"+spd+"-"+spd2)
        }
    }
    else if(Binary.compare("'cyd!inst'") ) {
        bDetected = 1;
        sName = "Klystrack instrument (.KI)";
    }
    else if(Binary.compare("'cyd!efex'") ) {
        bDetected = 1;
        sName = "Klystrack effects (.KF?)";
    }
    else if(Binary.compare("'KSCC'") || Binary.compare("'KSSX'")) {
        bDetected = 1;
        sName = "KSS module (.KSS)";
        if(Binary.isVerbose())
          if(Binary.compare("'MBM'",0x20))
            sOptionT(File.read_ansiString(0x24,0x34))
    }
    else if( (Binary.isHeuristicScan() || extIs("lion"))
      && Binary.compare("28",3) && Binary.compare("21",0x20) && Binary.compare("FFFF",0x22)
      && Binary.compare("10",0x2B) && Binary.compare("32",0x33) ) {
        bDetected = 1;
        sName = "Lionheart module (.LION)";
    }
    else if( Binary.compare("Liquid Module:") ||
      ( (Binary.isHeuristicScan() || Binary.compare("'NO'"))
         && extIs("liq"))
      ||  Binary.compare("21",0x01D6) && Binary.compare("21",0x0AD2) && Binary.compare("21",0x0BE6)
      && Binary.compare("FF",0x0ED4) && Binary.compare("FF",0x0EE8) ) {
        bDetected = 1;
        sName = "Liquid Tracker module (.LIQ)";
        sVersion = File.read_ansiString(0x41,0x14).trim()
        if(Binary.isVerbose()) {
            if(File.read_ansiString(0,2)=="NO")
                sOptionT(File.read_ansiString(5,0x14))
            else if(File.read_ansiString(0,3)=="Liq") {
                sOptionT(File.read_ansiString(0x0E,0x32));
                sOptionT(File.read_ansiString(0x0E,0x0F),"by: ");
            }
        }
    }
    else if(Binary.compare("'LME'00'(c)1990 Leggless/Silents UK     ") ) {
        bDetected = 1;
        sName = "Leggless module (.LME)";
    }
    else if( (Binary.isHeuristicScan() || extIs("m"))
      && (Binary.compare("001A") || Binary.compare("011A")) ) {
        // ref https://raw.githubusercontent.com/ValleyBell/MidiConverters/master/pmd_SeqFormat.txt
        // & https://lithcore.cn/2318/
        bDetected = 1;
        sName = "Professional Music Driver module (.M)";
        sVersion = "v"+File.read_uint8(0);
        if(Binary.isVerbose() && sVersion=="v0") {
            p = Binary.getSize()-0x0E;
            title = File.read_codePageString(File.read_uint16(p+4,_LE)+1,0x100,"Shift_JIS");
            artist = File.read_codePageString(File.read_uint16(p+6,_LE)+1,0x100,"Shift_JIS");
            arenji = File.read_codePageString(File.read_uint16(p+8,_LE)+1,0x100,"Shift_JIS");
            rem = File.read_codePageString(File.read_uint16(p+10,_LE)+1,0x100,"Shift_JIS");
            sOption(title,"name: ");
            sOption(artist,"by: ");
            sOption(arenji,"mixed by: ");
            sOption(rem)
        }
    }
    else if( (Binary.isHeuristicScan() || extIs("ma"))
      && Binary.compare("'Nusa-team 89'",0x016A) ) {
        bDetected = 1;
        sName = "Music Assembler module (.MA)";
    }
    else if(Binary.compare("'MADG'") ) {
        bDetected = 1;
        sName = "B. Birney's PlayerPro module (.MAD)";
        if(Binary.isVerbose())
            sOption(File.read_ansiString(4,0x12))
    }
    else if(Binary.compare("D040D0404EFB")) {
        bDetected = 1;
        sName = "Mark Cooksey's module (.MC)"; sVersion = "new"
    }
    else if(Binary.compare("'MCMD'")) {
        bDetected = 1;
        sName = "MCMD module (.MCMD)"
    }
    else if( (Binary.isHeuristicScan() || extIs("md"))
      && Binary.compare("2D58",0x134) && Binary.compare("183D7C",0x137)
      && Binary.compare("144DEE",0x13D) && Binary.compare("362D58",0x141) ) {
        bDetected = 1;
        sName = "Mike Davies's module (.MD)"
    }
    else if(Binary.compare("'TFMX-SONG'")) {
        //TODO deal with the tags, because sometimes the field is "by blahblah",
        // and other times it's like a bitfield
        bDetected = 1;
        sName = "TFMX or TFMX ST module (MDAT.)"
    }
    else if(Binary.compare("'DMDL'..'IN'")) {
        bDetected = 1;
        sName = "Digitrakker module (.MDL)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x0B,32));
            sOptionT(File.read_ansiString(0x2B,20),"by: ");
        }
    }
    else if(Binary.compare("48E700F041FA....4CD8")) {
        bDetected = 1;
        sName = "Mark II module (.MK2)";
    }
    else if(Binary.compare("'MED'") || Binary.compare("'MMD'")) {
        // from https://web.archive.org/web/20220109073109/http://www.textfiles.com/programming/FORMATS/med-form.txt
        // and http://fileformats.archiveteam.org/wiki/OctaMED_module_(MED)
        bDetected = 1;
        var rawv = File.read_uint8(3)-48;
        var sig = File.read_ansiString(0,4);
        switch(sig){
            case "MED2": sName = "MED module (.MED)"; sVersion = "v1.12"; break;
            case "MED3": sName = "MED module (.MED)"; sVersion = "v2.00"; break;
            case "MED4": sName = "MED module (.MED)"; sVersion = "v2.10"; break;
            case "MED\x04": sName = "MED module (.MED)"; sVersion = "v3.20"; break;
            case "MMD0": sName = "MED module (.MED)"; sVersion = "v2.1";
                sOptions = sOptions.append("Med MoDule 0"); break;
            case "MMD1": sName = "OctaMED Professional module (.MED)"; break;
            case "MMD2": sName = "OctaMED Professional module (.MED)"; sVersion = "v5"; break;
            case "MMD3": sName = "OctaMED Professional module (.MED)"; sVersion = "v6?"
        }
        // TODO follow up on nextmod, shorten this code
        if(Binary.isVerbose() && Binary.isDeepScan()) {
            pMMD0exp = File.read_uint32(0x20,_BE);
            songname = "";
            if(pMMD0exp > 0 && pMMD0exp < Binary.getSize()) {
                psongname = File.read_uint32(pMMD0exp+44,_BE);
                if(psongname > 0) {
                    songnamelen = File.read_uint32(pMMD0exp+48,_BE);
                    songname = File.read_codePageString(psongname,songnamelen,"CP1252");
                    if(songname != "<unnamed>" && songnamelen > 0) {
                        sOptions = sOptions.append("name: "+songname);
                    }
                }
                pannotxt = File.read_uint32(pMMD0exp+12,_BE);
                if(pannotxt > 0) {
                    annolen = File.read_uint32(pMMD0exp+16,_BE);
                    anno = File.read_codePageString(pannotxt,annolen,"CP1252");
                    sOptions = sOptions.append("cmnt: "+anno);
                }
                pMMDInstrInfo = File.read_uint32(pMMD0exp+20,_BE);
                if(pMMDInstrInfo > 0) {
                    iinfo = File.read_codePageString(pMMDInstrInfo,40,"CP1252");
                    if(iinfo != "") {
                        sOptions = sOptions.append("ins0: "+iinfo);
                    }
                }
            } //if pMMD0exp
            if(sig==="MED\x04"){//requires ~special~ crap.
                fs1 = Math.max(Binary.getSize()-0x200,0); fs2 = Math.min(Binary.getSize(),0x200);
                annot=Binary.findSignature(fs1,fs2,"'ANNO'00");
                if(annot>=0) {
                    cs=File.read_uint32(annot+4,_BE);
                    sOptionT(File.read_ansiString(annot+8,cs));
                }
                else
                    sOptionT(File.read_ucsdString(0x09));
            }
        }//Verbose&DeepScan
    }
    else if(Binary.compare("'MMDC'")) {
        bDetected = 1;
        sName = "Tony Crowther's packed MED module (.MMDC)";
    }
    else if(Binary.compare("'GameBoy Music Module'",0x0C)) {
        bDetected = 1;
        sName="Gameboy Tracker module (.MGB)";
    }
    else if(Binary.compare("'MGT'11BD'MCS'")) {
        bDetected = 1;
        sName="Megatracker module (.MGT)";
        sOptionT(File.read_ansiString(0x3A,0x20))
    }
    else if(Binary.compare("'MThd'00000006")) {
        bDetected = 1;
        sName="MIDI music (.MID)";
    }
    else if(Binary.compare("'MIX1LeOnArD!")) {
        bDetected = 1;
        sName="Atari Digi-Mix module (.MIX)";
        //TODO find the pointer to the floating title
    }
    else if(Binary.compare("'MLEDMODL'")) {
        bDetected = 1;
        sName="Musicline Editor module (.ML)";
        pv = Binary.findSignature(12,0x100,"'VERS'");
        if(pv>0) {
          sVersion = "v"+File.read_uint8(pv+8)+".";
          m= File.read_uint8(pv+9); sVersion+=""+(m>>4)+(m&0x0F)
        }
        if(Binary.isVerbose()) {
            pt = Binary.findSignature(12,0x100,"'TUNE'");
            if(pt>0) sOptionT(File.read_ansiString(pt+8))
        }
    }
    else if(Binary.compare("'FORM'........'MMV8SDAT'................'SE'")) {
        bDetected = 1;
        sName="Music Maker module (.MM8)";
        sVersion = "v8";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x1A,0x14));
            sOptionT(File.read_ansiString(0x36,0x29));
        }
    }
    else if(Binary.compare("'SO31'",0x7C)) {
        bDetected = 1;
        sName="MultiMedia Sound module (.MMS)";
    }
    else if(Binary.compare("'MO3'")) {
        bDetected = 1;
        sName="MO3 MOD module (.MO3)";
    }
    else if(Binary.compare("'M.K.'",0x438) || Binary.compare("'M&K&'",0x438) || Binary.compare("'M!K!'",0x438)) {
        bDetected = 1;
        sName="Generic MOD module (.MOD)";
        sVersion=File.read_ansiString(0x438,4);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by: ")
        }
    }
    else if(Binary.compare("'RASP'",0x438)) {
        // TODO find out what format it was...
        bDetected = 1;
        sName="Generic module (.MOD)";
        sVersion=File.read_ansiString(0x438,4);
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by/inst: ")
    }
    else if(/FA\d\d/.test(File.read_ansiString(0x438,4))) {
        bDetected = 1;
        sName="Digital Tracker module (.MOD)";
        sVersion = "v030";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by/inst: ")
        }
    }
    else if(/^(\dCHN|\d\dCH)$/.test(File.read_ansiString(0x438,4))) {
        bDetected = 1;
        sName="FastTracker module (.MOD)";
        sVersion=File.read_ansiString(0x438,4);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by/inst: ")
        }
    }
    else if(/^(FLT[48]|EXO4$)/.test(File.read_ansiString(0x438,4))) {
        bDetected = 1;
        sName="StarTrekker module (.MOD)";
        sVersion=File.read_ansiString(0x438,4);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by/inst: ")
        }
    }
    else if(/^CD[0-9]1$/.test(File.read_ansiString(0x438,4))) {
        sName="Octalyser module (.MOD)"; bDetected = 1;
        sVersion=File.read_ansiString(0x438,4);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by/inst: ")
        }
    }
    else if(Binary.compare("000000000000'MTN'000000", 0x5B2)) {
        sName="SoundTracker module (.MOD)"; sVersion="v2.6"; bDetected = 1;
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x14));
            sOptionT(File.read_ansiString(0x14,0x16),"by: ")
        }
    }
    else if(Binary.compare("AE") && (Binary.isHeuristicScan() || (extIs("mod")))) {
        bDetected = 1;
        sName="Polly Tracker module (.MOD)";
    }
    else if( Binary.isHeuristicScan() && extIs("mok")) {
        bDetected = 1;
        sName="Silmarils module (.MOK)";
    }
    else if(Binary.compare("08'MONOTONE'")) {
        //ref https://github.com/MobyGamer/MONOTONE/blob/master/MTSRC/MT_SONG.PAS
        bDetected = 1;
        sName="MONOTONE module (.MON)";
        sVersion = "v"+File.read_uint8(0x5B);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ucsdString(0x09))
            sOptionT(File.read_ucsdString(0x32));
            sOptions = sOptions.append("ptn: "+File.read_uint8(0x5C)+
                " trk: "+File.read_uint8(0x5D));
        }
    }
    else if(Binary.isHeuristicScan() && Binary.compare("4EFA....4EFA....4EFA")) {
        bDetected = 1;
        sName="Maniacs of Noise module (.MON)";
    }
    else if(/SONG[0-9.]{4}(COMP|NORM)/.test(File.read_ansiString(0,12))) {
        bDetected = 1;
        sName="Megastation track (.MS)";
        sVersion="v"+File.read_ansiString(4,5).toLowerCase()
    }
    else if(/SNGM[0-9.]{4}(COMP|NORM)/.test(File.read_ansiString(0,12))) {
        bDetected = 1;
        sName="Megastation MOD module (.MSM)";
        sVersion="v"+File.read_ansiString(4,5).toLowerCase()
    }
    else if(Binary.compare("'MSOB'00")) {
        bDetected = 1;
        sName="Medley module (.MSO)";
    }
    else if(Binary.compare("'MTM'")) {
        //ref https://github.com/libxmp/libxmp/blob/master/docs/formats/Mtm-form.txt
        bDetected = 1;
        sName="Multitracker module (.MTM)";
        v=File.read_uint8(3); sVersion="v"+(v>>4)+"."+(v & 0x0F);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(4,20));
            trk=File.read_uint16(0x16,_BE); ptn=File.read_uint8(0x18);
            songlen=File.read_uint8(0x19)+1; nos = File.read_uint8(0x1E);
            pxc = 194+nos*37+trk*192+(ptn+1)*32*2;
            sVersion+="/"+Hex(pxc);
            sOptions = sOptions.append("trk:"+trk+" ptn:"+ptn+" len:"+songlen)
        }
    }
    else if(Binary.compare("'MT20'")) {
        bDetected = 1;
        sName = "MadTracker 2 module (.MT2)";
        v = /[0-9.]{3,5}$/.exec(File.read_ansiString(0x0A,0x20).trim());
        if(v!=null) sVersion = "v"+v;
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x2A,0x40))
    }
    else if(Binary.compare("'MVM'00")) {
        bDetected = 1;
        sName = "MVX Module (.MVM)";
    }
    else if(Binary.compare("'MVSM1'")) {
        bDetected = 1;
        sName = "MVS Tracker module (.MUS)";
    }
    else if(Binary.compare("'MUS'1A") && (File.read_uint16(4,_LE)>=File.read_uint16(6,_LE))) {
        bDetected = 1;
        sName = "DOOM music module (.MUS)";
        if(Binary.isVerbose())
            sOption(File.read_uint16(4,_LE),"len:")
    }
    else if(Binary.compare("'MXM'00")) {
        bDetected = 1;
        sName = "Cubic Tiny XM module (.MXM)";
    }
    else if(Binary.compare("'MXTX'00")) {
        bDetected = 1;
        sName = "MaxTrax module (.MXTX)";
    }
    else if((Binary.isHeuristicScan() || extIs("ned")) 
      && Binary.compare("'NED'")) {
        bDetected = 1;
        sName = "Nerd Tracker ][ module (.NED)";
    }
    else if(Binary.compare("'TWNNSNG'..00")) {
        bDetected = 1;
        sName = "Noise Trekker module (.NTK)";
        sVersion = "v"+File.read_ansiString(0x7,1);
        if(Binary.isVerbose())
            sOption(File.read_ansiString(9,20))
    }
    else if((Binary.isHeuristicScan() || extIs("ntp")) 
      && Binary.compare("'MODU'")) {
        bDetected = 1;
        sName="NovoTrade Packer module (.NTP)";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(4,16))
    }
    else if(Binary.compare("'NESM'1A") || Binary.compare("'NSFE'")) {
        //ref https://web.archive.org/web/20180817053626/http://wiki.nesdev.com/w/index.php/NSF
        sName = "Nintendo Sound Format module (.NSF,.NSFe)";
        bDetected = 1;
        sig=File.read_ansiString(0,4);
        if (sig==="NSFE") filever="NSFE"
        else if(File.read_uint8(5)==2) filever="NSF2"
        else filever="NSF";
        sVersion = filever; 
        INFOready=false; NEND=false; hkhd=""; pn=4;
        nsf2jump=-1; palntsc=-1; spd=""; vrc7repl=0;
        playtime=Number(0); g=""; t=""; a=""; c=""; taut=[]; tlbl=[]; xc="";
        if (filever==="NSFE") {
            if (Binary.isVerbose() && Binary.isDeepScan()) //no base header, find INFO first
              while(pn<Binary.getSize()){
                hksz=File.read_uint32(pn,_BE); hkhd = File.read_ansiString(pn+4,4);
                if (hkhd==="INFO") break;
                else pn+=hksz+8;
            }
            nsf2jump=0;
        }
        else { //NSF v1/2 have a hardplaced header, let's read that
            bDetected = 1;
            sVersion = filever;
            if(Binary.isVerbose()) {
                palntsc = File.read_uint8(0x7A); 
                chip=File.read_uint8(0x7B);
                tc=File.read_uint8(6); t=File.read_ansiString(0x0E,0x20);
                a=File.read_ansiString(0x2E,0x20); c=File.read_ansiString(0x4E,0x20);
            }
            INFOready = true;
            nsf2jump=File.read_uint8(0x7D)<<16+File.read_uint8(0x7E)<<8+File.read_uint8(0x7F);
            pn+=nsf2jump; //NSF v1 files ALSO can have this jump... orz
        }
        // at this point we're standing at the first hunk unless it's a pure NSF, and at "INFO" for NSFe
        if(nsf2jump>=0 && Binary.isVerbose() && Binary.isDeepScan())
            while(pn<Binary.getSize() && !NEND) {
                hksz=File.read_uint32(pn,_LE); hkhd = File.read_ansiString(pn+4,4); pn+=8;
                switch (hkhd) {
                    case "NEND": //end of hunks
                        NEND=true; break;
                    case "INFO": //really shouldn't happen with nsf1/2
                        if(INFOready) break;
                        palntsc = File.read_uint8(pn+6);
                        chip = File.read_uint8(pn+7);
                        tc = File.read_uint8(pn+7);
                        INFOready=true; break;
                    case "RATE":
                        spd="rate :: NTSC: "+File.read_uint16(pn,_LE)+" ticks";
                        palspd=File.read_uint16(pn+2,_LE);
                        if(palspd>0) spd+=", PAL: "+palspd+" ticks";
                        dendyspd=File.read_uint16(pn+4,_LE);
                        if(dendyspd>0) spd+=", Dendy: "+dendyspd+" ticks";
                        break;
                    case "VRC7":
                        vrc7repl=File.read_uint8(pn); break;
                    case "time":
                        for(i=0;i*4<hksz;i++) {
                            if (i*4 >= hksz) curtime=-1
                            else curtime=File.read_int32(pn+i*4,_LE);
                            if(curtime < 0) curtime = 2*60000; //default time: 2 minutes :V
                            playtime += curtime/1000;
                        } break;
                    case "auth":
                        apn=0;
                        g=File.read_ansiString(pn,0x100); apn+=g.length+1;
                        a=File.read_ansiString(pn+apn,0x100); apn+=a.length+1;
                        c=File.read_ansiString(pn+apn,0x100); apn+=c.length+1;
                        c+=", rip: "+File.read_ansiString(pn+apn,0x100);
                        break;
                    case "taut":
                        if (Binary.isDeepScan()) {
                            apn=0;
                            while(apn<hksz) {
                                trkauth=File.read_ansiString(pn+apn,hksz-apn); apn+=trkauth.length+1;
                                taut.push(trkauth)
                            }
                        } break;
                    case "tlbl":
                        if (Binary.isDeepScan()) {
                            apn=0;
                            while(apn<hksz) {
                                trklbl=File.read_ansiString(pn+apn,hksz-apn); apn+=trklbl.length+1;
                                tlbl.push(trklbl)
                            }
                        } break;
                    case "text":
                        if (Binary.isDeepScan())
                            xc = File.read_ansiString(pn,hksz);
                }
                pn+=hksz;
            }
        if(Binary.isVerbose() && INFOready) {
            switch(palntsc) {
                case 0: sVersion += " NTSC"; break;
                case 1: sVersion += " PAL"; break;
                case 2: sVersion += " NTSC/PAL"
            }
            if((chip&0x01) > 0) sVersion += "#VRC6";
            if((chip&0x02) > 0) switch(vrc7repl) {
                case 1: sVersion += "#YM2413"; break;
                default: sVersion += "#VRC7" }
            if((chip&0x04) > 0) sVersion += "#FDS";
            if((chip&0x08) > 0) sVersion += "#MMC5";
            if((chip&0x10) > 0) sVersion += "#Namco163";
            if((chip&0x20) > 0) sVersion += "#Sunsoft5B";
            sOptionT(t);
            if(tc>1) sOptions = sOptions.append("x "+tc);
            sOptionT(a,"by: ");
            sOptionT(c);
        }
        if(Binary.isVerbose() && Binary.isDeepScan()) {
            if(playtime>1)
            sOptions = sOptions.append("Playtime: "
                + new Date(Math.round(playtime*1000)).toISOString().substr(11, 8));
            if(Math.max(tlbl.length,taut.length)>0) sOptions = sOptions.append("[Tracks]");
            for (i=0; i < Math.max(tlbl.length,taut.length); i++) {
                if(i < tlbl.length) ttlbl=tlbl[i]
                else ttlbl="#"+(i+1);
                if(i < taut.length) ttaut=" by "+taut[i]
                else ttaut="";
                sOptions=sOptions.append(ttlbl+ttaut)
            }
            if(xc!="") sOptions = sOptions.append("[Commentary]: "+xc)
        }
    }
    else if(Binary.compare("'OKTASONG'")) {
        bDetected = 1;
        sName="Oktalyzer module (.OKTA)";
        if(Binary.isVerbose()) {
            sOptions=sOptions.append("[Sample names]");
            for(i=0x20;i<=0x100;i+=0x20)
                sOptionT(File.read_ansiString(i,0x14))
        }
    }
    else if(Binary.compare("'Onyx Music File'1A")) {
        bDetected = 1;
        sName="Onyx Music File (.OMF)";
        sVersion="";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x195,0x1F));
    }
    else if(Binary.compare("'Org-02'")) {
        bDetected = 1;
        sName="Organya module (.ORG)";
        sVersion="v1";
    }
    else if(Binary.compare("'Org-03'")) {
        bDetected = 1;
        sName="Organya module (.ORG)";
        sVersion="v2";
    }
    else if(Binary.compare("'OBISYNTHPACK'")) {
        bDetected = 1;
        sName="Synth Pack module (.OSP)";
    }
    else if(Binary.compare("'PACG'")) {
        bDetected = 1;
        sName="SBStudio module (.PAC)";
        if(File.read_uint32(4)+8 != File.getSize()) sVersion = "malformed";
        if(Binary.isVerbose()) {
            p = 8; //header skipped
            t=''; v='';
            while (p<File.getSize()) {
                hkhd = File.read_ansiString(p,4);
                hksz = File.read_uint32(p+4,_LE);
                p+=0x08; //chunk header = 4xfourcc + 4xsize
                switch (hkhd) {
                    case "SONA": t = File.read_codePageString(p,hksz,"CP850"); break;
                    case "PAOR": v = File.read_codePageString(p,hksz,"CP850"); break;
                    case "END ": p = File.getSize(); break
                }
                p += hksz;
            }
            sOptionT(t); sOption(v,"via: ")
        }
    }
    else if(Binary.compare("'PLM'1A6010")) {
        bDetected = 1;
        sName="Disorder Tracker 2 module (.PLM)";
        sVersion = "v2";
        smp=File.read_uint8(0x5C,_LE); ptn=File.read_uint8(0x5D); ord=File.read_uint16(0x5E);
        sz = 0x61; ofs=0;
        for(i=0;i<ord+ptn+smp;i++) { z = File.read_uint32(i*4+sz,_LE); _log(Hex(z)); if(z>ofs) ofs=z }
        sz += 4*ord+4*ptn+4*smp;
        _log(Hex(ofs));
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(6,0x30));
            sOption("ch:"+File.read_uint8(0x36)+" bpm0:"+File.read_uint8(0x3A)
                +" spd0:"+File.read_uint8(0x3B)+" ord:"+ord+" ptn:"+ptn+" smp:"+smp);
        }
    }
    else if(Binary.compare("FFFFE002E102")) {
        bDetected = 1;
        sName="POKEYNoise chiptune (.PN)";
    }
    else if(Binary.compare("'PRT'")) {
        bDetected = 1;
        sName = "PreTracker module (.PRT)";
        nV = File.read_uint8(3);
        if(nV<25) sVersion = "v<0.3"; else
        if(nV==25) sVersion = "v0.3~0.866"; else
        if(nV==26) sVersion = "v0.87~0.92"; else
        if(27<=nV<30) sVersion = "v.[0.93~1.5)"; else
        if(nV==30) sVersion = "v1.5+"; else
          sVersion = "v.TODO";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x14,0x14));
            sOptionT(File.read_ansiString(0x28,0x14),"by: ");
            trks = File.read_uint8(0x5A); if(trks>31) sVersion += " malformed";
            sOption("trks:"+File.read_uint8(0x5A)+
                " ord:"+File.read_uint8(0x5F)+
                " ptn:"+File.read_uint8(0x5D)+"("+File.read_uint8(0x5E)+")"+
                " restart:"+File.read_uint8(0x5C))
        }
    }
    else if(Binary.compare("'PSA'00")) {
        bDetected = 1;
        sName = "Professional Sound Artists module (.PSA)";
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x4,0x14))
    }
    else if(Binary.compare("'PSC V'........' COMPILATION OF '")) {
        bDetected = 1; sVersion = "v"+File.read_ansiString(5,4);
        sName = "Pro Sound Creator module (.PSC)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x19,0x14));
            sOptionT(File.read_ansiString(0x31,0x14),"by: ")
        }
    }
    else if(Binary.compare("'PSF'")) {
        //ref https://gist.github.com/SaxxonPike/a0b47f8579aad703b842001b24d40c00
        nV = File.read_uint8(3);
        if(nV>=0) {
            bDetected = 1;
            sName = "Playstation Sound Format module (.";
            switch(nV) {
                case 0x01: sName += "PSF,.MINIPSF)"; sVersion="Playstation"; break;
                case 0x03: sName += "PSF2,.MINIPSF2)"; sVersion="Playstation 2"; break;
                case 0x11: sName += "SSF,.MINISSF)"; sVersion="Saturn"; break;
                case 0x12: sName += "DSF,.MINIDSF)"; sVersion="Dreamcast"; break;
                case 0x21: sName += "USF,.MINIUSF)"; sVersion='Ultra64'; break;
                case 0x22: sName += "GSF,.MINIGSF)"; sVersion='Gameboy'; break;
                case 0x23: sName += "SNSF,.MINISNSF)"; sVersion='Super Nintendo'; break;
                case 0x24: sName += "2SF,.MINI2SF)"; sVersion='Nintendo DS'; break;
                case 0x41: sName += "QSF,.MINIQSF)"; sVersion='Capcom Q-Sound'
            }
            if (Binary.isVerbose()) {
                //fs1 = Math.max(Binary.getSize()-0x200,0); fs2 = Math.min(Binary.getSize(),0x200);
                //ptags = Binary.findSignature(fs1,fs2,"'[TAG]'")+5;
                ptags = File.read_uint32(8,_LE)+0x15; sig2 = false;
                if(0x15<ptags<File.getSize()) sig2 = File.read_ansiString(ptags-5,5) === "[TAG]";
                if(!sig2) { //coincides with pv==2
                    ptags = File.read_uint32(4,_LE)+0x15;
                    if(0x15<ptags<File.getSize()) sig2 = File.read_ansiString(ptags-5,5) === "[TAG]";
                }
                if(sig2) {
                    tags = File.read_codePageString(ptags,File.getSize()-ptags,"UTF8").trim();
                    a=""; g=""; t=""; y=""; l="";
                    tagl = tags.split('\n');
                      for (i=0; i<tagl.length;i++) {
                        tag = tagl[i].split("=");
                        switch (tag[0]) {
                            case "length": l = tag[1]; break;
                            case "title": t = tag[1]; break;
                            case "game": g = tag[1]; break;
                            case "artist": a = tag[1]; break;
                            case "copyright": if(a=="") a = tag[1]; break;
                            case "year": y = tag[1]; break;
                            case "dsfby","psfby","ssfby": if(a=="") a = tag[1]; break;
                        }
                      }
                    sOption(t);
                    sOption(a,"by: ")
                    sOption(y,"'");
                    sOption(g,"for: ");
                    sOption(l,"len ");
                }
                else sVersion += " library";
            }
            if(Binary.getSize() < 0x300) sVersion += " header"
        }
    }
    else if((Binary.isHeuristicScan() || extIs("psf")) && Binary.compare("00000114000001", 0x24)
      && Binary.compare("000001140000011400000114000001140000011400000114", 0x74)) {
        sName = "Sound Factory module (.PSF)"; bDetected = 1;
    }
    else if(Binary.compare("'PSM '") || Binary.compare("'PSM'FE")) {
        bDetected = 1;
        sName = "Epic Megagames MASI module (.PSM)";
        if(Binary.isVerbose()) {
            pt = Binary.findString(0,50,"MAINSONGTITL");
            if(pt>0) {
                ts = File.read_uint32(pt+12);
                sOptionT(File.read_ansiString(pt+16,ts));
            }
        }
    }
    else if(Binary.compare("'psm1'00", 0x08)) {
        sName = "Pro Sound Maker module (.PSM)"; bDetected = 1;
    }
    else if(Binary.compare("'PSY'..'SONG")) {
        bDetected = 1;
        sName = "Psycle module (.PSY)";
        sV = File.read_ansiString(3,1);
        sVersion = "v"+sV;
        if(Binary.isVerbose()) {
            switch(sV) {
                case "2":
                //ref https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle-core/src/psycle/core/psy2filter.cpp
                    sOptionT(File.read_codePageString(0x08,0x20,"CP1252"));
                    sOptionT(File.read_codePageString(0x28,0x20,"CP1252"),"by: ");
                    sOptionT(File.read_codePageString(0x48,0x80,"CP1252"));
                    bpm = File.read_int32(0xC8,_LE);
                    ord = File.read_int32(0x0191,_LE);
                    sOption("bpm:"+bpm+" ord:"+ord);
                    break;
                case "3":
                //ref https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle-core/src/psycle/core/psy3filter.cpp
                    nV = File.read_int32(8);
                    sVersion+="/"+nV;
                    p = 0x10+File.read_uint32(0x0C); //chunk header = 4xfourcc + 4xver + 4xsize
                    hkn = File.read_uint32(0x10,_LE);
                    t=''; a=''; c=''; trk=0; bpm=0; ptnlns=0; ptn=0; mac=0; ins=0; eins=0;
                    while (hkn > 0) {
                        hkhd = File.read_ansiString(p,4);
                        hkn--;
                        cV = File.read_uint32(p+4,_LE);
                        hksz = File.read_uint32(p+8,_LE);
                        p+=0x0C;
                        switch (hkhd) {
                            case "INFO":
                                p1 = p;
                                if((cV & 0xFF00)==0) {
                                    t = File.read_codePageString(p1,128,"CP1252"); p1+=t.length+1;
                                    a = File.read_codePageString(p1,64,"CP1252"); p1+=a.length+1;
                                    c = File.read_codePageString(p1,0xFFFF,"CP1252"); p1+=c.length+1;
                                    if(cV == 0) hksz=t.length+a.length+c.length+3 //bug workaround
                                }
                                break;
                            case "SNGI":
                                if((cV & 0xFF00) == 0) {
                                    trk=File.read_int32(p,_LE);
                                    if(cV==0) hksz = 11*4+trk*2; //bug workaround
                                    bpm=File.read_int16(p+4,_LE) + File.read_int16(p+6,_LE)/100.0;
                                }
                                break;
                            case "SEQD":
                                if((cV & 0xFF00) == 0)
                                    seqlen=File.read_int32(p+4,_LE);
                                break;
                            case "PATD":
                                if((cV & 0xFF00) == 0) {
                                    _idx = File.read_int32(p,_LE);
                                    ptnlns = File.read_int32(p+4,_LE);
                                    ptnn = File.read_ansiString(p+0x0C,0x20);
                                    p1=p+0x0C+ptnn.length+1;
                                    ptnsz = File.read_int32(p1,_LE);
                                    ptn++; p1+=4;
                                    if((cV == 0) && (p1+ptnsz == p+hksz+4)) hksz += 4; //bug workaround
                                }
                                break;
                            case "MACD":
                                mac++;
                                //bugfix impossible here, let's pretend it won't happen and move on
                                break;
                            case "INSD":
                                ins++;
                                break;
                            case "EINS":
                                if((cV & 0xFFFF0000) == 0x10000) {
                                    eins = File.read_uint32(p,_LE)
                                    //and again, pretending there's no bug (and no samples)
                                }
                                break;
                        }
                        p += hksz;
                    }
                    sOptionT(t); sOptionT(a,"by: "); sOptionT(c);
                    sOption("bpm:"+bpm+" trk:"+trk+" ptnlns:"+ptnlns+
                        " mac:"+mac+" ptn:"+ptn+"/idx:"+_idx+" ins:"+ins+" eins:"+eins);
                    break;
                case "4":
                //ref https://sourceforge.net/p/psycle/code/HEAD/tree/trunk/psycle-core/src/psycle/core/psy4filter.cpp
                    sVersion+="/future";
                    //yet another zipped-xmls format
            }
        }
    }
    else if(Binary.compare("''")) {
        bDetected = 1; sVersion = "v.VT2";
        sName = "ProTracker 3 module (.PT3)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x1E,0x20));
            sOptionT(File.read_ansiString(0x42,0x20),"by: ")
        }
    }
    else if(Binary.compare("'PTCOLLAGE-'") || Binary.compare("'PTTUNE--20'")) {
        //ref source code / pxtnService.cpp, pxtnDescriptor.cpp
        bDetected = 1;
        v1=File.read_ansiString(2,1);
        if(v1=="T") { var rough = 10; //used for clocking
            sName = "pxtone tune (.PTTUNE)" }
        else {  var rough = 1;
            sName="pxtone project (.PTCOP)" }
        dt = File.read_ansiString(10,6);
        if(dt<="050227") { sV = "v.x1x"; nV = 1 }
        else if(dt<="050608") { sV = "v.x2x"; nV = 2 }
        else if(dt<="060115") { sV = "v.x3x"; nV = 3 }
        else if(dt<="060930") { sV = "v.x4x"; nV = 4 }
        else if(dt<="071119") { sV = "v5"; nV = 5 }
        d = dt.substr(0,2)+"-"+dt.substr(2,2)+"-"+dt.substr(4,2);
        sVersion = sV+"/20"+d;
        if(Binary.isVerbose()) {
            switch(nV) {
                case 1: case 2: p = 0x10; break;
                case 3: case 4: p = 0x14; break;
                default: p=0x14
            }
            t=""; c=""; bclock = 0; bnum = 0; btempo = 0; bps = 0; bEnd = false;
            while((!bEnd) && (p<File.getSize())) {
                hkhd = File.read_ansiString(p,8); hksz = File.read_uint32(p+8,_LE);
                switch (hkhd) {
                    case "PROJECT=":
                        t = File.read_codePageString(p+12,0x10,"CP932");
                        btempo = File.read_float32(p+0x1C,_LE).toFixed(0);
                        bclock = File.read_int16(p+0x20,_LE);
                        bnum = File.read_int16(p+0x22,_LE);
                        break;
                    case "evenMAST":
                        if(File.read_uint16(p+12,_LE)!=3)
                            sVersion+= "/unk";
                        break; //not gonna parse what vari-read gives me
                    case "MasterV5":
                        bclock = File.read_int16(p+12,_LE)*rough;
                        bnum = File.read_int16(p+14,_LE)&0xFF; // TODO use int8 when that works
                        btempo = File.read_float32(p+15,_LE).toFixed(0);
                        break;
                    case "Event V5": //this is so incredibly slow!!! And yet, no way around it
                        if(Binary.isDeepScan()) {
                            var evtn = File.read_uint32(p+12,_LE);
                            hksz = 4;
                            for(e=0; e<evtn; e++) { //hksz is broken in this chunk so it's like this 
                                for(i=0;i<5;i++) { hksz++; if (File.read_uint8(p+11+hksz) < 0x80) break }
                                hksz+=2;
                                for(i=0;i<5;i++) { hksz++; if (File.read_uint8(p+11+hksz) < 0x80) break }
                            }
                        }
                        else
                            bEnd = true; //we'll just give up here and save half a second
                        break;
                    case "textNAME":
                        t = File.read_codePageString(p+12,hksz,"CP932"); break;
                    case "textCOMM":
                        c = File.read_codePageString(p+12,hksz,"CP932"); break;
                    case "END=====": case "pxtoneND":
                        bEnd = true; break
                }
                p+=12+hksz
            }
            if (t != "no name") sOption(t);  sOptionT(c);
            if(bclock+btempo+bnum>0)
                sOption("btempo:"+btempo+" bclock:"+bclock+" bnum:"+bnum)
        }
    }
    else if(Binary.compare("'PTNOISE-'")) {
        bDetected = 1;
        sName = "pxtone Noise instrument (.PTNOISE)"
    }
    else if(Binary.compare("'PTVOICE-'")) {
        bDetected = 1;
        sName = "pxtone Voice instrument (.PTVOICE)"
    }
    else if(Binary.compare("'TWNNSNG6'")) {
        bDetected = 1;
        sName = "ProTrekkr module (.PTK)";
        sVersion = "v<2"
    }
    else if(Binary.compare("'PROTREK'")) {
        bDetected = 1;
        sName = "ProTrekkr module (.PTK)";
        sVersion = "v2"
    }
    else if(Binary.compare("'TWNNSNG1'")) {
        bDetected = 1;
        sName = "NoiseTrekkr module (.NTK)"; //looks like the old name of protrekkr
    }
    else if(Binary.compare("'PTMF'",0x2C) && Binary.compare("1A",0x1C)) {
        // ref  https://web.archive.org/web/20151027135201if_/http://hackipedia.org/File%20formats/Music/Sample%20based/text/Poly%20Tracker%20module%20format.cp437.txt.utf-8.txt
        bDetected = 1;
        sName = "Poly Tracker module (.PTM)";
        sV = Hex(File.read_uint16(0x1D,_LE));
        sVersion = "v"+sV.substr(0,1)+"."+sV.substr(1,2);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x1C));
            malformed = false;
            ch = File.read_uint16(0x26,_LE); if(ch==0 || ch>32) malformed = true;
            ord = File.read_uint16(0x20,_LE); if(ord>256) malformed = true;
            ins = File.read_uint16(0x22,_LE); if(ins==0 || ins>255) malformed = true;
            ptn = File.read_uint16(0x24,_LE); if(ptn==0 || ptn>128) malformed = true;
            if(malformed) sVersion+=" malformed";
            sOption("ch:"+ch+" ins:"+ins+" ptn:"+ptn+" ord:"+ord)
        }
    }
    else if(Binary.compare("'FORM'.. .. .. ..'MODLVERS'")){
        bDetected = 1;
        sName = "ProTracker IFF module (.PTM)";
        sVersion = File.read_ansiString(0x18,File.read_uint32(0x10,_BE)-0x10);
        if(Binary.isVerbose()) {
            p = Binary.findSignature(0x1E,0x200,"'INFO'");
            if(p>0) sOptionT(File.read_ansiString(p+8,0x20));
            p = Binary.findSignature(p+File.read_uint32(p+4,_BE),0x200,"'CMNT'");
            if(p>0) sOptionT(File.read_ansiString(p+8,File.read_uint32(p+4,_BE)))
        }
    }
    else if(Binary.isHeuristicScan() && extIs("puma")) {
        bDetected = 1;
        sName = "Pumatracker module (.PUMA)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0,0x0C));
        }
    }
    else if(Binary.compare("'RAD by REALiTY!!'")) {
        //partial ref https://www.3eality.com/Public/Releases/RAD/radv2_0a.zip source/validate20.cpp
        // & https://www.3eality.com/Public/Releases/RAD/radv1_1a.zip player.asm
        bDetected = 1;
        sName = "Reality Adlib Tracker module (.RAD)";
        sV = File.read_uint8(0x10).toString(16);
        sVersion = "v"+sV[0]+"."+sV[1];
        if(Binary.isVerbose()) {
            al = File.read_uint8(0x11);
            if(sV>"10" || al&0x80) { //desc present
                l=0x13;
                for(i=0;i<3;i++) { //originally just one big description with \x01 used for line breaks (w.h.y.)
                    r=File.findSignature(l,0x100,"01");
                    if(r<0) break;
                    else if(r==0) l++;
                    else { sOption(File.read_ansiString(l,r-l)); break; }
                }
            }
            sOption(al&0x1F,"spd0: ");
        }
    }
    else if(Binary.compare("'RNS0'") && Binary.compare("'>>> Chunk Start <<<'", 0x09)) {
        bDetected = 1;
        sName = "Renoise module (.RNS)"; sV=File.read_ansiString(3,4); sVersion = "v"+sV;
        if(sV<"05") sVersion+="/RN<1.1.1";
        else if(sV==="05") sVersion+="/RN1.1.1";
        else if(sV<"015") sVersion+="/RN<1.2.7";
        else if(sV==="015") sVersion+="/RN1.2.7";
        else if(sV<"018") sVersion+="/RN<1.5.2";
        else if(sV==="018") sVersion+="/RN1.5.2";
        else sVersion+="/RN>1.5.2"
        if (Binary.isVerbose()) {
            p=8;
            while (p<File.getSize()) {
                p = File.findSignature(p+1,File.getSize()-p-20,"'>>> Chunk Start <<<'");
                if (p == -1)
                    break;
                p += 20;
                if (File.read_ansiString(p,10)=="Header V00") {
                    sOptionT(File.read_codePageString(p+20,20,"CP1252"));
                    sOptionT(File.read_codePageString(p+40,20,"CP1252"),"by: ");
                    sOptionT(File.read_codePageString(p+60,20,"CP1252"),"style: ");
                    break;
                }
                else if (File.read_ansiString(p,10)=="Header V01") {  p+=20;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252")); p+=sz+4;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252"),"by: "); p+=sz+4;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252"),"style: ");
                    break;
                }
                else if (File.read_ansiString(p,10)=="Header V02") {  p+=20;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252")); p+=sz+4;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252"),"by: "); p+=sz+4;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252"),"style: "); p+=sz+4+5;
                    sz=File.read_uint32(p); sOptionT(File.read_codePageString(p+4,sz,"CP1252"));
                    break;
                }
                p = File.findSignature(p+1,File.getSize()-p-20,"''>>>  Chunk End  <<<'");
                if (p == -1)
                    break
            }
        }
    }
    else if(extIs("xrns") && Binary.compare("'PK'0304")) {
        //TODO when ZIP parsing is available
        bDetected = 1;
        sName = "Renoise module (.XRNS)"; sVersion = "xml"
        //versions vs doc_version:
        // 10: 1.9.1    14: 2.0.0    15: 2.1.0    21: 2.5.1    22: 2.6.1    30: 2.7.0, 2.7.1, 2.7.2
        // 37: 2.8.2    54: 3.0.1    63: 3.1.0    65: 3.3.2    66: 3.4.2
    }
    else if(extIs("xrdp") && File.findString(0,0x100,"<FilterDevicePreset")>=0) {
        bDetected = 1;
        sName = "Renoise filter device preset (.XRDP)";
        p=File.findString(20,0x100,"doc_version=");
        if(p<0) sVersion = "malformed";
        else {
            sVp = File.read_ansiString(p,0x10); sVersion = "v"+/doc_version=\"(\d*)\"/.exec(sVp)[1]
        }
    }
    else if(extIs("xrni") && Binary.compare("'PK'0304")) {
        //TODO when ZIP parsing is available
        bDetected = 1;
        sName = "Renoise instrument (.XRNI)"; sVersion = "xml"
    }
    else if(extIs("xrno") && (File.findString(0,0x100,"<SampleModulationSet")>=0)) {
        bDetected = 1;
        sName = "Renoise sample modulation set (.XRNO)";
        p=File.findString(15,0x100,"doc_version=");
        if(p<0) sVersion = "malformed";
        else {
            sVp = File.read_ansiString(p,0x10); sVersion = "v"+/doc_version=\"(\d*)\"/.exec(sVp)[1]
        }
    }
    else if(extIs("xrnt") && (File.findString(0,0x100,"<RenoiseDeviceChain")>=0)) {
        bDetected = 1;
        sName = "Renoise effect chain (.XRNT)";
        p=File.findString(15,0x100,"doc_version=");
        if(p<0) sVersion = "malformed";
        else {
            sVp = File.read_ansiString(p,0x10); sVersion = "v"+/doc_version=\"(\d*)\"/.exec(sVp)[1]
        }
    }
    else if(extIs("xrnt") && Binary.compare("'PK'0304")) {
        bDetected = 1;
        sName = "Renoise effect chain (.XRNT)"; sVersion="v<3.0"
    }
    else if(extIs("xrnz") && (File.findString(0,0x100,"<InstrumentPhrase")>=0)) {
        bDetected = 1;
        sName = "Renoise instrument phrase (.XRNZ)";
        p=File.findString(15,0x100,"doc_version=");
        if(p<0) sVersion = "malformed";
        else {
            sVp = File.read_ansiString(p,0x10); sVersion = "v"+/doc_version=\"(\d*)\"/.exec(sVp)[1]
        }
    }
    else if((Binary.isHeuristicScan() || extIs("rh")) && Binary.compare("60...... 60...... 60...... 60")) {
        bDetected = 1;
        sName = "Rob Hubbard module (.RH)";
    }
    else if((Binary.isHeuristicScan() || extIs("rho")) && Binary.compare("00'@'7F'@'00C081C0FF")) {
        bDetected = 1;
        sName = "Rob Hubbard ST module (.RHO)";
    }
    else if(Binary.compare("'NuRIFFRAFF'",0x22)) {
        bDetected = 1;
        sName = "Riff Raff module (.RIFF)";
        if (Binary.isVerbose()) {
            t = File.read_codePageString(0x64,0x100,"CP1252");
            a = File.read_codePageString(0x64+t.length+1,0x100,"CP1252");
            c = File.read_codePageString(0x64+t.length+a.length+2,0x100,"CP1252");
            sOptionT(t); sOptionT(a,"by: "); sOptionT(c)
        }
    }
    else if(Binary.compare("'RON_KLAREN_SOUNDMODULE!'00", 0x28)) {
        bDetected = 1;
        sName = "Ron Klaren module (.RK)";
    }
    else if(Binary.compare("'RTMM '")) {
        bDetected = 1;
        sName = "Real Tracker module (.RTM)";
        sVersion = "v"+File.read_ansiString(0x37,7);
        if(Binary.isVerbose()) {
            sOptionT(File.read_codePageString(5,0x20,"CP1252"));
            sOptionT(File.read_codePageString(0x3E,0x20,"CP1252"),"by: ");
            sOptionT(File.read_codePageString(0x8C,0x20,"CP1252"),"orig.: ")
        }
    }
    else if(Binary.compare("'SCRM'", 0x2C)) {
        //ref https://moddingwiki.shikadi.net/wiki/S3M_Format
        // & https://github.com/OpenMPT/openmpt/blob/master/soundlib/S3MTools.h & Load_s3m.cpp
        bDetected = 1;
        _V = Hex(File.read_uint16(0x28,_LE));
        sVersion = "v"+_V.substr(1,1)+"."+_V.substr(2,2);
        if(_V==="1300") sVersion="v3.00"; else //because they're weird duh
        if(_V==="1320") sVersion="v3.20"; else
        if(_V==="1301") sVersion="v3.01";
        switch(_V.substr(0,1)) {
            case "1": sName = "ScreamTracker 3 module (.S3M)"; break;
            case "2": sName = "Imago Orpheus module (.S3M)"; break;
            case "3": sName = "Impulse Tracker module (.S3M)"; break;
            case "4":
                if(_V==="4100") {
                    sName = "BeRoTracker module (.S3M)"; sVersion = "v.2004-2012" }
                else
                    sName = "Schism Tracker module (.S3M)";
                break;
            case "5":
                _log(_V);
                if(_V==="4754") {
                    sName = "GraoumfTracker module (.S3M)"; sVersion = "" }
                else
                    sName = "OpenMPT module (.S3M)"; break;
            case "6": sName = "BeRoTracker module (.S3M)"; break;
            case "7": sName = "CreamTracker module (.S3M)"; break;
            case "C":
                if(_V==="CA00") {
                    sName = "Camoto/libgamemusic module (.S3M)"; sVersion = ""; break }
            default: sName = "ScreamTracker-compatible module (.S3M)";
        }
        if(Binary.isVerbose()) {
            malformed = "";
            if (!Binary.compare("1A",0x1C)) malformed+=":sg";
            z1 = File.read_uint16(0x1E); if(z1>0) malformed+=":0";
            ords = File.read_uint16(0x20,_LE); if((ords%2) == 1) malformed+=":od";
            ins = File.read_uint16(0x22,_LE); ptn = File.read_uint16(0x24,_LE);
            gvol = File.read_uint8(0x30); spd = File.read_uint8(0x31);
            tmp = File.read_uint8(0x32); mvol = File.read_uint8(0x33);
            if(malformed.length>0) sVersion += "/malformed"+malformed;
            sOptionT(File.read_codePageString(0,0x1C,"CP1252"));
            ch = 1 + (mvol >> 7); mvol &= 0x7F;
            sOption("ch:"+ch+" ord:"+ords+" ptn:"+ptn+" ins:"+ins
                +" gvol:"+gvol+" mvol:"+mvol+" r/s:"+(tmp/spd).toFixed(1));
        }
    }
    else if(Binary.compare("'!Scream!'", 0x14)) {
        bDetected = 1;
        sName = "ScreamTracker 2 module (.STM)";
        if(Binary.isVerbose())
            sOptionT(File.read_codePageString(0,0x14,"CP1252"))
    }
    else if(Binary.compare("'S98'3.")) {
        //ref https://www.purose.net/befis/download/lib/t98/ins98131s.zip / s98spec1.txt,
        //    http://www.purose.net/befis/download/kmp/old/s98spec2.txt,
        //    https://github.com/rururutan/s98spec3/blob/master/s98spec3-ja.txt
        bDetected = 1;
        sName = "S98 tune (.S98)";
        sV = File.read_ansiString(3,1); sVersion = "v"+sV;
        if (Binary.isVerbose()) {
            a=""; c="";s=""; g=""; t=""; y="";
            switch(sV) {
                case "0": case "1": case "2":
                    p = File.read_uint32(0x10,_LE);
                    if(p > 0)
                        sOptionT(File.read_ansiString(p,0x40));
                    pk = File.read_uint32(0x0C,_LE);
                    if(pk>0) sVersion+=" compressed";
                    break;
                case "3":
                    ptags = File.read_uint32(0x10,_LE);
                    if(ptags > 6 && File.read_ansiString(ptags,5)=="[S98]") {
                        ptags+=5;
                        bUTF8 = File.compare("EFBBBF",ptags); //if BOM's there
                        if (bUTF8) tags = File.read_utf8String(ptags+3,0x200); //test whether this parses BOM too
                        else tags = File.read_codePageString(ptags,0x200,"Shift_JIS");
                        tagl = tags.split('\x0A');
                        for (i=0; i<tagl.length;i++) {
                            tag = tagl[i].split("=");
                            switch (tag[0]) {
                                case "title": case "ｔｉｔｌｅ": t = tag[1]; break;
                                case "game": case "ｇａｍｅ": g = tag[1]; break;
                                case "system": case "ｓｙｓｔｅｍ": s = tag[1]; break;
                                case "artist": case "ａｒｔｉｓｔ": a = tag[1]; break;
                                case "year": case "ｙｅａｒ": y = tag[1]; break;
                                case"copyright": case "ｃｏｐｙｒｉｇｈｔ": if(a=="") a = tag[1]; break;
                                case"s98by": case "ｓ９８ｂｙ": if(a=="") a = tag[1]; break;
                                case"comment": case "ｃｏｍｍｅｎｔ": c = tag[1]; break;                        
                            }
                        }
                        sOption(t); sOption(a,"by: "); sOption(y,"'");
                        sOption(g,"for: "); sOption(s,"on: "); sOption(c);
                        dev = File.read_uint32(0x1C,_LE); if(dev==0) dev = 1;
                        if(dev>64) sVersion+=" malformed";
                        sOption("devices: "+dev)
                        break
                    } //if correct tag
            }//switch sV
        }//isVerbose
    }
    else if(Binary.compare("'SOARV'")) {
        bDetected = 1; sName = "Sonic Arranger module (.SA)";
        sVersion = "v"+File.read_ansiString(5,3)
    }
    else if( (Binary.isHeuristicScan() || extIs("sa")) && Binary.compare("4EFA00")) {
        bDetected = 1; sName = "Sonic Arranger module (.SA)";
    }
    else if(Binary.compare("'SAP'0D0A")) {
        bDetected = 1;
        sName="Slight Atari Player module (.SAP)";
        if(Binary.isVerbose()) {
            malformed=false; t=""; a=""; dt=""; tp=""; tm="";
            p=File.findSignature(3,File.getSize()-5,"0D0AFFFF");
            if(p==0) malformed=true;
            else {
                taghunk=File.read_ansiString(5,p);
                if(taghunk.length>5) {
                    tags = taghunk.split("\x0D\x0A");
                    for(i=0;i<tags.length;i++) {
                        tagdiv=tags[i].indexOf(" ");
                        tagl=tags[i].substr(0,tagdiv);
                        tagr=tags[i].substr(tagdiv+1,tags[i].length);
                        switch(tagl) {
                            case "NAME": if(tagr!='"<?>"') t=tagr.substr(1,tagr.length-2); break;
                            case "AUTHOR": if(tagr!='"<?>"') a=tagr.substr(1,tagr.length-2); break;
                            case "DATE": if(tagr!='"<?>"') dt=tagr.substr(1,tagr.length-2); break;
                            case "TYPE": sVersion="v."+tagr; break;
                            case "TIME": tm=tagr; break
                        }
                    }
                }
            }
            if(malformed) sVersion.append("malformed");
            sOption(t); sOption(a,"by: "); sOption(dt,"'"); sOption(tm,"len: ")
        }
    }
    else if(Binary.compare("'SC68 Music-file'")) {
        bDetected = 1;
        sName = "SC68 module (.SC68)";
        if(Binary.isVerbose()) {
            hdrl = File.read_ansiString(0,0x100).length+1; 
            p = hdrl; //header skipped
            t=''; a=''; cp=''; st=''; df=-1; x=0; ef=false; mn = [];
            while (p<File.getSize()) {
                hkhd = File.read_ansiString(p,4);
                hksz = File.read_uint32(p+4,_LE);
                p+=0x08; //chunk header = 4xfourcc + 4xsize
                switch (hkhd) {
                    case "SC68": if(hksz+hdrl!=File.getSize()) sVersion= "malformed"; hksz=0; break;
                    case "SCFN": t = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "SCDF": df = File.read_uint32(p,_LE)+1; break;
                    case "SCMN":
                        mn.push(File.read_codePageString("CP1252",p,hksz));
                        if((st=="") || (df==mn.length))
                            st = mn[mn.length-1];
                        break;
                    case "SCAN": a = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "SCCN": cp = File.read_codePageString(p,hksz,"CP1252"); break;
                    case "SCEF": ef = true; p = File.getSize(); break
                }
                p += hksz;
            }
            sOptionT(t); if(mn.length>1) sOption(mn.length,"x");
            if(Binary.isDeepScan()) sOptionT(mn.join(";"));
            else { if(df<0) df=0; sOptionT(mn[df]); }
            sOptionT(a,"by: "); sOptionT(cp,"(c)");
            if(!ef) sVersion="malformed"
        }
    }
    else if( (Binary.isHeuristicScan() || extIs("scn"))
      && Binary.compare("6000") && Binary.compare("6000", 0x04)
      && Binary.compare("6000", 0x08) && Binary.compare("6000", 0x0C)
      && Binary.compare("003000", 0x722) && Binary.compare("FAF1", 0x1051))  {
        bDetected = 1;
        sName = "Sean Connolly's module (.SCN)"
    }
    else if( (Binary.isHeuristicScan() || extIs("scr"))
      && Binary.compare("000F", 0x03) && Binary.compare("000E", 0x13)
      && Binary.compare("000E", 0x17) && Binary.compare("000E", 0x1B)
      && Binary.compare("000E", 0x1F) && Binary.compare("000E", 0x23) )  {
        bDetected = 1;
        sName = "Sean Conran's module (.SCR)"
    }
    else if((Binary.isHeuristicScan() || extIs("sfx")) && Binary.compare("'SONG'", 0x3C)) {
        sName = "SoundFX module (.SFX)"; bDetected = 1;
    }
    else if(Binary.compare("'shro'020000")) {
        bDetected = 1;
        sName = "Mario Paint's Shroom module (.SHO)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(7,0x20));
            sOptionT(File.read_ansiString(0x27,0x20),"by: ")
        }
    }
    else if(Binary.compare("0000001C000000040000", 0x06)
      && Binary.compare("'SIDMON II - THE MIDI VERSION'", 0x3A)) {
        bDetected = 1;
        sName = "SidMon II module (.sid2)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(7,0x20));
            sOptionT(File.read_ansiString(0x27,0x20),"by: ")
        }
    }
    else if(Binary.compare("'PSID'") || Binary.compare("'RSID'")) {
        bDetected = 1;
        v1 = File.read_ansiString(0,1);
        if(v1 == "P")  sName="PlaySID chiptune (.SID, .PSID)";
        else sName = "RealSID chiptune (.SID, .RSID)";
        v2 = File.read_uint16(4,_BE);
        sVersion = "v"+Hex(v2);
        malformed = 0;
        x = File.read_uint16(0x0E,_BE);
        if(x<1 || x>256) malformed = 1;
        else if (x>1) sOption(x,"x");
        startSong = File.read_uint16(0x10,_BE);
        if(startSong>x) malformed = 2;
        dataOfs = File.read_uint16(0x06,_BE);
        if((v2==1 && dataOfs != 0x0076) || (v2==2 && dataOfs != 0x007C))
            malformed = 3;
        loadAddr = File.read_uint16(0x08,_BE);
        if((v1="R") && (loadAddr>0) && (loadAddr<0x07E8)) malformed = 4;
        initAddr = File.read_uint16(0x0A,_BE);
        if((v1="R") &&
           ( (initAddr<0x07E8) || (0xA000<=initAddr && initAddr<0xC000) || (0xD000<=initAddr) ))
            malformed = 5;
        flags = File.read_uint16(0x76,_BE);
        if(v1=="R" && (flags&2 >> 1) == 1 && initAddr>0)
            malformed = 6;
        switch((flags&0x30)>>4) {
            case 1: sVersion+="/6581"; break;
            case 2: sVersion+="/8580"; break;
            case 3: sVersion+="/6581&8580"; break;
            default: sVersion+="/unk.chip"
        }
        switch((flags&0x0C)>>2) {
            case 1: sVersion+="/PAL"; break;
            case 2: sVersion+="/NTSC"; break;
            case 3: sVersion+="/PAL&NTSC"; break;
        }
        sidn = (dataOfs-0x7C)/2+1;
        if (sidn>1) sVersion += "/"+sidn+"SID";
        if (malformed>0) sVersion += " malformed:"+malformed;
        if (Binary.isVerbose()) {
            t = File.read_codePageString(0x16,0x20,"CP1252"); if(t=="<?>") t=""; sOptionT(t);
            a = File.read_codePageString(0x36,0x20,"CP1252"); if(a=="<?>") a=""; sOptionT(a,"by: ");
            c = File.read_codePageString(0x56,0x20,"CP1252"); if(c=="<?>") c=""; sOptionT(c);
        }
    }
    else if(Binary.compare("0100FEFF09000000'ALIM3'")) {
        bDetected = 1;
        sName="Skale Tracker module (.SKM)";
        if(Binary.isVerbose()) sOptionT(File.read_ansiString(0x19));
    }
    else if(Binary.compare("'SMOD'")) {
        bDetected = 1;
        sName="Future Composer module (.SMOD)";
        sVersion="v1.0~3";
    }
    else if(Binary.compare("6000000A60000090600000'xM'F900DFF00047FA07FC'=|'000F0096'=|'00FF009E700641EBFFF810FC000051C8FFFA41EB0006'p-'117C00000034117C00000068117C0000009C10FC000051C8FFE87600162B00DF1743FFFB610001E241EBFFFA1082176B00DD00DB08B9000100BFE001'J+'00E1660808F9000100BFE001'NuG'FA078C1740FFFD1740FFFC177C0001FFFE'NuM'F900DFF0007E0347FA07'pK'FA07'l3'EB00D200DFF096'7|'000000D0177C000000DA'Jm'001A661041EB00E6'-H'00A0'=|'000100A4600C'=m'001A00A4'-m'001C00A0'J+'FFF8660E'J+'FFF96604610000B2610001E4DDFC00000010DBFC000000'4Q'CFFFBC33EB00D000DFF096'7|'820000D0'7|'000000D44DF900DFF0007E0347FA06F24BFA06EE'J+'FFF8660461000200610003D8DDFC00000010DBFC000000'4Q'CFFFE46100008C'0+'00D0806B00D233C000DFF096'7k'00D400D2'J+'FFFD6706'S+'FFFD'`4'102BFFFC12000201007F'g('1741FFFD4A006B0C'J+'00D9671A'S+'00D960140C2B004000D96608177C0000FFFC6004'R+'00D9'Nur'007000122BFFFA41EB02E8D1ED000010301800D1FC000004001B70180000'0r'00122B00DEC2C041EB1068D288'+A'0016'Nuv'00162BFFFB142BFFFA122BFFF9102BFFF8'J+'00DA6706122B00DE55015200B02B00DB'f6p'005401B22B00DE'f,r'00B42B00D767045202'` R'03B62B00E06616162B00DF177C0000FFFC176B00DD00DB177C000000D961121743FFFB1742FFFA1741FFF91740FFF8'NuA'EB00E843EB00D6780212F03800D1FC000000'@Q'CCFFF41A'08'0043EB00'1x'03D1FC0000004012B03800D3FC000000'4Q'CCFFEE'J+'FFFE'fRJ'056B08177C0000FFFC")) {
        bDetected = 1;
        sName="Sound Master II module (.SMPRO)";
        sVersion="v1";
    }
    else if((Binary.isHeuristicScan() || extIs("sm3")) && (Binary.compare("600000..6000....6000")
      && Binary.compare("177C00", 0xEA) && Binary.compare("2BFF", 0x1D9) && Binary.compare("000667", 0x4D4))) {
        bDetected = 1; sName = "Sound Master II module (.SM3)"; sVersion = "v3"
    }
    else if(Binary.compare("'<track'") && (Binary.findString(6,0x100,"rowhighliohtingminor=")>0)) {
        bDetected = 1;
        sName="Picatune module v1 (.SMUFI) or v2 (.PT2)";
        if(Binary.isVerbose()) {
            s = File.read_utf8String(0,0x100);
            t = /.*name="([^"]*)".*/.exec(s);
            if(t!=null) sOptionT(t[1]);
            a = /.*author="([^"]*)".*/.exec(s);
            if(a!=null) sOptionT(a[1],"by: ");
            spd = /.*speed="([^"]*)".*/.exec(s);
            bpm = /.*bpm="([^"]*)".*/.exec(s);
            if(spd!=null) sVersion+=" spd: "+spd[1];
            if(bpm!=null) sVersion+=" bpm: "+bpm[1];
        }
    }
    else if(Binary.compare("'SNGs'")) {
        bDetected = 1; sName="Sound Club module (.SN)"; sVersion = "v1";
        if(Binary.isVerbose()) sOptionT(File.read_ansiString(0x0F));
    }
    else if(Binary.compare("'SN2'") && Binary.compare("'NAM'",0x1F)) {
        bDetected = 1; sName="Sound Club module (.SN)"; sVersion = "v2";
        if(Binary.isVerbose()) sOptionT(File.read_ansiString(0x26,File.read_uint32(0x22,_LE)));
    }
    else if( (Binary.isHeuristicScan() || extIs("sndh"))
      && (Binary.compare("'Ice!'") || Binary.compare("'ICE!'")) ) {
        bDetected = 1; sName = "Atari ST chiptune (.SNDH)"; sVersion = "compressed"
    }
    else if(Binary.compare("'SNDH'", 0x0C)) {
        bDetected = 1; sName = "Atari ST chiptune (.SNDH)"; sVersion = "unpacked";
        if(Binary.isVerbose()) {
            p=0x10; t=""; a="";
            for(i=0; i<8;i++) {
                x = File.read_ansiString(p,File.getSize()-p);
                if(x.length > 4) {
                    tag = x.substr(4,x.length);
                    switch(x.substr(0,4)) {
                        case "##01": i=100; break;
                        case "TITL": if(tag.substr(0,7) != "Unknown") t = tag; break;
                        case "COMM": if(tag.substr(0,7) != "Unknown") a = tag; break
                    }
                }
                p += x.length+1
            }
            sOptionT(t); sOptionT(a,"by: ")
        }
    }
    else if(/GTS[25!]/.test(File.read_ansiString(0,4))) {
        bDetected = 1;
        sName="GoatTracker module (.SNG)";
        if([0x32,0x35].indexOf(File.read_uint8(3))>=0) sVersion = "v2"
        else sVersion = "v1";
        sOptionT(File.read_ansiString(4,0x20));
        tc = File.read_uint8(0x64);
        if(tc>1) sOptions = sOptions.append("x "+tc);
        sOptionT(File.read_ansiString(0x24,0x20),"by: ");
        sOptionT(File.read_ansiString(0x44,0x20))
    }
    else if(Binary.compare("'RJP'3.'SMOD'")) {
        bDetected = 1;
        sVersion = "v"+File.read_ansiString(3,1)
        sName = "Richard Joseph's module (.SNG)";
    }
    else if(Binary.compare("'SYNC'") || Binary.compare("'SYNB'")) {
        bDetected = 1;
        sVersion = "ver."+File.read_ansiString(3,1)
        sName = "Synder SNG-player module (.SNG)";
        if(Binary.isVerbose()) sOption(File.read_ansiString(0x10,0x200))
    }
    else if(Binary.compare("'SYND'....'S0'")) {
        bDetected = 1;
        sVersion = "ver."+File.read_ansiString(3,1)
        sName = "Synder Tracker module (.SNG)";
        if(Binary.isVerbose()) sOption(File.read_ansiString(0x10,0x200))
    }
    else if(Binary.compare("'SYND'") || Binary.compare("'SYNF'") || Binary.compare("'SYNH'")) {
        bDetected = 1;
        sVersion = "ver."+File.read_ansiString(3,1)
        sName = "Synder SNG-player Stereo module (.SNG)";
        if(Binary.isVerbose()) sOption(File.read_ansiString(0x10,0x200))
    }
    else if(Binary.compare("'RJP'3. 0000 0000")) {
        bDetected = 1;
        sVersion = "v"+File.read_ansiString(3,1)
        sName = "Richard Joseph's module instruments (.INS)";
    }
    else if(Binary.compare("'SNES-SPC700 Sound File Data'")) {
        // ref http://snesmusic.org/files/spc_file_format.txt   and snippets from
        // http://www.alpha-ii.com/Source/SAmp310s.rar/snes/id666.cpp, .h 
        sName="Nintendo SPC module (.SPC)";
        sVersion="v0."+File.read_uint8(0x24); bDetected = 1;
        if(Binary.isVerbose()) {
            id666inhdr = File.read_uint8(0x23)==0x1A;
            t=""; a=""; c=""; g=""; dumper="";
            preferBin=false;
            if (id666inhdr){
                t = File.read_ansiString(0x2E,0x20);
                g = File.read_ansiString(0x4E,0x20);
                dumper = File.read_ansiString(0x6E,0x10);
                dumpdate = File.read_ansiString(0x9E,11);
                emu = File.read_uint8(0xD2);
                if (emu>=0x30 && emu<=0x39)
                    emu -= 0x30;
                switch(emu) {
                    case 1: emu = "ZSNES"; break;
                    case 2: emu = "Snes9x"; break;
                    case 3: emu = "ZST2SPC"; break;
                    case 4: emu = "ETC"; break;
                    case 5: emu = "SNEShout"; break;
                    case 6: emu = "ZSNESW"; break;
                    default: emu = "" 
                }
                c = File.read_ansiString(0x7E,0x20);
                slen = File.read_ansiString(0xA9,3); lp=File.read_ansiString(0xAC,4);
                if((slen+lp+dumpdate).length<5) {
                    chnDis = File.read_uint8(0xD1);
                    if(chnDis==1 && emu == "") bin = true; else bin = preferBin;
                } else if( /[0-9/]*/.test(slen+lp+dumpdate) ) { //id666 text format
                    songlen = Number(slen); //in seconds
                    a = File.read_ansiString(0xB1,0x20);
                } else {
                    bin=true;
                    //check songlen bitness
                    songlen = File.read_uint8(0xA9)<<16+File.read_uint8(0xAA)<<8+File.read_uint8(0xAB); //in seconds
                    a = File.read_ansiString(0xB0,0x20);
                }
            }
            else {
                t = File.read_ansiString(0x30,0x14);
            }
            if(t!="") sOptions = sOptions.append(t);
            if(g!="") sOptions = sOptions.append("for: "+g);
            if(a!="") sOptions = sOptions.append("by: "+a);
            if(c!="") sOptions = sOptions.append(c);
            if(emu!="") sVersion+=" "+emu;
        }
    }
    else if(Binary.compare("'SONG'") && Binary.isDeepScan()) {
        //ref https://github.com/kometbomb/prototracker/blob/master/doc/FORMAT.TXT
        secsz = File.read_uint32(4,_BE);
        v = File.read_uint8(8); sVersion="v"+v;
        trk = File.read_uint8(9); pn=0xA; 
        fxc = 0; if (v >= 17) { fxc = File.read_uint8(pn); pn++ }
        t = File.read_ansiString(pn,0x100);
        if(Binary.compare("'SEQU'",pn+t.length+1+2)) {
            bDetected = 1;
            sName="ProtoTracker module (.SONG)";
            sOptions = sOptions.append(t);
            sVersion += " trk:"+trk;
            if(fxc>0) sVersion += " fx:"+fxc
        }
    }
  if(!bDetected) {
    if(Binary.compare("'STK'..'.'..'SONG")) {
        sName="Starkos module (.SKS)"; sVersion = "v"+File.read_ansiString(3,3); bDetected = 1;
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x14,0x20));
            sOptionT(File.read_ansiString(0x0A,0x0A),"by: ")
        }
    }
    if(Binary.compare("'Nu!SOPROL!'", 0x22)) {
        sName="Sound Programming Language module (.SPL)"; bDetected = 1;
        if(Binary.isVerbose()) {
            t=File.read_ansiString(0x58,0x100);
            a=File.read_ansiString(0x58+t.length+1,0x100);
            c=File.read_ansiString(0x58+t.length+a.length+2,0x100);
            sOptionT(t); sOptionT(a,"by: "); sOptionT(c)
        }
    }
    if(Binary.compare("'SPM'0.")) {
        //ref http://aminet.net/mus/edit/stonefree1.lha
        sName="Stonetracker tune file (.SPM)"; bDetected = 1; sVersion="v"+File.read_uint8(3);
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(4,0x20));
            //TODO find pointer and read the extra stuff
        }
    }
    if(Binary.compare("'SPS'0.0.")) {
        sName="Stonetracker samples file (.SPS)"; bDetected = 1; sVersion="v"+File.read_uint8(3);
        if(Binary.isVerbose()) {
            smp=File.read_uint8(5);
            sOption(smp,"smp:")
            for(i=0; i < Math.min(10,smp); i++)
                sOptionT(File.read_ansiString(0x20*i+6,8))
        }
    }
    else if(Binary.compare("'SPU'00") || Binary.compare("'SPU1'")) {
        bDetected = 1; sName = "Sony Playstation SPU module (.SPU)";
        if(File.read_uint8(3)==0) sVersion = "v0"; else sVersion = "v1";
        if(Binary.isVerbose()) {
            game = File.read_codePageString(4,0x40,"Shift_JIS");
            title = File.read_codePageString(0x44,0x40,"Shift_JIS");
            sOptionT(title); sOptionT(game, "game: ");
            artist = File.read_codePageString(0x84,0x20,"Shift_JIS"); sOptionT(artist,"by: ");
            cmt = File.read_codePageString(0xA4,0xF00,"Shift_JIS"); sOptionT(cmt);
        }
    }
    else if(Binary.isHeuristicScan() && Binary.compare("'2a'16'I'FA",0x17)
      && Binary.compare("'2a'0C'I'FA",0x21) && Binary.compare("'Ff'EE'J@g'06')K'",0x45)) {
        sName = "Quartet PSG module (.SQT)"; bDetected = 1;
    }
    else if(Binary.compare("'SPEEDY-SYSTEM'")) {
        bDetected = 1; sName = "Speedy System module (.SS)"; sVersion = "v1"
    }
    else if(Binary.compare("'ZXAYST11'")) {
        sName = "Sound Tracker module (.ST1, .ST11)"; sVersion = "v1.1/uncompiled";
        bDetected = 1;
        if (Binary.isVerbose()) {
            t_= File.read_uint8(0x17); t = File.read_ansiString(0x18,t_-1).trim();
            if(t!="Some SoundTracker Song") sOption(t);
        }
    }
/*    else if(Binary.compare("'SOUND TRACKER v1.3'", 0x07)) {
        sName = "Sound Tracker module (.STC, .ST13)"; sVersion = "v1.3";
        bDetected = 1;
    }  moved to the heuristics down the file */
    else if(Binary.compare("'KSA SOFTWARE COMPILATION OF '",0x0A) && (File.getSize()<0x2800)) {
        sName="Sound Tracker Pro module (.STP)"; sVersion="compiled"; bDetected = 1;
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x26,0x19)); sOption("tempo: "+File.read_uint8(0))
    }
    else if(Binary.compare("'STP3'")) {
        sName="Soundtracker Pro II module (.STP)"; bDetected = 1;
    }
    else if(Binary.compare("48E7FFFE4DFA")) {
        bDetected = 1; sName = "SunTronic module (.SUN)";
    }
    else if(Binary.compare("'SVOX'00000000")) {
        bDetected = 1; sName = "SunVox module (.SUNVOX)";
        if(Binary.isVerbose()) {
            p=8;
            t=''; ef=false; bpm=0; spd=0; tme=0; ptn=0; gen=0;
            while (p<File.getSize()) {
                hkhd = File.read_ansiString(p,4);
                hksz = File.read_uint32(p+4,_LE);
                p+=0x08; //chunk header = 4xfourcc + 4xsize
                switch (hkhd) {
                    case "VERS":
                        nV = File.read_uint32(p,_LE); aV=[];
                        for(i=0;i<4;i++) aV[3-i]=(nV>>(8*i))%256; 
                        sVersion += "v"+aV.join("."); break;
                    case "BVER":
                        nB = File.read_uint32(p,_LE);
                        if(nB!=nV) { aV=[];
                            for(i=0;i<4;i++) aV[3-i]=(nB>>(8*i))%256; 
                            sVersion += "/v"+aV.join(".");
                        }
                        break;
                    case "NAME": t = File.read_ansiString(p,hksz); break;
                    case "BPM ": bpm = File.read_uint32(p,_LE); break;
                    case "SPED": spd = File.read_uint32(p,_LE); break;
                    case "TIME": tme = File.read_uint32(p,_LE); break;
                    case "LGEN": gen = File.read_uint32(p,_LE); break;
                    case "PATN": ptn = File.read_uint32(p,_LE); break;
                    case "PDTA": ef = true; p = File.getSize(); break
                }
                if(ef) break; p += hksz;
            }
            sOptionT(t);
            if(bpm>0)sOption(bpm,"bpm:"); if(spd>0)sOption(spd,"spd:");
            if(tme>0)sOption(tme,"time:"); if(ptn>0)sOption(ptn,"ptn:"); if(gen>0)sOption(gen,"gen:");
        }
    }
    else if(Binary.compare("'SymM'")) {
        //ref https://github.com/OpenMPT/openmpt/blob/master/soundlib/Load_symmod.cpp
        bDetected = 1; sName = "Symphonie module (.SYMMOD)";
        sVersion = "v"+File.read_uint32(4,_BE);
        if(Binary.isVerbose()) {
            p=8;
            t=''; ef=false; ch=0; len=0; extsmp=false; pro=false;
            while (p<File.getSize()) {
                if(ef) break;
                hkhd = File.read_int32(p,_BE); hkhx = Hex(File.read_uint32(p,_BE)); hksz=4;
                p+=0x04; //chunk header = 4xtype (the following will change by chunk type)
                switch (hkhd) {
                    case -16: pklen=File.read_uint32(p,_BE); hksz+=pklen; o=4; //InfoText RLE-COMPRESSED YO
                      if((pklen>=10) && Binary.compare(p+o,"'PACK'FFFF")) { o+=6;
                        _log("yes we're FUCKING doing this. The algo's 100% from openMPT");
                        unplen=File.read_uint32(p+o,_BE); o+=4;
                        maxlen=pklen-10; if(4294967295/170>=maxlen) maxlen+=170; else maxlen=4294967295;
                        if(unplen>maxlen) unplen=maxlen;
                        done=false; ofs=0; left=unplen;
                        while((!done) && (o<hksz)) {
                            tp=File.read_int8(p+o); o++;
                            switch(tp) {
                            case 0:
                                l=File.read_uint8(p+o); o++;
                                if(left>=l) {
                                    t+=File.read_ansiString(p+o,l);
                                    o+=l; left-=l;
                                } else done=true;
                                break;
                            case 1:
                                l=FIle.read_uint8(p+o); o++;
                                dw=File(read_ansiString(p+o,4)); o+=4;
                                if(left >= (l*4) && (o<pklen)) {
                                    left-=l*4;
                                    while(l--) t+=dw;
                                } else done=true;
                                break;
                            case 2:
                                dw=File(read_ansiString(p+o,4)); o+=4;
                                if(left >= (l*4) && (o<pklen)) {
                                    unp+=dw+dw;
                                    left-=8;
                                } else done=true;
                                break;
                            case 3:
                                l=File.read_uint8(p+o); o++;
                                if(left>=l) left-=l;
                                else done=true;
                                break;
                            case -1: done=true; break;
                            default: sVersion+=" malformed"; done=true; break;
                            }//switch
                        }
                      } else //if no RLE sig:
                      t=File.read_codePageString(p+o,hksz,"CP1252"); break;
                    case -1: ch=File.read_int32(p,_BE); break; //NumChannels
                    case -2: len=File.read_int32(p,_BE); if(len>1024) len="malformed"; break; //TrackLength
                    case -3: case -4: case -5: break;
                    case -7: extsmp=true; break; //ExternalSamples
                    case 10: case 11: case 12: pro=true; break; //Sample Boost/Detune/Phase
                    case -6: tmp=Math.round(1.24*Math.min(File.read_int32(p,_BE),800)); break; //Tempo
                    case -12: hksz=0; break; //EmptySample
                    case -10: case -11: case -13: case -14: case -15: case -17: case -18:
                    case -19: case -20: case -21: hksz+=File.read_int32(p,_BE); break;
                    default: _log(Hex(p,8)+": "+hkhd+"/"+hkhx+" ("+Hex(hksz,8)+"): ?!?!?!?!?!");
                }
                p += hksz;
            }
        }
        sOption(t); sOption(ch,"ch:"); sOption(len,"len:"); sOption(tmp,"tmp:");
        if(extsmp) sOption("extsmp"); if(pro) sVersion+="/Pro"
    }
    else if(Binary.compare("'Synth'")) {
        bDetected = 1;
        sName = "Synthesis module (.SYN)";
        if(Binary.compare("'Synth'",0x1F0E)) {
            sVersion = "v"+File.read_ansiString(0x1F13,3);
            if(Binary.isVerbose()) {
                sOptionT(File.read_ansiString(0x1F32,0x1B));
                sOptionT(File.read_ansiString(0x1F4E,0x100))
            }
        } else {
            sVersion = "v"+File.read_ansiString(5,3);
            if(Binary.isVerbose()) {
                sOptionT(File.read_ansiString(0x24,0x1B));
                sOptionT(File.read_ansiString(0x40,0x100))
            }
        }
    }
    else if(Binary.compare("'SYNTRACKER-SONG:'00")) {
        bDetected = 1;
        sName = "SynTracker module (.SYNMOD)";
        if(Binary.isVerbose()) {
            t1 = File.read_codePageString(0x14,0x20,"CP1252"); sOptionT(t1,"title/inst: ");
            t2 = File.read_codePageString(0x34,0x20,"CP1252"); sOptionT(t2);
            t3 = File.read_codePageString(0x54,0x20,"CP1252"); sOptionT(t3);
        }
    }
    else if(Binary.compare("'T0AST'")) {
        bDetected = 1;
        sName = "The 0ok Amazing Synth Tracker module (.T0AST)";
        //TODO tags at the end of file ._. not worth the parser?
    }
    else if(Binary.compare("'AN COOL'")) {
        //ref ftp://ftp.scene.org/pub/resources/gotpapers/manuals/tcb_tracker_1.0_manual_1990.pdf
        bDetected = 1;
        sName = "-TCB TRACKER- module (.TCB)";
        if(Binary.isVerbose())
            sOption("ptn:"+File.read_uint32(8,_BE)+" tempo:"+File.read_uint8(0x0C));
    }
    else if(Binary.compare("'TFMfmtV2'")) {
        bDetected = 1;
        sName = "TFM Music Maker module (.TFE)";
    }
    else if((Binary.isHeuristicScan() || extIs("tme"))
      && Binary.compare("0000050F0000050F0000050F0000050F0000050F0000050F0000050F0000050F0000050F0000050F0000050F0000050F", 0x14)
      && Binary.compare("FFFF001000000030000000", 0x181)) {
        bDetected = 1;
        sName = "The Musical Enlightenment module (.TME)"
    }
    else if(Binary.compare("'TRK01/TV.ES.'")) {
        bDetected = 1;
        sName = "RamTracker module (.TRK)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_codePageString(0x0F,0x20,"CP850"));
            sOptionT(File.read_codePageString(0x2F,0x20,"CP850"),"by: ");
        }
    }
    else if(Binary.compare("'S'8F'NG.'")) {
        bDetected = 1;
        sName = "Unique Development module (UDS.+SMP.)";
    }
    else if((File.getSize() > 0x133) && Binary.compare("'MAS_UTrack_V00'")) {
        //ref https://github.com/OpenMPT/openmpt/blob/master/soundlib/Load_ult.cpp
        bDetected = 1;
        sName = "UltraTracker module (.ULT)";
        nV = File.read_uint8(0X0E);
        if(nV<0x31 || nV>0x34) sVersion = "malformed";
        else {
            sVersion = "v"+["<1.4","1.4","1.5","1.6"][nV-0x31];
            if(Binary.isVerbose()) {
                sOptionT(decAnsi(0x0F,0x20,CP437)); //TODO File.read_codePageString(0x0F,0x20,"CP437") 
                msgn = File.read_uint8(0x2F);
                if(File.getSize() < msgn*0x20+0x50) sVersion += " malformed";
                else { msg=[];
                    sOption(File.read_uint8(msgn*0x20+0x30),"smp:");
                    for(i=0;i<msgn;i++)
                      msg[i]=decAnsi(i*0x20+0x30,0x20,CP437).trim();
                    sOption(msg.join(" "));
                }
            }
        }
    }
    else if(/UN\d{2}/.test(File.read_ansiString(0,4))) {
        bDetected = 1;
        sName = "MikMod UNITRK module (.UNI)";
        v = File.read_ansiString(3,1);
        sVersion = "v"+File.read_ansiString(2,1)+"."+v;
        if(Binary.isVerbose()) {
            if(v<0x35) pn=0x130; else pn=0x131;
            ts = File.read_uint16(pn,_LE);
            sOptionT(File.read_ansiString(pn+2,ts));
            porig = pn+2+ts;
            origs = File.read_uint16(porig,_LE);
            if(origs>0) sOptionT(File.read_ansiString(porig+2,origs),"orig: ")
        }
    }
    else if(Binary.compare("'VGEfmtV'3.")) {
        bDetected = 1;
        sName = "VGM Music Maker module (.VGE)";
        sVersion = "v"+File.read_ansiString(7,1);
    }
    else if(Binary.compare("'Vgm '")) {
        //ref https://vgmrips.net/wiki/VGM_Specification
        // & https://vgmrips.net/wiki/GD3_Specification
        bDetected = 1; malformed = false;
        sName = "Video Game Music log tune (.VGM)";
        eofofs = File.read_uint32(4,_LE)+4;
        sV = Hex(File.read_uint32(8,_LE),8);
        sVersion = "v"+sV.substr(5,1)+"."+sV.substr(6,2);
        if(Binary.isVerbose()) {
            tags=[]; //11 of them
            //te="";tj="";ge="";gj="";se="";sj="";ae="";aj="";ymd="";pr="";c="";
            gd3p = File.read_uint32(0x14,_LE)+0x14;
            if(gd3p>0x14)
            if(File.read_ansiString(gd3p)=="Gd3 ") {
                taglen=File.read_uint32(gd3p+4,_LE); gd3p+=8;
                i=0;
                while((i<11) && (gd3p<File.getSize())) {
                    tpos = File.findSignature(gd3p,taglen,"0000");
                    if(tpos>=0) {
                        tags[i]=File.read_utf16String(gd3p,tpos);
                        gd3p+=tpos
                    }
                    else tags[i]="";
                    i++
                }
                if(i<11) malformed = true; else tagn=Math.max(0,i-1);
                for(j=0;j<=n;j++) {
                    switch(j) { //TODO show tags normally with a / depending on their presence
                    case 0: sOption(tags[j],"title.en: "); break;
                    case 1: sOption(tags[j],"title.jp: "); break;
                    case 2: sOption(tags[j],"game.en: "); break;
                    case 3: sOption(tags[j],"game.jp: "); break;
                    case 4: sOption(tags[j],"system.en: "); break;
                    case 5: sOption(tags[j],"system.jp: "); break;
                    case 6: sOption(tags[j],"system.jp: "); break;
                    case 7: sOption(tags[j],"artist.en: "); break;
                    case 8: sOption(tags[j],"artist.jp: "); break;
                    case 9: sOption(tags[j],"date: "); break;
                    case 10: sOption(tags[j],"ripper: "); break;
                    case 11: sOption(tags[j],"notes: "); break;
                    }
                }
            }
            psgclk = File.read_uint32(0x0C,_LE); //TODO add a chip list?
            ym2413clk = File.read_uint32(0x10,_LE);
            rate = File.read_uint32(0x24,_LE);
            if(sV>="v1.10") {
                ym2612clk = File.read_uint32(0x2C,_LE);
                ym2151clk = File.read_uint32(0x30,_LE)
            }
            if(eofofs < File.getSize())
                if(File.read_ansiString(eofofs,4)=="Vgm ") sOption("multisong");
                else sOption("+extra data")
        }
        dataofs = File.read_uint32(0x34,_LE);
        if((sV>="v1.50") && (!dataofs)) malformed = true;
        if(malformed) sVersion+= "malformed";
    }
    else if(Binary.compare("00'3T1'")) {
        bDetected = 1;
        sName = "VicTracker module (.VT)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x19E,0x10));
            sOptionT(File.read_ansiString(0x1AE,0x10),"by: ");
            sOptionT(File.read_ansiString(0x1BE,0x10),"'");
        }
    }
    else if(Binary.compare("'Creative Voice File'1a1a")) {
        bDetected = 1;
        sName = "Creative Voice audio (.VOC)";
    }
    else if(Binary.isHeuristicScan() && extIs("vss")) {
        bDetected = 1;
        sName = "Voodoo Supreme Synthesizer audio (.VSS)";
    }
    else if( ([0x5941,0x4D59,0x7961,0x6D79].indexOf(File.read_uint16(0,_LE))>0) &&
      (Binary.isHeuristicScan() || extIs("vtx"))) {
        //ref https://bulba.untergrund.net/Ay_Emul30alpha11.src.7z / Formats.pas
        bDetected = 1; fmode=0;
        sName = "Vortex Project audio (.VTX)";
        if(File.read_uint8(2)==0) sVersion="1ch"; else sVersion+="2ch";
        switch (File.read_uint16(0,_LE)) {
            case 0x5941,0x4D59: fmode=1;
            case 0x5941,0x7961: sVersion += " AY-3-8910/12"; break;
            case 0x4D59,0x6D79: sVersion += " YM2149"; break;
            default: sVersion += " unk.chip"
        }
        if(Binary.isVerbose()) {
            p=0x10;
            t = File.read_ansiString(p,Math.min(256,File.getSize()-p)); p+=t.length+1;
            sOption(t);
            a = File.read_ansiString(p,Math.min(256,File.getSize()-p)); p+=a.length+1;
            sOptionT(a,"by: ");
            if(!fmode) {
                y = File.read_uint16(0x0A,_LE);
                if(y>0) sOption(y,"'");
                pr = File.read_ansiString(p,Math.min(256,File.getSize()-p)); p+=pr.length+1;
                sOptionT(pr,"for: ");
                tn = File.read_ansiString(p,Math.min(256,File.getSize()-p)); p+=tn.length+1;
                sOptionT(tn,"trk: ");
                c = File.read_ansiString(p,Math.min(256,File.getSize()-p)); p+=c.length+1;
                sOptionT(c,"in: ");
            }
            sOption(File.read_uint32(5,_LE)+"Hz","chip freq:");
            sOption(File.read_uint8(9)+"kHz","int.freq:");
        }
    }
    else if(Binary.compare("'Vortex Tracker II 1.0'")) {
        bDetected = 1;
        sVersion="v"+File.read_ansiString(0x12,3).trim();
        sName="Vortex Tracker II audio (.VT2)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x1E,0x20));
            sOptionT(File.read_ansiString(0x42,0x20),"by: ");
        }        
    }
    else if(Binary.compare("6000") && Binary.compare("48E7FFFE610000",0x04)
        && Binary.compare("4CDF7FFF'Nu'",0x0C)) {
        bDetected = 1;
        sName="Wally Beben's module (.WB)";
    }
    else if(Binary.compare("'WSRF'",File.getSize()-0x20) && Binary.compare("EA",File.getSize()-0x10)) {
        //ref http://daifukkat.su/docs/wsman/#wsr
        bDetected = 1;
        sName = "WonderSwan music log file (.WSR)";
        p = File.getSize()-0x20;
        sVersion = "v"+File.read_uint8(p+4);
        if(Binary.isVerbose()) {
            sOption(Hex(File.read_uint8(p+0x18)),"CartID:")
            pub = File.read_uint8(p+0x16);
            switch(pub) {
            case 0: sOption("(invalid publisher)"); break;
            case 1: sOption("Bandai"); break;
            case 2: sOption("Taito"); break;
            case 3: sOption("Tomy"); break;
            case 4: sOption("Koei"); break;
            case 5: sOption("Data East"); break;
            case 6: sOption("Asmik Ace"); break;
            case 7: sOption("Media Entertainment"); break;
            case 8: sOption("Nichibutsu"); break;
            case 0x0A: sOption("Coconuts Japan"); break;
            case 0x0B: sOption("Sammy"); break;
            case 0x0C: sOption("Sunsoft"); break;
            case 0x0D: sOption("Mebius"); break;
            case 0x0E: sOption("Banpresto"); break;
            case 0x10: sOption("Jaleco"); break;
            case 0x11: sOption("Imagineer"); break;
            case 0x12: sOption("Konami"); break;
            case 0x16: sOption("Kobunsha"); break;
            case 0x17: sOption("Bottom Up"); break;
            case 0x18: sOption("Kaga Tech"); break;
            case 0x19: sOption("Sunrise"); break;
            case 0x1A: sOption("Cyber Front"); break;
            case 0x1B: sOption("Mega House"); break;
            case 0x1D: sOption("Interbec"); break;
            case 0x1E: sOption("Nihon Application"); break;
            case 0x1F: sOption("Bandai Visual"); break;
            case 0x20: sOption("Athena"); break;
            case 0x21: sOption("KID"); break;
            case 0x22: sOption("HAL Corporation"); break;
            case 0x23: sOption("Yuki Enterprise"); break;
            case 0x24: sOption("Omega Micott"); break;
            case 0x25: sOption("Layup"); break;
            case 0x26: sOption("Kadokawa Shoten"); break;
            case 0x27: sOption("Shall Luck"); break;
            case 0x28: sOption("Squaresoft"); break;
            case 0x2B: sOption("Tom Create"); break;
            case 0x2D: sOption("Namco"); break;
            case 0x2E: sOption("Movic(?)"); break;
            case 0x2F: sOption("E3 Staff(?)"); break;
            case 0x31: sOption("Vanguard"); break;
            case 0x32: sOption("Megatron"); break;
            case 0x33: sOption("Wiz"); break;
            case 0x34: sOption("Capcom"); break;
            default: sOption("(unknown publisher)")
            }
            sOption(File.read_uint8(p+5),"1sttrk: ");
        }
    }
    else if(Binary.compare("3026b2758e66cf11a6d900aa0062ce6c")) {
        bDetected = 1;
        sName="Windows Media (.WMV/WMA)";
    }
    else if(Binary.compare("'XAD!'")) {
        bDetected = 1;
        sName="Exotic AdLib module (.XAD)";
        if(Binary.isVerbose()) {
            sOptionT(File.read_ansiString(0x04,0x24));
            sOptionT(File.read_ansiString(0x28,0x24),"by: ");
        }
    }
    else if(Binary.compare("'Extended Module:'")) {
        bDetected = 1;
        sName="Fast Tracker 2 Extended module (.XM)";
        sVersion = File.read_ansiString(0x26,0x14).trim();
        if(Binary.isVerbose())
            sOptionT(File.read_ansiString(0x11,0x14));
    }
    else if(Binary.compare("'YM'3.'!'") || Binary.compare("'YM3b!'")
          || Binary.compare("'YMT'3.'LeOnArD!'") || Binary.compare("'MIX1LeOnArD!'")) {
        //ref https://github.com/cpcsdk/libstsound/blob/master/Ymload.cpp
        bDetected = 1; malformed = 0; frm = 0; smp = 0; voc = 0; loop = 0;
        sName = "ST-Sound music log file (.YM)";
        sV = File.read_ansiString(0,4).replace(/!/,"");
        switch(sV) {
        case "YM1": sVersion = "YM1"; break;
        case "YM2": sVersion = "MADMAX specific"; break;
        case "YM3": sVersion = "YM-Atari"; break;
        case "YM3b": sVersion = "YM-Atari+loopinfo";
            loop=File.read_uint32(File.getSize()-4,_LE); break;
        case "YM4": sVersion = "YM-Atari extended"; break;
        case "YM5": case "YM6": sVersion = "Generic YM2149 extended"; break;
        case "MIX1": sVersion = "Atari Remix digital"; break;
        case "YMT1": case "YMT2": sVersion = "YM-Tracker"; break;
        }
        if(["YM2","YM3","YM3b"/*,"YM4"*/].indexOf(sVersion) >= 0) {//No YM4 in the wilderness, ignoring
            frm = (File.getSize-4)/14;
        }
        if(["YM5","YM6","YMT1","YMT2","MIX1"].indexOf(sV) >= 0) {
            if(File.read_ansiString(4,8)!="LeOnArD!") malformed = 1;
            if((["YM6!","YMT1","YMT2"].indexOf(sV) >=0)
                && (File.read_ansiString(File.getSize()-4)!="End!")) malformed=2;
            //if(sV==="YM4") p=0x1C; else
            if(["YM5","YM6"].indexOf(sV) >= 0) {
                ddn = File.read_uint16(0x14,_BE);
                loop = File.read_uint32(0x1C,_BE);
                p = File.read_uint16(0x20,_BE)+0x22;
                for(i=0;i<ddn;i++) {
                    ds = File.read_uint16(p,_BE); p += 2+ds;
                    if(p >= File.getSize()) {malformed = 3; break}
                }
            } else
            if(sV === "MIX1") {
                p = 0x18;
                smp = File.read_uint32(0x10,_BE);
                mixblk = File.read_uint32(0x14,_BE);
                for(i=0;i<mixblk;i++) p+=12; //u32 smpst,smplen; u16 repeat,rplfreq skipped
            } else
            if(["YMT1","YMT2"].indexOf(sV)>=0) {
                ddn = File.read_uint16(0x18,_BE);
                voc = File.read_uint16(0x0D,_BE);
                frm = File.read_uint32(0x10,_BE);
                p = 0x1E;
            }
            //read the three tags from here
            if(p>=File.getSize()) malformed = 4;
            else {
                t = p;
                t_ = File.findSignature(p,File.getSize()-p-8,"00")-p;
                if(t_ >= 0) {
                    p += t_+1; a = p;
                    a_ = File.findSignature(p,File.getSize()-p-7,"00")-p;
                    p += a_+1; c = p;
                    c_ = File.findSignature(p,File.getSize()-p-6,"00")-p;
                    p += c_+1;
                    if(["YM5","YM6"].indexOf(sV)>=0) {
                        frm = File.read_uint32(0x0C,_BE);
                        if(frm<<4 != File.getSize()-p-4) {
                            malformed = 5; sOption("frm/frames: "+(frm<<4)+"/"+(File.getSize()-p-4)) }
                    }
                }
            }
            if(Binary.isVerbose()) {
                sOptionT(File.read_ansiString(t,t_));
                sOptionT(File.read_ansiString(a,a_),"by: ");
                sOptionT(File.read_ansiString(c,c_));
            }
        }
        if(Binary.isVerbose()) {
            if(voc) sOption(voc,"voc:");
            if(smp) sOption(smp,"smp:");
            if(ddn) sOption(ddn, "digidrums:")
            if(frm) sOption(frm,"len:");
        }
        if(malformed) sVersion+="/malformed"+malformed;
    }
    else if(Binary.compare("'YMST'")) {
        bDetected = 1;
        sName = "MYST ST-YM module (.YMST,.YM)";
        if(Binary.isVerbose()) {
            p=4;
            for(i=0;i<48;i++) {
                p+=8; if(File.read_uint16(p-8,_BE)==0) break;
            }
            t=p;
            t_=File.findSignature(p,File.getSize()-p-8,"00")-p;
            if(t_>=0) {
                p+=t_+1; a=p;
                a_=File.findSignature(p,File.getSize()-p-7,"00")-p;
                p+=a_+1; c=p;
                c_=File.findSignature(p,File.getSize()-p-6,"00")-p;
                //p+=c_+1 //unused
                if(Binary.isVerbose()) {
                    sOptionT(File.read_ansiString(t,t_));
                    sOptionT(File.read_ansiString(a,a_),"by: ");
                    sOptionT(File.read_ansiString(c,c_));
                }
            }
        }
    }
    //signature-enabled detection end
  }


// AND NOW,
// THE HEAVIEST STUFF GOES DOWN HERE


function isGMC() { //GMC detection is based purely on sanity checks
    ssize = 0; numpat = 0; data = 0;
    for(i = 0; i < 15; i++) {
        di7 = File.read_uint8(16*i+7); //volume
        if(di7>0x40) return false;
        len = File.read_uint16(16*i+4,_BE)<<1;
        lsize = File.read_uint16(16*i+12,_BE); //size
        if(len > 0xFFFF || lsize > len) return false;
        ssize += len }
    if(ssize<=4) return false;
    dF3 = File.read_uint8(0xF3); //ptn table size
    if(dF3 > 0x64 || dF3==0) return false;
    for (i = 0; i < 100; i++) { //ptn order table
        k = File.read_uint16(0xF4 + i * 2,_BE);
        if (k & 0x03FF) return false;
        kshr10 = k>>10; if (kshr10 > numpat) numpat = kshr10;
    } numpat++;
    if (numpat == 1 || numpat > 100) return false;
    for (i = 0; i < numpat; i++) { //test pattern data
        for (j = 0; j < 256; j++) {
            offset = 444 + i * 1024 + j * 4;
            if ((offset > 0x10000 - 4) || offset>File.getSize()) return false;
            d2 = File.read_uint8[offset+2]; d3 = File.read_uint8[offset+3];
            if ((d2 & 0x0f) == 3 && d3 > 0x40) return false;
            if ((d2 & 0x0f) == 4 && d3 > 0x63) return false;
            if ((d2 & 0x0f) == 5 && d3 > (data243 + 1)) return false;
            if ((d2 & 0x0f) == 6 && d3 >= 0x02) return false;
            if ((d2 & 0x0f) == 7 && d3 >= 0x02) return false;
        }
    } return true
}
    if(!bDetected && Binary.isDeepScan() && Binary.isHeuristicScan() && isGMC()) {
        bDetected = 1;
        sName = "Game Music Creator module (.GMC)";
    }

function isProTracker1() { //PT1 detection is based purely on sanity checks
    //ref zxtune src\formats\chiptune\aym\
    // & https://bulba.untergrund.net/Ay_Emul29.src.7z/Formats.pas -> FoundPT1
    if(File.getSize() < 0x66) return false;
    tempo = File.read_uint8(0); if((tempo < 2) || (tempo > 15)) return false; //delay
    ptntp = File.read_uint16(0x43,_LE); if(ptntp >= File.getSize()) return false; //patterns table pointer
    numofpos = File.read_uint8(1);// if(len == 0) return false; //numofpositions, can be corrupted?
    loop = File.read_uint8(2);
    j = 0; j1 = 65535;
    for(i=0; i < 0x10; i++) { //samples pointers
        sp = File.read_uint16(3+i*2,_LE); if(sp > File.getSize()) return false;
        if (j < sp) j=sp;
        op = File.read_uint16(0x23+i*2,_LE); if(op > File.getSize()) return false;
        if(op != 0 && j1 > op) j1 = op
    }
    if((j1 < 0x67) || (j < 0x67) || (j > 65534) || (j > File.getSize())
      || (j + File.read_uint8(j) * 3 + 2 != j1)) return false;
    j = 0;
    for(i=0; i < 0x10; i++) { //ornaments pointers
        op = File.read_uint16(0x23+i*2,_LE); if(op > File.getSize()) return false;
        if(j < op) j = op
    }
    if(j<0x67) return false;
    len = j+0x40; if(len>65536) return false; if(len>File.getSize()+1) return false;
    j = 0x63;
    while(j <= ptntp && File.read_uint8(j) < 0xFF) j++;
    if(j+1 != ptntp) return false;
    ord = j-0x63;
    if(loop > ord) return false;

    return true //return (ord==numofpos)
}
if(!bDetected && Binary.isHeuristicScan() && isProTracker1()) {
    sName = "ProTracker module (.PT1)"; sVersion = "v1.x"; bDetected = 1;
    if(Binary.isVerbose()) {
        sOptionT(File.read_ansiString(0x45,0x1E)); //musicname
        sOption("tempo:"+tempo+" ord:"+ord+" len:"+len)
    }
}

function isProTracker2() { //PT2 detection is based purely on sanity checks
    //ref zxtune src\formats\chiptune\aym\
    tmp = File.read_uint8(0); if((tmp<2)||(tmp>15)) return false;
    len = File.read_uint8(1); if(len==0) return false;
    loop = File.read_uint8(2); if(loop>len) return false;
    for(i=0;i<0x30;i++) if(File.read_uint16(0x03+i*2,_LE)>File.getSize()-2) return false;
    if(File.read_uint8(0x64) > 1) return false;
    for(ord=0; ord<=0xFF; ord++) {
        if(0x83+ord > File.getSize()-2) return false;
        posn = File.read_uint8(0x83+ord);
        if(posn==0xFF) break;
    }
    if((ord==0) || (ord>0xFF)) return false;
    return (ord==len)
}
if(!bDetected && Binary.isHeuristicScan() && isProTracker2()) {
    sName = "ProTracker module (.PT2)"; sVersion = "v2.x"; bDetected = 1;
    if(Binary.isVerbose()) {
        sOptionT(File.read_ansiString(0x65,0x1E));
        sOption("tmp:"+tmp+" ord:"+len)
    }
}

function isProTracker3() { //PT3's detection, ALSO sanity checks because SOME people -_-
    //ref zxtune src\formats\chiptune\aym\
    ftn = File.read_uint8(0x63); //0=Pro Tracker, 1=Sound Tracker, 2=ASM or PSC, 3=RealSound.
    tmp = File.read_uint8(0x64); if((tmp<3)||(tmp>63)) return false;
    len = File.read_uint8(0x65); if(len>0x55) return false;
    loop = File.read_uint8(0x66); if(loop>63) return false;
    pptn = File.read_uint16(0x67,_LE); if(pptn>File.getSize()-2) return false;
    for(i=0;i<0x30;i++) if(File.read_uint16(0x69+i*2,_LE)>File.getSize()-2) return false;
    for(ord=0;ord<=0xFF;ord++) {
        if(0xC9+ord>File.getSize()-2) return false;
        posn=File.read_uint8(0xC9+ord);
        if(posn==0xFF) break;
    }
    if((ord==0) || (ord>0xFF)) return false;
    return (ord==len)
}
if(!bDetected && Binary.isHeuristicScan() && isProTracker3()) {
    sName = "ProTracker module (.PT3)"; sVersion = "v3."+File.read_ansiString(0x0D,1);
    bDetected = 1;
    if(Binary.isVerbose()) {
        sOptionT(File.read_ansiString(0x1E,0x20));
        sOptionT(File.read_ansiString(0x42,0x20),"by: ")
        sOption("tmp:"+tmp+" ord:"+len)
    }
    switch(ftn) {
        case 0: sOption("Pro Tracker tuning"); break;
        case 1: sOption("Sound Tracker tuning"); break;
        case 2: sOption("ASM/PSC tuning"); break;
        case 3: sOption("RealSound tuning"); break;
        default: sOption("Unknown tuning")
    }
}

function isSoundTrackerProUnp() { //this detection is based purely on sanity checks
    //ref zxtune src\formats\chiptune\aym\soundtracker.cpp
    // & https://bulba.untergrund.net/STPDocs.7z -> Xplay79f.txt
    if(File.getSize()<3009+576) //header + patterns
        return false;
    const stpsmpnum=15; p=0;
    for(smp=0;smp<15;smp++) {
        for(i=0;i<32;i++) { j = File.read_uint8(p); if(j>0x0F) return false; p++ } //levels
        for(i=0;i<32;i++) { j = File.read_uint8(p); if(j&0x20) return false; p++ } //noises
        for(i=0;i<32;i++) { p++; j = File.read_uint8(p); if(j>0x1F) return false; p++ } //additions
        for(i=0;i<2;i++) { j = File.read_uint8(p); if(j>0x1F) return false; p++ } //loops
    }
    for(i=0;i<256;i++) { j = File.read_uint8(p); if((j<1) || (j>0x20)) return false; p+=2 } //positions
    // at this point, p should be 15*0x82
    if(File.read_uint8(p)&0x80) return false;
    p++;  p+=32*17; //ornaments
    dly=File.read_uint8(p); if((dly<1) || (dly>0x0F)) return false; p++;
    pts=File.read_uint8(p); if((pts<1) || (pts>0x40)) return false;
    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isSoundTrackerProUnp()) {
    sName = "SoundTracker Pro module (.STF)"; sVersion  = "uncompiled/unpacked"; bDetected = 1;
}

function isSoundTrackerPro() { //this detection is based purely on sanity checks
    // taken from https://bulba.untergrund.net/STPDocs.7z -> stp.txt
    tmp = File.read_uint8(0); if ((tmp == 0) || (tmp > 50)) return false;
    tabl1 = File.read_uint16(1,_LE); if (File.getSize()-2 < tabl1) return false;
    tabl2 = File.read_uint16(3,_LE); if (File.getSize()-2 < tabl2) return false; //pattern descriptors
    tabl3 = File.read_uint16(5,_LE); if (File.getSize()-2 < tabl3) return false; //ornament offsets
    tabl4 = File.read_uint16(7,_LE); if (File.getSize()-2 < tabl4) return false; //sample offsets
    len = File.read_uint8(tabl1); if (tabl2 != tabl1+2+len*2) return false;
    for(i=0;i<len;i++)  if (File.getSize() < File.read_uint16(tabl2+i*2,_LE)) return false;
    if (File.getSize() < tabl4+30) return false;
    for(i=0;i<15;i++)  if (File.getSize() < File.read_uint16(tabl4+i*2,_LE)) return false;

    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isSoundTrackerPro()) {
    sName = "SoundTracker Pro module (.STP)"; sVersion  = "uncompiled"; bDetected = 1;
    sOption("tmp:"+tmp+" ord:"+len)
}

function isSQTracker() { //this detection is based purely on sanity checks
    //ref https://bulba.untergrund.net/Ay_Emul29.src.7z/Players.pas -> FoundSQT
    if (File.getSize()<17 || File.getSize()>0x3600) return false;
    //sz = File.read_uint16(0,_LE); if (File.getSize() != sz) return false; //nobody seems to check this!
    smptp = File.read_uint16(0x02,_LE); if(smptp < 10) return false; //samples table ptr
    orntp = File.read_uint16(0x04,_LE); ptntp = File.read_uint16(0x06,_LE); //ornaments & patterns table ptrs
    postp = File.read_uint16(0x08,_LE); lpp = File.read_uint16(0x0A,_LE); //positions table ptr & loop ptr
    if(orntp <= smptp+1) return false; if(ptntp < orntp) return false;
    if(postp <= ptntp) return false; if(lpp < postp) return false;
    membase = smptp-10; //to eventually realign to file, not memory
    if(lpp-membase >= File.getSize()) return false;
    j1 = postp-membase; b = File.read_uint8(j1); if(b==0) return false; //can't have 0 patterns
    ord = 0; j2 = 0;
    while(b) { //find the highest pattern #
        if(j1+7 >= File.getSize()) return false;
        ord++;
        if(j2 < (b&0x7F)) j2 = b&0x7F; j1 += 2; b = File.read_uint8(j1);
        if(j2 < (b&0x7F)) j2 = b&0x7F; j1 += 2; b = File.read_uint8(j1);
        if(j2 < (b&0x7F)) j2 = b&0x7F; j1 += 3; b = File.read_uint8(j1)
    }
    ptn = j2;
    p = File.read_uint16(smptp-membase+2,_LE);
    if(p-ptntp-2 != j2*2) return false;
    p = 12; //len = j1+7;
    j2 = File.read_uint16(12,_LE);
    for(j1 = 1; j1 <= ((orntp-smptp)>>1); j1++) {
        p += 2; j3 = File.read_uint16(p,_LE);
        if(j3-j2 != 0x62) return false;
        j2 = j3;
    }
    for(j1 = 1; j1 <= ((ptntp-orntp)>>1); j1++) {
        p += 2; j3 = File.read_uint16(p,_LE);
        if(j3-j2 != 0x22) return false;
        j2 = j3;
    }

    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isSQTracker()) {
    sName = "Scalex Qjeta Tracker module (.SQT)"; sVersion  = "compiled"; bDetected = 1;
    sOption("ord:"+ord+" ptn:"+ptn)//+" len:"+len)
}

function isST() { //this detection is based purely on sanity checks
    //ref https://bulba.untergrund.net/Ay_Emul29.src.7z/Players.pas -> FoundST
    if (File.getSize() < 6) return false;
    tempo = File.read_uint8(0); if ((tempo == 0) || (tempo > 0x20)) return false; //delay
    postp = File.read_uint16(1,_LE); if (postp > File.getSize()) return false; //positions pointer
    orntp = File.read_uint16(3,_LE); //ornaments pointer
    ptntp = File.read_uint16(5,_LE); if(ptntp > File.getSize()) return false; //patterns pointer
    ord = File.read_uint8(postp);
    j2 = ptntp - orntp; if(j2 = 0) return false; //j2<0: ST, >0: S_SONIC
    fID = false;
    if(j2 > 0) { if(j2%0x21 > 0) return false; }
    else if(j2%0x21 > 0) {
        if((j1<55) || ((j1-55)%0x21 > 0)) return false;
        fID = true; //we'll check for a preset ID at the end;
    }
    j = File.read_uint8(postp)*2+3;
    if(j2 < 0) { if(j+j2 != 0) return false; }
    else if(j+postp-orntp != 0) {
        if((ptntp < 82) || (j+postp-ptntp+55 != 0)) return false;
        fID = true;
    } 
    j = orntp+0x21;
    if(j > 65535 || j > File.getSize()) return false;
    while(j) { j--; if(File.read_uint8(j) > 0) return false; if(j == orntp) break }
    j = ptntp; j1 = 0; j2 = 0; ptn = 0;
    while((j+6 <= File.getSize()) && (j+6 < 65536) && (File.read_uint8(j) < 0xFF)) {
        //find the highest channel data pointer
        j++; //ptn #
        j2 = File.read_uint16(j,_LE); if(j1 < j2) j1 = j2; j+=2; //channel A data ofs
        j2 = File.read_uint16(j,_LE); if(j1 < j2) j1 = j2; j+=2; //ditto B
        j2 = File.read_uint16(j,_LE); if(j1 < j2) j1 = j2; j+=2; //ditto C
        ptn++
    }
    if((File.read_uint8(j) < 0xFF) ) return false;
    if(j1 > File.getSize()) return false;
    if (File.read_uint8(j1-1) < 0xFF) return false;
    malformed = 0; //some modules are a bit more broken! Like "- unknown/ay5.stc"
    while(1) { //scan through the channel data at the end of file
        if(0x83 <= File.read_uint8(j1) <= 0x8E) j1++; //I don't get what this does, no docs either
        j1++;
        if(j1 > 65535) return false;
        if(j1 > File.getSize()) {malformed = 1; break } //return false; 
        if((File.read_uint8(j1) == 0xFF) || (j1 == File.getSize())) break
    }
    if(File.read_uint8(j1) == 0xFF) len = j1+1;
    // len = File.read_uint16(0x19,_LE); // because ~somebody~ used it for names
    if(fID)
      if( (File.read_ansiString(ptntp-55,29) != 'SOUND TRACKER COMPILATION OF ') &&
        (File.read_ansiString(ptntp-55,28) != 'KSA SOFTWARE COMPILATION OF ') ) return false;

    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isST()) {
    sName = "SoundTracker module (.STC)"; sVersion  = "v1.x"; bDetected = 1;
    if(malformed) sVersion += "/malformed"+malformed;
    sz = File.read_uint16(0x19,_LE); i_d = File.read_ansiString(7,18);
    if (['SONG BY ST COMPILE','SONG BY MB COMPILE','SONG BY ST-COMPILE',
      'SOUND TRACKER v1.1','S.T.FULL EDITION  ','SOUND TRACKER v1.3'].indexOf(i_d)>=0)
        msg = '';
    else
        msg = i_d;
    if(sz != File.getSize()) { //the message must've hit the size field, HAPPENS
        if(Binary.isVerbose()) sOption("ord:"+ord+" ptn:"+ptn+" len:"+len);
        if(32 <= (sz&0xFF) <= 0x7F) {
            msg += String.fromCharCode(sz&0xFF);
            if(32 <= (sz>>8) <= 0x7F) msg += String.fromCharCode(sz>>8)
        }
    } else
        if(sz == len) if(Binary.isVerbose()) sOption("ord:"+ord+" ptn:"+ptn+" len:"+sz); else;
        else if(Binary.isVerbose())
          sOption("ord:"+ord+" ptn:"+ptn+" sz:"+sz+" calc.len:"+len); //keeping len in: debug
    if(Binary.isVerbose()) sOptionT(msg,"msg: ");
}

function isV2M() { //this detection is based purely on sanity checks
    //ref https://github.com/farbrausch/fr_public/blob/master/v2/v2mplayer.cpp ::InitBase
    //with kind commentary from Tammo "kb" Hinrichs
    notes = 0; chs = 0;
    if(File.getSize()<480) return false;
    timediv = File.read_uint32(0,_LE); if(timediv==0) return false;
    maxtime = File.read_uint32(4,_LE); if(maxtime==0) return false;
    gdnum = File.read_uint32(8,_LE); if(gdnum==0) return false;
    p = 12+10*gdnum;
    for(var ch=0;ch<16;ch++) {
        notenum = File.read_uint32(p,_LE); p+=4; notes+=notenum;
        if(notenum) { chs++;
            p+=5*notenum;
            if(p>File.getSize()) return false;
            pcnum = File.read_uint32(p,_LE); p += 4+4*pcnum; //pgm change events
            if(p>File.getSize()) return false;
            pbnum = File.read_uint32(p,_LE); p += 4+5*pbnum; //pitch bends
            if(p>File.getSize()) return false;
            for(cn=0;cn<7;cn++) {
                ccnum = File.read_uint32(p,_LE); p += 4+4*ccnum; //control changes
                if(p>File.getSize()) return false;
            }
        }
    }
    size = File.read_uint32(p,_LE); if(size>16384) return false; //uint32 so "|| size<0" removed
    p+=4+size; if(p>File.getSize()) return false;
    size = File.read_int32(p,_LE);
    if(size>1048576 || size<0) return false;
    p+=4+size; if(p>File.getSize()) return false;

    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isV2M()) {
    sName = "farbrausch V2 Synthesizer module (.V2M)"; bDetected = 1;
    if(Binary.isVerbose()) {
        sOption(chs,"ch: "); sOption(notes,"notes: "); sOption(timediv,"timediv: "); sOption(maxtime,"maxtime: ")
    }
}

function isETracker() { //more sanity-check detections
    if(File.getSize()<96) return false; //just because modland's collection didn't have anything smaller
    //...but we have a signature this time too. Except it's not always there...
    if(Binary.compare('"ETracker (C) BY ESI."',10)) return true;
    if(File.read_uint8(1)>0x7F) return false;
    if(File.read_uint8(3)>0x7F) return false;
    if(File.read_uint8(5)>0x7F) return false;
    if(File.read_uint8(7)>0x7F) return false;
    if(File.read_uint8(9)>0x7F) return false;

    return true
}
if(!bDetected && Binary.isHeuristicScan() && isETracker()) {
    bDetected = 1;
    sName = "SAM Coupe/E-Tracker file (.COP,.SNG)";
}

function isADXAHX() { //non-module! sanity check a bit because 8000 is too broad
    if(!Binary.compare("8000")) return false;
    fmt = File.read_uint8(4);
    if([2,3,4,0x10,0x11].indexOf(fmt)<0) return false;
    if(File.read_uint8(6)!=4) return false; //4 bit per sample only
    if([1,2].indexOf(File.read_uint8(7))<0) return false; //1/2 ch only
    nV = File.read_uint8(0x12);
    if([3,4,5].indexOf(nV)<0) return false;
    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isADXAHX()) {
    //from https://wiki.multimedia.cx/index.php/CRI_ADX_file
    bDetected = 1;
    sVersion = "";
    if ([2,3,4].indexOf(fmt)>=0)
        sName = "CRI ADX 4-bit ADPCM audio (.ADX)";
    else if ([0x10,0x11].indexOf(fmt)>=0)
        sName = "CRI AHX 4-bit ADPCM audio (.AHX)";
    switch(nV) {
        case 3: sVersion="v3"; break;
        case 4: sVersion="v4"; break;
        case 5: sVersion="v4NL"
    }
    switch(fmt) {
        case 2: sVersion+=" PPC"; break;
        case 3: sVersion+=" std"; break;
        case 4: sVersion+=" exSc"; break;
        case 0x11: sVersion+=" AHX" }
    if(File.read_uint8(0x13)>0) sVersion += " encrypted";
    if (Binary.isVerbose()) {
        pcprt = File.read_uint16(2,_LE)-2;
        sOption("blk: "+File.read_uint8(5));
        sOption("ch: "+File.read_uint8(7));
        sOption("sr: "+File.read_uint32(8,_LE)+"Hz");
        sOption("smp: "+File.read_uint32(0x0C,_LE));
        sOption(File.read_ansiString(pcprt,6));
    }
}

// function isTuneFish4() { //this detection is based purely on sanity checks
//     // taken from tunefish src\tunefish4\source\synth\tfrecorder.cpp
//     //TODO
//     if() return false;
//     return true;
// }
// if(!bDetected && Binary.isHeuristicScan() && isTuneFish4()) {
//     sName = "TuneFish module (.TF4)"; sVersion  = "v4"; bDetected = 1;
//     //sOption(len,"ord:"); sOptionT(File.read_ansiString(7,18),"id: ")
// }



function isFLS() {
    //this detection is based purely on sanity checks
    //  AND a tiiiiiny ~bruteforce~ of membase, which is why it comes in last
    //ref https://bulba.untergrund.net/Ay_Emul29.src.7z/Players.pas -> FoundFLS
    if(File.getSize() < 6) return false;
    postp = File.read_uint16(0x00,_LE); orntp = File.read_uint16(0x02,_LE); //positions & ornaments table ptrs
    smptp = File.read_uint16(0x04,_LE); ptnp = 6; //samples table ptr & patterns
    j = postp - smptp; if((j < 0) || ((j&3) != 0)) return false;
    j = orntp-16; if(j < 0) return false;
    while(1) {
        j2 = smptp+2-j;
        if(j2 > File.getSize()+6) return false; // tentatively
        if(8 <= j2 < File.getSize()) {
            j3 = File.read_uint16(j2,_LE);
            j1 = j3 - j;
            if(8 <= j1 < File.getSize()) {
                j3 = File.read_uint16(j2 - 4);
                j2 = j3 - j;
                if(6 <= j2 < File.getSize())
                    if(j1-j2 == 0x20) {
                        j2 = File.read_uint16(ptnp+2,_LE)-j;
                        if(21 < j2 < File.getSize()) {
                            j1 = File.read_uint16(ptnp,_LE)-j;
                            if(20 < j1 < File.getSize())
                                if (!File.read_uint8(j1-1)) {
                                    while((j1 <= File.getSize()) && (File.read_uint8(j1) < 0xFF)) {
                                        while(1) {
                                            bt = File.read_uint8(j1);
                                            if((bt <= 0x5F) || (bt == 0x80) || (bt == 0x81)) { j1++; break }
                                            else if(0x82 <= bt <= 0x8E) j1++;
                                            if(j1>File.getSize()) break;
                                        }
                                    }
                                }
                                if(j1+1 == j2) break;
                        }
                    }
            }
        }
        j--; if(j < 0) break;
    }
    if(j < 0) return false;
    if((postp-j > File.getSize()) || (postp-j < 16)) return false;
    len = File.read_uint16(postp-j-2,_LE)+0x60-j;
    if(len <= postp-j) return false;
    if(len > File.getSize()+1) return false;
    if(((smptp-j)&1) != 0) return false;
    membase = j;
    return true;
}
if(!bDetected && Binary.isHeuristicScan() && isFLS()) {
    sName = "Flash Tracker module (.FLS)"; bDetected = 1; sVersion = "@"+Hex(j);
    if(Binary.isVerbose()) sOption(len,"len:")
}

    return result(bShowType,bShowVersion,bShowOptions);
}
