'DATE', 'e' => 'EMAIL', 'f' => 'FULLNAME', 'n' => 'USER', 'p' => 'PASS', 't' => 'TIME', 'y' => 'NODE', 'z' => 'TOKEN', ]; public static function strlenv($text):int { return strlen($text)-substr_count($text,ESC); } public function __construct(int $frame,string $index='a') { parent::__construct($frame,$index); $this->mo = Mode::where('name','Viewdata')->single(); } public function __get(string $key): mixed { switch ($key) { case 'color_page': return chr(self::WHITE); case 'color_unit': return chr(self::GREEN); default: return parent::__get($key); } } public function attr(array $field): string { // Noop return ''; } /** * This function converts Viewtex BIN data into an array of attributes * * With viewdata, a character is used/display regardless of whether it is a control character, or an actual display * character. * * @param string $contents Our ANSI content to convert * @param int $width Canvas width before we wrap to the next line * @param int $yoffset fields offset when rendered (based on main window) * @param int $xoffset fields offset when rendered (based on main window) * @param int|null $debug Enable debug mode * @return array * @throws \Exception */ public function parse(string $contents,int $width,int $yoffset=0,int $xoffset=0,?int $debug=NULL): array { $result = []; $lines = collect(explode("\r\n",$contents)); if ($debug) dump(['lines'=>$lines]); $i = 0; // Intensity $bg = self::BG_BLACK; // Background color $fg = self::WHITE; // Foreground color $new_line = $fg + $bg + $i; // Attribute int // Attribute state on a new line $attr = $new_line; $y = 0; while ($lines->count() > 0) { $x = 0; $line = $lines->shift(); $result[$y+1] = []; if ($this->debug) dump(['next line'=>$line,'length'=>strlen($line)]); while (strlen($line) > 0) { if ($debug) dump(['y:'=>$y,'attr'=>$attr,'line'=>$line,'length'=>strlen($line)]); if ($x >= $width) { $x = 0; // Each new line, we reset the attrs $attr = $new_line; $y++; } /* parse control codes */ $m = []; preg_match('/^([\x00-\x09\x0c-\x1a\x1c-\x1f])/',$line,$m); if (count($m)) { $line = substr($line,strlen(array_shift($m))); $attr = 0; switch ($xx=ord(array_shift($m))) { case 0x00: $attr += self::BLACK; break; case 0x01: $attr += self::RED; break; case 0x02: $attr += self::GREEN; break; case 0x03: $attr += self::YELLOW; break; case 0x04: $attr += self::BLUE; break; case 0x05: $attr += self::MAGENTA; break; case 0x06: $attr += self::CYAN; break; case 0x07: $attr += self::WHITE; break; case 0x08: $attr = self::BLINK; break; case 0x09: $attr = self::STEADY; break; /* case 0x0a: //$attr = self::ENDBOX; // End Box (Unused?) break; case 0x0b: //$attr = self::STARTBOX; // Start Box (Unused?) break; */ case 0x0c: $attr = self::NORMAL; break; case 0x0d: $attr = self::DOUBLE; break; case 0x0e: $attr = self::NORMAL; // @todo Double Width (Unused)? break; case 0x0f: $attr = self::NORMAL; // @todo Double Width (Unused?) break; case 0x10: $attr = self::MOSIAC|self::BLACK; break; case 0x11: $attr = self::MOSIAC|self::RED; break; case 0x12: $attr = self::MOSIAC|self::GREEN; break; case 0x13: $attr = self::MOSIAC|self::YELLOW; break; case 0x14: $attr = self::MOSIAC|self::BLUE; break; case 0x15: $attr = self::MOSIAC|self::MAGENTA; break; case 0x16: $attr = self::MOSIAC|self::CYAN; break; case 0x17: $attr = self::MOSIAC|self::WHITE; break; case 0x18: $attr = self::CONCEAL; break; case 0x19: $attr = self::BLOCKS; break; case 0x1a: $attr = self::SEPARATED; break; /* // We are using this for field input case 0x1b: //$attr = self::NORMAL; // CSI break; */ case 0x1c: $attr = self::BLACKBACK; // Black Background break; case 0x1d: $attr = self::NEWBACK; // New Background break; case 0x1e: $attr = self::HOLD; // Mosiac Hold break; case 0x1f: $attr = self::RELEASE; // Mosiac Release break; // Catch all for other codes default: dump(['char'=>$xx]); $attr = 0xff00; } if ($debug) dump(sprintf('- got control code [%02x] at [%02dx%02d]',$attr,$y,$x)); $result[$y+1][$x+1] = new Char(NULL,$attr); $x++; continue; } /** * For response frames, a dialogue field is signalled by a CLS (0x0c) followed by a number of dialogue * characters [a-z]. The field ends by the first different character from the initial dialogue character. * The CLS is a "privileged space" and the dialogue characters defined the dialogue field. * * Standard dialogue characters: * + n = name * + t = telephone number * + d = date and time * + a = address * + anything else free form, typically 'f' is used * * Source: Prestel Bulk Update Technical Specification */ /* parse an input field */ // Since 0x0c is double, we'll use good ol' ESC 0x1b $m = []; preg_match('/^([\x1b|\x9b])([a-z])\2+/',$line,$m); if (count($m)) { $line = substr($line,strlen($m[0])); $len = strlen(substr($m[0],1)); $field = new Field([ 'attribute' => [], 'name' => Arr::get(self::input_map,$m[2],$m[2]), 'pad' => '.', 'size' => $len, 'type' => $m[2], 'value' => NULL, 'x' => $x+$xoffset, 'y' => $y+$yoffset, ]); (($m[1] === "\x1b") ? $this->fields_input : $this->fields_dynamic)->push($field); $result[$y+1][++$x] = new Char(' ',$attr); // The \x1b|\x9b is the privileged space. for ($xx=0;$xx<$len;$xx++) $result[$y+1][$x+1+$xx] = new Char('.',$attr); $x += $len; continue; } /* set character and attribute */ $ch = $line[0]; $line = substr($line,1); if ($debug) dump(sprintf('Storing [%02xx%02x] [%s] with [%02x]',$y,$x,$ch,$attr)); /* validate position */ if ($y < 0) $y = 0; if ($x < 0) $x = 0; if ($attr === null) throw new \Exception('Attribute is null?'); $result[$y+1][$x+1] = new Char($ch,$attr); $x++; } // Each new line, we reset the attrs $attr = $new_line; $y++; } return $result; } }