public OProg(byte[] data, byte[] footer, int id, int type)
        {
            this.type   = type;
            this.data   = data;
            this.footer = footer;
            this.id     = id;
            return;

            u0       = BinHelper.read_U16LE(footer, 0);
            exitsCnt = BinHelper.read_U16LE(footer, 2);
            if (type == 1)
            {
                entry    = exitsCnt;
                exitsCnt = 0;
                unpack();
                return;
            }
            actorsCnt = BinHelper.read_U16LE(footer, 4);
            u6        = BinHelper.read_U16LE(footer, 6);
            exits     = BinHelper.read_U16LE(footer, 8);
            actors    = BinHelper.read_U16LE(footer, 0x0A);
            sprites   = BinHelper.read_U16LE(footer, 0x0C);
            entry     = BinHelper.read_U16LE(footer, 0x0E);
            if (u0 != 0 || u6 != exits)
            {
                throw new Exception("Unknown prog values");
            }
            unpack();
        }
        void process()
        {
            BinaryReader rd  = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read));
            int          len = rd.ReadUInt16();

            rd.BaseStream.Position = 0;
            data   = rd.ReadBytes(len);
            footer = rd.ReadBytes((int)(rd.BaseStream.Length - rd.BaseStream.Position));
            rd.Close();
            descramble(data, 2);
            progcnt[0]       = BinHelper.read_U16LE(footer, 0x06);
            progcnt[1]       = BinHelper.read_U16LE(footer, 0x08);
            progmap          = BinHelper.read_U16LE(footer, 0x0A);
            actorsPart.count = BinHelper.read_U16LE(footer, 0x10);
            actorsPart.ofs   = BinHelper.read_U16LE(footer, 0x12);
            entry            = BinHelper.read_U16LE(footer, 0x42);
            charmap          = BinHelper.read_U16LE(footer, 0x48);
            graphsPart.count = BinHelper.read_U16LE(footer, 0x20);
            graphsPart.ofs   = BinHelper.read_U16LE(footer, 0x22);
            if (pref == "cer")
            {
                proc_cer();
            }
            parse();
        }
        void proc_cer()
        {
            musPart.count = BinHelper.read_U16LE(footer, 0x34);
            musPart.ofs   = BinHelper.read_U16LE(footer, 0x36);
            int bid = 0;

            strings.Add(new StringBlock(bid++, 0x72C9, 0x7353, data));
            strings.Add(new StringBlock(bid++, 0xC100, 0xC26E, data));
            strings.Add(new StringBlock(bid++, 0xC4DD, 0xD55C, data));
        }
        void unpack()
        {
            int spos = 2;
            int slen = data.Length - spos;

            if (type == 0)
            {
                List <UInt16> rms = new List <UInt16>();
                UInt16        rm  = 0;
                while (rm != 0xFFFF)
                {
                    rm    = BinHelper.read_U16LE(data, spos);
                    spos += 2;
                    rms.Add(rm);
                }
                rooms = rms.ToArray();
                slen  = exits - spos;
                if (actors - exits > 0)
                {
                    exit_data = new byte[actors - exits];
                    Array.Copy(data, exits, exit_data, 0, exit_data.Length);
                }
                if (sprites - actors > 0)
                {
                    actor_data = new byte[sprites - actors];
                    Array.Copy(data, actors, actor_data, 0, actor_data.Length);
                }
                if (entry - sprites > 0)
                {
                    sprite_data = new byte[entry - sprites];
                    Array.Copy(data, sprites, sprite_data, 0, sprite_data.Length);
                }
            }
            else
            {
                slen = entry - spos;
            }
            code = new byte[data.Length - entry];
            Array.Copy(data, entry, code, 0, code.Length);
            int enda = spos + slen;
            int sid  = 0;

            while (spos < enda)
            {
                strings.Add(new OString(sid++, data, (UInt16)spos));
                spos += strings[strings.Count - 1].length + 1;
            }
        }
        public static string decompile(OProg prog, string eol)
        {
            foreach (OProg.OString s in prog.strings)
            {
                s.state = 0;
            }
            int pos = 0;

            byte[] data = prog.code;
            string res  = "";

            while (pos < data.Length)
            {
                byte op = data[pos++];
                if (op >= Opcodes.OPCODE_SIZE)
                {
                    throw new Exception(string.Format("Bad Opcode {0:X2} @ {1:X4}", op, pos));
                }
                foreach (Opcodes.opcode b in Opcodes.opcodes)
                {
                    if (b.op == op)
                    {
                        string s = b.name + "(";
                        for (int i = 0; i < b.prms; i++)
                        {
                            byte ptp = data[pos++];
                            s   += getParam(ptp, BinHelper.read_U16LE(data, pos), prog);
                            pos += 2;
                            if (i < b.prms - 1)
                            {
                                s += ",";
                            }
                        }
                        s   += ")" + eol;
                        res += s;
                        break;
                    }
                }
            }
            return(res);
        }
            public OString(int id, byte[] data, UInt16 ofs)
            {
                this.id  = id;
                this.ofs = ofs;
                int    len = 0;
                byte   b   = 0;
                string s   = "";

                while ((b = data[ofs + len]) != 0)
                {
                    len++;
                    switch (b)
                    {
                    case RETURN:
                        s += "\\r";
                        break;

                    case SLASH:
                        s += "\\\\";
                        break;

                    case COUNT_SP_TERM:
                        s += "\\[t]";
                        break;

                    case STRING_MOVE:
                        UInt16 p1 = BinHelper.read_U16LE(data, ofs + len);
                        UInt16 p2 = BinHelper.read_U16LE(data, ofs + len + 2);
                        len += 4;
                        s   += String.Format("\\[r{0:X4},{1:X4}]", p1, p2);
                        break;

                    case STRING_MENU_OPTION:
                        List <byte> nm = new List <byte>();
                        while (data[ofs + len] != 0)
                        {
                            nm.Add(data[ofs + len]);
                            len++;
                        }
                        UInt16 p4 = BinHelper.read_U16LE(data, ofs + len);
                        len += 2;
                        s   += String.Format("\\[o{0:s},{1:X4}]", new String(Encoding.Unicode.GetChars(
                                                                                 Encoding.Convert(Encoding.GetEncoding(866), Encoding.Unicode, nm.ToArray())
                                                                                 )), p4);
                        break;

                    case COUNT_SP0:
                        s += "\\[n]";
                        break;

                    case COUNT_SP1:
                        s += "\\[s]";
                        break;

                    case CENTER:
                        s += "\\[m]";
                        break;

                    case WAIT:
                        s += "\\[w]";
                        break;

                    case SHARP:
                        s += "!";
                        break;

                    case ADVANCE:
                        s += String.Format("\\[c{0:X2}]", data[ofs + len]);
                        len++;
                        break;

                    case DEF_COLOUR:
                        s += "\\[d]";
                        break;

                    case SET_COLOUR:
                        s += String.Format("\\[c{0:X2}]", data[ofs + len]);
                        len++;
                        break;

                    case GLOBAL_WORD:
                        UInt16 p3 = BinHelper.read_U16LE(data, ofs + len);
                        len += 2;
                        s   += String.Format("\\[g{0:X4}]", p3);
                        break;

                    default:
                        if (b < 0x20 || (b > 0xAF && (b < 0xE0 || b > 0xF1)))
                        {
                            s += String.Format("\\[x{0:X2}]", b);
                        }
                        else
                        {
                            s += new String(Encoding.Unicode.GetChars(
                                                Encoding.Convert(Encoding.GetEncoding(866), Encoding.Unicode, new byte[] { b })
                                                ));
                        }
                        break;
                    }
                }
                this.data = s;
                length    = len;
            }