From eda0926758ae210454cdb25638a917cad159771a Mon Sep 17 00:00:00 2001 From: Deon George Date: Sun, 26 Sep 2021 20:42:21 +1000 Subject: [PATCH] Added converting ANSI to a binary format, and custom font rendering --- app/Classes/ANSI.php | 380 ++++++++++++++++++++++++++++ app/Classes/Font.php | 258 +++++++++++++++++++ app/Classes/Fonts/Thick.php | 142 +++++++++++ app/Classes/Fonts/Thin.php | 203 +++++++++++++++ app/Console/Commands/ANSIDecode.php | 35 +++ app/Console/Commands/ANSIEncode.php | 40 +++ public/logo/netmail.bin | 1 + 7 files changed, 1059 insertions(+) create mode 100644 app/Classes/ANSI.php create mode 100644 app/Classes/Font.php create mode 100644 app/Classes/Fonts/Thick.php create mode 100644 app/Classes/Fonts/Thin.php create mode 100644 app/Console/Commands/ANSIDecode.php create mode 100644 app/Console/Commands/ANSIEncode.php create mode 100644 public/logo/netmail.bin diff --git a/app/Classes/ANSI.php b/app/Classes/ANSI.php new file mode 100644 index 0000000..8ccb580 --- /dev/null +++ b/app/Classes/ANSI.php @@ -0,0 +1,380 @@ +width = collect(); + $this->ansi = collect(); + + $f = fopen($file,'r'); + while (! feof($f)) { + $line = stream_get_line($f,self::BUFREAD,"\r"); + + // If the last line is blank, we'll ignore it + if ((! feof($f)) || $line) { + $this->width->push(self::line_width($line,FALSE)); + $this->ansi->push(array_map(function($item) { return ord($item); },str_split($line,1))); + } + } + fclose($f); + + return $this->ansi; + } + + public function __get($key) + { + switch ($key) { + case 'width': + return $this->width; + + case 'max_width': + return $this->width->max()+self::LOGO_BUFFER_WIDTH+self::LOGO_OFFSET_WIDTH; + + case 'height': + return $this->ansi->count()+self::LOGO_BUFFER_HEIGHT+self::LOGO_OFFSET_HEIGHT; + } + } + + /* STATIC METHODS */ + + /** + * Convert a binary ANSI file back to its ANSI version + * + * @param string $file + * @return string + */ + public static function ansi(string $file) + { + return static::bin_to_ansi((new self($file))->ansi->toArray()); + } + + /** + * Convert a binary ANS file to ANSI text + * + * @param array $ansi + * @param bool $return + * @return string + */ + public static function bin_to_ansi(array $ansi,bool $return=TRUE): string + { + $output = ''; + $escape = FALSE; + $current = []; // Default Screen + + foreach ($ansi as $line) { + foreach ($line as $char) { + if ($char == 0x1b) { + $escape = TRUE; + continue; + } + + if ($escape) { + if ($x=static::color($char,$current)) + $output .= "\x1b[".$x; + + $escape = FALSE; + + } else { + $output .= chr($char); + } + } + + if ($return) + $output .= "\r\n"; + } + + return $output; + } + + /** + * Convert an ANSI file into a binary form + * + * @param string $file + * @return Collection + */ + public static function binary(string $file): Collection + { + $f = fopen($file,'r'); + $escape = FALSE; + $ansi = FALSE; + $buffer = ''; + $line = ''; + $result = collect(); + + $current = self::reset(); + + while (! feof($f)) { + $c = fread($f,1); + + switch (ord($c)) { + // Ignore \n (0x0a) + case 0x0a: + continue 2; + + // New line \r (0x0d) + case 0x0d: + $result->push($line); + $line = ''; + continue 2; + + // We got our ESC + case 0x1b: + $escape = TRUE; + continue 2; + + case ord('['): + if ($escape) { + $ansi = TRUE; + continue 2; + } + + break; + } + + if ($ansi) { + switch ($c) { + case ';': + case 'm': + if ((int)$buffer === 0) { + $current = self::reset(); + + } elseif ((int)$buffer === 1) { + $current['h'] = 1; + + } elseif (((int)$buffer >= 30) && (int)$buffer <= 37) { + $current['f'] = (int)$buffer; + + } elseif (((int)$buffer >= 40) && (int)$buffer <= 47) { + $current['b'] = (int)$buffer; + } + + if ($c == 'm') { + $ansi = FALSE; + $escape = FALSE; + $line .= chr(0x1b).chr(self::code($current)); + } + + $buffer = ''; + break; + + default: + $buffer .= $c; + } + + } else { + // If escape is still set, but we didnt get an ANSI starter, then we need to record the ESC. + if ($escape) { + $line .= chr(0x1b); + $escape = FALSE; + } + + $line .= $c; + } + } + + // In case our line didnt end \r and we still have data + if ($line) + $result->push($line); + + fclose($f); + + return $result; + } + + /** + * Convert an array of 8 bit color codes to a binary form + * + * @param array $code + * @return int + */ + private static function code(array $code): int + { + $result = 0; + foreach ($code as $item) { + switch ($item) { + // Color Reset + case 0: $result = 0; break; + // High Intensity + case 1: $result |= self::COLOR_HIGH; break; + + // Foreground + case 30: $result |= (self::COLOR_BLACK<<1); break; + case 31: $result |= (self::COLOR_RED<<1); break; + case 32: $result |= (self::COLOR_GREEN<<1); break; + case 33: $result |= (self::COLOR_YELLOW<<1); break; + case 34: $result |= (self::COLOR_BLUE<<1); break; + case 35: $result |= (self::COLOR_MAGENTA<<1); break; + case 36: $result |= (self::COLOR_CYAN<<1); break; + case 37: $result |= (self::COLOR_WHITE<<1); break; + + // Background + case 40: $result |= (self::COLOR_BLACK<<5); break; + case 41: $result |= (self::COLOR_RED<<5); break; + case 42: $result |= (self::COLOR_GREEN<<5); break; + case 43: $result |= (self::COLOR_YELLOW<<5); break; + case 44: $result |= (self::COLOR_BLUE<<5); break; + case 45: $result |= (self::COLOR_MAGENTA<<5); break; + case 46: $result |= (self::COLOR_CYAN<<5); break; + case 47: $result |= (self::COLOR_WHITE<<5); break; + + default: + dd('unhandled code:'.$item); + } + } + + return $result; + } + + /** + * Convert binary code to ANSI escape code + * + * @param int $code + * @param array $current + * @return string + */ + private static function color(int $code,array &$current): string + { + if (! $current) + $current = static::reset(); + + $h = ($code&0x01); + + switch ($x=(($code>>1)&0x07)) { + case self::COLOR_BLACK: $f = '30'; break; + case self::COLOR_RED: $f = '31'; break; + case self::COLOR_GREEN: $f = '32'; break; + case self::COLOR_YELLOW: $f = '33'; break; + case self::COLOR_BLUE: $f = '34'; break; + case self::COLOR_MAGENTA: $f = '35'; break; + case self::COLOR_CYAN: $f = '36'; break; + case self::COLOR_WHITE: $f = '37'; break; + default: + dump(['unknown color'=>$x]); + } + + switch ($x=(($code>>5)&0x07)) { + case self::COLOR_BLACK: $b = '40'; break; + case self::COLOR_RED: $b = '41'; break; + case self::COLOR_GREEN: $b = '42'; break; + case self::COLOR_YELLOW: $b = '43'; break; + case self::COLOR_BLUE: $b = '44'; break; + case self::COLOR_MAGENTA: $b = '45'; break; + case self::COLOR_CYAN: $b = '46'; break; + case self::COLOR_WHITE: $b = '47'; break; + default: + dump(['unknown color'=>$x]); + } + $return = ''; + $highlight_changed = false; + + if ($h !== $current['h']) { + $return .= $h; + $current['h'] = $h; + $highlight_changed = TRUE; + } + + if ($f !== $current['f']) { + if (! $highlight_changed || $h || (($f != self::DEFAULT_FORE) || ($b != self::DEFAULT_BACK))) + $return .= (strlen($return) ? ';' : '').$f; + + $x = $f; + $current['f'] = $f; + } + + if ($b !== $current['b']) { + if (! $highlight_changed || $h || (($x != self::DEFAULT_FORE) || ($b != self::DEFAULT_BACK))) + $return .= (strlen($return) ? ';' : '').$b; + + $current['b'] = $b; + } + + return ($return !== '') ? $return.'m' : ''; + } + + /** + * Calculate the width of a line + * + * @param string $line + * @param bool $buffer + * @return int + */ + public static function line_width(string $line,bool $buffer=TRUE): int + { + return strlen(preg_replace('/\x1b./','',$line))+($buffer ? self::LOGO_OFFSET_WIDTH+self::LOGO_BUFFER_WIDTH : 0); + } + + /** + * The ANSI was reset (normally CSI [ 0m) + * + * @return int[] + */ + private static function reset(): array + { + return [ + 'h'=>0, + 'f'=>self::DEFAULT_FORE, + 'b'=>self::DEFAULT_BACK + ]; + } + + /* METHODS */ + + /** + * Return a specific line + * + * @param int $line + * @return array + */ + public function line(int $line): array + { + return $this->ansi->get($line); + } + + /** + * Return the binary line + * + * @param int $line + * @return string + */ + public function line_raw(int $line): string + { + return join('',array_map(function($item) { return chr($item); },$this->line($line))); + } +} \ No newline at end of file diff --git a/app/Classes/Font.php b/app/Classes/Font.php new file mode 100644 index 0000000..84294ff --- /dev/null +++ b/app/Classes/Font.php @@ -0,0 +1,258 @@ +width->skip(intdiv($result_height,$step)*$step)->take($step)->max() : 1; + + // Add our logo + if ($result_height <= $logo->height-1) { + $line = ANSI::bin_to_ansi([$logo->line($result_height)],FALSE); + $lc = $logo->line_width($logo->line_raw($result_height),FALSE); + $result_line = str_repeat(' ',ANSI::LOGO_OFFSET_WIDTH) + .$line + .str_repeat(' ',ANSI::LOGO_BUFFER_WIDTH+($right ? 0 : $buffer-$lc)); + } + + // Look for a return + $return_pos = strpos($text,"\r",$current_pos); + + // We have a return + if ($return_pos !== FALSE) { + $subtext = substr($text,$current_pos,$return_pos-$current_pos); + + // If the reset of the string will fit on the current line + } elseif (strlen($text)-$current_pos < static::MSG_WIDTH-$lc) { + $subtext = substr($text,$current_pos); + + // Get the next lines worth of chars + } else { + $subtext = substr($text,$current_pos,static::MSG_WIDTH-$buffer); + + // Include the text up to the last space + if (substr($text,$current_pos+strlen($subtext),1) !== ' ') + $subtext = substr($text,$current_pos,strrpos($subtext,' ')); + } + + $result .= $result_line. + str_repeat(' ',($right ? static::MSG_WIDTH-$lc-strlen($subtext)+((! $lc) ? (ANSI::LOGO_OFFSET_WIDTH+ANSI::LOGO_BUFFER_WIDTH) : 0) : 0)) + .$subtext."\r\n"; + $current_pos += strlen($subtext)+1; + $result_height++; + } + + // In case our text is shorter than the logo + for (;$result_height<$logo->height;$result_height++) { + $result .= str_repeat(' ',ANSI::LOGO_OFFSET_WIDTH) + .ANSI::bin_to_ansi([$logo->line($result_height)],FALSE) + .str_repeat(' ',ANSI::LOGO_BUFFER_WIDTH) + ."\r\n"; + } + + return $result; + } + + /** + * The height of this font (based on the 1st char) + * + * @return int + */ + public static function height(): int + { + return count(Arr::get(static::FONT,'a')); + } + + /** + * Convert text into a graphical font + * + * @param string $text + * @param Collection $width + * @param int $height + * @param int $step + * @return string + */ + public static function fontText(string $text,Collection $width,int $height,int $step): string + { + return self::text_to_font($text,$width,$height,$step); + } + + /** + * Convert text to this font + * This function will pad the text to fit around the icon, so that the icon+font fils to self::MSG_WIDTH + * + * @param string $text + * @param Collection $icon_width Width to make the font + * @param int $icon_height Minimal width for this height, then full width (self::MSG_WIDTH) + * @param int $step The grouping of lines (normally font height) around the icon + * @return string + */ + protected static function text_to_font(string $text,Collection $icon_width,int $icon_height,int $step): string + { + $chars = collect(); // Characters needed for this $text + $font_height = 0; // Max height of text using font + + // Trim any leading/trailing spaces + $text = trim(strtolower($text)); + + // Work out the characters we need + foreach (array_unique(str_split($text)) as $c) { + if (($c == ' ') || (! $x=Arr::get(static::FONT,$c))) { + continue; + } + + $chars->put($c,$x); + $font_height = (($y=count($x)) > $font_height) ? $y : $font_height; + } + + if (self::DEBUG) dump(['uniquechars'=>$chars->count(),'font_height'=>$font_height]); + if (self::DEBUG) dump(['drawing'=>$text,'textlen'=>strlen($text),'logo_width'=>$icon_width,'logo_height'=>$icon_height]); + + $result = ''; // Our result + $current_pos = 0; // Our current position through $text + $result_height = 0; // Our current line height + $line_pos = 0; // Our current character position for this line of the font + + while ($current_pos < strlen($text)) { + if (self::DEBUG) dump(sprintf('current position %d of %d',$current_pos,strlen($text))); + + for ($line=0;$line<$font_height;$line++) { + if ($line == 0) { + $line_icon_width = $icon_width + ->skip(intdiv($result_height,$step)*$step) + ->take($step) + ->max(); + + if ($line_icon_width) + $line_icon_width += ANSI::LOGO_OFFSET_WIDTH+ANSI::LOGO_BUFFER_WIDTH; + + $line_width = self::MSG_WIDTH-$line_icon_width; // Width we are working towards, initially $icon_width until height then its self::MSG_WIDTH + } + + $line_result = ''; // Our current line of font + if (self::DEBUG) dump(sprintf('- current line %d of %d',$line+1,$font_height)); + + // If we are mid way through rendering a font, and have already finished with the height offset, we'll need to fill with blanks + if (($line_width !== self::MSG_WIDTH) && ($result_height > $icon_height-1)) + $line_result .= str_repeat(' ',$line_icon_width); + + $line_pos = $current_pos; + $next_space_pos = $current_pos; + $next_next_space_width = 0; // What our width will be after the next next space + $next_next_space_pos = 0; // The position of the next space after the next one + $next_next_space_chars = 0; // The number of chars between the next space and the next next space + $current_line_width = 0; // Our current width of the line + + while ($current_line_width < $line_width) { + if (self::DEBUG) dump(sprintf(' - current width %d of %d, and we are working on char %d',$current_line_width,$line_width,$line_pos)); + $find_space_pos = $line_pos; + + // Find our next char + if (self::DEBUG) dump(sprintf(' - find our next space from %d after %d',$find_space_pos,$next_space_pos)); + + $next_space_chars = 0; + if ($next_space_pos <= $line_pos) { + if (! $next_next_space_pos) { + while (($find_space_pos < strlen($text)) && (($c=substr($text,$find_space_pos++,1)) !== ' ')) { + $x = count(Arr::get($chars->get($c),$line)); + if (self::DEBUG) dump(sprintf(' + char is [%s] (%x) and will take %d chars',$c,ord($c),$x)); + $next_space_chars += $x; + } + + $next_space_pos = $find_space_pos; + $next_next_space_pos = $find_space_pos; + $next_next_space_width = $current_line_width+$next_space_chars; + + } else { + $next_space_pos = $next_next_space_pos; + $next_space_chars = $next_next_space_chars; + } + + // Find our next next space, which we'll use to decide whether we need to include a space when we find one + $next_next_space_chars = 0; + while (($next_next_space_pos < strlen($text)) && (($c=substr($text,$next_next_space_pos++,1)) !== ' ')) { + $next_next_space_chars += count(Arr::get($chars->get($c),$line,[])); + if (self::DEBUG) dump(sprintf(' + char is [%s] (%x) and will take us to %d',$c,ord($c),$next_next_space_chars)); + } + + $next_next_space_width = $current_line_width+$next_space_chars+$next_next_space_chars; + }; + + if (self::DEBUG) dump(sprintf(' - our next space is: [%s] (%x) at %d in %d chars, taking %d chars (taking our width to %d)',$c,ord($c),$find_space_pos,$find_space_pos-$line_pos,$next_space_chars,$current_line_width+$next_space_chars)); + + // We are only spaces, so we can return to the next line + if ($current_line_width+$next_space_chars > $line_width) { + if (self::DEBUG) dump(' = next char should go onto new line'); + + // Only go to a new line if we already have chars + if ($current_line_width) + break; + } + + $c = substr($text,$line_pos,1); + if (($c == ' ') || (! $font_chars=$chars->get($c))) { + // Ignore this space if we are at the beginning of the line + if ($current_line_width && ($next_next_space_width < $line_width)) { + $line_result .= $c; + $current_line_width++; + } + + } else { + if (self::DEBUG) dump(sprintf('adding char [%s] which is [%s]',$c,join('|',Arr::get($font_chars,$line)))); + + foreach ($x=Arr::get($font_chars,$line) as $char) + $line_result .= chr($char); + + $current_line_width += count($x); + } + + $line_pos++; + if (self::DEBUG) dump(sprintf(' = line width [%d of %d] and we are on char [%d] our space is [%d]',$current_line_width,$line_width,$line_pos,$find_space_pos)); + + if ($line_pos == strlen($text)) { + if (self::DEBUG) dump(sprintf(' = we are finished, as we are on char %d on line %d',$line_pos,$line)); + break; + } + } + + $result_height++; + $result .= $line_result."\r"; + } + + $current_pos = $line_pos; + if (self::DEBUG) dump(sprintf('= new line starting with char [%d] - our width is [%d] and we are on line [%d]',$current_pos,$line_width,$result_height)); + } + + if (self::DEBUG) + dd(['result'=>$result]); + + return $result; + } +} \ No newline at end of file diff --git a/app/Classes/Fonts/Thick.php b/app/Classes/Fonts/Thick.php new file mode 100644 index 0000000..b39a7e5 --- /dev/null +++ b/app/Classes/Fonts/Thick.php @@ -0,0 +1,142 @@ + [ + [0xdc,0xdc,0xdc,0x20], + [0xdc,0xdc,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'b' => [ + [0xdc,0x20,0x20,0x20], + [0xdb,0xdf,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'c' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdf,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'd' => [ + [0x20,0x20,0xdc,0x20], + [0xdb,0xdf,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'e' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0xdc,0xdb,0x20], + [0xdb,0xdc,0xdc,0x20], + ], + 'f' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0xdc,0x20,0x20], + [0xdb,0x20,0x20,0x20], + ], + 'g' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0xdc,0xdb,0x20], + [0xdc,0xdc,0xdb,0x20], + ], + 'h' => [ + [0xdc,0x20,0x20,0x20], + [0xdb,0xdf,0xdb,0x20], + [0xdb,0x20,0xdb,0x20], + ], + 'i' => [ + [0xdc,0x20], + [0xdc,0x20], + [0xdb,0x20], + ], + 'j' => [ + [0x20,0x20,0xdc,0x20], + [0x20,0x20,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'k' => [ + [0xdc,0x20,0xdc,0x20], + [0xdb,0xdc,0xdf,0x20], + [0xdb,0x20,0xdb,0x20], + ], + 'l' => [ + [0xdc,0x20,0x20,0x20], + [0xdb,0x20,0x20,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'm' => [ + [0xdc,0xdc,0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdb,0x20,0xdb,0x20], + [0xdb,0x20,0x20,0x20,0xdb,0x20], + ], + 'n' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdb,0x20,0xdb,0x20], + ], + 'o' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'p' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdb,0xdf,0xdf,0x20], + ], + 'q' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdf,0xdf,0xdb,0x20], + ], + 'r' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0x20,0xdf,0x20], + [0xdb,0x20,0x20,0x20], + ], + 's' => [ + [0xdc,0xdc,0xdc,0x20], + [0xdb,0xdc,0xdc,0x20], + [0xdc,0xdc,0xdb,0x20], + ], + 't' => [ + [0xdc,0xdc,0xdc,0x20], + [0x20,0xdb,0x20,0x20], + [0x20,0xdb,0x20,0x20], + ], + 'u' => [ + [0xdc,0x20,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdb,0xdc,0xdb,0x20], + ], + 'v' => [ + [0xdc,0x20,0xdc,0x20], + [0xdb,0x20,0xdb,0x20], + [0xdf,0xdc,0xdf,0x20], + ], + 'w' => [ + [0xdc,0x20,0x20,0x20,0xdc,0x20], + [0xdb,0x20,0xdb,0x20,0xdb,0x20], + [0xdb,0xdc,0xdb,0xdc,0xdb,0x20], + ], + 'x' => [ + [0xdc,0x20,0xdc,0x20], + [0xdf,0xdb,0xdf,0x20], + [0xdb,0xdf,0xdb,0x20], + ], + 'y' => [ + [0xdc,0x20,0xdc,0x20], + [0xdb,0xdc,0xdb,0x20], + [0xdc,0xdc,0xdb,0x20], + ], + 'z' => [ + [0xdc,0xdc,0xdc,0x20], + [0x20,0xdc,0xdf,0x20], + [0xdb,0xdc,0xdc,0x20], + ], + ]; +} \ No newline at end of file diff --git a/app/Classes/Fonts/Thin.php b/app/Classes/Fonts/Thin.php new file mode 100644 index 0000000..8084532 --- /dev/null +++ b/app/Classes/Fonts/Thin.php @@ -0,0 +1,203 @@ + [ + [0xda,0xc4,0xbf], + [0xda,0xc4,0xb3], + [0xc0,0xc4,0xd9], + ], + 'b' => [ + [0xc3,0xc4,0xbf], + [0xb3,0x20,0xb3], + [0xc0,0xc4,0xd9], + ], + 'c' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0x20], + [0xc0,0xc4,0xd9], + ], + 'd' => [ + [0xda,0xc4,0xb4], + [0xb3,0x20,0xb3], + [0xc0,0xc4,0xd9], + ], + 'e' => [ + [0xda,0xc4,0xbf], + [0xb3,0xc4,0xd9], + [0xc0,0xc4,0xd9], + ], + 'f' => [ + [0xda,0xc4,0xbf], + [0xb3,0xc4,0x20], + [0xc0,0x20,0x20], + ], + 'g' => [ + [0xda,0xc4,0xbf], + [0xc0,0xc4,0xb3], + [0xc0,0xc4,0xd9], + ], + 'h' => [ + [0xc2,0x20,0x20], + [0xc3,0xc4,0xbf], + [0xc1,0x20,0xc1], + ], + 'i' => [ + [0xfe], + [0xc2], + [0xc1], + ], + 'j' => [ + [0x20,0x20,0xc2], + [0x20,0x20,0xb3], + [0xc0,0xc4,0xd9], + ], + 'k' => [ + [0xc2,0x20,0xc2], + [0xb3,0x2f,0xbf], + [0xc1,0x20,0xc1], + ], + 'l' => [ + [0xc2,0x20,0x20], + [0xb3,0x20,0x20], + [0xc0,0xc4,0xd9], + ], + 'm' => [ + [0xda,0xc4,0xc2,0xbf], + [0xb3,0x20,0xb3,0xb3], + [0xc1,0x20,0x20,0xc1], + ], + 'n' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0xb3], + [0xc1,0x20,0xc1], + ], + 'o' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0xb3], + [0xc0,0xc4,0xd9], + ], + 'p' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0xb3], + [0xc3,0xc4,0xd9], + ], + 'q' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0xb3], + [0xc0,0xc4,0xb4], + ], + 'r' => [ + [0xda,0xc4,0xbf], + [0xb3,0x20,0x20], + [0xc1,0x20,0x20], + ], + 's' => [ + [0xda,0xc4,0xbf], + [0xc0,0xc4,0xbf], + [0xc0,0xc4,0xd9], + ], + 't' => [ + [0xc2,0x20,0x20], + [0xc5,0xc4,0x20], + [0xc1,0xc4,0xd9], + ], + 'u' => [ + [0xda,0x20,0xbf], + [0xb3,0x20,0xb3], + [0xc0,0xc4,0xd9], + ], + 'v' => [ + [0xda,0x20,0xbf], + [0xb3,0x20,0xb3], + [0x20,0x56,0x20], + ], + 'w' => [ + [0xda,0x20,0x20,0xbf], + [0xb3,0xb3,0x20,0xb3], + [0xc0,0xc1,0xc4,0xd9], + ], + 'x' => [ + [0xda,0x20,0xbf], + [0x20,0x58,0x20], + [0xc0,0x20,0xd9], + ], + 'y' => [ + [0xda,0x20,0xbf], + [0xc0,0xc4,0xb4], + [0xc0,0xc4,0xd9], + ], + 'z' => [ + [0xda,0xc4,0xbf], + [0xda,0x2f,0xd9], + [0xc0,0xc4,0xd9], + ], + '1' => [ + [0xda,0xbf,0x20], + [0x20,0xb3,0x20], + [0xc4,0xc1,0xc4], + ], + '2' => [ + [0xda,0xc4,0xbf], + [0xda,0xc4,0xd9], + [0xc0,0xc4,0xd9], + ], + '3' => [ + [0xda,0xc4,0xbf], + [0x20,0xc4,0xb4], + [0xc0,0xc4,0xd9], + ], + '4' => [ + [0xda,0x20,0xbf], + [0xc0,0xc4,0xb4], + [0x20,0x20,0xd9], + ], + '5' => [ + [0xda,0xc4,0xbf], + [0xc0,0xc4,0xbf], + [0xc0,0xc4,0xd9], + ], + '6' => [ + [0xda,0xc4,0xbf], + [0xc3,0xc4,0xbf], + [0xc0,0xc4,0xd9], + ], + '7' => [ + [0xda,0xc4,0xbf], + [0x20,0xda,0xd9], + [0x20,0xc1,0x20], + ], + '8' => [ + [0xda,0xc4,0xbf], + [0xc3,0xc4,0xb4], + [0xc0,0xc4,0xd9], + ], + '9' => [ + [0xda,0xc4,0xbf], + [0xc0,0xc4,0xb4], + [0x20,0x20,0xd9], + ], + '0' => [ + [0xda,0xc4,0xbf], + [0xb3,0x2f,0xb3], + [0xc0,0xc4,0xd9], + ], + '#' => [ + [0x5f,0x7c,0x7c,0x5f], + [0xc4,0xb3,0xb3,0xc4], + [0x20,0x20,0x20,0x20], + ], + '!' => [ + [0xb3], + [0xb3], + [0xfe], + ], + ]; +} \ No newline at end of file diff --git a/app/Console/Commands/ANSIDecode.php b/app/Console/Commands/ANSIDecode.php new file mode 100644 index 0000000..f3648e3 --- /dev/null +++ b/app/Console/Commands/ANSIDecode.php @@ -0,0 +1,35 @@ +argument('file')); + } +} \ No newline at end of file diff --git a/app/Console/Commands/ANSIEncode.php b/app/Console/Commands/ANSIEncode.php new file mode 100644 index 0000000..5525133 --- /dev/null +++ b/app/Console/Commands/ANSIEncode.php @@ -0,0 +1,40 @@ +argument('file')) as $line) { + foreach (str_split(bin2hex($line),2) as $y) + echo hex2bin($y); + + echo "\r"; + } + } +} \ No newline at end of file diff --git a/public/logo/netmail.bin b/public/logo/netmail.bin new file mode 100644 index 0000000..f7f2cdc --- /dev/null +++ b/public/logo/netmail.bin @@ -0,0 +1 @@ + ÜÛÛÛÜÜ ÜáÛÛßßßÛßÛÜ ÜÜÜáÛßÛÛÛÛî Ûî ÛÜÜÜ ÜÛÛÛßÞ Ûà-ÛÛÛÛà-Û ÝßÛÛÛ ßÛÜÜÜ ßÛÜßßßßÜÛß ÜÜÜÛ ßÛÛßß ßÛÛÛÛßÜ ÛÛÛß ÜÛÛÜÛÜ þ ÛÛÛÛß ÜÛÛÜ ÛÛÛÛ Ü!Ü ÜÜÛ ÛÛÛÛ ÛßßÝÜÜÜß ß ÛßßÝÜÝÛÛßßß ÛÛß \ No newline at end of file