Example #1
0
class FramebufferParser(SignalReceiver, SocketObserver, EeekObject):
    def __init__(self):
        EeekObject.__init__(self)
        SignalReceiver.__init__(self)
        SocketObserver.__init__(self)

        self.gameStarted = False
        self.screen      = []

        self.state       = ""
        self.y           = 0
        self.x           = 0
        self.color       = TermColor()

        self._file       = open("logs/frames.txt", "wa")
        self.firstParse  = True
        self.last        = ""

        for x in range(0, WIDTH*HEIGHT):
            self.screen.append(FBTile())

        self.ansi =\
            {
                "\xf0"     : "ignore",
                "\x1b[m"   : "reset color",
                "\x1b[0m"  : "reset color",
                "\x1b[1m"  : "bold",
                "\x1b[7m"  : "reverse",
                "\x1b[?47l": "ignore",
                "\x1b[?47h": "ignore",
                "\x1b[?1l" : "ignore",
                "\x1b8"    : "ignore",
                "\x1b7"    : "ignore",
                "\xff\xfcc": "ignore",
                "\xfa"     : "ignore",
                "\x00"     : "ignore",
                "\x08"     : "backspace",
                "\x0a"     : "LF",
                "\x0a"     : "LF",
                "\x0d"     : "CR",
                "\x1b(B"   : "ignore",
                "\x1b)0"   : "ignore",
                "\x1b>"    : "ignore",
                "\x1b[?1l" : "ignore",
                "\x1b[H"   : "home",
                "\x1b[J"   : "clear to end of screen",
                "\x1b[0J"  : "clear to end of screen",
                "\x1b[1J"  : "clear to beginning",
                "\x1b[2J"  : "clear screen",
                "\x1b[K"   : "clear to end of line",
                "\x1b[A"   : "up",
                "\x1b[B"   : "down",
                "\x1b[C"   : "right",
                "\x1b[D"   : "left",
                "\x1by"    : "ignore",
            }
        for x in range(ord(' '), ord('~')+1):
            self.ansi[chr(x)] = "print"

    def mapTiles(self):
        return self.screen[WIDTH:-WIDTH*2]

    def getChars(self):
        return "".join(x.char for x in self.screen)
    def mapLines(self):
        return "".join(x.char for x in self.screen[WIDTH:-2*WIDTH])
    def topLine(self):
        return "".join(x.char for x in self.screen[:WIDTH])
    def botLines(self):
        return "".join(x.char for x in self.screen[22*WIDTH:])
    def getRowLine(self, row):
        if row < 1 or row > 24:
            return ""
        return "".join(x.char for x in self.screen[(row-1)*WIDTH:row*WIDTH])

    def printChar(self, char):
        cur = self.screen[self.x + self.y*WIDTH]
        cur.set(char, self.color.copy())
        self.x = self.x+1

    def log(self, line):
        self._file.write(line+"\n")
        self._file.flush()

    def parse(self, line):
        self.last  = line

        self.state = ""
        if not self.gameStarted:
            sys.stdout.write(line)
            sys.stdout.flush()
            return

        for char in line:
            self.state = self.state + char
            if self.state in self.ansi:
                action = self.ansi[self.state]
                if action == "print":
                    self.printChar(char)
                elif action == "reset color":
                    self.color = TermColor()
                elif action == 'CR':
                    self.x = 0
                elif action == 'backspace':
                    self.x = self.x - 1
                elif action in ('LF', "down"):
                    self.y = self.y + 1
                elif action == "up":
                    self.y = self.y - 1
                elif action == "left":
                    self.x = self.x - 1
                elif action == "right":
                    self.x = self.x + 1
                elif action == 'ignore':
                    pass
                elif action == "home":
                    self.x = 0
                    self.y = 0
                elif action == "clear to end of screen":
                    for i in range(self.x + self.y * WIDTH, WIDTH * HEIGHT):
                        self.screen[i].set(' ', TermColor())
                elif action == "clear to end of line":
                    for i in range(self.x + self.y * WIDTH, WIDTH * (self.y + 1)):
                        self.screen[i].set(' ', TermColor())
                elif action == "clear to beginning":
                    for i in range(self.x + self.y * WIDTH, 0, -1):
                        self.screen[i].set(' ', TermColor())
                elif action == "clear screen":
                    for i in range(0, HEIGHT*WIDTH):
                        self.screen[i].set(' ', TermColor())
                elif action == "bold":
                    self.color.bold = True
                elif action == "reverse":
                    self.color.reverse = True

                self.state = ""
                if self.x >= WIDTH:
                    self.x = 0
                    self.y = self.y + 1
                if self.y >= HEIGHT:
                    self.y = HEIGHT - 1
                continue

            match = re.match("\x1b\[(\d+);(\d+)H", self.state)
            if match:
                self.y = int(match.group(1)) - 1
                self.x = int(match.group(2)) - 1
                self.state = ""
                continue

            if len(self.state)>10:
                Kernel.instance.log("Couldn't parse ANSI: %s\nBuffer was: %s" % (line, self.state))
                Kernel.instance.die("Couldn't parse ANSI")
                return

            match = re.match("\x1b\[(\d+)m", self.state)
            if match:
                col = int(match.group(1))
                if col>=30 and col<=37:
                    self.color.fg = col
                elif col>=40 and col<=47:
                    self.color.bg = col
                else:
                    Kernel.instance.die("Invalid color: %d" % col)
                self.state = ""
                continue

        if True:
            for y in range(0, HEIGHT):
                for x in range(0, WIDTH):
                    cur = self.screen[x+y*WIDTH]
                    sys.stdout.write("\x1b[%dm\x1b[%d;%dH%s" % (cur.color.fg, y+1, x+1, cur.char))
            sys.stdout.flush()
        else:
            sys.stdout.write(line)
            sys.stdout.flush()

        self.logScreen()
        Kernel.instance.screenParsed()

    def logScreen(self):
        for y in range(0, HEIGHT):
            self._file.write("\n")
            for x in range(0, WIDTH):
                if y == HEIGHT-1 and x > WIDTH-5:
                    break
                self._file.write(self.screen[x+y*WIDTH].char)
        if Kernel.instance.Dungeon.curBranch:
            self._file.write(str(Kernel.instance.curTile().coords()))
            self._file.write("\n"+str(self.y)+","+str(self.x))
        self._file.flush()

    def game_start(self):
        Kernel.instance.log("Got game_start signal in FrameBufferParser")

        Kernel.instance.NethackSock.s.recv(99999)
        Kernel.instance.NethackSock.s.send("\x12")

        self.gameStarted = True