Compare commits
10
Commits
ee844db845
...
v1.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcbfd4c98b | ||
|
|
7519b719ba | ||
|
|
bfb4b6513f | ||
|
|
effca97ac0 | ||
|
|
b5e3328f64 | ||
|
|
7aff48a428 | ||
|
|
f0f954d463 | ||
|
|
3b16b2141e | ||
|
|
d77d131661 | ||
|
|
0b9d62202a |
No files matched your search
@@ -0,0 +1,21 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./configure && make
|
||||
- run: ./mikusays -V && ./mikusays "hi"
|
||||
|
||||
windows-cross:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: sudo apt-get update && sudo apt-get install -y gcc-mingw-w64-x86-64
|
||||
- run: ./configure --host=x86_64-w64-mingw32 && make
|
||||
@@ -0,0 +1,90 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
jobs:
|
||||
dist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install autotools
|
||||
run: sudo apt-get update && sudo apt-get install -y autoconf automake
|
||||
- run: autoreconf -fi
|
||||
- run: touch configure configure.ac aclocal.m4 Makefile.in
|
||||
- name: tag must match version in configure.ac
|
||||
run: |
|
||||
ver=$(./configure --version | head -1 | awk '{print $NF}')
|
||||
test "v$ver" = "$GITHUB_REF_NAME"
|
||||
- run: ./configure && make distcheck
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
path: mikusays-*.tar.gz
|
||||
|
||||
# linux binary, built from the tarball (so it tests what packagers get)
|
||||
build:
|
||||
needs: dist
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: install compiler
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
- name: build and package
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
tar xf mikusays-$VERSION.tar.gz
|
||||
cd mikusays-$VERSION
|
||||
./configure LDFLAGS="-static"
|
||||
make
|
||||
strip mikusays
|
||||
./mikusays -V
|
||||
./mikusays "hi" > /dev/null
|
||||
mv mikusays ../mikusays-$VERSION-linux-x86_64
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bin-linux-x86_64
|
||||
path: mikusays-*-linux-x86_64
|
||||
|
||||
build-windows:
|
||||
needs: dist
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: install compiler
|
||||
run: sudo apt-get update && sudo apt-get install -y build-essential gcc-mingw-w64-x86-64
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dist
|
||||
- name: build and package
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
tar xf mikusays-$VERSION.tar.gz
|
||||
cd mikusays-$VERSION
|
||||
./configure --host=x86_64-w64-mingw32 LDFLAGS="-static"
|
||||
make
|
||||
x86_64-w64-mingw32-strip mikusays.exe
|
||||
mv mikusays.exe ../mikusays-$VERSION-windows-x86_64.exe
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bin-windows-x86_64
|
||||
path: mikusays-*-windows-x86_64.exe
|
||||
|
||||
release:
|
||||
needs: [dist, build, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: artifacts
|
||||
- name: checksums
|
||||
run: |
|
||||
cd artifacts
|
||||
find . -mindepth 2 -type f -exec mv {} . \;
|
||||
find . -type d -empty -delete
|
||||
sha256sum * > SHA256SUMS
|
||||
- uses: akkuman/gitea-release-action@v1
|
||||
with:
|
||||
files: artifacts/*
|
||||
@@ -1,49 +0,0 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
unix:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: ./configure && make
|
||||
- run: ./mikusays -V && ./mikusays "hi"
|
||||
|
||||
windows:
|
||||
runs-on: windows-2025
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: UCRT64
|
||||
install: mingw-w64-ucrt-x86_64-gcc make
|
||||
- run: ./configure && make
|
||||
- run: ./mikusays.exe -V && ./mikusays.exe "hi"
|
||||
|
||||
# runs in a real FreeBSD VM with its stock cc + BSD make
|
||||
freebsd:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
run: |
|
||||
./configure
|
||||
make
|
||||
./mikusays -V
|
||||
./mikusays "hi"
|
||||
@@ -1,116 +0,0 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# builds the source tarball (configure included) and checks tag == configure.ac version
|
||||
dist:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: install autotools
|
||||
run: sudo apt-get update && sudo apt-get install -y autoconf automake
|
||||
- run: autoreconf -fi
|
||||
- run: touch configure configure.ac aclocal.m4 Makefile.in
|
||||
- name: tag must match version in configure.ac
|
||||
run: |
|
||||
ver=$(./configure --version | head -1 | awk '{print $NF}')
|
||||
test "v$ver" = "$GITHUB_REF_NAME"
|
||||
- run: ./configure && make distcheck
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: dist
|
||||
path: mikusays-*.tar.gz
|
||||
|
||||
# linux + macos binaries, built from the tarball (so it tests what packagers get)
|
||||
build:
|
||||
needs: dist
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x86_64, os: ubuntu-24.04, ldflags: "-static" }
|
||||
- { name: linux-aarch64, os: ubuntu-24.04-arm, ldflags: "-static" }
|
||||
- { name: macos-arm64, os: macos-15, ldflags: "" }
|
||||
- { name: macos-x86_64, os: macos-15-intel, ldflags: "" }
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "11.0"
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: dist
|
||||
- name: build and package
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
tar xf mikusays-$VERSION.tar.gz
|
||||
cd mikusays-$VERSION
|
||||
./configure LDFLAGS="${{ matrix.ldflags }}"
|
||||
make
|
||||
strip mikusays
|
||||
./mikusays -V
|
||||
./mikusays "hi" > /dev/null
|
||||
out=mikusays-$VERSION-${{ matrix.name }}
|
||||
mkdir "$out"
|
||||
cp mikusays LICENSE README.md "$out"/
|
||||
tar czf ../"$out".tar.gz "$out"
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bin-${{ matrix.name }}
|
||||
path: mikusays-*-${{ matrix.name }}.tar.gz
|
||||
|
||||
# windows exe via MSYS2/MinGW (configure needs a posix shell)
|
||||
windows:
|
||||
needs: dist
|
||||
runs-on: windows-2025
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: UCRT64
|
||||
install: mingw-w64-ucrt-x86_64-gcc make zip
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: dist
|
||||
- name: build and package
|
||||
run: |
|
||||
VERSION=${GITHUB_REF_NAME#v}
|
||||
tar xf mikusays-$VERSION.tar.gz
|
||||
cd mikusays-$VERSION
|
||||
./configure LDFLAGS="-static"
|
||||
make
|
||||
strip mikusays.exe
|
||||
./mikusays.exe -V
|
||||
./mikusays.exe "hi" > /dev/null
|
||||
out=mikusays-$VERSION-windows-x86_64
|
||||
mkdir "$out"
|
||||
cp mikusays.exe LICENSE README.md "$out"/
|
||||
zip -r ../"$out".zip "$out"
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: bin-windows-x86_64
|
||||
path: mikusays-*-windows-x86_64.zip
|
||||
|
||||
release:
|
||||
needs: [dist, build, windows]
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts
|
||||
merge-multiple: true
|
||||
- name: checksums
|
||||
run: cd artifacts && sha256sum * > SHA256SUMS
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: artifacts/*
|
||||
generate_release_notes: true
|
||||
@@ -11,3 +11,4 @@ mikusays
|
||||
|
||||
*.tar.gz
|
||||
.dirstamp
|
||||
configure~
|
||||
+3
-1
@@ -6,7 +6,9 @@ mikusays_SOURCES = \
|
||||
src/helper.c \
|
||||
defs/all.h \
|
||||
defs/mikuart.h \
|
||||
defs/helper.h
|
||||
defs/helper.h \
|
||||
defs/colors.h \
|
||||
defs/widechar.h
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra
|
||||
|
||||
|
||||
Generated
+3
-1
@@ -296,7 +296,9 @@ mikusays_SOURCES = \
|
||||
src/helper.c \
|
||||
defs/all.h \
|
||||
defs/mikuart.h \
|
||||
defs/helper.h
|
||||
defs/helper.h \
|
||||
defs/colors.h \
|
||||
defs/widechar.h
|
||||
|
||||
AM_CFLAGS = -Wall -Wextra
|
||||
EXTRA_DIST = LICENSE README.md
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div align=center>
|
||||
<div align="center" markdown="1">
|
||||
|
||||
<img width="866" height="711" alt="image" src="https://github.com/user-attachments/assets/81ed5996-5643-4d19-a27a-0ab740f320c7" />
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.73 for mikusays 0.1.1.
|
||||
# Generated by GNU Autoconf 2.73 for mikusays 1.1.0.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2026 Free Software Foundation,
|
||||
@@ -584,10 +584,10 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='mikusays'
|
||||
PACKAGE_TARNAME='mikusays'
|
||||
PACKAGE_VERSION='0.1.1'
|
||||
PACKAGE_STRING='mikusays 0.1.1'
|
||||
PACKAGE_VERSION='1.1.0'
|
||||
PACKAGE_STRING='mikusays 1.1.0'
|
||||
PACKAGE_BUGREPORT=''
|
||||
PACKAGE_URL='https://github.com/davideldios7/mikusays'
|
||||
PACKAGE_URL='https://git.squeakyrat.moe/mikusays-c'
|
||||
|
||||
ac_unique_file="src/main.c"
|
||||
# Factoring default headers for most tests.
|
||||
@@ -1281,7 +1281,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
'configure' configures mikusays 0.1.1 to adapt to many kinds of systems.
|
||||
'configure' configures mikusays 1.1.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1348,7 +1348,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of mikusays 0.1.1:";;
|
||||
short | recursive ) echo "Configuration of mikusays 1.1.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1379,7 +1379,7 @@ Use these variables to override the choices made by 'configure' or to help
|
||||
it to find libraries and programs with nonstandard names/locations.
|
||||
|
||||
Report bugs to the package provider.
|
||||
mikusays home page: <https://github.com/davideldios7/mikusays>.
|
||||
mikusays home page: <https://git.squeakyrat.moe/mikusays-c>.
|
||||
_ACEOF
|
||||
ac_status=$?
|
||||
fi
|
||||
@@ -1443,7 +1443,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
mikusays configure 0.1.1
|
||||
mikusays configure 1.1.0
|
||||
generated by GNU Autoconf 2.73
|
||||
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
@@ -1555,7 +1555,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by mikusays $as_me 0.1.1, which was
|
||||
It was created by mikusays $as_me 1.1.0, which was
|
||||
generated by GNU Autoconf 2.73. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@@ -3171,7 +3171,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='mikusays'
|
||||
VERSION='0.1.1'
|
||||
VERSION='1.1.0'
|
||||
|
||||
|
||||
printf '%s\n' "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
|
||||
@@ -5437,7 +5437,7 @@ cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by mikusays $as_me 0.1.1, which was
|
||||
This file was extended by mikusays $as_me 1.1.0, which was
|
||||
generated by GNU Autoconf 2.73. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -5489,7 +5489,7 @@ Configuration commands:
|
||||
$config_commands
|
||||
|
||||
Report bugs to the package provider.
|
||||
mikusays home page: <https://github.com/davideldios7/mikusays>."
|
||||
mikusays home page: <https://git.squeakyrat.moe/mikusays-c>."
|
||||
|
||||
_ACEOF
|
||||
ac_cs_config=`printf '%s\n' "$ac_configure_args" | sed "$ac_safe_unquote"`
|
||||
@@ -5497,7 +5497,7 @@ ac_cs_config_escaped=`printf '%s\n' "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
mikusays config.status 0.1.1
|
||||
mikusays config.status 1.1.0
|
||||
configured by $0, generated by GNU Autoconf 2.73,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([mikusays], [0.2.0], [], [mikusays], [https://github.com/davideldios7/mikusays])
|
||||
AC_INIT([mikusays], [1.1.0], [], [mikusays], [https://git.squeakyrat.moe/mikusays-c])
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall])
|
||||
|
||||
+5
-2
@@ -6,11 +6,14 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "helper.h"
|
||||
|
||||
//COLORS.C
|
||||
void Void(void); //function that does FUCKING NOTHING BECAUSE MY STUPID LSP THINK I'M NOT USING THIS FUCKING FILE AND GIVES ME A WARNINGGENROENIGGHUIOAGHUIHUIAUHIDFG
|
||||
|
||||
//COLORS.C
|
||||
#include "colors.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef COLORS_H
|
||||
#define COLORS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum color_mode {
|
||||
MODE_NONE,
|
||||
MODE_ANSI16,
|
||||
MODE_ANSI256,
|
||||
MODE_TRUECOLOR
|
||||
}color_mode;
|
||||
|
||||
|
||||
typedef struct{
|
||||
unsigned char red;
|
||||
unsigned char green;
|
||||
unsigned char blue;
|
||||
}rgbcolor;
|
||||
|
||||
typedef struct colorconfig {
|
||||
enum color_mode mode;
|
||||
bool has_solid_color;
|
||||
rgbcolor solid_color;
|
||||
bool rainbow_enabled; //flag
|
||||
int saturation; //default 100
|
||||
int brightness; //default 50
|
||||
rgbcolor *rainbow_cache; //array of rgbcolor malloced
|
||||
size_t rainbow_count;
|
||||
bool has_gradient;
|
||||
rgbcolor gradient_start;
|
||||
rgbcolor gradient_end;
|
||||
}colorconfig;
|
||||
|
||||
typedef enum ColorParseError {
|
||||
COLOR_OK,
|
||||
COLOR_ERR_FORMAT,
|
||||
COLOR_ERR_HEX,
|
||||
COLOR_ERR_RGB,
|
||||
COLOR_ERR_HSL,
|
||||
}ColorParseError;
|
||||
|
||||
void colorconfig_init(colorconfig *config);
|
||||
enum color_mode detect_color_mode(void);
|
||||
bool enable_windows_ansi(void);
|
||||
ColorParseError parse_color(const char *s, rgbcolor *out);
|
||||
const char *color_parse_error_message(ColorParseError error);
|
||||
unsigned char rgb_to_ansi16(rgbcolor rgb);
|
||||
unsigned char rgb_to_ansi256(rgbcolor rgb);
|
||||
rgbcolor hsl_to_rgb(unsigned short h, unsigned char s, unsigned char l);
|
||||
rgbcolor lerp_color_hsv(rgbcolor start, rgbcolor end, float t);
|
||||
void rgb_to_fg_escape(rgbcolor rgb, color_mode mode, char *out, size_t size);
|
||||
void print_color_debug(const colorconfig *config, bool ansi_enabled);
|
||||
|
||||
#endif
|
||||
@@ -2,5 +2,7 @@
|
||||
#define helper_h
|
||||
|
||||
int displaywidth(const char *text);
|
||||
int utf8_decode(const unsigned char *bytes, unsigned long *codepoint);
|
||||
int codepoint_width(unsigned long codepoint);
|
||||
|
||||
#endif
|
||||
+185
@@ -733,5 +733,190 @@ static char **mikuart[] = {
|
||||
|
||||
#define migucount (sizeof(mikuart)/sizeof(mikuart[0]))
|
||||
|
||||
static char *tetoart_0[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⢚⡫⠽⢗⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣞⠞⠁⠀⠀⠀⠈⠙⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠴⠚⠋⠉⠉⠉⢻⡟⠉⠉⠉⠛⠲⢤⡀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠁⠀⠀⠀⠀⠀⠀⠘⠀⠀⠀⠀⠀⠀⠀⠉⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡞⢀⠀⠀⢀⡠⠒⠉⠀⠀⠀⠘⣦⣀⠀⠉⠒⢦⡀⠀⠘⢦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⣀⢤⣤⣤⣒⣒⣒⣶⣖⣠⣴⣳⡠⠃⣠⠖⠁⠀⠀⠀⢠⠃⠀⠀⢸⡌⠳⣄⠀⠀⠙⣄⠀⠘⣇⢀⣀⣀⣀⣀⣀⣀⣀⠀⠀⠀",
|
||||
"⢸⠺⣏⠉⠁⠀⠀⠉⠉⠉⣹⠇⡰⣡⠞⠁⡀⠀⠀⠀⣠⡎⠀⠀⠀⢨⡇⠀⠈⠢⡀⠀⠘⣆⠀⠸⡟⠛⠋⠉⠛⠛⠻⠿⠿⢯⣶",
|
||||
"⠈⡦⢄⣉⠒⠒⠀⠒⢒⣩⣿⣼⡿⡁⠤⡼⢂⣀⢀⡴⢻⠁⠀⠀⠀⢸⠱⡀⠀⠀⠀⠀⠀⠈⢆⢣⣗⣦⠤⣀⣀⣀⣀⠤⠖⠫⡾",
|
||||
"⠀⢯⠒⠦⠬⠍⠙⢧⣤⡤⠟⠋⣁⣠⠟⠀⣠⠴⠋⠀⢸⠀⠀⠀⢠⠟⠒⠳⣄⠀⠀⢰⡀⠀⠈⠣⡧⣉⣓⠒⠠⠤⠄⠒⠂⣩⡇",
|
||||
"⠀⠸⣯⣓⣒⣒⣒⣒⣲⠯⣿⢉⡵⠋⣠⢞⣿⣿⣦⠢⢸⢠⠇⣠⢋⡀⢀⣀⣈⠢⣀⠀⠻⡦⣤⣀⣬⡿⠯⣍⣉⣀⣀⣤⠬⢴⠁",
|
||||
"⠀⠀⢇⠉⠉⠉⠉⢁⣒⣭⢿⣫⢴⣻⡇⢸⣈⢿⡤⡇⢰⣞⡜⠁⢠⣾⣿⡿⢿⠻⡏⠓⠦⣝⣦⣟⡷⣉⠙⠓⠒⠶⠶⠶⠟⡇⠀",
|
||||
"⠀⠀⣯⣉⠉⠉⣉⣙⡿⢿⣿⢇⡎⢸⡃⠈⠣⠤⠝⢀⠞⠋⠀⠀⠀⣧⠜⠣⡺⢠⡃⠀⠀⡎⢻⠋⢿⣫⢝⡒⠲⠤⠤⠤⢖⡇⠀",
|
||||
"⠀⠀⢻⢛⡻⠯⠥⠤⠖⠋⣾⢸⣼⠸⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠚⢁⡼⡇⠀⣾⢠⡏⠀⢸⣝⠯⣙⣒⣲⣶⣲⢖⡇⠀",
|
||||
"⠀⠀⠘⠦⠴⠿⠛⣲⣺⣿⠁⠀⠱⣦⡗⣿⣗⡤⣄⣀⠐⠤⠤⠀⠀⠀⠀⢀⣨⢴⣁⡾⠷⠏⠀⠀⠘⢢⡥⣤⣀⣀⣀⣒⣹⠀⠀",
|
||||
"⠀⠀⠀⠀⠸⣉⣉⣹⠾⠋⠀⠀⠀⠁⠙⠊⠛⠾⢇⡽⣿⣿⣗⣲⣶⠿⡍⠉⠰⠟⠁⠀⠀⠀⠀⠀⠀⠸⣍⠺⣦⡤⢤⠈⠁⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠳⢾⣄⡀⠀⠀⠀⠀⠀⠀⠀⣠⠿⣏⣈⢹⣏⣉⣧⡰⢿⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢩⣳⡞⠋⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠀⠀⠀⠀⠀⢀⡴⠁⢀⡟⠜⣦⡟⠶⠛⢻⡓⠙⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠒⠛⠁⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⣿⡿⢢⢫⠇⠀⡷⡇⠀⠀⠸⣷⠀⢈⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⡛⣫⡴⢣⣾⡦⢤⣷⣇⣀⣠⠤⡷⣧⠿⠟⣳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⡰⣻⡹⠁⠀⡇⠀⡇⠀⢇⠘⣜⣦⣼⢚⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⢗⣒⣤⣇⣀⣥⣒⣚⢭⣽⠊⠈⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡧⠤⠤⡏⡧⠹⠮⢿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿⡇⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇⢸⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠘⠛⠛⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char * tetoart_1[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣠⣬⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⢠⣶⠟⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⢠⣿⠏⣼⣿⣿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⣾⣿⡆⣿⣿⡃⣿⣧⣝⢿⡻⣿⣿⣿⣿⣿⣷⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⢸⣿⣿⣿⢸⣿⣴⣾⣿⣿⣯⣳⣜⢿⣿⣿⣿⣿⡇⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⡌⣿⣿⣿⡈⣿⢀⣤⣤⣭⡉⠙⢿⡏⣿⣿⣿⣿⡇⠀⠀⠀",
|
||||
"⠀⠀⠀⢀⢸⡟⣿⣿⢷⡌⢸⣿⣿⣿⣿⣷⡜⢷⣿⣿⣿⣿⡇⠀⠀⠀",
|
||||
"⠀⠀⠀⠸⢘⡀⢻⣿⣦⡀⣙⡛⠛⠛⠿⠿⠿⢸⣿⣿⣿⣿⠃⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⢰⠀⢠⣍⠙⠻⣿⣿⣿⣷⣶⣦⠀⣾⣿⣿⣿⠃⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠈⢦⣾⢹⣿⣦⢘⣋⡁⠀⠀⢙⠃⣿⣿⠟⠁⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠈⢻⣾⣿⡏⠸⣿⡿⠿⠿⠟⢀⣡⣄⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠈⣾⠟⠀⠀⠠⢤⣼⣿⣆⢻⣿⣿⣷⡀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠘⠁⣊⣐⡀⣴⣶⡼⠟⣡⣾⣿⣿⣿⣷⡀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⡀⠀⢀⣴⣿⣿⣿⠿⠋⠀⠘⣻⣿⡿⢻⣿⣿⣿⢆⠀⠀⠀",
|
||||
"⠀⠀⠀⣠⡅⠐⢿⣙⠾⠿⠁⣰⣶⡾⢘⣋⣩⣾⡆⣿⣿⣿⣿⡆⠀⠀",
|
||||
"⠀⠀⠀⣿⡇⠀⠀⠙⠷⢷⣶⣭⣍⡁⣾⣿⣿⣿⡗⣿⣿⣿⣿⠇⠀⠀",
|
||||
"⠀⠀⠀⢿⡇⠀⠀⠀⠀⠀⣿⣶⣶⡄⢿⡛⠛⠛⠛⣸⠿⢏⠉⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠙⠛⠛⠁⠀⠈⠁⠀⠁⠀⠀⠀⠀⠀⠀⠀",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *tetoart_2[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⠴⠶⠖⠒⠒⠲⠶⠦⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⣟⣆⣧⣤⣶⣶⣶⣦⣤⣤⣤⣄⣀⡇⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⡀⠀⠀⣸⡿⠟⠛⠛⠛⠛⠛⠛⠛⠛⠋⠉⠙⠓⢦⣀⣠⠴⠶⠚⠉⠉⠀⠘⡇",
|
||||
"⠀⠀⠀⠀⠀⠀⣼⠁⠀⠀⠉⠉⣹⡿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣦⢸⡋⠁⠀⠀⠀⠀⠀⠀⠀⠀⢹",
|
||||
"⠀⠀⠀⠀⠀⠀⡿⠀⠀⠀⠀⢠⡟⠁⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘",
|
||||
"⠀⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⡿⠀⠀⠀⢸⠇⠀⠀⠀⠀⠀⣷⠀⠀⠀⠀⠀⠈⢿⣆⠀⠀⠀⠀⢀⣤⣤⣴⣾",
|
||||
"⠀⠀⠀⠀⠀⠈⣳⣶⣤⣤⢸⠇⠀⠀⠀⢸⡀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠈⣿⡀⠀⠀⠀⠀⠀⠀⠀⠘",
|
||||
"⠀⠀⠀⠀⠀⠀⡿⠀⠀⠀⢸⠀⠀⠀⢀⣸⡇⠀⠀⠀⠀⠀⢸⣧⡴⠆⠀⠀⠀⠀⠸⣟⣶⡶⠖⠀⠀⠀⠀⣀",
|
||||
"⠀⠀⠀⠀⠀⠀⣇⡀⠀⠀⣿⠀⠀⠀⢨⡟⢳⡒⠀⠀⠀⠈⡟⠉⠻⣤⠀⠀⠀⠀⣀⢻⣿⡀⠀⠀⠠⠾⠟⢳",
|
||||
"⠀⠀⠀⠀⠀⠀⢠⠟⠛⠀⣿⣾⠀⢀⣾⠀⠈⢳⣼⣷⣄⢰⠇⠀⠀⠈⠓⢦⣄⣀⡙⢷⣿⣷⠀⠀⠀⠀⠀⢸",
|
||||
"⠀⠀⠀⣾⡳⣄⢸⡀⠀⠐⢿⢇⣠⡟⢻⡀⠀⣀⣽⡿⠈⠉⠀⣰⣶⣆⣠⣼⢧⡟⠹⣍⡟⠈⢻⠖⠀⠀⣴⠚",
|
||||
"⠀⠀⠀⢻⠀⢻⡀⠳⣤⡄⠘⠛⣯⢻⣹⣟⠛⢛⠋⠁⠀⠀⠀⠉⠉⠩⠍⢸⣼⠁⡼⠋⠀⠀⠘⠦⠤⣄⢈⡿",
|
||||
"⠀⠀⠀⣠⣇⠀⣧⢺⣍⢠⡴⠶⠋⠈⢻⣿⣆⠀⠀⠀⣴⠚⠙⢷⠀⢀⡴⠿⠷⠚⠁⠀⠀⠀⠀⠀⢠⠿⠋⠀",
|
||||
"⠀⡴⠺⡅⠘⣆⠘⣆⠉⠻⢧⠀⠀⠀⠀⠈⠈⠙⠢⢤⣘⣦⣤⡼⢶⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⢰⢷⡀⢻⡀⢿⠀⠙⠚⠛⠛⣦⠀⠀⠀⠀⢀⣀⣠⣤⢞⡟⠒⠒⠺⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠘⣦⡻⣤⠿⠚⠀⢀⡶⠋⠉⠉⠀⠀⢀⡴⡏⣽⣽⣳⣿⣇⠀⠀⣴⣿⣷⠶⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠈⡿⠗⠀⠒⠒⢻⡄⠀⠀⠀⠀⣠⢞⣽⢱⣿⡟⣿⣿⣿⣤⢾⢿⢇⣹⠀⣿⠸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠓⣶⠒⠒⠒⢿⠀⠀⢀⣄⡾⡿⢊⡏⡞⡼⣸⣿⣿⢹⠓⠚⠚⢼⡿⠀⣿⠀⡀⠀⠀⠀⠀⠀⠀⢀⠀⠀",
|
||||
"⠀⠀⠀⢻⠀⠀⠀⠘⡆⣴⢋⢜⠻⣴⣿⢹⢿⢷⣿⣿⣿⠈⣽⢿⠖⢿⠁⠀⡗⠀⡀⠀⠀⠀⠀⠀⠀⢸⠙⠆",
|
||||
"⠀⠀⠀⠈⡇⠀⠀⠀⢻⠡⡫⣋⢎⢝⣇⣏⡎⣾⣿⣿⡟⢸⣾⣼⢸⢸⡀⠀⡇⠀⡇⠀⠀⠀⠀⠀⢀⡜⠀⠰",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *tetoart_3[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡴⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣈⡧⠖⠚⠉⠉⠀⠀⠀⠀⠀⠘⡆⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡞⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢧⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⢀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠦⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣨⡆⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⢀⡨⠻⣗⢄⠀⠀⣀⣠⡤⣤⠔⠛⢩⠟⠀⠀",
|
||||
"⠀⢀⣀⡤⠤⠤⣾⠀⠀⠀⠀⠰⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣆⠀⠀⠀⢀⡀⢤⡒⠥⠒⠉⠘⢯⡽⢿⠉⢀⣴⣁⣤⠴⠧⡄⠀⠀",
|
||||
"⠁⠀⠀⠀⠀⠀⡇⠁⠒⠀⠀⠀⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣆⠀⢀⡴⠉⠳⣄⠀⠀⠀⠀⠀⠻⣼⣖⠋⠁⠀⠀⠀⠀⢹⡀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⡇⠐⠒⠂⠤⠤⠤⡿⣄⠤⠤⠀⠀⠀⠀⠀⠀⠀⣽⣾⣋⠀⠀⠀⠈⠃⠀⠀⠀⠀⠀⠀⠺⠿⣤⠀⠀⠀⣀⡤⡿⠀",
|
||||
"⡄⠀⠀⠀⠀⠀⣹⠀⠀⠀⠀⠀⢠⣇⠘⢦⠀⠀⠀⠀⠀⠀⠰⠏⣽⡟⠉⠉⠓⣶⣴⣤⡀⣽⠦⠤⠴⡶⠚⠉⠁⣀⣴⠞⠁⡼⠃⠀",
|
||||
"⠈⡆⢀⡴⠲⠯⣭⡆⠀⠀⠀⠀⢸⡱⠉⠩⠿⣖⠂⣄⠀⠀⠀⠀⢀⣧⣶⣶⣿⣿⣯⣿⣷⠺⡄⠀⠀⡿⠤⢞⣩⣾⣥⡤⠾⠓⠙⡇",
|
||||
"⠀⠙⠯⠤⠤⣤⡴⠻⡄⠀⠀⠀⡞⠁⠀⠀⢀⣈⣳⣼⠛⢦⣀⠀⢲⣿⣿⡿⣿⡇⢹⡟⠛⠀⡧⠀⢠⡇⠀⠘⡆⠀⠀⠀⠀⠀⢠⡇",
|
||||
"⠀⠀⠀⠸⣍⠁⠀⠀⡇⠀⠀⣸⡇⣶⠿⣿⣿⣿⣷⡮⠀⠀⠈⠉⠓⠋⠘⢷⡬⠔⠋⡇⠀⠀⢧⠀⡼⠀⠀⠀⠉⠒⢠⡤⠤⣶⠞⠁",
|
||||
"⠀⠀⠀⠀⠈⠳⣄⣤⣿⡄⣰⣽⢻⡹⣆⠘⣿⡛⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⢸⠸⣅⠀⠀⠀⢰⠏⠙⢶⣋⣁⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡿⢽⡏⢧⠀⠀⠈⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡼⠒⠚⠳⠀⠀⠈⠓⠦⠤⠼⠛⠁⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠈⠋⢧⠘⡇⠘⣇⠀⠀⠀⠀⠀⠀⠀⣠⠴⣒⡦⢤⡀⠀⠀⢀⣿⠀⢠⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠓⠿⡄⢸⣦⣀⠀⠀⠀⠀⠀⡟⠉⠀⠀⠀⣳⢀⡴⠋⢸⣄⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⣛⠋⣳⣰⡇⠈⠛⠲⠤⣄⣀⣙⠦⠤⢤⣲⠵⠋⣀⣤⣼⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠀⠛⠀⠀⠀⠀⠀⠀⣠⣿⣿⣭⡉⣠⣴⠿⡛⢉⣻⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣀⣠⣄⣀⣰⣿⢻⣷⣔⣿⣗⢀⣠⣶⣿⣿⣿⣿⢦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠁⠀⠑⡀⣿⣶⣴⣼⣿⣿⣿⢿⣿⣿⢿⣿⡿⠏⣡⠿⠉⠀⢨⣿⡟⡩⠏⠉⠉⠓⠶⡄⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡼⠀⠀⠀⠀⠀⢸⢿⡿⠿⠿⢿⣆⠙⣿⣻⣿⣿⢉⣡⡾⠃⠀⠀⠀⠨⣿⡏⠀⠀⠀⠀⠀⠀⢹⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⡞⠀⠏⠂⠢⡀⢹⣿⣿⣽⣿⣿⠾⠯⠀⠄⠤⠀⠔⠚⢸⠃⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠃⠀⠀⠀⠀⢠⠇⠀⠀⠀⠀⠈⠁⣿⡿⣿⠛⡟⣴⠈⠁⠀⠀⠀⠀⠀⢸⠁⠀⠀⠀⠀⠀⠀⢸⠃⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⢀⡾⠚⠛⠛⠐⠒⢶⣤⡇⡇⣿⢰⣿⢃⡤⠖⠚⠉⠙⠓⠲⡏⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢈⠀⠀⠀⢠⣼⢷⣄⣿⡿⢀⣠⣾⣼⠁⠀⣿⣼⠃⣟⠀⠀⠰⣶⡆⣠⣴⣧⠀⠀⠀⠀⠀⠀⠀⡏⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⢀⢦⣿⡀⠈⣿⠏⠉⢱⡇⢿⣶⣿⣿⡏⠀⡽⠉⠛⠛⣿⠛⡉⣸⠈⢄⡀⠀⠀⠀⠀⢰⡇⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⣤⠾⠒⠒⢳⣾⠀⠉⠂⡏⠀⠀⣾⡇⢸⣿⠻⢿⠁⠀⡇⠀⠀⠀⣿⢠⡇⣿⠶⠒⠛⠉⠉⠉⠙⢚⡆⠀⠀⠀",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *tetoart_4[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠞⠉⠐⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣿⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣻⡟⢯⣛⢿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⢷⣫⢇⣟⣣⠟⣮⢞⣯⡝⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⢀⣶⣾⣿⣿⣿⣿⣿⣿⡿⢉⣾⢱⢏⣶⣿⢿⣸⣉⣷⢹⣆⣿⣷⣰⣶⣰⣶⣆⡀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠘⣾⣭⢩⣝⣿⣻⣿⣟⣵⡋⣤⣿⢿⠿⡟⢚⣵⣿⢿⣶⢿⡾⣿⣿⣿⢿⣿⣿⣿⣿⡆",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⢳⢾⡋⠼⣿⣯⣷⡿⢧⣿⡻⠟⠁⢀⣏⣟⡟⠁⠈⢻⣯⢿⣿⣿⣿⠲⠴⣾⡿⣿⠇",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⢘⣿⣷⣿⣾⣿⣿⣫⣿⡾⣷⣮⠁⢰⡾⠋⣴⣤⣤⡀⣿⣯⢿⣟⣿⣓⡻⢳⣻⡟⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣶⠶⣯⣿⣿⣿⣧⠃⠹⠋⠀⠀⠀⠀⠸⣻⠏⡿⣿⣿⣿⣿⣟⡛⣿⣿⡏⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣟⣹⣿⠟⣿⣿⡀⠀⠀⠀⠠⠀⠀⠀⠀⢀⣾⣿⣿⣿⣿⣩⣽⣾⠿⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢯⣷⠀⠈⡟⡿⠢⢀⠁⠀⠈⠁⢀⡤⢺⠟⠛⠋⢹⣅⣫⢿⠟⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠋⠀⠀⠈⠋⢉⣀⢗⡠⠔⣞⡁⠀⠋⠀⠀⠀⠈⣿⠆⠁⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠄⠀⣤⣴⣶⣿⡏⠀⠁⠃⠘⣿⣦⣴⣶⣤⢄⡐⠉⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡐⠁⠀⠀⣿⣞⣿⣿⢧⡀⠀⣀⣾⣿⣿⣿⡿⠁⠀⠈⢂⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣆⡀⠀⠀⢰⢹⣭⣿⣿⣿⣿⣾⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⡆⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣆⠀⣾⣿⣧⢿⣽⣿⢻⢿⣯⢿⣿⣿⣇⡄⠀⢠⣤⣶⡇⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⡿⢚⣿⢿⡻⢿⣿⢾⢫⢾⣽⣫⢿⣿⣿⣿⡄⠈⠉⠁⠠⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⠟⠁⠀⠸⣯⢷⣛⣯⣿⣿⠿⣿⣾⣯⢷⡞⣽⣿⣷⣀⡠⣴⣾⣄⠀⠀⠀⠀⠀",
|
||||
"⢠⣿⡻⣼⣹⣯⣿⣿⡟⠁⠀⠀⠀⠀⣿⢿⡻⣟⢷⡏⢾⣿⢺⡟⣿⢻⣿⣿⠃⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀",
|
||||
"⠸⣸⣿⢱⣇⢿⣸⢿⣇⠀⠀⠀⠀⠀⣿⣇⡿⣾⢹⣏⣹⣏⡷⢿⣸⢏⣿⣿⠀⢸⣿⣿⣿⡿⡆⠀⠀⠀⠀",
|
||||
"⠀⠘⠬⡑⠌⠛⠮⣏⣿⣵⣄⠀⠀⢸⣿⣜⣳⣭⣿⢽⣻⡿⣼⢻⡜⣯⣿⡇⠀⠈⣿⣿⢿⡿⣇⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠈⢳⣾⣔⣨⣗⢯⣿⣷⣄⡾⣿⣜⣳⢮⣿⣧⢻⡷⣭⡗⣯⣳⣿⣗⠀⠀⢻⡽⣾⣽⡿⡀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠙⢿⣿⣿⣯⣿⣿⣿⢀⣿⡾⣱⣯⣿⣽⣻⡽⣖⢯⣷⡿⣯⣏⠀⠀⠈⡷⣏⡿⣿⡗⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠻⣿⣿⣟⠯⣿⣳⣻⣿⣳⡽⣟⠻⣻⡿⣭⣿⢯⡽⣾⣿⡄⠀⠀⢿⣱⣏⣿⠳⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣻⡱⢎⣷⡯⣿⣿⣿⣷⣿⣿⣿⣛⡮⣗⣿⣿⣿⣿⡄⠀⢺⣷⢺⣿⣿⡀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⢠⡴⣿⣿⣷⣞⣿⡷⣿⢿⣿⣿⣟⢶⣯⢳⣿⣿⣿⣿⣿⣷⡀⢸⣷⣻⡿⢿⡅⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠃⠈⢹⢹⣿⡳⢯⣟⣿⡿⣿⣿⣿⣿⣿⣾⣿⢿⡿⣿⣿⣿⣿⣿⣽⢶⡛⣷⢦⡇⠀⠀",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *tetoart_5[] = {
|
||||
"",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⣠⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠻⡿⠆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢘⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠿⠥⠤⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⡠⡄⣤⠀⠀⠀⢀⠃⣷⡄⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠠⠀⠆⠀⠀⠐⠀⠈⠀⠨⠂⡄⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠚⠀⠠⠀⠀⠀⠀⢀⠀⠈⠐⣠⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠐⠀⠀⠀⠂⠀⠀⡀⠀⠀⠂⢁⢊⡅⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⢀⠉⠀⠀⠀⠀⠀⠀⠀⠀⠄⠀⠂⡐⢼⡀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠂⠀⠀⠀⠂⠀⠀⣆⠀⠀⠀⠈⠄⠀⡔⣣⠀⠀⠀",
|
||||
"⠀⠀⠀⡡⠊⢆⠀⡀⠀⠀⡇⠈⠣⡀⠀⠀⠡⠀⠌⠐⣣⠀⠀",
|
||||
"⠀⡐⣬⢑⡅⢊⢎⣛⢬⡅⢧⠡⣂⣀⣄⠀⠈⠠⠈⠠⢘⢧⠀",
|
||||
"⢀⢱⡖⣍⣿⣧⠀⠣⣧⠓⠀⡜⣅⣿⣿⣧⢀⠐⠀⠂⢤⠭⡂",
|
||||
"⠠⠸⢇⠻⣿⡟⠂⠀⠘⢷⠀⠈⢿⣿⡿⡻⡉⠄⡁⠀⢀⠨⡆",
|
||||
"⠨⠀⠀⠀⠀⠀⠀⠐⠀⠀⠀⠀⠀⠀⠈⠀⠣⠀⠄⠐⠀⢁⡂",
|
||||
"⠈⡀⠀⠀⠀⠀⠀⢤⣀⣀⣠⠆⠀⠀⠀⠀⠁⠐⠌⠄⡠⢷⠃",
|
||||
"⠀⠐⡠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠁⠀⡃⠠⣂⢢⡜⠀",
|
||||
"⠀⠀⠈⢄⠁⢀⠀⡀⠀⠀⡀⠀⠠⠀⠀⠀⡠⠀⢁⢒⠈⠀⠀",
|
||||
"⠀⠀⠀⠀⠐⠠⡀⠂⠈⠄⠀⣀⠀⠈⢂⠈⠠⡰⠖⠁⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠉⠛⠷⣶⢾⣼⣧⡿⠷⠋⠁⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static char **tetoart[] = {
|
||||
tetoart_0,
|
||||
tetoart_1,
|
||||
tetoart_2,
|
||||
tetoart_3,
|
||||
tetoart_4,
|
||||
tetoart_5,
|
||||
|
||||
};
|
||||
|
||||
#define tetocount (sizeof(tetoart)/sizeof(tetoart[0]))
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+607
-2
@@ -1,3 +1,608 @@
|
||||
#include "../defs/all.h"
|
||||
//i'm gonna do this later
|
||||
void Void(void){};
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
//try to enable ansi (virtual terminal processing) on windows consoles.
|
||||
//returns true if enabeling succeded or isn't necesary, false otherwise.
|
||||
bool enable_windows_ansi(void){
|
||||
#ifdef _WIN32
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
#endif
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
|
||||
if(out == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
if(GetConsoleMode(out, &mode) == 0)
|
||||
return false;
|
||||
if(SetConsoleMode(out, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
//returns true if str ends with suffix, ignorning case
|
||||
static bool ends_with_ignore_case(const char *str, const char *suffix){
|
||||
size_t str_len = strlen(str);
|
||||
size_t suffix_len = strlen(suffix);
|
||||
size_t i;
|
||||
|
||||
if(suffix_len > str_len)
|
||||
return false;
|
||||
str += str_len - suffix_len;
|
||||
for(i = 0; i < suffix_len; i++){
|
||||
if(tolower((unsigned char)str[i]) != tolower((unsigned char)suffix[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//detect the parent shell name on windows, using enviroment hints.
|
||||
//returns null if nothing matched
|
||||
static const char *detect_windows_shell(void){
|
||||
const char *comspec;
|
||||
|
||||
//heuristic checks using common env vars
|
||||
if(getenv("PSModulePath"))
|
||||
return "powershell";
|
||||
if(getenv("PWshPROFILE") || getenv("PWSH_VERSION"))
|
||||
return "pwsh";
|
||||
comspec = getenv("COMSPEC");
|
||||
if(comspec && ends_with_ignore_case(comspec, "cmd.exe"))
|
||||
return "cmd.exe";
|
||||
if(getenv("WT_SESSION"))
|
||||
return "windows_terminal";
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum color_mode detect_color_mode(void){
|
||||
const char *colorterm, *term;
|
||||
enum color_mode mode = MODE_ANSI16;
|
||||
|
||||
if(getenv("NO_COLOR"))
|
||||
return MODE_NONE;
|
||||
|
||||
//early checks
|
||||
colorterm = getenv("COLORTERM");
|
||||
if(colorterm && (strstr(colorterm, "truecolor") || strstr(colorterm, "24bit")))
|
||||
return MODE_TRUECOLOR;
|
||||
|
||||
term = getenv("TERM");
|
||||
if(term && strstr(term, "256color"))
|
||||
return MODE_ANSI256;
|
||||
if(term && strstr(term, "color"))
|
||||
return MODE_ANSI16;
|
||||
|
||||
//default fallback is mode_ansi16 (set above)
|
||||
|
||||
#ifdef _WIN32
|
||||
//if running on windows, try to enable ansi (virtual terminal processing)
|
||||
if(enable_windows_ansi()){
|
||||
//re-check for a better mode
|
||||
if(colorterm && (strstr(colorterm, "truecolor") || strstr(colorterm, "24bit")))
|
||||
mode = MODE_TRUECOLOR;
|
||||
if(term && strstr(term, "256color"))
|
||||
mode = MODE_ANSI256;
|
||||
}else{
|
||||
const char *shell = detect_windows_shell();
|
||||
if(shell){
|
||||
if(strstr(shell, "cmd.exe")){
|
||||
//cmd historically doesn't support ansi; use 16 colors fallback
|
||||
mode = MODE_ANSI16;
|
||||
}else if(strstr(shell, "powershell") || strstr(shell, "pwsh")){
|
||||
if(term && strstr(term, "256color"))
|
||||
mode = MODE_ANSI256;
|
||||
}else{
|
||||
mode = MODE_ANSI16;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
void colorconfig_init(colorconfig *config){
|
||||
memset(config, 0, sizeof(*config));
|
||||
config->saturation = 100;
|
||||
config->brightness = 50;
|
||||
config->mode = detect_color_mode();
|
||||
}
|
||||
|
||||
unsigned char rgb_to_ansi16(rgbcolor rgb){
|
||||
unsigned char r = rgb.red;
|
||||
unsigned char g = rgb.green;
|
||||
unsigned char b = rgb.blue;
|
||||
|
||||
unsigned char brightness = (unsigned char)((r * 299 + g * 587 + b * 114 + 500) / 1000); //avoid using math.h because i don't want to use it
|
||||
int is_bright = brightness > 128;
|
||||
|
||||
//determine base color using simple thresholding on each channel, producing an index 0..7
|
||||
int r_on = r > 128;
|
||||
int g_on = g > 128;
|
||||
int b_on = b > 128;
|
||||
unsigned char base_idx = (unsigned char)r_on + (unsigned char)g_on * 2 + (unsigned char)b_on * 4; // 0..7
|
||||
|
||||
//sgr color codes: 30..37 for normal, 90..97 for bright
|
||||
if(is_bright)
|
||||
return (unsigned char)(90 + base_idx);
|
||||
return (unsigned char)(30 + base_idx);
|
||||
}
|
||||
|
||||
//round half away from zero for positive values only
|
||||
static int round_pos(float x){
|
||||
return (int)(x + 0.5f);
|
||||
}
|
||||
|
||||
unsigned char rgb_to_ansi256(rgbcolor rgb){
|
||||
unsigned char r = rgb.red;
|
||||
unsigned char g = rgb.green;
|
||||
unsigned char b = rgb.blue;
|
||||
|
||||
if(r == g && g == b){
|
||||
//calulate the grayscale value
|
||||
unsigned char gray = (unsigned char)round_pos(r * 0.299f + g * 0.587f + b * 0.114f);
|
||||
//ramp (232-255)
|
||||
if(gray <= 8)
|
||||
return 16;
|
||||
if(gray >= 248)
|
||||
return 231;
|
||||
return (unsigned char)(round_pos(((float)gray - 8.0f) / 247.0f * 24.0f) + 232);
|
||||
}
|
||||
|
||||
//color cube (16-231)
|
||||
{
|
||||
int ri = round_pos(r / 255.0f * 5.0f);
|
||||
int gi = round_pos(g / 255.0f * 5.0f);
|
||||
int bi = round_pos(b / 255.0f * 5.0f);
|
||||
|
||||
return (unsigned char)(16 + ri * 36 + gi * 6 + bi);
|
||||
}
|
||||
}
|
||||
|
||||
//convert ansi color code to rgb
|
||||
static rgbcolor ansi_to_rgb(unsigned char code){
|
||||
//standard 16 colors
|
||||
static const rgbcolor standard[16] = {
|
||||
{0, 0, 0}, //black
|
||||
{128, 0, 0}, //red
|
||||
{0, 128, 0}, //green
|
||||
{128, 128, 0}, //yellow
|
||||
{0, 0, 128}, //blue
|
||||
{128, 0, 128}, // magenta
|
||||
{0, 128, 128}, //cyan
|
||||
{192, 192, 192}, //white
|
||||
{128, 128, 128}, //bright blakc whatever that is
|
||||
{255, 0, 0}, //bright red
|
||||
{0, 255, 0}, //brightgreen
|
||||
{255, 255, 0},//bright yellow
|
||||
{0, 0, 255}, //bright blue
|
||||
{255, 0, 255}, //bright magenta
|
||||
{0, 255, 255}, //bright cyan
|
||||
{255, 255, 255}, //Brightwhite
|
||||
};
|
||||
rgbcolor out;
|
||||
|
||||
if(code < 16)
|
||||
return standard[code];
|
||||
|
||||
//256-color mode
|
||||
if(code <= 231){
|
||||
//color cube
|
||||
int cube = code - 16;
|
||||
out.red = (unsigned char)(((cube / 36) % 6) * 51);
|
||||
out.green = (unsigned char)(((cube / 6) % 6) * 51);
|
||||
out.blue = (unsigned char)((cube % 6) * 51);
|
||||
return out;
|
||||
}
|
||||
|
||||
//grayscale ramp
|
||||
out.red = out.green = out.blue = (unsigned char)((code - 232) * 10 + 8);
|
||||
return out;
|
||||
}
|
||||
|
||||
//helper for hsl_to_rgb
|
||||
static float hue_to_rgb(float p, float q, float t){
|
||||
if(t < 0.0f)
|
||||
t += 1.0f;
|
||||
if(t > 1.0f)
|
||||
t -= 1.0f;
|
||||
if(t < 1.0f / 6.0f)
|
||||
return p + (q - p) * 6.0f * t;
|
||||
if(t < 1.0f / 2.0f)
|
||||
return q;
|
||||
if(t < 2.0f / 3.0f)
|
||||
return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
|
||||
return p;
|
||||
}
|
||||
|
||||
//convert hsl to rgb
|
||||
//h: 0-360, s: 0-100, l: 0-100
|
||||
rgbcolor hsl_to_rgb(unsigned short h, unsigned char s, unsigned char l){
|
||||
float hue = (float)h / 360.0f;
|
||||
float sat = (float)s / 100.0f;
|
||||
float lig = (float)l / 100.0f;
|
||||
float r, g, b;
|
||||
rgbcolor out;
|
||||
|
||||
if(sat == 0.0f){
|
||||
r = g = b = lig;
|
||||
}else{
|
||||
float q = lig < 0.5f ? lig * (1.0f + sat) : lig + sat - lig * sat;
|
||||
float p = 2.0f * lig - q;
|
||||
|
||||
r = hue_to_rgb(p, q, hue + 1.0f / 3.0f);
|
||||
g = hue_to_rgb(p, q, hue);
|
||||
b = hue_to_rgb(p, q, hue - 1.0f / 3.0f);
|
||||
}
|
||||
|
||||
out.red = (unsigned char)round_pos(r * 255.0f);
|
||||
out.green = (unsigned char)round_pos(g * 255.0f);
|
||||
out.blue = (unsigned char)round_pos(b * 255.0f);
|
||||
return out;
|
||||
}
|
||||
|
||||
//convert color to ansi escape code for foreground
|
||||
//out gets an empty string for mode_none
|
||||
void rgb_to_fg_escape(rgbcolor rgb, color_mode mode, char *out, size_t size){
|
||||
switch(mode){
|
||||
case MODE_TRUECOLOR:
|
||||
snprintf(out, size, "\x1b[38;2;%d;%d;%dm", rgb.red, rgb.green, rgb.blue);
|
||||
break;
|
||||
case MODE_ANSI256:
|
||||
snprintf(out, size, "\x1b[38;5;%dm", rgb_to_ansi256(rgb));
|
||||
break;
|
||||
case MODE_ANSI16:
|
||||
snprintf(out, size, "\x1b[%dm", rgb_to_ansi16(rgb));
|
||||
break;
|
||||
case MODE_NONE:
|
||||
default:
|
||||
if(size > 0)
|
||||
out[0] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static float float_mod(float a, float b){
|
||||
return a - b * (float)(long)(a / b);
|
||||
}
|
||||
|
||||
static float float_abs(float x){
|
||||
return x < 0.0f ? -x : x;
|
||||
}
|
||||
|
||||
//converts rgb to hsv
|
||||
static void rgb_to_hsv(rgbcolor rgb, float *h_out, float *s_out, float *v_out){
|
||||
float r = rgb.red / 255.0f;
|
||||
float g = rgb.green / 255.0f;
|
||||
float b = rgb.blue / 255.0f;
|
||||
float max = r > g ? r : g;
|
||||
float min = r < g ? r : g;
|
||||
float delta, h;
|
||||
|
||||
if(b > max) max = b;
|
||||
if(b < min) min = b;
|
||||
delta = max - min;
|
||||
|
||||
if(delta == 0.0f)
|
||||
h = 0.0f;
|
||||
else if(max == r)
|
||||
h = 60.0f * float_mod((g - b) / delta, 6.0f);
|
||||
else if(max == g)
|
||||
h = 60.0f * ((b - r) / delta + 2.0f);
|
||||
else
|
||||
h = 60.0f * ((r - g) / delta + 4.0f);
|
||||
|
||||
if(h < 0.0f)
|
||||
h += 360.0f;
|
||||
|
||||
*h_out = h;
|
||||
*s_out = max == 0.0f ? 0.0f : delta / max;
|
||||
*v_out = max;
|
||||
}
|
||||
|
||||
//convert hsv to rgb
|
||||
static rgbcolor hsv_to_rgb(float h, float s, float v){
|
||||
float c = v * s;
|
||||
float x = c * (1.0f - float_abs(float_mod(h / 60.0f, 2.0f) - 1.0f));
|
||||
float m = v - c;
|
||||
float r, g, b;
|
||||
rgbcolor out;
|
||||
|
||||
if(h >= 0.0f && h < 60.0f)
|
||||
{r = c; g = x; b = 0.0f;}
|
||||
else if(h >= 60.0f && h < 120.0f)
|
||||
{r = x; g = c; b = 0.0f;}
|
||||
else if(h >= 120.0f && h < 180.0f)
|
||||
{r = 0.0f; g = c;b = x;}
|
||||
else if(h >= 180.0f && h < 240.0f)
|
||||
{r = 0.0f; g = x; b = c;}
|
||||
else if(h >= 240.0f && h < 300.0f)
|
||||
{r = x; g = 0.0f; b = c; }
|
||||
else {r = c; g = 0.0f; b = x;}
|
||||
|
||||
out.red = (unsigned char)round_pos((r + m) * 255.0f);
|
||||
out.green = (unsigned char)round_pos((g + m) * 255.0f);
|
||||
out.blue = (unsigned char)round_pos((b + m) * 255.0f);
|
||||
return out;
|
||||
}
|
||||
|
||||
//linear interpolation between two colors in hsv space
|
||||
rgbcolor lerp_color_hsv(rgbcolor start, rgbcolor end, float t){
|
||||
float h1, s1, v1, h2, s2, v2;
|
||||
float h_diff, h, s, v;
|
||||
|
||||
rgb_to_hsv(start, &h1, &s1, &v1);
|
||||
rgb_to_hsv(end, &h2, &s2, &v2);
|
||||
|
||||
//handle hue interpolation correctly (shortest path around the color wheel)
|
||||
h_diff = float_mod(h2 - h1 + 180.0f, 360.0f) - 180.0f;
|
||||
h = float_mod(h1 + h_diff * t + 360.0f, 360.0f);
|
||||
|
||||
s = s1 + (s2 - s1) * t;
|
||||
v = v1 + (v2 - v1) * t;
|
||||
|
||||
return hsv_to_rgb(h, s, v);
|
||||
}
|
||||
|
||||
//parsing helpers
|
||||
|
||||
//returns the numeric value of a hex digit,, or -1
|
||||
static int hex_value(char c){
|
||||
if(c >= '0' && c <= '9') return c - '0';
|
||||
if(c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if(c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//skip spaces at both ends of [*start, *end)
|
||||
static void trim_range(const char **start, const char **end){
|
||||
while(*start < *end && isspace((unsigned char)**start))
|
||||
(*start)++;
|
||||
while(*end > *start && isspace((unsigned char)(*end)[-1]))
|
||||
(*end)--;
|
||||
}
|
||||
|
||||
//parse [start, end) as a decimal number from 0 to max. 1 good 0 bad
|
||||
static int parse_uint(const char *start, const char *end, unsigned long max, unsigned long *out){
|
||||
unsigned long value = 0;
|
||||
|
||||
if(start >= end)
|
||||
return 0;
|
||||
for(; start < end; start++){
|
||||
if(!isdigit((unsigned char)*start))
|
||||
return 0;
|
||||
value = value * 10 + (unsigned long)(*start - '0');
|
||||
if(value > max)
|
||||
return 0;
|
||||
}
|
||||
*out = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//returns 1 if str starts with prefix ignoring case
|
||||
static int starts_with_ignore_case(const char *str, const char *prefix){
|
||||
for(; *prefix != '\0'; str++, prefix++){
|
||||
if(*str == '\0' || tolower((unsigned char)*str) != tolower((unsigned char)*prefix))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ColorParseError parse_hex_color(const char *s, rgbcolor *out){
|
||||
const char *hex = s;
|
||||
int value[6];
|
||||
size_t length, i;
|
||||
|
||||
while(*hex == '#')
|
||||
hex++;
|
||||
length = strlen(hex);
|
||||
|
||||
if(length != 3 && length != 6)
|
||||
return COLOR_ERR_HEX;
|
||||
for(i = 0; i < length; i++){
|
||||
value[i] = hex_value(hex[i]);
|
||||
if(value[i] < 0)
|
||||
return COLOR_ERR_HEX;
|
||||
}
|
||||
|
||||
if(length == 3){
|
||||
//#rgb format
|
||||
out->red = (unsigned char)(value[0] * 17);
|
||||
out->green = (unsigned char)(value[1] * 17);
|
||||
out->blue = (unsigned char)(value[2] * 17);
|
||||
}else{
|
||||
//#rrggbb format
|
||||
out->red = (unsigned char)(value[0] * 16 + value[1]);
|
||||
out->green = (unsigned char)(value[2] * 16 + value[3]);
|
||||
out->blue = (unsigned char)(value[4] * 16 + value[5]);
|
||||
}
|
||||
return COLOR_OK;
|
||||
}
|
||||
|
||||
//splits [start, end) on exactly two commas into three trimmed ranges. 1 good 0 bad
|
||||
static int split_three(const char *start, const char *end, const char *range[3][2]){
|
||||
const char *comma1 = NULL, *comma2 = NULL, *p;
|
||||
int commas = 0;
|
||||
|
||||
for(p = start; p < end; p++){
|
||||
if(*p != ',')
|
||||
continue;
|
||||
commas++;
|
||||
if(commas == 1) comma1 = p;
|
||||
else if(commas == 2) comma2 = p;
|
||||
}
|
||||
if(commas != 2)
|
||||
return 0;
|
||||
|
||||
range[0][0] = start; range[0][1] = comma1;
|
||||
range[1][0] = comma1 + 1; range[1][1] = comma2;
|
||||
range[2][0] = comma2 + 1; range[2][1] = end;
|
||||
for(commas = 0; commas < 3; commas++){
|
||||
trim_range(&range[commas][0], &range[commas][1]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ColorParseError parse_rgb_color(const char *s, rgbcolor *out){
|
||||
const char *range[3][2];
|
||||
unsigned long r, g, b;
|
||||
|
||||
if(!split_three(s, s + strlen(s), range))
|
||||
return COLOR_ERR_RGB;
|
||||
if(!parse_uint(range[0][0], range[0][1], 255, &r)|| !parse_uint(range[1][0], range[1][1], 255, &g)|| !parse_uint(range[2][0], range[2][1], 255, &b))
|
||||
return COLOR_ERR_RGB;
|
||||
|
||||
out->red = (unsigned char)r;
|
||||
out->green = (unsigned char)g;
|
||||
out->blue = (unsigned char)b;
|
||||
return COLOR_OK;
|
||||
}
|
||||
|
||||
static ColorParseError parse_hsl_color(const char *s, rgbcolor *out){
|
||||
const char *start = s;
|
||||
const char *end = s + strlen(s);
|
||||
const char *range[3][2];
|
||||
unsigned long h, sat, l;
|
||||
|
||||
//strip the "hsl(" prefix and the ")" suffix
|
||||
while(strncmp(start, "hsl(", 4) == 0)
|
||||
start += 4;
|
||||
while(end > start && end[-1] == ')')
|
||||
end--;
|
||||
|
||||
if(!split_three(start, end, range))
|
||||
return COLOR_ERR_HSL;
|
||||
|
||||
//hue can have a trailing unit like "180deg", so drop trailing non-digits
|
||||
while(range[0][1] > range[0][0] && !isdigit((unsigned char)range[0][1][-1]))
|
||||
range[0][1]--;
|
||||
//saturation and lightness can have trailing '%'
|
||||
while(range[1][1] > range[1][0] && range[1][1][-1] == '%')
|
||||
range[1][1]--;
|
||||
while(range[2][1] > range[2][0] && range[2][1][-1] == '%')
|
||||
range[2][1]--;
|
||||
|
||||
if(!parse_uint(range[0][0], range[0][1], 65535, &h)
|
||||
|| !parse_uint(range[1][0], range[1][1], 255, &sat)
|
||||
|| !parse_uint(range[2][0], range[2][1], 255, &l))
|
||||
return COLOR_ERR_HSL;
|
||||
|
||||
if(h > 360 || sat > 100 || l > 100)
|
||||
return COLOR_ERR_HSL;
|
||||
|
||||
*out = hsl_to_rgb((unsigned short)h, (unsigned char)sat, (unsigned char)l);
|
||||
return COLOR_OK;
|
||||
}
|
||||
|
||||
static int parse_named_color(const char *s, rgbcolor *out){
|
||||
static const struct { const char *name; rgbcolor rgb; } table[] = {
|
||||
{"black", {0,0,0}}, {"red", {197,15,31}}, {"green", {19,161,14}},
|
||||
{"yellow", {193,156,0}}, {"blue", {0,55,218}}, {"magenta", {136,23,152}},
|
||||
{"cyan", {58,150,221}}, {"white", {204,204,204}},
|
||||
{"brightblack", {118,118,118}}, {"brightred", {231,72,86}},
|
||||
{"brightgreen", {22,198,12}}, {"brightyellow", {249,241,165}},
|
||||
{"brightblue", {59,120,255}}, {"brightmagenta", {180,0,158}},
|
||||
{"brightcyan", {97,214,214}}, {"brightwhite", {242,242,242}},
|
||||
};
|
||||
char norm[32];
|
||||
size_t n = 0, i;
|
||||
|
||||
for(; *s && n < sizeof(norm) - 1; s++) //lowercase, drop '_'
|
||||
if(*s != '_') norm[n++] = (char)tolower((unsigned char)*s);
|
||||
norm[n] = '\0';
|
||||
for(i = 0; i < sizeof(table) / sizeof(table[0]); i++){
|
||||
if(strcmp(norm, table[i].name) == 0){
|
||||
*out = table[i].rgb;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//parse any supported color format into rgb:
|
||||
//#rrggbb, #rgb, hsl(h,s%,l%), r,g,b or an ansi code 0-255
|
||||
ColorParseError parse_color(const char *s, rgbcolor *out){
|
||||
if(parse_named_color(s, out)) return COLOR_OK;
|
||||
size_t length;
|
||||
unsigned long code;
|
||||
|
||||
//try hex color (#rrggbb or #rgb)
|
||||
if(s[0] == '#')
|
||||
return parse_hex_color(s, out);
|
||||
|
||||
//try hsl (hsl(180,100%,50%))
|
||||
length = strlen(s);
|
||||
if(starts_with_ignore_case(s, "hsl(") && length > 0 && s[length - 1] == ')')
|
||||
return parse_hsl_color(s, out);
|
||||
|
||||
//try rgb tuple (255,0,128)
|
||||
if(strchr(s, ','))
|
||||
return parse_rgb_color(s, out);
|
||||
|
||||
//try ansi code (number between 0-255)
|
||||
if(parse_uint(s, s + length, 255, &code)){
|
||||
*out = ansi_to_rgb((unsigned char)code);
|
||||
return COLOR_OK;
|
||||
}
|
||||
|
||||
return COLOR_ERR_FORMAT;
|
||||
}
|
||||
|
||||
const char *color_parse_error_message(ColorParseError error){
|
||||
switch(error){
|
||||
case COLOR_ERR_FORMAT:
|
||||
return "Invalid color format";
|
||||
case COLOR_ERR_HEX:
|
||||
return "Invalid hex color code";
|
||||
case COLOR_ERR_RGB:
|
||||
return "Invalid RGB format. Expected format: R,G,B";
|
||||
case COLOR_ERR_HSL:
|
||||
return "Invalid HSL format. Expected format: hsl(H,S%,L%)";
|
||||
case COLOR_OK:
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
static const char *color_mode_name(color_mode mode){
|
||||
switch(mode){
|
||||
case MODE_TRUECOLOR: return "Truecolor";
|
||||
case MODE_ANSI256: return "Ansi256";
|
||||
case MODE_ANSI16: return "Ansi16";
|
||||
case MODE_NONE:
|
||||
default: return "NoColor";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_env_debug(const char *name){
|
||||
const char *value = getenv(name);
|
||||
if(value)
|
||||
printf("%s: Some(\"%s\")\n", name, value);
|
||||
else
|
||||
printf("%s: None\n", name);
|
||||
}
|
||||
|
||||
void print_color_debug(const colorconfig *config, bool ansi_enabled){
|
||||
printf("Detected color mode: %s\n", color_mode_name(config->mode));
|
||||
printf("ANSI enabled: %s\n", ansi_enabled ? "true" : "false");
|
||||
print_env_debug("NO_COLOR");
|
||||
print_env_debug("COLORTERM");
|
||||
print_env_debug("TERM");
|
||||
#ifdef _WIN32
|
||||
{
|
||||
const char *shell = detect_windows_shell();
|
||||
if(shell)
|
||||
printf("Windows shell hint: Some(\"%s\")\n", shell);
|
||||
else
|
||||
printf("Windows shell hint: None\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
/* this reads one utf81 character from the start of bytes
|
||||
it puts the character's number in *codepoint and returns how many bytes it used (its always 1 or more)
|
||||
invalid input it stores U+FFFD (�) and returns 1, so the caller always moves forward. */
|
||||
static int utf8_decode(const unsigned char *bytes, unsigned long *codepoint){
|
||||
int utf8_decode(const unsigned char *bytes, unsigned long *codepoint){
|
||||
unsigned long value = bytes[0];
|
||||
unsigned long minallowed = 0; //rejects overlong encodings
|
||||
int sequence_length;
|
||||
@@ -48,7 +48,7 @@ static int utf8_decode(const unsigned char *bytes, unsigned long *codepoint){
|
||||
}
|
||||
|
||||
/* this turns the library's return value into a column count so 0 1 or 2 */
|
||||
static int codepoint_width(unsigned long codepoint){
|
||||
int codepoint_width(unsigned long codepoint){
|
||||
int width = widechar_wcwidth(codepoint);
|
||||
if(width > 0)
|
||||
return width;
|
||||
|
||||
+446
-85
@@ -1,12 +1,15 @@
|
||||
#include "../defs/all.h"
|
||||
#include "../defs/mikuart.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char **getmikuart(int style){
|
||||
char **getmikuart(int style, int isteto){
|
||||
if(isteto == 0){
|
||||
if(style < 0)
|
||||
style = rand() % migucount;
|
||||
return mikuart[style];
|
||||
}
|
||||
if(style < 0)
|
||||
style = rand() % migucount;
|
||||
return mikuart[style];
|
||||
style = rand() % tetocount;
|
||||
return tetoart[style];
|
||||
}
|
||||
|
||||
struct win {
|
||||
@@ -18,28 +21,46 @@ static struct win term = {80, 24}; //if the function fails this is the defualt
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
//i have no way to test this because i don't have any windows pc in my house qwq
|
||||
//(i asked claude to write this for windows because i never looked at the windows api in my life)
|
||||
static void getterminalsize(void) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
int found = GetConsoleScreenBufferInfo(console, &info);
|
||||
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
term.winwidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
term.winheight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
if(!found){
|
||||
HANDLE fallback = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if(fallback != INVALID_HANDLE_VALUE){
|
||||
found = GetConsoleScreenBufferInfo(fallback, &info);
|
||||
CloseHandle(fallback);
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
term.winwidth = info.srWindow.Right - info.srWindow.Left + 1;
|
||||
term.winheight = info.srWindow.Bottom - info.srWindow.Top + 1;
|
||||
}
|
||||
}
|
||||
#else //or this if they're not on windows which should be posixish like linux or macos
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static void getterminalsize(void) {
|
||||
struct winsize ws;
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
|
||||
if(ws.ws_col > 0)
|
||||
term.winwidth = ws.ws_col;
|
||||
if(ws.ws_row > 0)
|
||||
term.winheight = ws.ws_row;
|
||||
struct winsize size_info;
|
||||
int tty_descriptor = -1;
|
||||
int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size_info);
|
||||
|
||||
if(status != 0){
|
||||
tty_descriptor = open("/dev/tty", O_RDONLY);
|
||||
if(tty_descriptor >= 0)
|
||||
status = ioctl(tty_descriptor, TIOCGWINSZ, &size_info);
|
||||
}
|
||||
if(status == 0){
|
||||
if(size_info.ws_col > 0) term.winwidth = size_info.ws_col;
|
||||
if(size_info.ws_row > 0) term.winheight = size_info.ws_row;
|
||||
}
|
||||
if(tty_descriptor >= 0)
|
||||
close(tty_descriptor);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -55,6 +76,43 @@ static void freelines(char **lines){
|
||||
free(lines);
|
||||
}
|
||||
|
||||
static char **push_chunk(char **chunks, int *count, const char *start, size_t len){
|
||||
chunks = realloc(chunks, (*count + 2) * sizeof(char *));
|
||||
chunks[*count] = malloc(len + 1);
|
||||
memcpy(chunks[*count], start, len);
|
||||
chunks[*count][len] = '\0';
|
||||
(*count)++;
|
||||
chunks[*count] = NULL;
|
||||
return chunks;
|
||||
}
|
||||
|
||||
char **split_word_by_width(const char *word, int max_width){
|
||||
char **chunks = NULL;
|
||||
int count = 0;
|
||||
size_t length = strlen(word);
|
||||
size_t start = 0, i = 0;
|
||||
int chunk_width = 0;
|
||||
|
||||
while(i < length){
|
||||
unsigned long codepoint;
|
||||
int bytes = utf8_decode((const unsigned char *)word + i, &codepoint);
|
||||
int width = codepoint_width(codepoint);
|
||||
|
||||
if(i > start && chunk_width + width > max_width){
|
||||
chunks = push_chunk(chunks, &count, word + start, i - start);
|
||||
start = i;
|
||||
chunk_width = 0;
|
||||
}
|
||||
chunk_width += width;
|
||||
i += bytes;
|
||||
}
|
||||
|
||||
if(i > start)
|
||||
chunks = push_chunk(chunks, &count, word + start, i - start);
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
char **wraptext(const char *text, int max, int *count){
|
||||
char *copy = strdup(text);
|
||||
char *current_line = calloc(strlen(text)+1, sizeof(char));
|
||||
@@ -69,17 +127,40 @@ char **wraptext(const char *text, int max, int *count){
|
||||
}
|
||||
strcat(current_line, tok);
|
||||
} else{
|
||||
if(current_line[0] != '\0'){
|
||||
lines = realloc(lines, (*count + 2) * sizeof(char *));
|
||||
lines[*count] = strdup(current_line);
|
||||
(*count)++;
|
||||
lines[*count] = NULL;
|
||||
}
|
||||
strcpy(current_line, tok);
|
||||
if(displaywidth(tok) > max){
|
||||
if(current_line[0] != '\0'){
|
||||
lines = realloc(lines, (*count + 2) * sizeof(char *));
|
||||
lines[*count] = strdup(current_line);
|
||||
(*count)++;
|
||||
lines[*count] = NULL;
|
||||
}
|
||||
|
||||
char **chunks = split_word_by_width(tok, max);
|
||||
int n = 0;
|
||||
while(chunks[n] != NULL) n++;
|
||||
|
||||
for(int i = 0; i < n - 1; i++){
|
||||
lines = realloc(lines, (*count + 2) * sizeof(char *));
|
||||
lines[*count] = chunks[i];
|
||||
(*count)++;
|
||||
lines[*count] = NULL;
|
||||
}
|
||||
|
||||
strcpy(current_line, chunks[n - 1]);
|
||||
free(chunks[n - 1]);
|
||||
free(chunks);
|
||||
} else{
|
||||
if(current_line[0] != '\0'){
|
||||
lines = realloc(lines, (*count + 2) * sizeof(char *));
|
||||
lines[*count] = strdup(current_line);
|
||||
(*count)++;
|
||||
lines[*count] = NULL;
|
||||
}
|
||||
strcpy(current_line, tok);
|
||||
}
|
||||
}
|
||||
tok = strtok(NULL, " \t\n");
|
||||
}
|
||||
|
||||
if(current_line[0] != '\0'){
|
||||
lines = realloc(lines, (*count + 2) * sizeof(char *));
|
||||
lines[*count] = strdup(current_line);
|
||||
@@ -96,11 +177,9 @@ char **wraptext(const char *text, int max, int *count){
|
||||
#define MIN_BUBBLE_WIDTH 20
|
||||
char **getbubblelines(char *text){
|
||||
char **templines = wraptext(text, MAX_BUBBLE_WIDTH - 4, &count);
|
||||
if(templines == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t content_width = 0;
|
||||
for (int i = 0; templines[i] != NULL; i++) {
|
||||
for (int i = 0; templines != NULL && templines[i] != NULL; i++) {
|
||||
size_t current_len = displaywidth(templines[i]);
|
||||
if (current_len > content_width)
|
||||
content_width = current_len;
|
||||
@@ -127,7 +206,7 @@ char **getbubblelines(char *text){
|
||||
strcat(bubblelines[0], horizontal);
|
||||
strcat(bubblelines[0], top_right);
|
||||
|
||||
for (int i = 0; lines[i] != NULL; i++) {
|
||||
for (int i = 0; lines != NULL && lines[i] != NULL; i++) {
|
||||
int line_width = displaywidth(lines[i]);
|
||||
int padding_needed = (bubble_width - 2) - line_width;
|
||||
if (padding_needed < 0) padding_needed = 0;
|
||||
@@ -157,31 +236,132 @@ static void print_help(void){
|
||||
"Arguments:\n"
|
||||
" [TEXT] Text to display in the speech bubble\n\n"
|
||||
"Options:\n"
|
||||
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n"
|
||||
" -l, --list List all available art styles with their indices\n"
|
||||
" -h, --help Print help\n"
|
||||
" -V, --version Print version\n");
|
||||
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n"
|
||||
" -l, --list List all available art (miku and teto) styles with their indices\n"
|
||||
" -lm, --listmiku List only the Miku art styles\n"
|
||||
" -lt, --listteto List only the Teto art styles\n"
|
||||
" -t, --teto Use Teto art instead of Miku\n"
|
||||
" --rainbow Apply a smooth rainbow gradient across the ASCII art\n"
|
||||
" --saturation <SATURATION> Saturation level for rainbow gradient (0-100) [default: 100]\n"
|
||||
" --brightness <BRIGHTNESS> Brightness level for rainbow gradient (0-100) [default: 50]\n"
|
||||
" --color <COLOR> Override with a single solid color\n"
|
||||
" --gradient <START:END> Define a custom two-color gradient (e.g., --gradient red:blue)\n"
|
||||
" --no-color Disable all coloring (respects NO_COLOR environment variable)\n"
|
||||
" --color-debug Print color detection/debug info and exit\n"
|
||||
" -h, --help Print help\n"
|
||||
" -V, --version Print version\n");
|
||||
}
|
||||
|
||||
static void list_all_art_styles(void){
|
||||
printf("Available Miku art styles:\n");
|
||||
printf("==========================\n");
|
||||
printf("Total styles: %zu\n", migucount);
|
||||
#define LIST_NONE 0
|
||||
#define LIST_BOTH 1
|
||||
#define LIST_MIKU 2
|
||||
#define LIST_TETO 3
|
||||
|
||||
for(size_t i = 0; i < migucount; i++){
|
||||
printf("\n--- Style %zu ---\n", i);
|
||||
for(int j = 0; mikuart[i][j] != NULL; j++)
|
||||
printf("%s\n", mikuart[i][j]);
|
||||
static void list_art_group(const char *name, char ***art, size_t total){
|
||||
printf("%s styles (total: %zu)\n", name, total);
|
||||
printf("==========================\n");
|
||||
for(size_t style_index = 0; style_index < total; style_index++){
|
||||
printf("\n--- %s style %zu ---\n", name, style_index);
|
||||
for(int line_index = 0; art[style_index][line_index] != NULL; line_index++)
|
||||
printf("%s\n", art[style_index][line_index]);
|
||||
}
|
||||
}
|
||||
|
||||
static void list_all_art_styles(int list_mode){
|
||||
printf("Available art styles:\n\n");
|
||||
|
||||
if(list_mode == LIST_BOTH || list_mode == LIST_MIKU)
|
||||
list_art_group("Miku", mikuart, migucount);
|
||||
if(list_mode == LIST_BOTH)
|
||||
printf("\n");
|
||||
if(list_mode == LIST_BOTH || list_mode == LIST_TETO)
|
||||
list_art_group("Teto", tetoart, tetocount);
|
||||
|
||||
printf("\nUse --style <number> to select a specific style, or omit for random selection.\n");
|
||||
printf("Use --teto to pick from the Teto styles instead of the Miku ones.\n");
|
||||
|
||||
printf("\nColor Options:\n"
|
||||
" --rainbow Apply a smooth rainbow gradient\n"
|
||||
" --saturation <0-100> Set rainbow saturation level (default: 100)\n"
|
||||
" --brightness <0-100> Set rainbow brightness level (default: 50)\n"
|
||||
" --color <COLOR> Set a single solid color (supports named colors, hex, rgb, hsl, ansi)\n"
|
||||
" --gradient <START:END> Define a custom two-color gradient\n"
|
||||
" --no-color Disable all coloring\n"
|
||||
"\nColor Format Examples:\n"
|
||||
" --color red Named color\n"
|
||||
" --color #FF0000 Hex color\n"
|
||||
" --color 255,0,128 RGB tuple\n"
|
||||
" --color hsl(180,100%%,50%%) HSL value\n"
|
||||
" --color 93 ANSI color code\n"
|
||||
" --gradient red:blue Custom gradient\n");
|
||||
}
|
||||
|
||||
void draw_miku_says(char *text, int style){
|
||||
static struct colorconfig colors;
|
||||
|
||||
static int is_art_space(unsigned long codepoint){
|
||||
return (codepoint >= 0x09 && codepoint <= 0x0D) || codepoint == 0x20 || codepoint == 0x85 || codepoint == 0xA0
|
||||
|| codepoint == 0x1680 || (codepoint >= 0x2000 && codepoint <= 0x200A) || codepoint == 0x2028
|
||||
|| codepoint == 0x2029 || codepoint == 0x202F || codepoint == 0x205F || codepoint == 0x3000;
|
||||
}
|
||||
|
||||
static size_t count_colored_chars(char **art){
|
||||
size_t total = 0;
|
||||
for(int i = 0; art[i] != NULL; i++){
|
||||
const unsigned char *p = (const unsigned char *)art[i];
|
||||
while(*p){
|
||||
unsigned long codepoint;
|
||||
p += utf8_decode(p, &codepoint);
|
||||
if(!is_art_space(codepoint)) total++;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static rgbcolor color_for_index(size_t index, size_t total){
|
||||
if(colors.has_gradient){
|
||||
float position = total <= 1 ? 0.0f : (float)index / (float)(total - 1);
|
||||
return lerp_color_hsv(colors.gradient_start, colors.gradient_end, position);
|
||||
}
|
||||
return hsl_to_rgb((unsigned short)((float)index / (float)total * 360.0f + 0.5f),
|
||||
(unsigned char)colors.saturation, (unsigned char)colors.brightness);
|
||||
}
|
||||
|
||||
static void print_art_line(const char *line, size_t *index, size_t total){
|
||||
char escape[32];
|
||||
const unsigned char *cursor = (const unsigned char *)line;
|
||||
int per_character = colors.rainbow_enabled || colors.has_gradient;
|
||||
|
||||
if(colors.mode == MODE_NONE || (!per_character && !colors.has_solid_color)){
|
||||
fputs(line, stdout);
|
||||
return;
|
||||
}
|
||||
if(!per_character){
|
||||
rgb_to_fg_escape(colors.solid_color, colors.mode, escape, sizeof(escape));
|
||||
printf("%s%s\x1b[0m", escape, line);
|
||||
return;
|
||||
}
|
||||
while(*cursor){
|
||||
unsigned long codepoint;
|
||||
int byte_count = utf8_decode(cursor, &codepoint);
|
||||
if(is_art_space(codepoint)){
|
||||
fwrite(cursor, 1, (size_t)byte_count, stdout);
|
||||
}else{
|
||||
rgb_to_fg_escape(color_for_index(*index, total), colors.mode, escape, sizeof(escape));
|
||||
fputs(escape, stdout);
|
||||
fwrite(cursor, 1, (size_t)byte_count, stdout);
|
||||
fputs("\x1b[0m", stdout);
|
||||
(*index)++;
|
||||
}
|
||||
cursor += byte_count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void draw_miku_says(char *text, int style, int teto){
|
||||
char **speech_bubble_lines = getbubblelines(text);
|
||||
if(speech_bubble_lines == NULL)
|
||||
return;
|
||||
char **miku_art = getmikuart(style);
|
||||
char **miku_art = getmikuart(style, teto);
|
||||
|
||||
int available_width = TERMINALWIDTH;
|
||||
int available_height = TERMINALHEIGHT;
|
||||
@@ -255,6 +435,8 @@ void draw_miku_says(char *text, int style){
|
||||
for(int i = 0; i < top_padding; i++){
|
||||
printf("\n");
|
||||
}
|
||||
size_t color_index = 0;
|
||||
size_t colored_total = count_colored_chars(miku_art);
|
||||
|
||||
for(int i = 0; all_content_lines[i] != NULL; i++){
|
||||
int is_bubble_line = i < bubble_line_count;
|
||||
@@ -268,7 +450,14 @@ void draw_miku_says(char *text, int style){
|
||||
else
|
||||
padding = overall_left_padding;
|
||||
|
||||
printf("%*s%s\n", padding, "", all_content_lines[i]);
|
||||
if(is_bubble_line){
|
||||
printf("%*s%s\n", padding, "", all_content_lines[i]);
|
||||
}else{
|
||||
printf("%*s", padding, "");
|
||||
print_art_line(all_content_lines[i], &color_index, colored_total);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(int i = 0; i < top_padding; i++){
|
||||
@@ -278,65 +467,237 @@ void draw_miku_says(char *text, int style){
|
||||
free(all_content_lines);
|
||||
}
|
||||
|
||||
#define COLOR_FORMAT_HELP "expected a named color, #RRGGBB, #RGB, R,G,B, hsl(H,S%%,L%%), or ANSI value 0-255"
|
||||
|
||||
static int usage_error(const char *format, ...){
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
fputs("error: ", stderr);
|
||||
vfprintf(stderr, format, arguments);
|
||||
va_end(arguments);
|
||||
fputs("\n\nUsage: mikusays [OPTIONS] [TEXT]\n\nFor more information, try '--help'.\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
|
||||
//returns 1 if argv[*index] is this option; *value stays NULL when the value is missing
|
||||
static int match_value_option(int argc, char **argv, int *index, const char *long_name, char short_name, const char **value){
|
||||
const char *argument = argv[*index];
|
||||
size_t long_length = strlen(long_name);
|
||||
|
||||
*value = NULL;
|
||||
if(strcmp(argument, long_name) == 0
|
||||
|| (short_name != '\0' && argument[0] == '-' && argument[1] == short_name && argument[2] == '\0')){
|
||||
if(*index + 1 < argc){
|
||||
(*index)++;
|
||||
*value = argv[*index];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if(strncmp(argument, long_name, long_length) == 0 && argument[long_length] == '='){
|
||||
*value = argument + long_length + 1;
|
||||
return 1;
|
||||
}
|
||||
if(short_name != '\0' && argument[0] == '-' && argument[1] == short_name && argument[2] != '\0'){
|
||||
*value = argument + 2;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_percent_option(const char *flag, const char *value, int *destination){
|
||||
char *end;
|
||||
long parsed;
|
||||
|
||||
if(value == NULL)
|
||||
return usage_error("a value is required for '%s' but none was supplied", flag);
|
||||
parsed = strtol(value, &end, 10);
|
||||
if(*value == '\0' || *end != '\0' || parsed < 0 || parsed > 100)
|
||||
return usage_error("invalid value '%s' for '%s'", value, flag);
|
||||
*destination = (int)parsed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_gradient(const char *specification, rgbcolor *start, rgbcolor *end){
|
||||
const char *separator = strchr(specification, ':');
|
||||
char start_text[64];
|
||||
size_t start_length;
|
||||
ColorParseError error;
|
||||
|
||||
if(separator == NULL)
|
||||
return usage_error("invalid gradient: expected START:END (for example, red:blue)");
|
||||
start_length = (size_t)(separator - specification);
|
||||
if(start_length == 0 || separator[1] == '\0' || strchr(separator + 1, ':') != NULL)
|
||||
return usage_error("invalid gradient: expected exactly two colors as START:END");
|
||||
if(start_length >= sizeof(start_text))
|
||||
return usage_error("invalid gradient start: %s; " COLOR_FORMAT_HELP,
|
||||
color_parse_error_message(COLOR_ERR_FORMAT));
|
||||
|
||||
memcpy(start_text, specification, start_length);
|
||||
start_text[start_length] = '\0';
|
||||
error = parse_color(start_text, start);
|
||||
if(error != COLOR_OK)
|
||||
return usage_error("invalid gradient start '%s': %s; " COLOR_FORMAT_HELP,
|
||||
start_text, color_parse_error_message(error));
|
||||
error = parse_color(separator + 1, end);
|
||||
if(error != COLOR_OK)
|
||||
return usage_error("invalid gradient end '%s': %s; " COLOR_FORMAT_HELP,
|
||||
separator + 1, color_parse_error_message(error));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
Void(); //look at all.h
|
||||
long style = -1; //-1 means random
|
||||
int index, result = 0, list_requested = 0, color_debug = 0;
|
||||
#ifdef _WIN32
|
||||
int no_color = 0;
|
||||
#endif
|
||||
int has_text = 0, options_ended = 0, ansi_enabled = 0;
|
||||
const char *value, *color_text = NULL, *gradient_text = NULL;
|
||||
ColorParseError color_error;
|
||||
size_t length = 0, art_total;
|
||||
char *text;
|
||||
int areyouteto = 0;
|
||||
|
||||
srand(time(NULL));
|
||||
getterminalsize();
|
||||
colorconfig_init(&colors);
|
||||
|
||||
long style = -1; //-1 means random
|
||||
int list = 0;
|
||||
int ret = 0;
|
||||
|
||||
size_t length = 0;
|
||||
for(int i = 1; i < argc; i++)
|
||||
length += strlen(argv[i]);
|
||||
char *text = malloc(length + argc);
|
||||
for(index = 1; index < argc; index++)
|
||||
length += strlen(argv[index]);
|
||||
text = malloc(length + (size_t)argc);
|
||||
if(!text) return 1;
|
||||
*text = '\0';
|
||||
|
||||
for(int i = 1; i < argc; i++){
|
||||
if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0){
|
||||
for(index = 1; index < argc; index++){
|
||||
const char *argument = argv[index];
|
||||
|
||||
if(!options_ended && strcmp(argument, "--") == 0){
|
||||
options_ended = 1;
|
||||
} else if(options_ended || argument[0] != '-' || argument[1] == '\0'){
|
||||
strcat(text, argument);
|
||||
strcat(text, " ");
|
||||
has_text = 1;
|
||||
} else if(strcmp(argument, "-h") == 0 || strcmp(argument, "--help") == 0){
|
||||
print_help();
|
||||
goto out;
|
||||
} else if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0){
|
||||
} else if(strcmp(argument, "-V") == 0 || strcmp(argument, "--version") == 0){
|
||||
printf("mikusays " VERSION "\n");
|
||||
goto out;
|
||||
} else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--list") == 0){
|
||||
list = 1;
|
||||
} else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--style") == 0){
|
||||
if(i + 1 >= argc){
|
||||
fprintf(stderr, "error: a value is required for '--style <STYLE>' but none was supplied\n");
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
i++;
|
||||
} else if(strcmp(argument, "-l") == 0 || strcmp(argument, "--list") == 0){
|
||||
list_requested = LIST_BOTH;
|
||||
} else if(strcmp(argument, "-lm") == 0 || strcmp(argument, "--listmiku") == 0){
|
||||
list_requested = LIST_MIKU;
|
||||
} else if(strcmp(argument, "-lt") == 0 || strcmp(argument, "--listteto") == 0){
|
||||
list_requested = LIST_TETO;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if(strcmp(argument, "--no-color") == 0){
|
||||
no_color = 1;
|
||||
colors.mode = MODE_NONE;
|
||||
}
|
||||
#endif
|
||||
else if(strcmp(argument, "--rainbow") == 0){
|
||||
colors.rainbow_enabled = true;
|
||||
} else if(strcmp(argument, "--color-debug") == 0){
|
||||
color_debug = 1;
|
||||
} else if(strcmp(argument, "--teto") == 0 || strcmp(argument, "-t") == 0){
|
||||
areyouteto = 1;
|
||||
}else if(match_value_option(argc, argv, &index, "--style", 's', &value)){
|
||||
char *end;
|
||||
long v = strtol(argv[i], &end, 10);
|
||||
if(*argv[i] == '\0' || *end != '\0'){
|
||||
fprintf(stderr, "error: invalid value '%s' for '--style <STYLE>': not a number\n", argv[i]);
|
||||
ret = 2;
|
||||
long parsed;
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--style <STYLE>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
style = v < 0 ? -1 : v; //negative = random
|
||||
parsed = strtol(value, &end, 10);
|
||||
if(*value == '\0' || *end != '\0'){
|
||||
result = usage_error("invalid value '%s' for '--style <STYLE>': invalid digit found in string", value);
|
||||
goto out;
|
||||
}
|
||||
if(parsed < 0){
|
||||
result = usage_error("invalid value '%s' for '--style <STYLE>': %ld is not in 0..2147483647", value, parsed);
|
||||
goto out;
|
||||
}
|
||||
style = parsed;
|
||||
} else if(match_value_option(argc, argv, &index, "--color", '\0', &value)){
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--color <COLOR>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
color_text = value;
|
||||
} else if(match_value_option(argc, argv, &index, "--gradient", '\0', &value)){
|
||||
if(value == NULL){
|
||||
result = usage_error("a value is required for '--gradient <START:END>' but none was supplied");
|
||||
goto out;
|
||||
}
|
||||
gradient_text = value;
|
||||
} else if(match_value_option(argc, argv, &index, "--saturation", '\0', &value)){
|
||||
result = parse_percent_option("--saturation <SATURATION>", value, &colors.saturation);
|
||||
if(result) goto out;
|
||||
} else if(match_value_option(argc, argv, &index, "--brightness", '\0', &value)){
|
||||
result = parse_percent_option("--brightness <BRIGHTNESS>", value, &colors.brightness);
|
||||
if(result) goto out;
|
||||
} else {
|
||||
strcat(text, argv[i]);
|
||||
strcat(text, " ");
|
||||
result = usage_error("unexpected argument '%s' found", argument);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(list){
|
||||
list_all_art_styles();
|
||||
} else if(text[0] == '\0'){
|
||||
fprintf(stderr, "Error: Text is required when not using --list\n");
|
||||
ret = 1;
|
||||
} else if(style >= (long)migucount){
|
||||
fprintf(stderr, "Style %ld does not exist! Choose one between 0 and %zu\n", style, migucount - 1);
|
||||
ret = 1;
|
||||
} else {
|
||||
draw_miku_says(text, (int)style);
|
||||
if(colors.rainbow_enabled && color_text){
|
||||
result = usage_error("the argument '--rainbow' cannot be used with '--color <COLOR>'");
|
||||
goto out;
|
||||
}
|
||||
//i didn't know you could do labels like in assembly (nasm) this is very useful knowledge
|
||||
if(colors.rainbow_enabled && gradient_text){
|
||||
result = usage_error("the argument '--rainbow' cannot be used with '--gradient <START:END>'");
|
||||
goto out;
|
||||
}
|
||||
if(color_text && gradient_text){
|
||||
result = usage_error("the argument '--color <COLOR>' cannot be used with '--gradient <START:END>'");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(list_requested){
|
||||
list_all_art_styles(list_requested);
|
||||
goto out;
|
||||
}
|
||||
if(!has_text){
|
||||
fprintf(stderr, "Error: Text is required when not using --list\n");
|
||||
result = 1;
|
||||
goto out;
|
||||
}
|
||||
art_total = areyouteto ? tetocount : migucount;
|
||||
if(style >= (long)art_total){
|
||||
result = usage_error("style %ld does not exist for %s; choose a value between 0 and %lu",
|
||||
style, areyouteto ? "Teto" : "Miku", (unsigned long)(art_total - 1));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(color_text){
|
||||
color_error = parse_color(color_text, &colors.solid_color);
|
||||
if(color_error != COLOR_OK){
|
||||
result = usage_error("invalid color '%s': %s; " COLOR_FORMAT_HELP,
|
||||
color_text, color_parse_error_message(color_error));
|
||||
goto out;
|
||||
}
|
||||
colors.has_solid_color = true;
|
||||
}
|
||||
if(gradient_text){
|
||||
result = parse_gradient(gradient_text, &colors.gradient_start, &colors.gradient_end);
|
||||
if(result) goto out;
|
||||
colors.has_gradient = true;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
ansi_enabled = !no_color && enable_windows_ansi();
|
||||
#endif
|
||||
if(color_debug){
|
||||
print_color_debug(&colors, ansi_enabled != 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
draw_miku_says(text, (int)style, (int)areyouteto);
|
||||
//i didn't know you could do labels like in assembly (nasm) this is very useful knowledge
|
||||
out:
|
||||
free(text);
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
Reference in new issue
Block a user