clrghouz/app/Classes/BBS/Frame/Char.php
2023-09-13 20:59:40 +10:00

290 lines
7.0 KiB
PHP

<?php
namespace App\Classes\BBS\Frame;
use App\Classes\BBS\Page\{Ansi,Viewdata};
use App\Models\BBS\Mode;
class Char {
/** @var int|null Attributes for the character (ie: color) */
private ?int $attr;
/** @var string|null Character to be shown */
private ?string $ch;
public function __construct(string $ch=NULL,int $attr=NULL)
{
$this->ch = $ch;
$this->attr = $attr;
}
public function __get(string $key): mixed
{
switch ($key) {
case 'attr': return $this->attr;
case 'ch': return $this->ch;
default:
throw new \Exception('Unknown key:'.$key);
}
}
public function __isset($key): bool
{
return isset($this->{$key});
}
public function __set(string $key,mixed $value): void
{
switch ($key) {
case 'ch':
if (strlen($value) !== 1)
throw new \Exception(sprintf('CH can only be 1 char: [%s]',$value));
$this->{$key} = $value;
break;
default:
throw new \Exception('Unknown key:'.$key);
}
}
public function __toString()
{
return sprintf('%04x [%s]|',$this->attr,$this->ch);
}
/**
* Return the color codes required to draw the current character
*
* @param Mode $mo Service we are rendering for
* @param int|null $last last rendered char
* @param bool $debug debug mode
* @return string|NULL
* @throws \Exception
*/
public function attr(Mode $mo,int $last=NULL,bool $debug=FALSE): string|NULL
{
$ansi = collect();
if ($debug)
dump('- last:'.$last.', this:'.$this->attr);
switch ($mo->name) {
case 'ansi':
if ($debug) {
dump(' - this BG_BLACK:'.($this->attr & Ansi::BG_BLACK));
dump(' - last BG_BLACK:'.($last & Ansi::BG_BLACK));
dump(' - this HIGH:'.($this->attr & Ansi::HIGH));
dump(' - last HIGH:'.($last & Ansi::HIGH));
dump(' - this BLINK:'.($this->attr & Ansi::BLINK));
dump(' - last BLINK:'.($last & Ansi::BLINK));
}
// If high was in the last, and we dont have high now, we need 0, but we need to turn back on flash if it was there
// If flash was in the last, and we dont have flash now, we need to 0 but we need to turn on high if it was there
$reset = FALSE;
if ((($this->attr & Ansi::BG_BLACK) && (! ($last & Ansi::BG_BLACK)))
|| ((! ($this->attr & Ansi::BLINK)) && ($last & Ansi::BLINK))
|| ((! ($this->attr & Ansi::HIGH)) && ($last & Ansi::HIGH)))
{
$ansi->push(Ansi::I_CLEAR_CODE);
$reset = TRUE;
$last = Ansi::BG_BLACK|Ansi::LIGHTGRAY;
}
if (($this->attr & Ansi::HIGH)
&& ((($this->attr & Ansi::HIGH) !== ($last & Ansi::HIGH)) || ($reset && ($last & Ansi::HIGH)))) {
$ansi->push(Ansi::I_HIGH_CODE);
}
if (($this->attr & Ansi::BLINK)
&& ((($this->attr & Ansi::BLINK) !== ($last & Ansi::BLINK)) || ($reset && ($last & Ansi::BLINK)))) {
$ansi->push(Ansi::I_BLINK_CODE);
}
$c = ($this->attr & 0x07);
$l = ($last & 0x07);
// Foreground
switch ($c) {
case Ansi::BLACK:
$r = Ansi::FG_BLACK_CODE;
break;
case Ansi::RED:
$r = Ansi::FG_RED_CODE;
break;
case Ansi::GREEN:
$r = Ansi::FG_GREEN_CODE;
break;
case Ansi::BROWN:
$r = Ansi::FG_BROWN_CODE;
break;
case Ansi::BLUE:
$r = Ansi::FG_BLUE_CODE;
break;
case Ansi::MAGENTA:
$r = Ansi::FG_MAGENTA_CODE;
break;
case Ansi::CYAN:
$r = Ansi::FG_CYAN_CODE;
break;
case Ansi::LIGHTGRAY:
$r = Ansi::FG_LIGHTGRAY_CODE;
break;
}
if ($r && ($c !== $l))
$ansi->push($r);
// Background
if ($this->attr & 0x70) {
$c = ($this->attr & 0x70);
$l = ($last & 0x70);
switch ($this->attr & 0x70) {
case Ansi::BG_BLACK:
$r = Ansi::BG_BLACK_CODE;
break;
case Ansi::BG_RED:
$r = Ansi::BG_RED_CODE;
break;
case Ansi::BG_GREEN:
$r = Ansi::BG_GREEN_CODE;
break;
case Ansi::BG_BROWN:
$r = Ansi::BG_BROWN_CODE;
break;
case Ansi::BG_BLUE:
$r = Ansi::BG_BLUE_CODE;
break;
case Ansi::BG_MAGENTA:
$r = Ansi::BG_MAGENTA_CODE;
break;
case Ansi::BG_CYAN:
$r = Ansi::BG_CYAN_CODE;
break;
case Ansi::BG_LIGHTGRAY:
$r = Ansi::BG_LIGHTGRAY_CODE;
break;
}
if ($r && ($c !== $l))
$ansi->push($r);
}
if ($debug)
dump([' - ansi:' =>$ansi]);
return $ansi->count() ? sprintf('%s[%sm',($debug ? '': "\x1b"),$ansi->join(';')) : NULL;
case 'viewdata':
if ($debug)
dump(sprintf('Last: %02x, Attr: %02x',$last,$this->attr));
switch ($this->attr) {
// \x08
case Viewdata::BLINK:
$r = Viewdata::I_BLINK_CODE;
break;
// \x09
case Viewdata::STEADY:
$r = Viewdata::I_STEADY;
break;
// \x0c
case Viewdata::NORMAL:
$r = Viewdata::I_NORMAL;
break;
// \x0d
case Viewdata::DOUBLE:
$r = Viewdata::I_DOUBLE_CODE;
break;
// \x18
case Viewdata::CONCEAL:
$r = Viewdata::I_CONCEAL;
break;
// \x19
case Viewdata::BLOCKS:
$r = Viewdata::I_BLOCKS;
break;
// \x1a
case Viewdata::SEPARATED:
$r = Viewdata::I_SEPARATED;
break;
// \x1c
case Viewdata::BLACKBACK:
$r = Viewdata::I_BLACKBACK;
break;
// \x1d
case Viewdata::NEWBACK:
$r = Viewdata::I_NEWBACK;
break;
// \x1e
case Viewdata::HOLD:
$r = Viewdata::I_HOLD;
break;
// \x1f
case Viewdata::RELEASE:
$r = Viewdata::I_REVEAL;
break;
// Not handled
// \x0a-b,\x0e-f,\x1b
case 0xff00:
dump($this->attr);
break;
default:
$mosiac = ($this->attr & Viewdata::MOSIAC);
$c = ($this->attr & 0x07);
if ($debug)
dump(sprintf('Last: %02x, Attr: %02x, Color: %02x',$last,$this->attr,$c));
// Color control \x00-\x07, \x10-\x17
switch ($c) {
/*
case Viewdata::BLACK:
$r = Viewdata::FG_BLACK_CODE;
break;
*/
case Viewdata::RED:
$r = $mosiac ? Viewdata::MOSIAC_RED_CODE : Viewdata::FG_RED_CODE;
break;
case Viewdata::GREEN:
$r = $mosiac ? Viewdata::MOSIAC_GREEN_CODE : Viewdata::FG_GREEN_CODE;
break;
case Viewdata::YELLOW:
$r = $mosiac ? Viewdata::MOSIAC_YELLOW_CODE : Viewdata::FG_YELLOW_CODE;
break;
case Viewdata::BLUE:
$r = $mosiac ? Viewdata::MOSIAC_BLUE_CODE : Viewdata::FG_BLUE_CODE;
break;
case Viewdata::MAGENTA:
$r = $mosiac ? Viewdata::MOSIAC_MAGENTA_CODE : Viewdata::FG_MAGENTA_CODE;
break;
case Viewdata::CYAN:
$r = $mosiac ? Viewdata::MOSIAC_CYAN_CODE : Viewdata::FG_CYAN_CODE;
break;
case Viewdata::WHITE:
$r = $mosiac ? Viewdata::MOSIAC_WHITE_CODE : Viewdata::FG_WHITE_CODE;
break;
default:
if ($debug)
dump('Not a color?:'.$c);
return NULL;
}
}
if ($debug)
dump(sprintf('= result: ESC[%s](%02x) for [%s]',chr($r),$r,$this->ch));
return chr($r);
default:
throw new \Exception($this->type.': has not been implemented');
}
}
}