function StateMachine(worker, video, isGL) {
  this.table = [ {
    name: "RESET      ",
    bits: 0,
    next0: START,
    next1: START,
    func: "reset"
  }, {
    name: "START      ",
    bits: 1,
    next0: PIXELS,
    next1: CMD,
    func: "no_op"
  }, {
    name: "PIXELS     ",
    bits: 1,
    next0: PIXFAN,
    next1: PIXLRU1,
    func: "no_op"
  }, {
    name: "PIXLRU1    ",
    bits: 1,
    next0: PIXELS,
    next1: PIXRPT1,
    func: "pixcodex"
  }, {
    name: "PIXLRU0    ",
    bits: 1,
    next0: PIXELS,
    next1: PIXRPT1,
    func: "pixcodex"
  }, {
    name: "PIXCODE1   ",
    bits: 1,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixcodex"
  }, {
    name: "PIXCODE2   ",
    bits: 2,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixcodex"
  }, {
    name: "PIXCODE3   ",
    bits: 3,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixcodex"
  }, {
    name: "PIXGREY    ",
    bits: 4,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixgrey"
  }, {
    name: "PIXRGBR    ",
    bits: 4,
    next0: PIXRGBG,
    next1: PIXRGBG,
    func: "pixrgbr"
  }, {
    name: "PIXRPT     ",
    bits: 1,
    next0: PIXELS,
    next1: PIXRPT1,
    func: "no_op"
  }, {
    name: "PIXRPT1    ",
    bits: 1,
    next0: PIXDUP,
    next1: PIXRPTSTD1,
    func: "no_op"
  }, {
    name: "PIXRPTSTD1 ",
    bits: 3,
    next0: PIXELS,
    next1: PIXELS,
    func: "pixrptstd1"
  }, {
    name: "PIXRPTSTD2 ",
    bits: 3,
    next0: PIXELS,
    next1: PIXELS,
    func: "pixrptstd2"
  }, {
    name: "PIXRPTNSTD ",
    bits: 8,
    next0: PIXELS,
    next1: PIXELS,
    func: "pixrptnstd"
  }, {
    name: "CMD        ",
    bits: 1,
    next0: CMD0,
    next1: MOVEXY0,
    func: "no_op"
  }, {
    name: "CMD0       ",
    bits: 1,
    next0: CMDX,
    next1: EXTCMD,
    func: "no_op"
  }, {
    name: "MOVEXY0    ",
    bits: 7,
    next0: MOVEXY1,
    next1: MOVEXY1,
    func: "movexy0"
  }, {
    name: "EXTCMD     ",
    bits: 1,
    next0: BLKRPT,
    next1: EXTCMD1,
    func: "no_op"
  }, {
    name: "CMDX       ",
    bits: 1,
    next0: MOVESHORTX,
    next1: MOVELONGX,
    func: "no_op"
  }, {
    name: "MOVESHORTX ",
    bits: 3,
    next0: START,
    next1: START,
    func: "moveshortx"
  }, {
    name: "MOVELONGX  ",
    bits: 7,
    next0: START,
    next1: START,
    func: "movelongx"
  }, {
    name: "BLKRPT     ",
    bits: 1,
    next0: BLKDUP,
    next1: BLKRPT1,
    func: "no_op"
  }, {
    name: "EXTCMD1    ",
    bits: 1,
    next0: EXTCMD2,
    next1: FIRMWARE,
    func: "no_op"
  }, {
    name: "FIRMWARE   ",
    bits: 8,
    next0: CORP,
    next1: CORP,
    func: "firmware"
  }, {
    name: "EXTCMD2    ",
    bits: 1,
    next0: MODE0,
    next1: TIMEOUT,
    func: "no_op"
  }, {
    name: "MODE0      ",
    bits: 7,
    next0: MODE1,
    next1: MODE1,
    func: "movexy0"
  }, {
    name: "TIMEOUT    ",
    bits: 0,
    next0: START,
    next1: START,
    func: "timeout"
  }, {
    name: "BLKRPT1    ",
    bits: 1,
    next0: BLKRPTSTD,
    next1: BLKRPTNSTD,
    func: "no_op"
  }, {
    name: "BLKRPTSTD  ",
    bits: 3,
    next0: START,
    next1: START,
    func: "blkrptstd"
  }, {
    name: "BLKRPTNSTD ",
    bits: 7,
    next0: START,
    next1: START,
    func: "blkrptnstd"
  }, {
    name: "PIXFAN     ",
    bits: 1,
    next0: PIXSPEC,
    next1: PIXCODE,
    func: "no_op"
  }, {
    name: "PIXCODE4   ",
    bits: 4,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixcodex"
  }, {
    name: "PIXDUP     ",
    bits: 0,
    next0: PIXELS,
    next1: PIXELS,
    func: "pixdup"
  }, {
    name: "BLKDUP     ",
    bits: 0,
    next0: START,
    next1: START,
    func: "blkdup"
  }, {
    name: "PIXCODE    ",
    bits: 0,
    next0: PIXCODE,
    next1: PIXCODE,
    func: "pixcode"
  }, {
    name: "PIXSPEC    ",
    bits: 1,
    next0: PIXGREY,
    next1: PIXRGB,
    func: "no_op"
  }, {
    name: "EXIT       ",
    bits: 0,
    next0: EXIT,
    next1: EXIT,
    func: "exit"
  }, {
    name: "LATCHED    ",
    bits: 1,
    next0: LATCHED,
    next1: LATCHED,
    func: "latched"
  }, {
    name: "MOVEXY1    ",
    bits: 7,
    next0: START,
    next1: START,
    func: "movexy1"
  }, {
    name: "MODE1      ",
    bits: 7,
    next0: MODE2,
    next1: MODE2,
    func: "mode1"
  }, {
    name: "PIXRGBG    ",
    bits: 4,
    next0: PIXRGBB,
    next1: PIXRGBB,
    func: "pixrgbg"
  }, {
    name: "PIXRGBB    ",
    bits: 4,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixrgbb"
  }, {
    name: "HUNT       ",
    bits: 1,
    next0: HUNT,
    next1: RESET,
    func: "hunt"
  }, {
    name: "PRINT0     ",
    bits: 8,
    next0: PRINT1,
    next1: PRINT1,
    func: "print0"
  }, {
    name: "PRINT1     ",
    bits: 8,
    next0: PRINT1,
    next1: PRINT1,
    func: "print1"
  }, {
    name: "CORP       ",
    bits: 1,
    next0: START,
    next1: FIRMWARE,
    func: "corp"
  }, {
    name: "MODE2      ",
    bits: 4,
    next0: START,
    next1: START,
    func: "mode2"
  }, {
    name: "PIXRGB     ",
    bits: 12,
    next0: PIXRPT,
    next1: PIXRPT,
    func: "pixrgb"
  } ], this.decoder_state = RESET, this.next_state = RESET, this.zero_count = 0, this.count_bytes = 0,
  this.ib_acc = 0, this.ib_bcnt = 0, this.sizex = 50, this.sizey = 37.5, this.blockwidth = 16,
  this.blockheight = 16, this.red = this.green = this.blue = 0, this.alpha = 255,
  this.ashift = 24, this.rshift = 20, this.gshift = 12, this.bshift = 4, this.video = video,
  this.stm_latched = !1, this.debug_state = !1, this.newx = this.newy = 0, this.sizex = 0,
  this.sizey = 0, this.sizey1 = 0, this.pixel_count = 0, this.block_count = 0, this.lastx = 0,
  this.lasty = 0, this.fatal_count = 0, this.timeout_count = -1, this.cmd_idx = 0,
  this.cmd_buf = [], this.print_chan = 0, this.print_buf = [], this.cache = new ColorCache(),
  this.pixels = null, this.cipher = "", this.licensed_irc = !1, this.licensed_murc = !1,
  this.licensed_playback = !1, this.licensed_scripted_vm = !1, this.vm_url_floppy_connected = !1,
  this.vm_url_cdrom_connected = !1, this.vm_applet_floppy_connected = !1, this.vm_applet_cdrom_connected = !1,
  this.half_height_capable = !1, this.process_header = function(cmd_buf) {
    this.set_bitspercolor(cmd_buf[0]), this.set_encryption(cmd_buf[1]), this.set_licensed(cmd_buf[2]),
    this.set_flags(cmd_buf[3]);
  }, this.set_licensed = function(lic) {
    this.licensed_irc = 1 == (1 & lic), this.licensed_playback = 2 == (2 & lic), this.licensed_murc = 4 == (4 & lic),
    this.licensed_scripted_vm = 8 == (8 & lic), video.notify_license({
      irc: this.licensed_irc,
      playback: this.licensed_playback,
      murc: this.licensed_murc
    });
  }, this.set_flags = function(flags) {
    this.vm_url_floppy_connected = 2 == (2 & flags), this.vm_url_floppy_connected && video.vm_change({
      isFloppy: !0,
      isURL: !0,
      isConnected: !0
    }), this.vm_url_cdrom_connected = 4 == (4 & flags), this.vm_url_cdrom_connected && video.vm_change({
      isFloppy: !1,
      isURL: !0,
      isConnected: !0
    }), this.half_height_capable = 8 == (8 & flags);
  }, this.set_encryption = function(encryption) {
    switch (encryption) {
     case 1:
      this.cipher = "RC4";
      break;

     case 2:
      this.cipher = "AES";
      break;

     case 3:
      this.cipher = "AES-256";
      break;

     case 0:
     default:
      this.cipher = "";
    }
  }, this.set_bitspercolor = function(bpc) {
    bpc = 5 - (3 & bpc), this.table[PIXGREY].bits = bpc, this.table[PIXRGBR].bits = bpc,
    this.table[PIXRGBG].bits = bpc, this.table[PIXRGBB].bits = bpc, this.table[PIXRGB].bits = 3 * bpc,
    this.rshift = 24 - 3 * bpc, this.gshift = 16 - 2 * bpc, this.bshift = 8 - bpc, this.cscale = bpc,
    this.bmask = (1 << bpc) - 1, this.gmask = this.bmask << bpc, this.rmask = this.gmask << bpc;
  }, this.set_halfheight = function(hh) {
    hh ? (this.table[MOVELONGX].bits = 8, this.table[MOVEXY0].bits = 8, this.table[MOVEXY1].bits = 8,
    this.table[BLKRPTNSTD].bits = 8, this.blockheight = 8) : (this.table[MOVELONGX].bits = 7,
    this.table[MOVEXY0].bits = 7, this.table[MOVEXY1].bits = 7, this.table[BLKRPTNSTD].bits = 7,
    this.blockheight = 16), this.pixels = this.new_pixels();
  }, this.intmode = function() {
    this.new_pixels = this.new_pixels_intmode, this.store_pixel = this.store_pixel_intmode,
    this.pixels = this.new_pixels();
  }, this.new_pixels_imagedata = function() {
    return this.context.createImageData(this.blockwidth, this.blockheight);
  }, this.imagemode = function(context) {
    this.context = context, context ? this.new_pixels = this.new_pixels_imagedata : this.new_pixels = this.new_pixels_orig,
    this.store_pixel = this.store_pixel_imagedata, this.pixels = this.new_pixels();
  }, this.reset = function(code) {
    this.cache.reset(), this.pixel_count = 0, this.block_count = 0, this.lastx = 0,
    this.lasty = 0, this.fatal_count = 0, this.timeout_count = -1, this.red = this.green = this.blue = 0,
    this.table[PIXFAN].next1 = PIXCODE;
  }, this.moveshortx = function(code) {
    code += this.lastx + 1, this.lastx = code;
  }, this.movelongx = function(code) {
    this.lastx = code;
  }, this.movexy0 = function(code) {
    this.newx = code;
  }, this.movexy1 = function(code) {
    this.newy = code, this.lastx = this.newx, this.lasty = this.newy;
  }, this.pixcode = function(code) {
    this.next_state = this.cache.pixcode;
  }, this.pixcodex = function(code) {
    1 === this.cache.active ? code = 0 : this.decoder_state === PIXLRU0 ? code = 0 : this.decoder_state === PIXLRU1 ? code = 1 : code && (code += 1);
    var color = this.cache.find(code);
    color ? this.store_pixel(color, 1) : this.next_state = LATCHED;
  }, this.pixrgb = function(code) {
    var color = code << 1 | 1, ret = this.cache.lru(color);
    this.table[PIXFAN].next1 = this.cache.pixcode, ret || this.store_pixel(color, 1);
  }, this.pixrgbr = function(code) {
    this.red = code;
  }, this.pixrgbg = function(code) {
    this.green = code;
  }, this.pixrgbb = function(code) {
    this.blue = code;
    var r = this.red << this.rshift, g = this.green << this.gshift, b = this.blue << this.bshift, a = this.alpha << this.ashift, color = r | g | b | a, ret = this.cache.lru(color);
    this.table[PIXFAN].next1 = this.cache.pixcode, ret ? this.count_bytes > 6 : this.store_pixel(color, 1);
  }, this.pixgrey = function(code) {
    var color = (code << 2 * this.cscale | code << this.cscale | code) << 1 | 1, ret = this.cache.lru(color);
    this.table[PIXFAN].next1 = this.cache.pixcode, ret ? this.count_bytes > 6 : this.store_pixel(color, 1);
  }, this.pixrptstd1 = function(code) {
    7 == code ? this.next_state = PIXRPTNSTD : 6 == code ? this.next_state = PIXRPTSTD2 : this.store_pixel(this.last_color, code + 2);
  }, this.pixrptstd2 = function(code) {
    code += 8, this.pixrptnstd(code);
  }, this.pixrptnstd = function(code) {
    this.decoder_state == PIXRPTNSTD && 16 > code, this.store_pixel(this.last_color, code);
  }, this.pixdup = function(code) {
    this.store_pixel(this.last_color, 1);
  }, this.timeout = function(code) {
    this.timeout_count == this.count_bytes - 1 && (this.next_state = LATCHED), this.getbit(7 & this.ib_bcnt),
    this.timeout_count = this.count_bytes;
  }, this.firmware = function(code) {
    0 == this.cmd_idx ? this.cmd_buf = [] : this.cmd_buf.push(this.cmd_last), this.cmd_idx++,
    this.cmd_last = code;
  }, this.corp = function(code) {
    0 == code && (1 == this.cmd_last ? this.next_state = EXIT : 2 == this.cmd_last ? this.next_state = PRINT0 : 3 == this.cmd_last ? this.cmd_idx : 4 == this.cmd_last ? video.power_change("ON") : 5 == this.cmd_last ? (this.newx = 50,
    this.newy = 38, this.video.clear(), video.power_change("OFF")) : 6 == this.cmd_last ? (this.video.clear(),
    this.video.show_text(-1, "Video Suspended")) : 7 == this.cmd_last || 8 == this.cmd_last || (9 == this.cmd_last ? 7 & this.ib_bcnt && this.getbit(7 & this.ib_bcnt) : 10 == this.cmd_last ? (this.video.clear(),
    this.video.show_text(-1, "Session Acquired by another user")) : 11 == this.cmd_last ? this.set_bitspercolor(this.cmd_buf[0]) : 12 == this.cmd_last || (13 == this.cmd_last ? this.process_header(this.cmd_buf) : 16 == this.cmd_last ? this.video.send_ack() : 128 == this.cmd_last)),
    this.cmd_idx = 0);
  }, this.print0 = function(code) {
    this.print_chan = code, this.print_buf = [];
  }, this.print1 = function(code) {
    code ? this.print_buf.push(String.fromCharCode(code)) : (this.video.show_text(this.print_chan, this.print_buf.join("")),
    this.next_state = START);
  }, this.latched = function(code) {
    0 == this.fatal_count && (this.debugx = this.lastx, this.debugy = this.lasty), 40 == this.fatal_count && this.video.refresh(),
    this.fatal_count += 1, 12e4 == this.fatal_count ? this.video.refresh() : 12e6 == this.fatal_count && (this.fatal_count = 41,
    this.video.refresh());
  }, this.blkdup = function(code) {
    this.next_block(1);
  }, this.blkrptstd = function(code) {
    code += 2, this.next_block(code);
  }, this.blkrptnstd = function(code) {
    this.next_block(code);
  }, this.mode1 = function(code) {
    (0 == this.newx || 0 == code) && (this.newx = 50, code = 38, this.video.clear()),
    this.sizex = this.newx, this.sizey = code;
  }, this.mode2 = function(code) {
    this.lastx = 0, this.lasty = 0, this.pixel_count = 0, this.sizey1 = code, x = 16 * this.sizex,
    y = 16 * this.sizey + this.sizey1, this.cache.reset(), this.video.set_size(x, y),
    this.set_halfheight(x > 1616 ? !0 : !1);
  }, this.hunt = function(code) {
    this.next_state != this.decoder_state && (this.ib_bcnt = 0, this.ib_acc = 0, this.zero_count = 0,
    this.count_bytes = 0);
  }, this.exit = function(code) {
    this.halt = !0;
  }, this.no_op = function(code) {}, this.process = function(data) {
    for (var i = 0; i < data.length; i++) {
      this.addbits(255 & data[i]);
      for (var loop = !0; loop && this.decoder_state !== EXIT; ) {
        this.decoder_state > this.table.length;
        var entry = this.table[this.decoder_state], getlen = entry.bits;
        if (getlen > this.ib_bcnt) break;
        var code = this.getbit(getlen);
        !this.stm_latched && this.debug_state, this.stm_latched = this.decoder_state === LATCHED;
        var n = code ? "next1" : "next0";
        this.next_state = this.table[this.decoder_state][n];
        var f = entry.func;
        if (this[f](code), this.next_state === PIXELS && this.pixel_count === this.blockheight * this.blockwidth && (this.next_block(1),
        this.cache.prune(), this.table[PIXFAN].next1 = this.cache.pixcode), this.decoder_state === this.next_state && this.decoder_state !== PRINT1 && this.decoder_state !== LATCHED && this.decoder_state !== HUNT) return 1;
        this.decoder_state = this.next_state;
      }
    }
    return 0;
  }, this.process_buffer = function(new_bits) {
    this.addbits_buffer(new_bits);
    for (var loop = !0; loop && this.decoder_state !== EXIT; ) {
      this.decoder_state > this.table.length;
      var entry = this.table[this.decoder_state], getlen = entry.bits;
      if (getlen > this.ib_bcnt) break;
      var code = this.getbit(getlen);
      !this.stm_latched && this.debug_state, this.stm_latched = this.decoder_state == LATCHED;
      var n = code ? "next1" : "next0";
      this.next_state = this.table[this.decoder_state][n];
      var f = entry.func;
      if (this[f](code), this.next_state == PIXELS && this.pixel_count == this.blockheight * this.blockwidth && (this.next_block(1),
      this.cache.prune(), this.table[PIXFAN].next1 = this.cache.pixcode), this.decoder_state == this.next_state && this.decoder_state != PRINT1 && this.decoder_state != LATCHED && this.decoder_state != HUNT) return 1;
      this.decoder_state = this.next_state;
    }
    return 0;
  }, this.store_pixel = function(color, count) {
    for (var p = 3 * this.pixel_count, i = 0; count > i; i++) {
      if (!(this.pixel_count < this.blockwidth * this.blockheight)) return void (this.next_state = LATCHED);
      this.pixels[p++] = String.fromCharCode(255 & color), this.pixels[p++] = String.fromCharCode(color >> 8 & 255),
      this.pixels[p++] = String.fromCharCode(color >> 16 & 255), this.pixel_count += 1;
    }
    this.last_color = color;
  }, this.store_pixel_intmode = function(color, count) {
    var col = color;
    this.pixels.fill(col, this.pixel_count, this.pixel_count + count), this.pixel_count += count,
    this.last_color = color;
  }, this.store_pixel_imagedata = function(color, count) {
    for (var col = color >> 1, p = 4 * this.pixel_count, i = 0; count > i; i++) {
      if (!(this.pixel_count < this.blockwidth * this.blockheight)) return void (this.next_state = LATCHED);
      this.pixels[p++] = (col & this.rmask) << this.rshift >> 16, this.pixels[p++] = (col & this.gmask) << this.gshift >> 8,
      this.pixels[p++] = (col & this.bmask) << this.bshift, this.pixels[p++] = 255, this.pixel_count += 1;
    }
    this.last_color = color;
  }, this.next_block = function(count) {
    this.next_state = START, this.pixel_count = 0;
    for (var w = this.blockwidth, h = this.blockheight, startx = this.lastx, starty = this.lasty, areah = h, i = 0; count > i; i++) {
      var x0 = this.lastx * w, y0 = this.lasty * h;
      this.video.blit(x0, y0, w, h, this.pixels), this.lastx += 1, this.lastx > this.sizex && (this.lastx = this.sizex - this.lastx,
      this.lasty += 1, this.lasty > this.sizey && (this.lasty = 0), startx = this.lastx,
      areah += h), this.block_count += 1;
    }
    this.video.render(startx * w, starty * h, w * count, areah);
  }, this.new_pixels_imagedata = function() {
    return this.context.createImageData(this.blockwidth, this.blockheight);
  }, this.new_pixels_intmode = function() {
    return new Uint16Array(this.blockwidth * this.blockheight);
  }, this.new_pixels_orig = function() {
    return new Uint8Array(this.blockwidth * this.blockheight * 4);
  }, this.getbit = function(length) {
    if (0 == length) return 0;
    var temp = this.ib_acc & (1 << length) - 1;
    return this.ib_bcnt -= length, this.ib_acc >>= length, temp = temp >> 8 & 255 | temp << 8 & 65280,
    temp = temp >> 4 & 3855 | temp << 4 & 61680, temp = temp >> 2 & 13107 | temp << 2 & 52428,
    temp = temp >> 1 & 21845 | temp << 1 & 43690, temp >>= 16 - length;
  }, this.addbits = function(bits) {
    return this.zero_count += RIGHT[bits], this.count_bytes += 1, this.ib_acc |= bits << this.ib_bcnt,
    this.ib_bcnt += 8, this.count_bytes % 1e3 == 0, this.zero_count > 30 ? (this.decoder_state == LATCHED && this.fatal_count < 40,
    this.decoder_state = this.next_state = HUNT, this.ib_bcnt = 8, 4) : (0 != bits && (this.zero_count = LEFT[bits]),
    0);
  }, this.setvideo = function(dvc_video) {
    this.video = dvc_video;
  };
}