From 11d87f16a5ec190c2eb0a155f37d80f184146e69 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Tue, 21 Apr 2026 02:12:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E5=A0=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 2 +- CMakeLists.txt | 2 +- cleonos/c/apps/bmpview_main.c | 411 ++++++++++++ cleonos/c/apps/help_main.c | 2 + cleonos/c/apps/qrcode/qrcodegen.c | 1039 +++++++++++++++++++++++++++++ cleonos/c/apps/qrcode/qrcodegen.h | 385 +++++++++++ cleonos/c/apps/qrcode_main.c | 265 ++++++++ cleonos/c/apps/shell/shell_cmd.c | 2 + cleonos/third-party/doomgeneric | 2 +- clks/kernel/runtime/exec.c | 98 +++ ramdisk/1/1.bmp | Bin 0 -> 147606 bytes 11 files changed, 2205 insertions(+), 3 deletions(-) create mode 100644 cleonos/c/apps/bmpview_main.c create mode 100644 cleonos/c/apps/qrcode/qrcodegen.c create mode 100644 cleonos/c/apps/qrcode/qrcodegen.h create mode 100644 cleonos/c/apps/qrcode_main.c create mode 100644 ramdisk/1/1.bmp diff --git a/.gitmodules b/.gitmodules index 957a45a..ae44d21 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/limine-bootloader/limine.git [submodule "cleonos/third-party/doomgeneric"] path = cleonos/third-party/doomgeneric - url = https://github.com/ozkl/doomgeneric.git + url = https://github.com/Leonmmcoset/doom.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 534769e..29fffd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,7 +624,7 @@ set(USER_SHELL_COMMAND_APPS bg fg jobs kill ps top procstat sysstat shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat - shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg + shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg bmpview qrcode ) foreach(SRC IN LISTS USER_APP_MAIN_SOURCES) diff --git a/cleonos/c/apps/bmpview_main.c b/cleonos/c/apps/bmpview_main.c new file mode 100644 index 0000000..2d7543e --- /dev/null +++ b/cleonos/c/apps/bmpview_main.c @@ -0,0 +1,411 @@ +#include "cmd_runtime.h" +#include + +#define USH_BMPVIEW_DEFAULT_COLS 80ULL +#define USH_BMPVIEW_MAX_COLS 160ULL +#define USH_BMPVIEW_MAX_ROWS 120ULL +#define USH_BMPVIEW_MAX_IMAGE_WIDTH 4096ULL +#define USH_BMPVIEW_MAX_IMAGE_HEIGHT 4096ULL +#define USH_BMPVIEW_MAX_ROW_BYTES 16384U + +typedef struct ush_bmpview_info { + u64 width; + u64 height; + u64 bytes_per_pixel; + u64 row_stride; + u64 pixel_offset; + int top_down; +} ush_bmpview_info; + +static unsigned char ush_bmpview_row_buf[USH_BMPVIEW_MAX_ROW_BYTES]; +static char ush_bmpview_render[USH_BMPVIEW_MAX_ROWS][USH_BMPVIEW_MAX_COLS + 1U]; +static u64 ush_bmpview_sample_rows[USH_BMPVIEW_MAX_ROWS]; + +static unsigned int ush_bmpview_le16(const unsigned char *ptr) { + return (unsigned int)ptr[0] | ((unsigned int)ptr[1] << 8U); +} + +static unsigned int ush_bmpview_le32(const unsigned char *ptr) { + return (unsigned int)ptr[0] | ((unsigned int)ptr[1] << 8U) | ((unsigned int)ptr[2] << 16U) | + ((unsigned int)ptr[3] << 24U); +} + +static int ush_bmpview_le32s(const unsigned char *ptr) { + return (int)((unsigned int)ush_bmpview_le32(ptr)); +} + +static int ush_bmpview_read_exact(u64 fd, unsigned char *out, u64 size) { + u64 done = 0ULL; + + if (out == (unsigned char *)0 || size == 0ULL) { + return 0; + } + + while (done < size) { + u64 got = cleonos_sys_fd_read(fd, out + done, size - done); + + if (got == (u64)-1 || got == 0ULL) { + return 0; + } + + done += got; + } + + return 1; +} + +static int ush_bmpview_skip_bytes(u64 fd, u64 size) { + unsigned char scratch[256]; + u64 left = size; + + while (left > 0ULL) { + u64 want = left; + u64 got; + + if (want > (u64)sizeof(scratch)) { + want = (u64)sizeof(scratch); + } + + got = cleonos_sys_fd_read(fd, scratch, want); + + if (got == (u64)-1 || got == 0ULL) { + return 0; + } + + left -= got; + } + + return 1; +} + +static int ush_bmpview_parse_header(u64 fd, ush_bmpview_info *out_info) { + unsigned char header[54]; + unsigned int dib_size; + int width_s; + int height_s; + unsigned int planes; + unsigned int bits_per_pixel; + unsigned int compression; + u64 width_u; + u64 height_u; + u64 row_stride; + u64 bytes_per_pixel; + + if (out_info == (ush_bmpview_info *)0) { + return 0; + } + + if (ush_bmpview_read_exact(fd, header, (u64)sizeof(header)) == 0) { + return 0; + } + + if (header[0] != (unsigned char)'B' || header[1] != (unsigned char)'M') { + return 0; + } + + dib_size = ush_bmpview_le32(&header[14]); + if (dib_size < 40U) { + return 0; + } + + width_s = ush_bmpview_le32s(&header[18]); + height_s = ush_bmpview_le32s(&header[22]); + planes = ush_bmpview_le16(&header[26]); + bits_per_pixel = ush_bmpview_le16(&header[28]); + compression = ush_bmpview_le32(&header[30]); + + if (planes != 1U) { + return 0; + } + + if (width_s <= 0 || height_s == 0) { + return 0; + } + + if (bits_per_pixel != 24U && bits_per_pixel != 32U) { + return 0; + } + + if (compression != 0U) { + return 0; + } + + width_u = (u64)(unsigned int)width_s; + if (height_s < 0) { + i64 signed_h = (i64)height_s; + height_u = (u64)(-signed_h); + out_info->top_down = 1; + } else { + height_u = (u64)(unsigned int)height_s; + out_info->top_down = 0; + } + + if (width_u == 0ULL || height_u == 0ULL) { + return 0; + } + + if (width_u > USH_BMPVIEW_MAX_IMAGE_WIDTH || height_u > USH_BMPVIEW_MAX_IMAGE_HEIGHT) { + return 0; + } + + bytes_per_pixel = (u64)(bits_per_pixel / 8U); + row_stride = (width_u * bytes_per_pixel + 3ULL) & ~3ULL; + + if (row_stride == 0ULL || row_stride > (u64)USH_BMPVIEW_MAX_ROW_BYTES) { + return 0; + } + + out_info->pixel_offset = (u64)ush_bmpview_le32(&header[10]); + if (out_info->pixel_offset < (u64)sizeof(header)) { + return 0; + } + + out_info->width = width_u; + out_info->height = height_u; + out_info->bytes_per_pixel = bytes_per_pixel; + out_info->row_stride = row_stride; + return 1; +} + +static char ush_bmpview_luma_char(unsigned int r, unsigned int g, unsigned int b) { + static const char ramp[] = " .:-=+*#%@"; + unsigned int luma = (77U * r + 150U * g + 29U * b) >> 8U; + unsigned int idx = (luma * 9U) / 255U; + return ramp[idx]; +} + +static int ush_bmpview_parse_args(const char *arg, char *out_path, u64 out_path_size, u64 *out_cols) { + char first[USH_PATH_MAX]; + char second[32]; + const char *rest = ""; + const char *rest2 = ""; + + if (out_path == (char *)0 || out_cols == (u64 *)0 || out_path_size == 0ULL) { + return 0; + } + + *out_cols = USH_BMPVIEW_DEFAULT_COLS; + out_path[0] = '\0'; + + if (arg == (const char *)0 || arg[0] == '\0') { + return 0; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + if (ush_streq(first, "--help") != 0 || ush_streq(first, "-h") != 0) { + return 2; + } + + ush_copy(out_path, out_path_size, first); + + if (rest != (const char *)0 && rest[0] != '\0') { + if (ush_split_first_and_rest(rest, second, (u64)sizeof(second), &rest2) == 0) { + return 0; + } + + if (ush_parse_u64_dec(second, out_cols) == 0 || *out_cols == 0ULL) { + return 0; + } + + if (rest2 != (const char *)0 && rest2[0] != '\0') { + return 0; + } + } + + return 1; +} + +static int ush_cmd_bmpview(const ush_state *sh, const char *arg) { + ush_bmpview_info info; + char path_arg[USH_PATH_MAX]; + char abs_path[USH_PATH_MAX]; + u64 cols = USH_BMPVIEW_DEFAULT_COLS; + u64 out_w; + u64 out_h; + u64 file_row; + u64 fd; + int parse_ret; + + if (sh == (const ush_state *)0) { + return 0; + } + + parse_ret = ush_bmpview_parse_args(arg, path_arg, (u64)sizeof(path_arg), &cols); + if (parse_ret == 2) { + ush_writeln("usage: bmpview [cols]"); + ush_writeln("note: supports uncompressed 24/32-bit BMP"); + return 1; + } + + if (parse_ret == 0) { + ush_writeln("bmpview: usage bmpview [cols]"); + return 0; + } + + if (ush_resolve_path(sh, path_arg, abs_path, (u64)sizeof(abs_path)) == 0) { + ush_writeln("bmpview: invalid path"); + return 0; + } + + if (cleonos_sys_fs_stat_type(abs_path) != 1ULL) { + ush_writeln("bmpview: file not found"); + return 0; + } + + fd = cleonos_sys_fd_open(abs_path, CLEONOS_O_RDONLY, 0ULL); + if (fd == (u64)-1) { + ush_writeln("bmpview: open failed"); + return 0; + } + + ush_zero(&info, (u64)sizeof(info)); + if (ush_bmpview_parse_header(fd, &info) == 0) { + (void)cleonos_sys_fd_close(fd); + ush_writeln("bmpview: unsupported or invalid bmp"); + return 0; + } + + if (info.pixel_offset > 54ULL) { + if (ush_bmpview_skip_bytes(fd, info.pixel_offset - 54ULL) == 0) { + (void)cleonos_sys_fd_close(fd); + ush_writeln("bmpview: failed to seek pixel data"); + return 0; + } + } + + if (cols > USH_BMPVIEW_MAX_COLS) { + cols = USH_BMPVIEW_MAX_COLS; + } + if (cols > info.width) { + cols = info.width; + } + if (cols == 0ULL) { + cols = 1ULL; + } + + out_w = cols; + out_h = (info.height * out_w) / (info.width * 2ULL); + if (out_h == 0ULL) { + out_h = 1ULL; + } + if (out_h > USH_BMPVIEW_MAX_ROWS) { + out_h = USH_BMPVIEW_MAX_ROWS; + } + if (out_h > info.height) { + out_h = info.height; + } + + { + u64 oy; + u64 ox; + + for (oy = 0ULL; oy < out_h; oy++) { + ush_bmpview_sample_rows[oy] = (oy * info.height) / out_h; + for (ox = 0ULL; ox < out_w; ox++) { + ush_bmpview_render[oy][ox] = ' '; + } + ush_bmpview_render[oy][out_w] = '\0'; + } + } + + for (file_row = 0ULL; file_row < info.height; file_row++) { + u64 display_row; + u64 oy; + + if (ush_bmpview_read_exact(fd, ush_bmpview_row_buf, info.row_stride) == 0) { + (void)cleonos_sys_fd_close(fd); + ush_writeln("bmpview: read pixel data failed"); + return 0; + } + + if (info.top_down != 0) { + display_row = file_row; + } else { + display_row = (info.height - 1ULL) - file_row; + } + + for (oy = 0ULL; oy < out_h; oy++) { + u64 ox; + + if (ush_bmpview_sample_rows[oy] != display_row) { + continue; + } + + for (ox = 0ULL; ox < out_w; ox++) { + u64 src_x = (ox * info.width) / out_w; + u64 pix_off = src_x * info.bytes_per_pixel; + unsigned int b; + unsigned int g; + unsigned int r; + + if (pix_off + 2ULL >= info.row_stride) { + ush_bmpview_render[oy][ox] = ' '; + continue; + } + + b = (unsigned int)ush_bmpview_row_buf[pix_off + 0ULL]; + g = (unsigned int)ush_bmpview_row_buf[pix_off + 1ULL]; + r = (unsigned int)ush_bmpview_row_buf[pix_off + 2ULL]; + ush_bmpview_render[oy][ox] = ush_bmpview_luma_char(r, g, b); + } + } + } + + (void)cleonos_sys_fd_close(fd); + + { + u64 oy; + for (oy = 0ULL; oy < out_h; oy++) { + ush_writeln(ush_bmpview_render[oy]); + } + } + + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "bmpview") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_bmpview(&sh, arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index b4806d3..e4ba85d 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -13,6 +13,8 @@ static int ush_cmd_help(void) { ush_writeln(" exec|run [args...]"); ush_writeln(" clear"); ush_writeln(" ansi / ansitest / color"); + ush_writeln(" bmpview [cols]"); + ush_writeln(" qrcode [--ecc ] "); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); diff --git a/cleonos/c/apps/qrcode/qrcodegen.c b/cleonos/c/apps/qrcode/qrcodegen.c new file mode 100644 index 0000000..981dbf9 --- /dev/null +++ b/cleonos/c/apps/qrcode/qrcodegen.c @@ -0,0 +1,1039 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#ifndef NDEBUG + #define NDEBUG 1 +#endif +#include +#include +#include +#include +#include "qrcodegen.h" + +#ifndef QRCODEGEN_TEST + #define testable static // Keep functions private +#else + #define testable // Expose private functions +#endif + + +/*---- Forward declarations for private functions ----*/ + +// Regarding all public and private functions defined in this source file: +// - They require all pointer/array arguments to be not null unless the array length is zero. +// - They only read input scalar/array arguments, write to output pointer/array +// arguments, and return scalar values; they are "pure" functions. +// - They don't read mutable global variables or write to any global variables. +// - They don't perform I/O, read the clock, print to console, etc. +// - They allocate a small and constant amount of stack memory. +// - They don't allocate or free any memory on the heap. +// - They don't recurse or mutually recurse. All the code +// could be inlined into the top-level public functions. +// - They run in at most quadratic time with respect to input arguments. +// Most functions run in linear time, and some in constant time. +// There are no unbounded loops or non-obvious termination conditions. +// - They are completely thread-safe if the caller does not give the +// same writable buffer to concurrent calls to these functions. + +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); + +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +testable int getNumRawDataModules(int ver); + +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]); +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]); +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); + +testable void initializeFunctionModules(int version, uint8_t qrcode[]); +static void drawLightFunctionModules(uint8_t qrcode[], int version); +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); +testable int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); + +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); +static long getPenaltyScore(const uint8_t qrcode[]); +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); +static void zeroRunHistory(int runHistory[7]); + +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark); +static bool getBit(int x, int i); + +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); +static int numCharCountBits(enum qrcodegen_Mode mode, int version); + + + +/*---- Private tables of constants ----*/ + +// The set of all legal characters in alphanumeric mode, where each character +// value maps to the index in the string. For checking text and encoding segments. +static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +// Sentinel value for use in only some functions. +#define LENGTH_OVERFLOW -1 + +// For generating error correction codes. +testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above + +// For generating error correction codes. +testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +// For automatic mask pattern selection. +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; +static const int PENALTY_N3 = 40; +static const int PENALTY_N4 = 10; + + + +/*---- High-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + size_t textLen = strlen(text); + if (textLen == 0) + return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + + struct qrcodegen_Segment seg; + if (qrcodegen_isNumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeNumeric(text, tempBuffer); + } else if (qrcodegen_isAlphanumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeAlphanumeric(text, tempBuffer); + } else { + if (textLen > bufLen) + goto fail; + for (size_t i = 0; i < textLen; i++) + tempBuffer[i] = (uint8_t)text[i]; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, textLen); + if (seg.bitLength == LENGTH_OVERFLOW) + goto fail; + seg.numChars = (int)textLen; + seg.data = tempBuffer; + } + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + +fail: + qrcode[0] = 0; // Set size to invalid value for safety + return false; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); + if (seg.bitLength == LENGTH_OVERFLOW) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + seg.numChars = (int)dataLen; + seg.data = dataAndTemp; + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); +} + + +// Appends the given number of low-order bits of the given value to the given byte-based +// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { + assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); + for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) + buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); +} + + + +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != LENGTH_OVERFLOW && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != LENGTH_OVERFLOW); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) { + int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1; + appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen); + } + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Compute ECC, draw modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawLightFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Do masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + applyMask(tempBuffer, qrcode, msk); + drawFormatBits(ecl, msk, qrcode); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + applyMask(tempBuffer, qrcode, mask); // Apply the final choice of mask + drawFormatBits(ecl, mask, qrcode); // Overwrite old format bits + return true; +} + + + +/*---- Error correction code generation functions ----*/ + +// Appends error correction bytes to each block of the given data array, then interleaves +// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains +// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will +// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = getNumDataCodewords(version, ecl); + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence + uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX]; + reedSolomonComputeDivisor(blockEccLen, rsdiv); + const uint8_t *dat = data; + for (int i = 0; i < numBlocks; i++) { + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + uint8_t *ecc = &data[dataLen]; // Temporary storage + reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc); + for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data + if (j == shortBlockDataLen) + k -= numShortBlocks; + result[k] = dat[j]; + } + for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC + result[k] = ecc[j]; + dat += datLen; + } +} + + +// Returns the number of 8-bit codewords that can be used for storing data (not ECC), +// for the given version number and error correction level. The result is in the range [9, 2956]. +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { + int v = version, e = (int)ecl; + assert(0 <= e && e < 4); + return getNumRawDataModules(v) / 8 + - ECC_CODEWORDS_PER_BLOCK [e][v] + * NUM_ERROR_CORRECTION_BLOCKS[e][v]; +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. +// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +testable int getNumRawDataModules(int ver) { + assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + assert(208 <= result && result <= 29648); + return result; +} + + + +/*---- Reed-Solomon ECC generator functions ----*/ + +// Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree]. +// This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm. +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + memset(result, 0, (size_t)degree * sizeof(result[0])); + result[degree - 1] = 1; // Start off with the monomial x^0 + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < degree; j++) { + result[j] = reedSolomonMultiply(result[j], root); + if (j + 1 < degree) + result[j] ^= result[j + 1]; + } + root = reedSolomonMultiply(root, 0x02); + } +} + + +// Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials. +// The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree]. +// All polynomials are in big endian, and the generator has an implicit leading 1 term. +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]) { + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + memset(result, 0, (size_t)degree * sizeof(result[0])); + for (int i = 0; i < dataLen; i++) { // Polynomial division + uint8_t factor = data[i] ^ result[0]; + memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0])); + result[degree - 1] = 0; + for (int j = 0; j < degree; j++) + result[j] ^= reedSolomonMultiply(generator[j], factor); + } +} + +#undef qrcodegen_REED_SOLOMON_DEGREE_MAX + + +// Returns the product of the two given field elements modulo GF(2^8/0x11D). +// All inputs are valid. This could be implemented as a 256*256 lookup table. +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + uint8_t z = 0; + for (int i = 7; i >= 0; i--) { + z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D)); + z ^= ((y >> i) & 1) * x; + } + return z; +} + + + +/*---- Drawing function modules ----*/ + +// Clears the given QR Code grid with light modules for the given +// version's size, then marks every function module as dark. +testable void initializeFunctionModules(int version, uint8_t qrcode[]) { + // Initialize QR Code + int qrsize = version * 4 + 17; + memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + qrcode[0] = (uint8_t)qrsize; + + // Fill horizontal and vertical timing patterns + fillRectangle(6, 0, 1, qrsize, qrcode); + fillRectangle(0, 6, qrsize, 1, qrcode); + + // Fill 3 finder patterns (all corners except bottom right) and format bits + fillRectangle(0, 0, 9, 9, qrcode); + fillRectangle(qrsize - 8, 0, 8, 9, qrcode); + fillRectangle(0, qrsize - 8, 9, 8, qrcode); + + // Fill numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); + } + } + + // Fill version blocks + if (version >= 7) { + fillRectangle(qrsize - 11, 0, 3, 6, qrcode); + fillRectangle(0, qrsize - 11, 6, 3, qrcode); + } +} + + +// Draws light function modules and possibly some dark modules onto the given QR Code, without changing +// non-function modules. This does not draw the format bits. This requires all function modules to be previously +// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. +static void drawLightFunctionModules(uint8_t qrcode[], int version) { + // Draw horizontal and vertical timing patterns + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 7; i < qrsize - 7; i += 2) { + setModuleBounded(qrcode, 6, i, false); + setModuleBounded(qrcode, i, 6, false); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = abs(dx); + if (abs(dy) > dist) + dist = abs(dy); + if (dist == 2 || dist == 4) { + setModuleUnbounded(qrcode, 3 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, qrsize - 4 + dx, 3 + dy, false); + setModuleUnbounded(qrcode, 3 + dx, qrsize - 4 + dy, false); + } + } + } + + // Draw numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Don't draw on the three finder corners + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) + setModuleBounded(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); + } + } + } + + // Draw version blocks + if (version >= 7) { + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = (long)version << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + int k = qrsize - 11 + j; + setModuleBounded(qrcode, k, i, (bits & 1) != 0); + setModuleBounded(qrcode, i, k, (bits & 1) != 0); + bits >>= 1; + } + } + } +} + + +// Draws two copies of the format bits (with its own error correction code) based +// on the given mask and error correction level. This always draws all modules of +// the format bits, unlike drawLightFunctionModules() which might skip dark modules. +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { + // Calculate error correction code and pack bits + assert(0 <= (int)mask && (int)mask <= 7); + static const int table[] = {1, 0, 3, 2}; + int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setModuleBounded(qrcode, 8, i, getBit(bits, i)); + setModuleBounded(qrcode, 8, 7, getBit(bits, 6)); + setModuleBounded(qrcode, 8, 8, getBit(bits, 7)); + setModuleBounded(qrcode, 7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setModuleBounded(qrcode, 14 - i, 8, getBit(bits, i)); + + // Draw second copy + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 0; i < 8; i++) + setModuleBounded(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setModuleBounded(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); + setModuleBounded(qrcode, 8, qrsize - 8, true); // Always dark +} + + +// Calculates and stores an ascending list of positions of alignment patterns +// for this version number, returning the length of the list (in the range [0,7]). +// Each position is in the range [0,177), and are used on both the x and y axes. +// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. +testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { + if (version == 1) + return 0; + int numAlign = version / 7 + 2; + int step = (version * 8 + numAlign * 3 + 5) / (numAlign * 4 - 4) * 2; + for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) + result[i] = (uint8_t)pos; + result[0] = 6; + return numAlign; +} + + +// Sets every module in the range [left : left + width] * [top : top + height] to dark. +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { + for (int dy = 0; dy < height; dy++) { + for (int dx = 0; dx < width; dx++) + setModuleBounded(qrcode, left + dx, top + dy, true); + } +} + + + +/*---- Drawing data modules and masking ----*/ + +// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of +// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < qrsize; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate + if (!getModuleBounded(qrcode, x, y) && i < dataLen * 8) { + bool dark = getBit(data[i >> 3], 7 - (i & 7)); + setModuleBounded(qrcode, x, y, dark); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i == dataLen * 8); +} + + +// XORs the codeword modules in this QR Code with the given mask pattern +// and given pattern of function modules. The codeword bits must be drawn +// before masking. Due to the arithmetic of XOR, calling applyMask() with +// the same mask value a second time will undo the mask. A final well-formed +// QR Code needs exactly one (not zero, two, etc.) mask applied. +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { + assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO + int qrsize = qrcodegen_getSize(qrcode); + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(functionModules, x, y)) + continue; + bool invert; + switch ((int)mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: assert(false); return; + } + bool val = getModuleBounded(qrcode, x, y); + setModuleBounded(qrcode, x, y, val ^ invert); + } + } +} + + +// Calculates and returns the penalty score based on state of the given QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +static long getPenaltyScore(const uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < qrsize; y++) { + bool runColor = false; + int runX = 0; + int runHistory[7]; + zeroRunHistory(runHistory); + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + finderPenaltyAddHistory(runX, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runX = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < qrsize; x++) { + bool runColor = false; + int runY = 0; + int runHistory[7]; + zeroRunHistory(runHistory); + for (int y = 0; y < qrsize; y++) { + if (getModuleBounded(qrcode, x, y) == runColor) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + finderPenaltyAddHistory(runY, runHistory, qrsize); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; + runColor = getModuleBounded(qrcode, x, y); + runY = 1; + } + } + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < qrsize - 1; y++) { + for (int x = 0; x < qrsize - 1; x++) { + bool color = getModuleBounded(qrcode, x, y); + if ( color == getModuleBounded(qrcode, x + 1, y) && + color == getModuleBounded(qrcode, x, y + 1) && + color == getModuleBounded(qrcode, x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of dark and light modules + int dark = 0; + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModuleBounded(qrcode, x, y)) + dark++; + } + } + int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; + assert(0 <= k && k <= 9); + result += k * PENALTY_N4; + assert(0 <= result && result <= 2568888L); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; +} + + +// Can only be called immediately after a light run is added, and +// returns either 0, 1, or 2. A helper function for getPenaltyScore(). +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { + int n = runHistory[1]; + assert(n <= qrsize * 3); (void)qrsize; + bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; + // The maximum QR Code size is 177, hence the dark run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); +} + + +// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { + if (currentRunColor) { // Terminate dark run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + currentRunLength = 0; + } + currentRunLength += qrsize; // Add light border to final run + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); + return finderPenaltyCountPatterns(runHistory, qrsize); +} + + +// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { + if (runHistory[0] == 0) + currentRunLength += qrsize; // Add light border to initial run + memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); + runHistory[0] = currentRunLength; +} + + +static void zeroRunHistory(int runHistory[7]) { + for (int i = 0; i < 7; i++) + runHistory[i] = 0; +} + + + +/*---- Basic QR Code information ----*/ + +// Public function - see documentation comment in header file. +int qrcodegen_getSize(const uint8_t qrcode[]) { + assert(qrcode != NULL); + int result = qrcode[0]; + assert((qrcodegen_VERSION_MIN * 4 + 17) <= result + && result <= (qrcodegen_VERSION_MAX * 4 + 17)); + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { + assert(qrcode != NULL); + int qrsize = qrcode[0]; + return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModuleBounded(qrcode, x, y); +} + + +// Returns the color of the module at the given coordinates, which must be in bounds. +testable bool getModuleBounded(const uint8_t qrcode[], int x, int y) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + return getBit(qrcode[(index >> 3) + 1], index & 7); +} + + +// Sets the color of the module at the given coordinates, which must be in bounds. +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + if (isDark) + qrcode[byteIndex] |= 1 << bitIndex; + else + qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; +} + + +// Sets the color of the module at the given coordinates, doing nothing if out of bounds. +testable void setModuleUnbounded(uint8_t qrcode[], int x, int y, bool isDark) { + int qrsize = qrcode[0]; + if (0 <= x && x < qrsize && 0 <= y && y < qrsize) + setModuleBounded(qrcode, x, y, isDark); +} + + +// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. +static bool getBit(int x, int i) { + return ((x >> i) & 1) != 0; +} + + + +/*---- Segment handling ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_isNumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (*text < '0' || *text > '9') + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_isAlphanumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { + int temp = calcSegmentBitLength(mode, numChars); + if (temp == LENGTH_OVERFLOW) + return SIZE_MAX; + assert(0 <= temp && temp <= INT16_MAX); + return ((size_t)temp + 7) / 8; +} + + +// Returns the number of data bits needed to represent a segment +// containing the given number of characters using the given mode. Notes: +// - Returns LENGTH_OVERFLOW on failure, i.e. numChars > INT16_MAX +// or the number of needed bits exceeds INT16_MAX (i.e. 32767). +// - Otherwise, all valid results are in the range [0, INT16_MAX]. +// - For byte mode, numChars measures the number of bytes, not Unicode code points. +// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. +// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { + // All calculations are designed to avoid overflow on all platforms + if (numChars > (unsigned int)INT16_MAX) + return LENGTH_OVERFLOW; + long result = (long)numChars; + if (mode == qrcodegen_Mode_NUMERIC) + result = (result * 10 + 2) / 3; // ceil(10/3 * n) + else if (mode == qrcodegen_Mode_ALPHANUMERIC) + result = (result * 11 + 1) / 2; // ceil(11/2 * n) + else if (mode == qrcodegen_Mode_BYTE) + result *= 8; + else if (mode == qrcodegen_Mode_KANJI) + result *= 13; + else if (mode == qrcodegen_Mode_ECI && numChars == 0) + result = 3 * 8; + else { // Invalid argument + assert(false); + return LENGTH_OVERFLOW; + } + assert(result >= 0); + if (result > INT16_MAX) + return LENGTH_OVERFLOW; + return (int)result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { + assert(data != NULL || len == 0); + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_BYTE; + result.bitLength = calcSegmentBitLength(result.mode, len); + assert(result.bitLength != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (len > 0) + memcpy(buf, data, len * sizeof(buf[0])); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + assert(digits != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (unsigned int)(c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + assert(text != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != LENGTH_OVERFLOW); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_ECI; + result.numChars = 0; + result.bitLength = 0; + if (assignVal < 0) + assert(false); + else if (assignVal < (1 << 7)) { + memset(buf, 0, 1 * sizeof(buf[0])); + appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength); + } else if (assignVal < (1 << 14)) { + memset(buf, 0, 2 * sizeof(buf[0])); + appendBitsToBuffer(2, 2, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength); + } else if (assignVal < 1000000L) { + memset(buf, 0, 3 * sizeof(buf[0])); + appendBitsToBuffer(6, 3, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength); + } else + assert(false); + result.data = buf; + return result; +} + + +// Calculates the number of bits needed to encode the given segments at the given version. +// Returns a non-negative number if successful. Otherwise returns LENGTH_OVERFLOW if a segment +// has too many characters to fit its length field, or the total bits exceeds INT16_MAX. +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { + assert(segs != NULL || len == 0); + long result = 0; + for (size_t i = 0; i < len; i++) { + int numChars = segs[i].numChars; + int bitLength = segs[i].bitLength; + assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= bitLength && bitLength <= INT16_MAX); + int ccbits = numCharCountBits(segs[i].mode, version); + assert(0 <= ccbits && ccbits <= 16); + if (numChars >= (1L << ccbits)) + return LENGTH_OVERFLOW; // The segment's length doesn't fit the field's bit width + result += 4L + ccbits + bitLength; + if (result > INT16_MAX) + return LENGTH_OVERFLOW; // The sum might overflow an int type + } + assert(0 <= result && result <= INT16_MAX); + return (int)result; +} + + +// Returns the bit width of the character count field for a segment in the given mode +// in a QR Code at the given version number. The result is in the range [0, 16]. +static int numCharCountBits(enum qrcodegen_Mode mode, int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int i = (version + 7) / 17; + switch (mode) { + case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } + case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } + case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } + case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } + case qrcodegen_Mode_ECI : return 0; + default: assert(false); return -1; // Dummy value + } +} + + +#undef LENGTH_OVERFLOW diff --git a/cleonos/c/apps/qrcode/qrcodegen.h b/cleonos/c/apps/qrcode/qrcodegen.h new file mode 100644 index 0000000..6bbc157 --- /dev/null +++ b/cleonos/c/apps/qrcode/qrcodegen.h @@ -0,0 +1,385 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * This library creates QR Code symbols, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * A QR Code structure is an immutable square grid of dark and light cells. + * The library provides functions to create a QR Code from text or binary data. + * The library covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). + * - Low level: Custom-make the list of segments and call + * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). + * (Note that all ways require supplying the desired error correction level and various byte buffers.) + */ + + +/*---- Enum and struct types----*/ + +/* + * The error correction level in a QR Code symbol. + */ +enum qrcodegen_Ecc { + // Must be declared in ascending order of error protection + // so that an internal qrcodegen function works properly + qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords +}; + + +/* + * The mask pattern used in a QR Code symbol. + */ +enum qrcodegen_Mask { + // A special value to tell the QR Code encoder to + // automatically select an appropriate mask pattern + qrcodegen_Mask_AUTO = -1, + // The eight actual mask patterns + qrcodegen_Mask_0 = 0, + qrcodegen_Mask_1, + qrcodegen_Mask_2, + qrcodegen_Mask_3, + qrcodegen_Mask_4, + qrcodegen_Mask_5, + qrcodegen_Mask_6, + qrcodegen_Mask_7, +}; + + +/* + * Describes how a segment's data bits are interpreted. + */ +enum qrcodegen_Mode { + qrcodegen_Mode_NUMERIC = 0x1, + qrcodegen_Mode_ALPHANUMERIC = 0x2, + qrcodegen_Mode_BYTE = 0x4, + qrcodegen_Mode_KANJI = 0x8, + qrcodegen_Mode_ECI = 0x7, +}; + + +/* + * A segment of character/binary/control data in a QR Code symbol. + * The mid-level way to create a segment is to take the payload data + * and call a factory function such as qrcodegen_makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and initialize a qrcodegen_Segment struct with appropriate values. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * Moreover, the maximum allowed bit length is 32767 because + * the largest QR Code (version 40) has 31329 modules. + */ +struct qrcodegen_Segment { + // The mode indicator of this segment. + enum qrcodegen_Mode mode; + + // The length of this segment's unencoded data. Measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. Not the same as the data's bit length. + int numChars; + + // The data bits of this segment, packed in bitwise big endian. + // Can be null if the bit length is zero. + uint8_t *data; + + // The number of valid data bits used in the buffer. Requires + // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + // The character count (numChars) must agree with the mode and the bit buffer length. + int bitLength; +}; + + + +/*---- Macro constants and functions ----*/ + +#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard +#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard + +// Calculates the number of bytes needed to store any QR Code up to and including the given version number, +// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' +// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). +// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. +#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) + +// The worst-case number of bytes needed to store one QR Code, up to and including +// version 40. This value equals 3918, which is just under 4 kilobytes. +// Use this more convenient value to avoid calculating tighter memory bounds for buffers. +#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) + + + +/*---- Functions (high level) to generate QR Codes ----*/ + +/* + * Encodes the given text string to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * The input text must be encoded in UTF-8 and contain no NULs. + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code may use numeric, + * alphanumeric, or byte mode to encode the text. + * + * In the most optimistic case, a QR Code at version 40 with low ECC + * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string + * up to 4296 characters, or any digit string up to 7089 characters. + * These numbers represent the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Encodes the given binary data to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion): + * - Before calling the function: + * - The array ranges dataAndTemp[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The input array range dataAndTemp[0 : dataLen] should normally be + * valid UTF-8 text, but is not required by the QR Code standard. + * - The initial state of dataAndTemp[dataLen : len] and qrcode[0 : len] + * can be uninitialized because the function always writes before reading. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - dataAndTemp contains no useful data and should be treated as entirely uninitialized. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * If successful, the resulting QR Code will use byte mode to encode the data. + * + * In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte + * sequence up to length 2953. This is the hard upper limit of the QR Code standard. + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version at the given ECC level, + * then false is returned. + * + * The smallest possible QR Code version is automatically chosen for + * the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Encodes the given segments to a QR Code, returning true if successful. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * + * Requires 1 <= minVersion <= maxVersion <= 40. + * + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). + * + * About the byte arrays, letting len = qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX): + * - Before calling the function: + * - The array ranges tempBuffer[0 : len] and qrcode[0 : len] must allow + * reading and writing; hence each array must have a length of at least len. + * - The two ranges must not overlap (aliasing). + * - The initial state of both ranges can be uninitialized + * because the function always writes before reading. + * - The input array segs can contain segments whose data buffers overlap with tempBuffer. + * - After the function returns: + * - Both ranges have no guarantee on which elements are initialized and what values are stored. + * - tempBuffer contains no useful data and should be treated as entirely uninitialized. + * - Any segment whose data buffer overlaps with tempBuffer[0 : len] + * must be treated as having invalid values in that array. + * - If successful, qrcode can be passed into qrcodegen_getSize() and qrcodegen_getModule(). + * + * Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + * + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ +bool qrcodegen_isNumeric(const char *text); + + +/* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +bool qrcodegen_isAlphanumeric(const char *text); + + +/* + * Returns the number of bytes (uint8_t) needed for the data buffer of a segment + * containing the given number of characters using the given mode. Notes: + * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or the internal + * calculation of the number of needed bits exceeds INT16_MAX (i.e. 32767). + * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. + * - It is okay for the user to allocate more bytes for the buffer than needed. + * - For byte mode, numChars measures the number of bytes, not Unicode code points. + * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. + * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. + */ +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); + + +/* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); + + +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + +/* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); + + +/*---- Functions to extract raw data from QR Codes ----*/ + +/* + * Returns the side length of the given QR Code, assuming that encoding succeeded. + * The result is in the range [21, 177]. Note that the length of the array buffer + * is related to the side length - every 'uint8_t qrcode[]' must have length at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). + */ +int qrcodegen_getSize(const uint8_t qrcode[]); + + +/* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. + */ +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); + + +#ifdef __cplusplus +} +#endif diff --git a/cleonos/c/apps/qrcode_main.c b/cleonos/c/apps/qrcode_main.c new file mode 100644 index 0000000..b08b298 --- /dev/null +++ b/cleonos/c/apps/qrcode_main.c @@ -0,0 +1,265 @@ +#include "cmd_runtime.h" +#include "qrcode/qrcodegen.h" + +#define USH_QRCODE_MAX_TEXT 640U +#define USH_QRCODE_MAX_VERSION 15 +#define USH_QRCODE_BORDER 2 + +static int ush_qrcode_streq_ignore_case(const char *left, const char *right) { + u64 i = 0ULL; + + if (left == (const char *)0 || right == (const char *)0) { + return 0; + } + + while (left[i] != '\0' && right[i] != '\0') { + char lc = left[i]; + char rc = right[i]; + + if (lc >= 'a' && lc <= 'z') { + lc = (char)(lc - ('a' - 'A')); + } + + if (rc >= 'a' && rc <= 'z') { + rc = (char)(rc - ('a' - 'A')); + } + + if (lc != rc) { + return 0; + } + + i++; + } + + return (left[i] == '\0' && right[i] == '\0') ? 1 : 0; +} + +static int ush_qrcode_parse_ecc(const char *text, enum qrcodegen_Ecc *out_ecc) { + if (text == (const char *)0 || out_ecc == (enum qrcodegen_Ecc *)0) { + return 0; + } + + if (ush_qrcode_streq_ignore_case(text, "L") != 0 || ush_qrcode_streq_ignore_case(text, "LOW") != 0) { + *out_ecc = qrcodegen_Ecc_LOW; + return 1; + } + + if (ush_qrcode_streq_ignore_case(text, "M") != 0 || ush_qrcode_streq_ignore_case(text, "MEDIUM") != 0) { + *out_ecc = qrcodegen_Ecc_MEDIUM; + return 1; + } + + if (ush_qrcode_streq_ignore_case(text, "Q") != 0 || ush_qrcode_streq_ignore_case(text, "QUARTILE") != 0) { + *out_ecc = qrcodegen_Ecc_QUARTILE; + return 1; + } + + if (ush_qrcode_streq_ignore_case(text, "H") != 0 || ush_qrcode_streq_ignore_case(text, "HIGH") != 0) { + *out_ecc = qrcodegen_Ecc_HIGH; + return 1; + } + + return 0; +} + +static void ush_qrcode_usage(void) { + ush_writeln("usage: qrcode [--ecc ] "); + ush_writeln(" qrcode --help"); + ush_writeln("note: pipeline input supported when omitted"); +} + +/* return: 0 fail, 1 ok, 2 help */ +static int ush_qrcode_parse_args(const char *arg, char *out_text, u64 out_text_size, enum qrcodegen_Ecc *out_ecc) { + char first[USH_PATH_MAX]; + char second[USH_PATH_MAX]; + const char *rest = ""; + const char *rest2 = ""; + + if (out_text == (char *)0 || out_text_size == 0ULL || out_ecc == (enum qrcodegen_Ecc *)0) { + return 0; + } + + *out_ecc = qrcodegen_Ecc_LOW; + out_text[0] = '\0'; + + if (arg == (const char *)0 || arg[0] == '\0') { + if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') { + if (ush_pipeline_stdin_len + 1ULL > out_text_size) { + return 0; + } + + ush_copy(out_text, out_text_size, ush_pipeline_stdin_text); + return 1; + } + + return 0; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + if (ush_streq(first, "--help") != 0 || ush_streq(first, "-h") != 0) { + return 2; + } + + if (ush_streq(first, "--ecc") != 0) { + if (ush_split_first_and_rest(rest, second, (u64)sizeof(second), &rest2) == 0) { + return 0; + } + + if (ush_qrcode_parse_ecc(second, out_ecc) == 0) { + return 0; + } + + if (rest2 == (const char *)0 || rest2[0] == '\0') { + if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') { + if (ush_pipeline_stdin_len + 1ULL > out_text_size) { + return 0; + } + + ush_copy(out_text, out_text_size, ush_pipeline_stdin_text); + return 1; + } + + return 0; + } + + ush_copy(out_text, out_text_size, rest2); + return 1; + } + + if (first[0] == '-' && first[1] == '-') { + const char *prefix = "--ecc="; + u64 i = 0ULL; + int match = 1; + + while (prefix[i] != '\0') { + if (first[i] != prefix[i]) { + match = 0; + break; + } + + i++; + } + + if (match != 0) { + if (first[i] == '\0') { + return 0; + } + + if (ush_qrcode_parse_ecc(&first[i], out_ecc) == 0) { + return 0; + } + + if (rest == (const char *)0 || rest[0] == '\0') { + if (ush_pipeline_stdin_text != (const char *)0 && ush_pipeline_stdin_text[0] != '\0') { + if (ush_pipeline_stdin_len + 1ULL > out_text_size) { + return 0; + } + + ush_copy(out_text, out_text_size, ush_pipeline_stdin_text); + return 1; + } + + return 0; + } + + ush_copy(out_text, out_text_size, rest); + return 1; + } + } + + ush_copy(out_text, out_text_size, arg); + return 1; +} + +static void ush_qrcode_emit_ascii(const uint8_t qrcode[]) { + int size = qrcodegen_getSize(qrcode); + int y; + int x; + + for (y = -USH_QRCODE_BORDER; y < size + USH_QRCODE_BORDER; y++) { + for (x = -USH_QRCODE_BORDER; x < size + USH_QRCODE_BORDER; x++) { + int dark = (x >= 0 && y >= 0 && x < size && y < size && qrcodegen_getModule(qrcode, x, y)) ? 1 : 0; + ush_write(dark != 0 ? "##" : " "); + } + + ush_write_char('\n'); + } +} + +static int ush_cmd_qrcode(const char *arg) { + char text[USH_QRCODE_MAX_TEXT]; + enum qrcodegen_Ecc ecc; + uint8_t qrcode[qrcodegen_BUFFER_LEN_FOR_VERSION(USH_QRCODE_MAX_VERSION)]; + uint8_t temp[qrcodegen_BUFFER_LEN_FOR_VERSION(USH_QRCODE_MAX_VERSION)]; + int parse_ret; + int ok; + + parse_ret = ush_qrcode_parse_args(arg, text, (u64)sizeof(text), &ecc); + if (parse_ret == 2) { + ush_qrcode_usage(); + return 1; + } + + if (parse_ret == 0 || text[0] == '\0') { + ush_qrcode_usage(); + return 0; + } + + ok = qrcodegen_encodeText(text, temp, qrcode, ecc, qrcodegen_VERSION_MIN, USH_QRCODE_MAX_VERSION, + qrcodegen_Mask_AUTO, true); + + if (ok == 0) { + ush_writeln("qrcode: encode failed (input too long or invalid)"); + return 0; + } + + ush_qrcode_emit_ascii(qrcode); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "qrcode") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_qrcode(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index ae3708a..c10411c 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -135,6 +135,8 @@ static int ush_cmd_help(void) { ush_writeln(" exec|run [args...]"); ush_writeln(" clear"); ush_writeln(" ansi / ansitest / color"); + ush_writeln(" bmpview [cols]"); + ush_writeln(" qrcode [--ecc ] "); ush_writeln(" wavplay [steps] [ticks] / wavplay --stop"); ush_writeln(" fastfetch [--plain]"); ush_writeln(" doom [wad_path] (framebuffer bootstrap renderer)"); diff --git a/cleonos/third-party/doomgeneric b/cleonos/third-party/doomgeneric index dcb7a8d..64d0633 160000 --- a/cleonos/third-party/doomgeneric +++ b/cleonos/third-party/doomgeneric @@ -1 +1 @@ -Subproject commit dcb7a8dbc7a16ce3dda29382ac9aae9d77d21284 +Subproject commit 64d06339fc218c4597e966537a7eb5e59008067b diff --git a/clks/kernel/runtime/exec.c b/clks/kernel/runtime/exec.c index 6c97534..24debe4 100644 --- a/clks/kernel/runtime/exec.c +++ b/clks/kernel/runtime/exec.c @@ -366,6 +366,47 @@ static clks_bool clks_exec_translate_legacy_user_rip(u64 rip, u64 *out_rip) { return CLKS_TRUE; } +static clks_bool clks_exec_translate_loaded_user_rip_to_vaddr(u64 rip, u64 *out_rip) { + i32 depth_index; + u64 vaddr_begin; + u64 vaddr_end; + u64 image_begin; + u64 image_end; + u64 off; + u64 translated; + + if (out_rip == CLKS_NULL || clks_exec_pid_stack_depth == 0U) { + return CLKS_FALSE; + } + + depth_index = (i32)(clks_exec_pid_stack_depth - 1U); + vaddr_begin = clks_exec_image_vaddr_begin_stack[(u32)depth_index]; + vaddr_end = clks_exec_image_vaddr_end_stack[(u32)depth_index]; + image_begin = clks_exec_image_begin_stack[(u32)depth_index]; + image_end = clks_exec_image_end_stack[(u32)depth_index]; + + if (vaddr_begin == 0ULL || vaddr_end <= vaddr_begin || image_begin == 0ULL || image_end <= image_begin) { + return CLKS_FALSE; + } + + if (rip < image_begin || rip >= image_end) { + return CLKS_FALSE; + } + + off = rip - image_begin; + if (off >= (vaddr_end - vaddr_begin)) { + return CLKS_FALSE; + } + + translated = vaddr_begin + off; + if (translated < vaddr_begin || translated >= vaddr_end) { + return CLKS_FALSE; + } + + *out_rip = translated; + return CLKS_TRUE; +} + static void clks_exec_copy_path(char *dst, usize dst_size, const char *src) { usize i = 0U; @@ -795,6 +836,41 @@ static u64 clks_exec_encode_signal_status(u64 signal, u64 vector, u64 error_code ((error_code & 0xFFFFULL) << 16); } +static clks_bool clks_exec_status_is_signal(u64 status) { + return ((status & CLKS_EXEC_STATUS_SIGNAL_FLAG) != 0ULL) ? CLKS_TRUE : CLKS_FALSE; +} + +static u64 clks_exec_status_signal(u64 status) { + return status & 0xFFULL; +} + +static u64 clks_exec_status_vector(u64 status) { + return (status >> 8) & 0xFFULL; +} + +static u64 clks_exec_status_error(u64 status) { + return (status >> 16) & 0xFFFFULL; +} + +static clks_bool clks_exec_status_is_user_terminated(u64 status) { + u64 signal; + + if (clks_exec_status_is_signal(status) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_exec_status_vector(status) != 0ULL || clks_exec_status_error(status) != 0ULL) { + return CLKS_FALSE; + } + + signal = clks_exec_status_signal(status); + if (signal == CLKS_EXEC_SIGNAL_TERM || signal == CLKS_EXEC_SIGNAL_KILL || signal == CLKS_EXEC_SIGNAL_STOP) { + return CLKS_TRUE; + } + + return CLKS_FALSE; +} + static i32 clks_exec_proc_find_slot_by_pid(u64 pid) { u32 i; @@ -1387,6 +1463,9 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { clks_exec_log_info_serial(proc->path); clks_exec_log_hex_serial("ENTRY", info.entry); clks_exec_log_hex_serial("PHNUM", (u64)info.phnum); + clks_exec_log_hex_serial("IMAGE_BASE", (u64)loaded.image_base); + clks_exec_log_hex_serial("IMAGE_SIZE", loaded.image_size); + clks_exec_log_hex_serial("VADDR_BASE", loaded.image_vaddr_base); clks_exec_running_depth++; depth_counted = CLKS_TRUE; @@ -1409,6 +1488,12 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { clks_exec_log_info_serial("RUN RETURNED"); clks_exec_log_info_serial(proc->path); clks_exec_log_hex_serial("RET", run_ret); + if (clks_exec_status_is_user_terminated(run_ret) == CLKS_TRUE) { + clks_exec_log_info_serial("TERMINATED BY USER"); + clks_exec_log_info_serial(proc->path); + clks_exec_log_hex_serial("PID", proc->pid); + clks_exec_log_hex_serial("SIGNAL", clks_exec_status_signal(run_ret)); + } clks_exec_success++; if (clks_exec_stop_requested_stack[(u32)depth_index] == CLKS_TRUE) { @@ -2364,6 +2449,13 @@ u64 clks_exec_proc_kill(u64 pid, u64 signal) { if (proc->state == CLKS_EXEC_PROC_PENDING || proc->state == CLKS_EXEC_PROC_STOPPED) { clks_exec_proc_mark_exited(proc, now_tick, status); + if (effective_signal == CLKS_EXEC_SIGNAL_TERM || effective_signal == CLKS_EXEC_SIGNAL_KILL || + effective_signal == CLKS_EXEC_SIGNAL_STOP) { + clks_exec_log_info_serial("TERMINATED BY USER"); + clks_exec_log_info_serial(proc->path); + clks_exec_log_hex_serial("PID", proc->pid); + clks_exec_log_hex_serial("SIGNAL", effective_signal); + } return 1ULL; } @@ -2544,6 +2636,12 @@ clks_bool clks_exec_handle_exception(u64 vector, u64 error_code, u64 rip, u64 *i clks_exec_log_hex_serial("VECTOR", vector); clks_exec_log_hex_serial("ERROR", error_code); clks_exec_log_hex_serial("RIP", rip); + { + u64 rip_vaddr = 0ULL; + if (clks_exec_translate_loaded_user_rip_to_vaddr(rip, &rip_vaddr) == CLKS_TRUE) { + clks_exec_log_hex_serial("RIP_VADDR", rip_vaddr); + } + } #if defined(CLKS_ARCH_X86_64) if (vector == 14ULL) { clks_exec_log_hex_serial("CR2", clks_exec_read_cr2()); diff --git a/ramdisk/1/1.bmp b/ramdisk/1/1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..1371de3893a99a4cecefa408c9781c0f79d5b21c GIT binary patch literal 147606 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